diff --git a/artifacts/config_files/Translations.ini b/artifacts/config_files/Translations.ini index 018c25042..5221ebe4e 100644 --- a/artifacts/config_files/Translations.ini +++ b/artifacts/config_files/Translations.ini @@ -1,5 +1,5 @@ [sfall] -SaveInCombat=Cannot save at this time +SaveInCombat=Cannot save at this time. KarmaGain=You gained %d karma. KarmaLoss=You lost %d karma. HighlightFail1=You aren't carrying a motion sensor. diff --git a/artifacts/config_files/npcarmor.ini b/artifacts/config_files/npcarmor.ini index 7bb98a82c..dca0babbc 100644 --- a/artifacts/config_files/npcarmor.ini +++ b/artifacts/config_files/npcarmor.ini @@ -19,6 +19,7 @@ Robe=16777218 ; Sulik [1] PID=16777313 +WeaponAnims=1,3,4,6 Default=16777280 Leather=16777325 Power=16777324 @@ -29,6 +30,7 @@ Combat=16777322 ; Vic [2] PID=16777278 +WeaponAnims=1,5,7 Default=16777307 Jacket=16777329 Combat=16777330 @@ -39,6 +41,7 @@ Leather=16777333 ; Cassidy [3] PID=16777305 +WeaponAnims=4,5,7 Default=16777354 Leather=16777260 Power=16777328 @@ -49,6 +52,7 @@ Combat=16777326 ; Myron [4] PID=16777376 +WeaponAnims=1,5 Default=16777304 Leather= Power=16777349 @@ -57,6 +61,7 @@ Combat=16777350 ; Cat Jules [5] PID=16777734 +WeaponAnims=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 Default=16777353 Leather=16777347 Metal=16777348 @@ -66,6 +71,7 @@ Combat=16777226 ; Kitsune [6] PID=16777724 +WeaponAnims=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 Default=16777222 Leather=16777221 Metal=16777223 diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index f7da10dd0..6b50a5c5a 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -1,5 +1,5 @@ ;sfall configuration settings -;v4.0.4 +;v4.0.5 [Main] ;Change to 1 if you want to use command line args to tell sfall to use another ini file. @@ -107,7 +107,7 @@ SpeedModKey=-1 ;A key to press to toggle the speed tweak on or off ;Specify 0 if you don't want a toggle key, or a DX scancode otherwise -SpeedToggleKey=0x00 +SpeedToggleKey=0 ;The keys corresponding to the 10 speed slots ;Set to 0 to disable a slot, otherwise specify the DX scancode of the key you want to use @@ -128,9 +128,15 @@ SpeedKey9=0x00 WindowScrollKey=0 ;A key to press to reload your currently equipped weapon +;Set to 0 if you don't want a reload key, or a DX scancode otherwise ReloadWeaponKey=0 -;A key to press to open a debug game editor +;A key to hold down to let you move/drop a whole stack of items at once without the 'Move Items' window +;Set to 0 if you don't want to use a modifier key, or a DX scancode otherwise +ItemFastMoveKey=0x1d + +;A key to press to open a debug game editor (requires FalloutClient.exe from the modders pack) +;Set to 0 to disable, or a DX scancode otherwise DebugEditorKey=0 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -164,7 +170,7 @@ WorldMapEncounterRate=5 ;The number of slots available in the locations list panel of the world map ;Set to 0 to leave unchanged. 17 is default. -;Setting this greater than 17 requires a replacement background frm, or you'll get glitched graphics +;Setting this greater than 17 requires a replacement background FRM, or you'll get glitched graphics WorldMapSlots=0 ;To start a new game somewhere other than artemple.map, uncomment the next line and set it to the map you want to load @@ -288,7 +294,7 @@ SaveInCombatFix=1 ;Player's level is capped once the highest specified level is reached ;XPTable=50,100,200 -;Set to 1 to enable additional weapon animations codes from o-t +;Set to 1 to enable additional weapon animation codes from o-t ;The 4 byte value at 0x39 of weapon protos may range from 0 to 15 rather than 0 to 10 ;Since the letters 'n' and 'r' are in use for other animations, an animation code of 11 corrisponds to 's' and 15 to 't' AdditionalWeaponAnims=1 @@ -344,7 +350,10 @@ NPCsTryToSpendExtraAP=0 ;Set to 2 if you provide a XLtiles.lst file in art/tiles/ containing a list of the tile ids that need checking AllowLargeTiles=0 -;Change the Skilldex cursor frm numbers +;Set to 1 to boost the maximum number of tile FRMs from 4096 to 16383 +MoreTiles=0 + +;Change the Skilldex cursor FRM numbers ;Default is 293 for all skills Lockpick=293 Steal=293 @@ -379,7 +388,7 @@ ExtraSaveSlots=0 ;Set to 2 to update the HP/AC counters instantly SpeedInterfaceCounterAnims=0 -;These lines allow you to control the karma frm's displayed on the character screen +;These lines allow you to control the karma FRMs displayed on the character screen ;Number of KarmaPoints should be 1 less than number of KarmaFRMs ;KarmaFRMs=47,48,49 ;KarmaPoints=-100,100 @@ -401,6 +410,9 @@ BonusHtHDamageFix=1 ;Set to 1 to display additional points of damage from Bonus HtH/Ranged Damage perks in the inventory DisplayBonusDamage=0 +;Set to 1 to display the range of the second attack mode in the inventory when you switch weapon modes in active item slots +DisplaySecondWeaponRange=1 + ;Modify the maximum number of animations allowed to run on a map. (Default is 32, and the maximum is 127) AnimationsAtOnceLimit=120 @@ -523,6 +535,9 @@ StackEmptyWeapons=0 ;If the amount of ammo boxes in the inventory is less than or equal to the reserve, only one box will be used ReloadReserve=-1 +;Set to 1 to change the counter in the 'Move Items' window to start at the maximum number of items +ItemCounterDefaultMax=0 + ;Allows 9 options (lines of text) to be displayed correctly in a dialog window DialogOptions9Lines=1 @@ -548,6 +563,9 @@ InstantWeaponEquip=0 ;Set to 1 to display numbered dialogue options NumbersInDialogue=0 +;Set to 1 to use Fallout's normal text font instead of DOS-like font on the world map +WorldMapFontPatch=0 + ;Set to 1 to display sfall built-in credits at the bottom of credits.txt contents instead of at the top CreditsAtBottom=0 diff --git a/artifacts/mods/gl_npcarmor.int b/artifacts/mods/gl_npcarmor.int index 3006723a0..c6cfde80a 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 f73298e5e..d6476dd32 100644 --- a/artifacts/mods/gl_npcarmor.ssl +++ b/artifacts/mods/gl_npcarmor.ssl @@ -4,18 +4,19 @@ Used to replace the scripted part of B-Team mod. Appropriate graphics are required for this mod to work. - Can be adopted to any mod by adjusting armor PIDs, NPC PIDs and NPC FIDs in INI file. + Can be adopted to any mod by adjusting armor PIDs, allowed weapon anim codes, NPC PIDs and NPC FIDs in INI file. NOTE: this script requires compiler from sfall modderspack with -s option (short circuit evaluation) - version 1.0 + version 1.1 */ #include "main.h" #define IS_ARMOR(item) (obj_type(item) == OBJ_TYPE_ITEM and obj_item_subtype(item) == item_type_armor) +#define IS_WEAPON(item) (obj_type(item) == OBJ_TYPE_ITEM and obj_item_subtype(item) == item_type_weapon) variable modIni := "npcarmor.ini", @@ -41,9 +42,28 @@ procedure check_armor_change(variable critter, variable item, variable isWorn) b return -1; end -// for NPCs when they change armor themselves +procedure check_weapon_change(variable critter, variable item, variable isWield) begin + variable npc, newWeaponAnim, weaponAnimList, i; + if (npcMap[obj_pid(critter)]) then begin + npc := npcMap[obj_pid(critter)]; + if isWield then begin + newWeaponAnim := get_proto_data(obj_pid(item), PROTO_WP_ANIM); + weaponAnimList := string_split(npc["WeaponAnims"], ","); + if newWeaponAnim then begin + foreach (i in weaponAnimList) begin + if (newWeaponAnim == atoi(i)) then return -1; + end + end else begin // anim code 0 - none/unarmed + return -1; + end + end + end + return 0; +end + +// for NPCs when they change armor/weapon themselves procedure invenwield_handler begin - variable critter, item, fid, slot, isWorn; + variable critter, item, fid, slot, isWorn, canWield; critter := get_sfall_arg; item := get_sfall_arg; slot := get_sfall_arg; @@ -55,6 +75,11 @@ procedure invenwield_handler begin art_change_fid_num(critter, fid); end end + + if (critter and item and (slot == INVEN_TYPE_RIGHT_HAND or slot == INVEN_TYPE_LEFT_HAND)) then begin + canWield := check_weapon_change(critter, item, isWorn); + set_sfall_return(canWield); + end end // when changing armor from inventory while controlling other NPCs @@ -69,12 +94,26 @@ procedure adjustfid_handler begin end end +// when changing weapon from inventory while controlling other NPCs +procedure inventorymove_handler begin + variable slot, item, canWield; + slot := get_sfall_arg; + item := get_sfall_arg; + if (dude_obj != real_dude_obj) then begin + if (IS_WEAPON(item) and (slot == INVEN_TYPE_RIGHT_HAND or slot == INVEN_TYPE_LEFT_HAND)) then begin + canWield := check_weapon_change(dude_obj, item, item != 0); + set_sfall_return(canWield); + end + end +end + procedure start begin variable sect, sects, armorTypes, armorType, npc, pid, pids, i; if game_loaded and (sfall_ver_major >= 4) then begin register_hook_proc(HOOK_INVENWIELD, invenwield_handler); register_hook_proc(HOOK_ADJUSTFID, adjustfid_handler); + register_hook_proc(HOOK_INVENTORYMOVE, inventorymove_handler); defaultFids := get_ini_section(modIni, "Default"); fix_array(defaultFids); @@ -97,6 +136,7 @@ procedure start begin sect := get_ini_section(modIni, ""+i); while (sect.PID) do begin npc := create_array_map; + npc["WeaponAnims"] := sect["WeaponAnims"]; npc["Default"] := atoi(sect["Default"]); foreach (armorType: pids in armorTypes) begin if (sect[armorType]) then begin diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 6114f980f..7a50d7dc7 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -51,6 +51,7 @@ #define HOOK_CARTRAVEL (28) #define HOOK_SETGLOBALVAR (29) #define HOOK_RESTTIMER (30) +#define HOOK_GAMEMODECHANGE (31) //Valid arguments to list_begin #define LIST_CRITTERS (0) @@ -138,6 +139,7 @@ #define set_attack_explosion_radius(x) metarule2_explosions(3, x, 0) #define set_attack_is_explosion(x) metarule2_explosions(4, x, 0) #define set_attack_is_explosion_fire set_attack_is_explosion(DMG_fire) +#define set_explosion_radius(grenade, rocket) metarule2_explosions(5, grenade, rocket) #define GAME_MSG_COMBAT (0) @@ -240,6 +242,7 @@ #define set_cursor_mode(mode) sfall_func1("set_cursor_mode", mode) #define set_dude_obj(critter) sfall_func1("set_dude_obj", critter) #define set_flags(obj, flags) sfall_func2("set_flags", obj, flags) +#define set_iface_tag_text(tag, text, color) sfall_func3("set_iface_tag_text", tag, text, color) #define set_ini_setting(setting, value) sfall_func2("set_ini_setting", setting, value) #define set_map_enter_position(tile, elev, rot) sfall_func3("set_map_enter_position", tile, elev, rot) #define set_outline(obj, color) sfall_func2("set_outline", obj, color) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 3f8a53304..44d6033e2 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -189,7 +189,7 @@ Critter arg1 - The critter that just died HOOK_FINDTARGET (hs_findtarget.int) -Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. Use sfall_return to give the 4 targets, in order of preference. All 4 must be given if you want to override normal sorting; if you want to specify less than 4 targets fill in the extra spaces with 0's. If you do not give 4 targets, the npcs normal sorting mechanism will be used. +Runs when the ai is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the ai caps of the attacker. This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way. Use sfall_return to give the 4 targets, in order of preference. All 4 must be given if you want to override normal sorting; if you want to specify less than 4 targets fill in the extra spaces with 0's. If you do not give 4 targets, the NPCs normal sorting mechanism will be used. The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries. @@ -312,7 +312,7 @@ int arg2 - The default max damage Item arg3 - The weapon used. (0 if unarmed) Critter arg4 - The critter doing the attacking int arg5 - The type of attack -int arg6 - non zero if this is an attack using a melee weapon +int arg6 - non-zero if this is an attack using a melee weapon int ret1 - Either the damage to be used, if ret2 isn't given, or the new minimum damage if it is int ret2 - The new maximum damage @@ -416,9 +416,9 @@ What you can do: - add AP costs for all inventory movement including reloading - apply or remove some special scripted effects depending on PC's armor -int arg1 - Target slot (0 - main backpack, 1 - left hand, 2 - right hand, 3 - armor slot, 4 - weapon, when reloading it by dropping ammo) +int arg1 - Target slot (0 - main backpack, 1 - left hand, 2 - right hand, 3 - armor slot, 4 - weapon, when reloading it by dropping ammo, 5 - container, like bag/backpack, 6 - dropping on the ground) Item arg2 - Item being moved -Item arg3 - Item being replaced or weapon being reloaded (can be 0) +Item arg3 - Item being replaced, weapon being reloaded, or container being filled (can be 0) int ret1 - Override setting (-1 - use engine handler, any other value - prevent relocation of item/reloading weapon) @@ -432,7 +432,7 @@ NOTE: when replacing a previously wielded armor or weapon, the unwielding hook w If you need to rely on this, try checking if armor/weapon already equipped when wielding hook is executed. Critter arg1 - critter -Obj arg2 - item being wielded or unwielded (weapon/armor), may be 0 when unwielding +Obj arg2 - item being wielded or unwielded (weapon/armor) int arg3 - slot (INVEN_TYPE_*) int arg4 - 1 when wielding, 0 when unwielding @@ -498,3 +498,11 @@ int arg3 - the hour part of the length of resting time int arg4 - the minute part of the length of resting time int ret1 - pass 1 to interrupt the resting + +------------------------------------------- + +HOOK_GAMEMODECHANGE (hs_gamemodechange.int) + +Runs once every time when the game mode was changed, like opening/closing the inventory, character screen, pipboy, etc. + +int arg1 - event type: 1 - when the player exits the game, 0 - otherwise diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index c9f0aa7c5..739fe8ed2 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -50,7 +50,7 @@ has_fake_trait and has_fake_perk return the number of levels the player has of t perk_add_mode, set_selectable_perk, set_perkbox_title, hide_real_perks, show_real_perks and clear_selectable_perks control the behaviour of the select a perk box. set_selectable_perk can be used to add additional items by setting the 'active' parameter to 1, and to remove them again by setting it to 0. set_perkbox_title can be used to change the title of the box, or by using "" it will be set back to the default. hide and show_real_perks can be used to prevent the dialog from displaying any of the original 119 perks. perk_add_mode modifies what happens when a fake perk is selected from the perks dialog. It is treated as a set of flags - if bit 1 is set then it is added to the players traits, if bit 2 is set it is added to the players perks, and if bit 3 is set it is removed from the list of selectable perks. The default is 0x2. clear_selectable_perks restores the dialog to it's default state. -show_iface_tag, hide_iface_tag and is_iface_tag_active relate to the boxes that appear above the interface such as SNEAK and LEVEL. You can use 3 for LEVEL and 4 for ADDICT, or the range from 5 to 9 for custom boxes. Remember to add your messages to intrface.msg and setup the font colours in ddraw.ini if you're going to use custom boxes. +show_iface_tag, hide_iface_tag and is_iface_tag_active relate to the boxes that appear above the interface such as SNEAK and LEVEL. You can use 3 for LEVEL and 4 for ADDICT, or the range from 5 to 9 for custom boxes. Remember to add your messages to intrface.msg and set up the font colours in ddraw.ini if you're going to use custom boxes. get/set_bodypart_hit_modifier alter the hit percentage modifiers for aiming at specific bodyparts. Valid bodypart id's are from 0 to 8. Changes are not saved, and will reset to the defaults (or to the values specified in ddraw.ini if they exist) at each reload. @@ -72,7 +72,7 @@ arrays are created and manipulated with the xxx_array functions. An array must f NOTE: the above description only applies when "arraysBehavior" is set to 0 in ddraw.ini. Refer to "arrays.txt" for detailed description of new arrays behavior. -force_aimed_shots and disable_aimed_shots allow overriding the normal rules regarding which weapons are allowed to make aimed attacks. (e.g. weapons that cause explosive damage normally cannot normally make aimed shots.) force_aimed_shots will allow a weapon to make aimed shots even if it normally couldn't, and disable_aimed_shots stops a weapon from making aimed shots even if it normally could. Both of these functions affect player and npcs alike. force_aimed_shots does not override the effects of the fast shot trait. The list of edited weapons is not saved over game loads, so you need to call the functions once at each reload. Use a pid of 0 to represent unarmed. +force_aimed_shots and disable_aimed_shots allow overriding the normal rules regarding which weapons are allowed to make aimed attacks. (e.g. weapons that cause explosive damage normally cannot normally make aimed shots.) force_aimed_shots will allow a weapon to make aimed shots even if it normally couldn't, and disable_aimed_shots stops a weapon from making aimed shots even if it normally could. Both of these functions affect player and NPCs alike. force_aimed_shots does not override the effects of the fast shot trait. The list of edited weapons is not saved over game loads, so you need to call the functions once at each reload. Use a pid of 0 to represent unarmed. get/set_critter_skill_points will get/set the number of additional points a critter has in a skill, on top of whatever they have from their stats and other bonuses. Note that skill points are part of the proto, so calling set_skill_points on a critter will affect all critters that share the same proto. @@ -101,10 +101,10 @@ array - array ID to be used with array-related functions (actually an integer) - returns 1 the first time it is called after a new game or game load, and 0 any time after. It works on an individual basis for each script, so one script wont interfere with others. It's primary use is for global scripts, so that they know when to call set_global_script_repeat, but it can be called from normal scripts too. > void inc_npc_level(string npc) -- takes an npc name as an argument. The npc must be in your party. This function ignores player level requirements and the minimum 3 player level delay between npc level gains. It also ignores the random element, regardless of sfall's NPCAutoLevel setting. +- takes an NPC name as an argument. The NPC must be in your party. This function ignores player level requirements and the minimum 3 player level delay between NPC level gains. It also ignores the random element, regardless of sfall's NPCAutoLevel setting. > int get_npc_level(string npc) -- also takes the npc name as an argument, and returns the npc's current level. Again, the npc needs to be in your party. +- also takes the NPC name as an argument, and returns the NPC's current level. Again, the NPC needs to be in your party. > void set_car_current_town(int town) - changes the current town index for the player's car @@ -231,6 +231,10 @@ was made as a dirty easy hack to allow dynamically change some explosion paramet > void set_attack_is_explosion_fire - if you call this right before using a weapon with fire damage type, it will produce explosion effects (and radius damage) just like "explosion" type, but all targets will still receive fire damage. +> void set_explosion_radius(grenade, rocket) +- sets a permanent radius of the explosion for grenades and/or rockets. Passing 0 means not changing the corresponding radius. +- changed radius will be reset each time the player reloads the game. + Some utility/math functions are available: @@ -476,6 +480,12 @@ Some utility/math functions are available: > int sfall_func0("attack_is_aimed") - returns 1 if the aimed attack mode is selected, 0 otherwise +> void sfall_func3("set_iface_tag_text", int tag, string text, int color) +- sets the text messages and colors for custom notification boxes to the interface without the need to add messages to intrface.msg and set up the font colors in ddraw.ini +- tag value is the same as used in show_iface_tag, hide_iface_tag, and is_iface_tag_active. The valid range is from 5 to 9 (custom boxes) +- The text is limited to 19 characters +- available colors: 0 - green, 1 - red, 2 - white, 3 - yellow, 4 - dark yellow, 5 - blue, 6 - purple + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index b2239b398..70ba63a10 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -74,7 +74,25 @@ void SkillSetTags(long* tags, long num) { fo::func::skill_set_tags(tags, num); } +int _fastcall GetItemType(GameObject* item) { + return fo::func::item_get_type(item); +} +_declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot) { + GameObject* itemPtr = 0; + switch (slot) { + case fo::INVEN_TYPE_LEFT_HAND : + itemPtr = fo::func::inven_left_hand(critter); + break; + case fo::INVEN_TYPE_RIGHT_HAND : + itemPtr = fo::func::inven_right_hand(critter); + break; + case fo::INVEN_TYPE_WORN : + itemPtr = fo::func::inven_worn(critter); + break; + } + return itemPtr; +} //--------------------------------------------------------- //print text to surface diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 9cf21f867..a1b0ad5f1 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -50,6 +50,10 @@ void SkillGetTags(long* result, long num); // wrapper for skill_set_tags with bounds checking void SkillSetTags(long* tags, long num); +int _fastcall GetItemType(GameObject* item); + +_declspec(noinline) GameObject* GetItemPtrSlot(GameObject* critter, InvenType slot); + // Print text to surface void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface); // gets the height of the currently selected font diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index b5e0dc2f2..dafc70073 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -420,8 +420,8 @@ enum Trait : long enum class ScenerySubType : long { DOOR = 0, - STAIRS = 1, - ELEVATOR = 2, + STAIRS = 1, + ELEVATOR = 2, LADDER_BOTTOM = 3, LADDER_TOP = 4, GENERIC = 5 @@ -579,6 +579,14 @@ enum ItemType : long item_type_key = 6, }; +// Inventory Equates +enum InvenType : long +{ + INVEN_TYPE_WORN = 0, + INVEN_TYPE_RIGHT_HAND = 1, + INVEN_TYPE_LEFT_HAND = 2, +}; + #define OBJFLAG_CAN_WEAR_ITEMS (0xf000000) #define OBJFLAG_HELD_IN_RIGHT (0x10000) diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 9ebda0e4e..0f96d09a2 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -30,6 +30,7 @@ WRAP_WATCOM_FUNC3(long, db_freadShortCount, DbFile*, file, WORD*, dest, long, co WRAP_WATCOM_FUNC3(long, db_freadIntCount, DbFile*, file, DWORD*, dest, long, count) WRAP_WATCOM_FUNC2(long, db_fwriteByte, DbFile*, file, long, value) WRAP_WATCOM_FUNC2(long, db_fwriteInt, DbFile*, file, long, value) +WRAP_WATCOM_FUNC0(void, display_stats) // perform combat turn for a given critter WRAP_WATCOM_FUNC2(long, combat_turn, GameObject*, critter, long, isDudeTurn) WRAP_WATCOM_FUNC1(long, critter_is_dead, GameObject*, critter) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 3f9b7cca0..b88fb3853 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -14,6 +14,7 @@ #define FO_VAR_art_vault_person_nums 0x5108A8 #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 @@ -61,6 +62,7 @@ #define FO_VAR_glblmode 0x5709D0 #define FO_VAR_gmouse_current_cursor 0x518C0C #define FO_VAR_gmovie_played_list 0x596C78 +#define FO_VAR_GoodColor 0x6AB4EF #define FO_VAR_GreenColor 0x6A3CB0 #define FO_VAR_gsound_initialized 0x518E30 #define FO_VAR_hit_location_penalty 0x510954 @@ -136,6 +138,7 @@ #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 @@ -153,6 +156,7 @@ #define FO_VAR_RedColor 0x6AB4D0 #define FO_VAR_retvals 0x43EA7C #define FO_VAR_rotation 0x631D34 +#define FO_VAR_sad 0x530014 #define FO_VAR_script_path_base 0x51C710 #define FO_VAR_scr_size 0x6AC9F0 #define FO_VAR_scriptListInfo 0x51C7C8 @@ -187,6 +191,7 @@ #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 diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index fe41348ba..4fd1585d7 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -12,6 +12,7 @@ VAR_(art_vault_guy_num, DWORD) VAR_(art_vault_person_nums, DWORD) VAR_(bckgnd, BYTE*) VAR_(black_palette, DWORD) +VAR_(BlueColor, BYTE) VAR_(bottom_line, DWORD) VAR_(btable, DWORD) VAR_(btncnt, DWORD) @@ -58,6 +59,7 @@ VAR_(gIsSteal, DWORD) VAR_(glblmode, DWORD) VAR_(gmouse_current_cursor, long) VARA(gmovie_played_list, BYTE, 17) +VAR_(GoodColor, BYTE) VAR_(GreenColor, BYTE) VAR_(gsound_initialized, DWORD) VARA(hit_location_penalty, long, 9) @@ -131,6 +133,7 @@ VAR_(pc_kill_counts, DWORD) VARA(pc_name, char, 32) VAR_(pc_proto, Proto) VARA(pc_trait, long, 2) // 2 of them +VAR_(PeanutButter, BYTE) VARA(perk_data, PerkInfo, PERK_count) VAR_(perkLevelDataList, long*) // dynamic array, limited to PERK_Count VAR_(pip_win, DWORD) @@ -182,6 +185,7 @@ VAR_(title_font, DWORD) VARA(trait_data, TraitInfo, TRAIT_count) VAR_(view_page, DWORD) VAR_(wd_obj, DWORD) +VAR_(WhiteColor, BYTE) VAR_(wmAreaInfoList, DWORD) VAR_(wmLastRndTime, DWORD) VAR_(wmWorldOffsetX, DWORD) diff --git a/sfall/InputFuncs.cpp b/sfall/InputFuncs.cpp index d0c107978..a6201a173 100644 --- a/sfall/InputFuncs.cpp +++ b/sfall/InputFuncs.cpp @@ -36,7 +36,7 @@ static DWORD wheelMod; static bool reverseMouse; -static bool middleMouseDown; +bool middleMouseDown; static DWORD middleMouseKey; static bool backgroundKeyboard; diff --git a/sfall/InputFuncs.h b/sfall/InputFuncs.h index 1c27e33e9..b087765f0 100644 --- a/sfall/InputFuncs.h +++ b/sfall/InputFuncs.h @@ -26,6 +26,7 @@ namespace sfall { extern bool useScrollWheel; +extern bool middleMouseDown; void SetMDown(bool down, bool right); void SetMPos(int x, int y); diff --git a/sfall/Modules/AnimationsAtOnceLimit.cpp b/sfall/Modules/AnimationsAtOnceLimit.cpp index acd0bacb8..656e64950 100644 --- a/sfall/Modules/AnimationsAtOnceLimit.cpp +++ b/sfall/Modules/AnimationsAtOnceLimit.cpp @@ -18,10 +18,11 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" +#include "LoadGameHook.h" #include "AnimationsAtOnceLimit.h" -namespace sfall +namespace sfall { static constexpr int animRecordSize = sizeof(fo::AnimationSet); @@ -33,7 +34,8 @@ static int animationLimit = 32; static std::vector new_anim_set; static std::vector new_sad; -static DWORD animSetAddr, sadAddr; +static DWORD animSetAddr = FO_VAR_anim_set; +static DWORD sadAddr = FO_VAR_sad; static const DWORD animPCMove[] = { 0x416E11, 0x416F64, 0x417143, 0x41725C, 0x4179CC, @@ -151,24 +153,58 @@ static const DWORD sad_28[] = { 0x4173CE, 0x4174C1, 0x4175F1, 0x417730, }; +static DWORD __fastcall AnimCombatFix(DWORD* scr, BYTE combatFlag) { + DWORD animAddr = animSetAddr; + + if (animationLimit > 32) { + animAddr += animRecordSize; // include a dummy + } + + if (combatFlag & 2) { // combat flag is set + _asm call fo::funcoffs::combat_anim_finished_; + } + + return animAddr; +} + static void __declspec(naked) anim_set_end_hack() { __asm { - mov edi, FO_VAR_anim_set; - cmp dword ptr animationLimit, 32; - jle skip; - mov edi, animSetAddr; - add edi, animRecordSize; // Include a dummy -skip: - test dl, 0x2; // Is the combat flag set? - jz end; // No - call fo::funcoffs::combat_anim_finished_; -end: - mov [edi][esi], ebx; + call AnimCombatFix; + mov [eax][esi], ebx; push 0x415DF2; retn; } } +static DWORD __fastcall CheckSetSad(BYTE openFlag, DWORD valueMul) { + bool result = false; + int offset = (sadSize * valueMul) + 32; + + if (*(DWORD*)(sadAddr + offset) == -1000) { + result = true; + } else if (!InCombat() && !(openFlag & 1)) { + *(DWORD*)(sadAddr + offset) = -1000; + result = true; + } + + return result; +} + +static void __declspec(naked) object_move_hack() { + __asm { + mov ecx, ds:[ecx + 0x3C]; // openFlag + mov edx, [esp + 0x4C - 0x20]; // valueMul + call CheckSetSad; + test eax, eax; + jz end; + push 0x417611; // fixed jump + retn; +end: + push 0x417616; // default + retn; + } +} + void ApplyAnimationsAtOncePatches(signed char aniMax) { if (aniMax <= 32) return; @@ -278,7 +314,11 @@ void AnimationsAtOnce::init() { ApplyAnimationsAtOncePatches(animationLimit); dlogr(" Done", DL_INIT); } + // Fix for calling anim() functions in combat MakeJump(0x415DE2, anim_set_end_hack); + + // Fix crash when the critter goes through a door with animation trigger + MakeJump(0x41755E, object_move_hack); } void AnimationsAtOnce::exit() { diff --git a/sfall/Modules/BarBoxes.cpp b/sfall/Modules/BarBoxes.cpp index 24cd5b92b..f8c3502d5 100644 --- a/sfall/Modules/BarBoxes.cpp +++ b/sfall/Modules/BarBoxes.cpp @@ -18,14 +18,18 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" +#include "LoadGameHook.h" #include "BarBoxes.h" -namespace sfall +namespace sfall { static const DWORD DisplayBoxesRet1 = 0x4615A8; static const DWORD DisplayBoxesRet2 = 0x4615BE; +static const DWORD SetIndexBoxRet = 0x4612E8; + +#define sSize (12) struct sBox { DWORD msg; DWORD colour; @@ -34,6 +38,21 @@ struct sBox { static sBox boxes[10]; static DWORD boxesEnabled[5]; +#define tSize (28) +struct tBox { + DWORD hasText; + DWORD color; + char text[tSize - 8]; +}; +static tBox boxText[5]; + +static DWORD clrBakup[5]; +static bool setCustomBoxText; + +static const DWORD bboxMemAddr[] = { + 0x461266, 0x4612AC, 0x461374, 0x4613E8, 0x461479, 0x46148C, 0x4616BB, +}; + static void __declspec(naked) DisplayBoxesHook() { __asm { mov ebx, 0; @@ -56,39 +75,146 @@ static void __declspec(naked) DisplayBoxesHook() { } } +static void __declspec(naked) BarBoxesTextHack() { + __asm { + //mov ecx, [esp + 0x440 - 0x1C]; + push ecx; // ecx = BoxIndex + sub ecx, 5; + imul ecx, tSize; + cmp boxText[ecx], 1; // .hasText + jnz end; + // get color + mov ebx, boxText[ecx + 4]; // .color + // set text + lea eax, [boxText + ecx + 8]; // .text + // set color + pop ecx; + imul ecx, sSize; + cmp boxes[ecx + 4], 2; // .colour + jb skip; + mov [esp + 0x440 - 0x20], ebx; // Color +skip: + retn; +end: + add esp, 4; + jmp fo::funcoffs::getmsg_; + } +} + +static void __declspec(naked) BarBoxesIndexHack() { + __asm { + mov eax, ds:[0x4612E2]; // fontnum + mov ecx, 5; // start index + mov edx, ecx; + jmp SetIndexBoxRet; + } +} + +static void ReconstructBarBoxes() { + __asm { + call fo::funcoffs::refresh_box_bar_win_; + call fo::funcoffs::reset_box_bar_win_; + call fo::funcoffs::construct_box_bar_win_; + } +} + +static void ResetBoxes() { + for (int i = 0; i < 5; i++) { + boxesEnabled[i] = 0; + boxes[i + 5].colour = clrBakup[i]; + } + + if (!setCustomBoxText) return; + + //Restore boxes + SafeWrite32(0x461343, 0x00023D05); // call getmsg_ + ReconstructBarBoxes(); + SafeWrite8(0x461243, 0x31); + SafeWrite32(0x461244, 0x249489D2); + + setCustomBoxText = false; +} + +void BarBoxes::SetText(int box, const char* text, DWORD color) { + boxes[box].colour = color; + box -= 5; + boxText[box].hasText = 1; + strncpy_s(boxText[box].text, text, _TRUNCATE); + + DWORD clr; + switch (color) { + case 2: + clr = fo::var::WhiteColor; + break; + case 3: + clr = fo::var::YellowColor; + break; + case 4: + clr = fo::var::PeanutButter; + break; + case 5: + clr = fo::var::BlueColor; + break; + case 6: + clr = fo::var::GoodColor; + break; + default: + clr = fo::var::GreenColor; + } + boxText[box].color = clr; + + int enabled[5]; + for (int i = 0; i < 5; i++) { + enabled[i] = boxesEnabled[i]; + boxesEnabled[i] = 0; + } + + if (!setCustomBoxText) { + MakeCall(0x461342, BarBoxesTextHack); + MakeJump(0x461243, BarBoxesIndexHack); + setCustomBoxText = true; + } + + ReconstructBarBoxes(); + + for (int i = 0; i < 5; i++) { + boxesEnabled[i] = enabled[i]; + } + __asm call fo::funcoffs::refresh_box_bar_win_; +} + void BarBoxes::init() { - SafeWrite32(0x461266, (DWORD)boxes + 8); - SafeWrite32(0x4612AC, (DWORD)boxes + 8); - SafeWrite32(0x4612FE, (DWORD)boxes + 4); - SafeWrite32(0x46133C, (DWORD)boxes + 0); - SafeWrite32(0x461374, (DWORD)boxes + 8); - SafeWrite32(0x4613E8, (DWORD)boxes + 8); - - SafeWrite32(0x461479, (DWORD)boxes + 8); - SafeWrite32(0x46148C, (DWORD)boxes + 8); - SafeWrite32(0x4616BB, (DWORD)boxes + 8); - - memset(boxes, 0, 12 * 10); + SafeWriteBatch((DWORD)boxes + 8, bboxMemAddr); //.mem + SafeWrite32(0x4612FE, (DWORD)boxes + 4); //.colour + SafeWrite32(0x46133C, (DWORD)boxes); //.msg + + int size = sSize * 10; + memset(boxes, 0, size); memset(boxesEnabled, 0, 5 * 4); - memcpy(boxes, (void*)0x518FE8, 12 * 5); + memcpy(boxes, (void*)0x518FE8, sSize * 5); for (int i = 5; i < 10; i++) { - boxes[i].msg = 0x69 + i - 5; + boxes[i].msg = 100 + i; } SafeWrite8(0x46127C, 10); SafeWrite8(0x46140B, 10); - SafeWrite8(0x461495, 0x78); + SafeWrite8(0x461495, size); MakeJump(0x4615A3, DisplayBoxesHook); auto boxBarColors = GetConfigString("Misc", "BoxBarColours", "", 6); - if (boxBarColors.size() == 5) { + if (boxBarColors.size() >= 5) { for (int i = 0; i < 5; i++) { if (boxBarColors[i] == '1') { boxes[i + 5].colour = 1; + clrBakup[i] = 1; + } else { + clrBakup[i] = 0; } } } + + LoadGameHook::OnGameReset() += ResetBoxes; } int _stdcall GetBox(int i) { diff --git a/sfall/Modules/BarBoxes.h b/sfall/Modules/BarBoxes.h index 5d7b2ed91..e73d7aabc 100644 --- a/sfall/Modules/BarBoxes.h +++ b/sfall/Modules/BarBoxes.h @@ -7,6 +7,8 @@ class BarBoxes : public Module { public: const char* name() { return "BarBoxes"; } void init(); + + static void SetText(int box, const char* text, DWORD color); }; int _stdcall GetBox(int i); diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index c51df11fb..65e1584c5 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1109,6 +1109,18 @@ static void __declspec(naked) obj_move_to_tile_hack() { } } +static void __declspec(naked) ai_move_steps_closer_hook() { + __asm { + call fo::funcoffs::combat_turn_run_; + movzx dx, word ptr [esi + 0x44]; // combat_data.results + test dx, DAM_DEAD or DAM_KNOCKED_OUT or DAM_LOSE_TURN; + jz end; + mov [esi + 0x40], 0; // pobj.curr_mp (source reset ap) +end: + retn; + } +} + void BugFixes::init() { @@ -1420,6 +1432,10 @@ void BugFixes::init() // Fix for being at incorrect hex after map change when the exit hex in source map is at the same position as // some exit hex in destination map MakeCall(0x48A704, obj_move_to_tile_hack); + + // Fix for critters killed in combat by scripting still being able to move in their combat turn if the distance parameter + // in their AI packages is set to stay_close/charge, or NPCsTryToSpendExtraAP is enabled + HookCall(0x42A1A8, ai_move_steps_closer_hook); // 0x42B24D } } diff --git a/sfall/Modules/Explosions.cpp b/sfall/Modules/Explosions.cpp index 55f063c59..c9b324786 100644 --- a/sfall/Modules/Explosions.cpp +++ b/sfall/Modules/Explosions.cpp @@ -21,6 +21,7 @@ #include "..\FalloutEngine\Fallout2.h" #include "..\Logging.h" #include "..\SimplePatch.h" +#include "LoadGameHook.h" #include "MainLoopHook.h" #include "ScriptExtender.h" @@ -30,6 +31,7 @@ namespace sfall { static bool lightingEnabled = false; +static bool explosionsMetaruleReset = false; static const DWORD ranged_attack_lighting_fix_back = 0x4118F8; @@ -153,12 +155,15 @@ static void __declspec(naked) fire_dance_lighting_fix1() { } -static const DWORD explosion_dmg_check_adr[] = {0x411709, 0x4119FC, 0x411C08, 0x4517C1, 0x423BC8}; +static const DWORD explosion_dmg_check_adr[] = {0x411709, 0x4119FC, 0x411C08, 0x4517C1, 0x423BC8, 0x42381A}; static const DWORD explosion_art_adr[] = {0x411A19, 0x411A29, 0x411A35, 0x411A3C}; static const DWORD explosion_art_defaults[] = {10, 2, 31, 29}; static const DWORD explosion_radius_grenade = 0x479183; static const DWORD explosion_radius_rocket = 0x47918B; +static DWORD set_expl_radius_grenade = 2; +static DWORD set_expl_radius_rocket = 3; + static const size_t numArtChecks = sizeof(explosion_art_adr) / sizeof(explosion_art_adr[0]); static const size_t numDmgChecks = sizeof(explosion_dmg_check_adr) / sizeof(explosion_dmg_check_adr[0]); @@ -166,9 +171,15 @@ enum MetaruleExplosionsMode { EXPL_FORCE_EXPLOSION_PATTERN = 1, EXPL_FORCE_EXPLOSION_ART = 2, EXPL_FORCE_EXPLOSION_RADIUS = 3, - EXPL_FORCE_EXPLOSION_DMGTYPE = 4 + EXPL_FORCE_EXPLOSION_DMGTYPE = 4, + EXPL_STATIC_EXPLOSION_RADIUS = 5 }; +static void SetExplosionRadius(int arg1, int arg2) { + SafeWrite32(explosion_radius_grenade, arg1); + SafeWrite32(explosion_radius_rocket, arg2); +} + int _stdcall ExplosionsMetaruleFunc(int mode, int arg1, int arg2) { switch (mode) { case EXPL_FORCE_EXPLOSION_PATTERN: @@ -180,27 +191,33 @@ int _stdcall ExplosionsMetaruleFunc(int mode, int arg1, int arg2) { SafeWrite8(0x411B54, 6); // last direction } break; - case EXPL_FORCE_EXPLOSION_ART: { + case EXPL_FORCE_EXPLOSION_ART: for (int i = 0; i < numArtChecks; i++) { SafeWrite32(explosion_art_adr[i], (BYTE)arg1); } break; - } case EXPL_FORCE_EXPLOSION_RADIUS: - SafeWrite32(explosion_radius_grenade, arg1); - SafeWrite32(explosion_radius_rocket, arg1); + SetExplosionRadius(arg1, arg1); break; - case EXPL_FORCE_EXPLOSION_DMGTYPE: { + case EXPL_FORCE_EXPLOSION_DMGTYPE: for (int i = 0; i < numDmgChecks; i++) { SafeWrite8(explosion_dmg_check_adr[i], (BYTE)arg1); } break; - } + case EXPL_STATIC_EXPLOSION_RADIUS: + if (arg1 > 0) set_expl_radius_grenade = arg1; + if (arg2 > 0) set_expl_radius_rocket = arg2; + SetExplosionRadius(set_expl_radius_grenade, set_expl_radius_rocket); + break; + default: + return -1; } + if (mode != EXPL_STATIC_EXPLOSION_RADIUS) explosionsMetaruleReset = true; return 0; } void ResetExplosionSettings() { + if (!explosionsMetaruleReset) return; // explosion pattern explosion_effect_starting_dir = 0; SafeWrite8(0x411B54, 6); // last direction @@ -209,27 +226,32 @@ void ResetExplosionSettings() { SafeWrite32(explosion_art_adr[i], explosion_art_defaults[i]); } // explosion radiuses - SafeWrite32(explosion_radius_grenade, 2); - SafeWrite32(explosion_radius_rocket, 3); + SetExplosionRadius(set_expl_radius_grenade, set_expl_radius_rocket); // explosion dmgtype for (int i = 0; i < numDmgChecks; i++) { - SafeWrite8(explosion_dmg_check_adr[i], 6); + SafeWrite8(explosion_dmg_check_adr[i], fo::DamageType::DMG_explosion); } + explosionsMetaruleReset = false; +} + +void ResetExplosionRadius() { + if (set_expl_radius_grenade != 2 || set_expl_radius_rocket != 3) + SetExplosionRadius(2, 3); } void Explosions::init() { MakeJump(0x411AB4, explosion_effect_hook); // required for explosions_metarule - if (GetConfigInt("Misc", "ExplosionsEmitLight", 0)) { + lightingEnabled = GetConfigInt("Misc", "ExplosionsEmitLight", 0) != 0; + if (lightingEnabled) { dlog("Applying Explosion changes.", DL_INIT); - lightingEnabled = true; MakeJump(0x4118E1, ranged_attack_lighting_fix); MakeJump(0x410A4A, fire_dance_lighting_fix1); MakeJump(0x415A3F, anim_set_check__light_fix); // this allows to change light intensity dlogr(" Done", DL_INIT); } - + DWORD tmp; tmp = SimplePatch(0x4A2873, "Misc", "Dynamite_DmgMax", 50, 0, 9999); SimplePatch(0x4A2878, "Misc", "Dynamite_DmgMin", 30, 0, tmp); @@ -238,6 +260,8 @@ void Explosions::init() { // after each combat attack, reset metarule_explosions settings MainLoopHook::OnAfterCombatAttack() += ResetExplosionSettings; + + LoadGameHook::OnGameReset() += ResetExplosionRadius; } } diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index c7268f6c9..d2c353005 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -1517,66 +1517,74 @@ static void __declspec(naked) FixCharScrnBack(void) { if (charScrnBackSurface == nullptr) { charScrnBackSurface = new BYTE [640*480]; - BYTE *OldCharScrnBackSurface = fo::var::bckgnd; //char screen background frm surface + UnlistedFrm *frm; + frm = LoadUnlistedFrm((*(long*)FO_VAR_glblmode) ? "AppChCrt.frm" : "AppChEdt.frm", 6); - //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); - - 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; - - 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 - fo::func::art_ptr_unlock(FrmObj); - - //Sexoff Frm - FrmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 188), 0, 0, &FrmObj); - //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 - 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 - 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 - 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 - 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 - fo::func::art_ptr_unlock(FrmObj); - FrmSurface = nullptr; + 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 + + //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); + + 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; + + 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 + fo::func::art_ptr_unlock(FrmObj); + + //Sexoff Frm + FrmSurface = fo::func::art_ptr_lock_data(BuildFrmId(6, 188), 0, 0, &FrmObj); + //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 + 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 - 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 - 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 + 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 + fo::func::art_ptr_unlock(FrmObj); + FrmSurface = nullptr; + } int oldFont; oldFont = GetFont(); @@ -1603,8 +1611,8 @@ static void __declspec(naked) FixCharScrnBack(void) { mov esp, ebp //epilog pop ebp mov eax, charScrnBackSurface -EndFunc: mov dword ptr ds:[FO_VAR_bckgnd], eax //surface ptr for char scrn back +EndFunc: retn } } diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 039b7739b..6217a0d96 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -64,6 +64,14 @@ void _stdcall MouseClickHook(DWORD button, bool pressed) { EndHook(); } +void _stdcall GameModeChangeHook(DWORD exit) { + BeginHook(); + argCount = 1; + args[0] = exit; + RunHookScript(HOOK_GAMEMODECHANGE); + EndHook(); +} + // END HOOKS DWORD _stdcall GetHSArgCount() { @@ -122,6 +130,7 @@ static void HookScriptInit2() { LoadHookScript("hs_keypress", HOOK_KEYPRESS); LoadHookScript("hs_mouseclick", HOOK_MOUSECLICK); + LoadHookScript("hs_gamemodechange", HOOK_GAMEMODECHANGE); dlogr("Finished loading hook scripts", DL_HOOK|DL_INIT); } @@ -157,6 +166,7 @@ void _stdcall RunHookScriptsAtProc(DWORD procId) { void HookScripts::init() { OnKeyPressed() += KeyPressHook; OnMouseClick() += MouseClickHook; + LoadGameHook::OnGameModeChange() += GameModeChangeHook; } } diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index f3a99ca0a..c800abca8 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -57,6 +57,7 @@ enum HookType HOOK_CARTRAVEL = 28, HOOK_SETGLOBALVAR = 29, HOOK_RESTTIMER = 30, + HOOK_GAMEMODECHANGE = 31, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/InventoryHs.cpp b/sfall/Modules/HookScripts/InventoryHs.cpp index 75031fcc8..6e5f3eefb 100644 --- a/sfall/Modules/HookScripts/InventoryHs.cpp +++ b/sfall/Modules/HookScripts/InventoryHs.cpp @@ -14,67 +14,84 @@ namespace sfall static const DWORD RemoveObjHookRet = 0x477497; static void __declspec(naked) RemoveObjHook() { __asm { - push ecx; - mov ecx, [esp + 4]; - hookbegin(4); + mov ecx, [esp + 8]; // call addr mov args[0], eax; mov args[4], edx; mov args[8], ebx; mov args[12], ecx; pushad; - push HOOK_REMOVEINVENOBJ; - call RunHookScript; + } + + BeginHook(); + argCount = 4; + RunHookScript(HOOK_REMOVEINVENOBJ); + EndHook(); + + __asm { popad; - hookend; - push esi; push edi; push ebp; - sub esp, 0xc; + sub esp, 0x0C; jmp RemoveObjHookRet; } } static void __declspec(naked) MoveCostHook() { __asm { - hookbegin(3); - mov args[0], eax; - mov args[4], edx; + mov args[0], eax; + mov args[4], edx; call fo::funcoffs::critter_compute_ap_from_distance_ - mov args[8], eax; + mov args[8], eax; pushad; - push HOOK_MOVECOST; - call RunHookScript; + } + + BeginHook(); + argCount = 3; + RunHookScript(HOOK_MOVECOST); + EndHook(); + + __asm { popad; cmp cRet, 1; - jl end; - mov eax, rets[0]; -end: - hookend; + cmovge eax, rets[0]; retn; } } -static int __stdcall SwitchHandHook2(fo::GameObject* item, fo::GameObject* itemReplaced, DWORD addr) { - int tmp; - if (itemReplaced && fo::func::item_get_type(itemReplaced) == fo::item_type_weapon && fo::func::item_get_type(item) == fo::item_type_ammo) { +/* Common inventory move hook */ +static int __fastcall InventoryMoveHook_Script(DWORD itemReplace, DWORD item, int type) { + + BeginHook(); + argCount = 3; + + args[0] = type; // event type + args[1] = item; // item being dropped + args[2] = itemReplace; // item being replaced here + + RunHookScript(HOOK_INVENTORYMOVE); + EndHook(); + + return (cRet > 0) ? rets[0] : -1; +} + +static int __fastcall SwitchHandHook_Script(fo::GameObject* item, fo::GameObject* itemReplaced, DWORD addr) { + if (itemReplaced && fo::GetItemType(itemReplaced) == fo::item_type_weapon && fo::GetItemType(item) == fo::item_type_ammo) { return -1; // to prevent inappropriate hook call after dropping ammo on weapon } BeginHook(); argCount = 3; - args[0] = (addr < 0x47136D) ? 1 : 2; + args[0] = (addr < 0x47136D) ? 1 : 2; // slot: 1 - left, 2 - right args[1] = (DWORD)item; args[2] = (DWORD)itemReplaced; - RunHookScript(HOOK_INVENTORYMOVE); // moveinventory - tmp = PartyControl::SwitchHandHook(item); + RunHookScript(HOOK_INVENTORYMOVE); + int tmp = PartyControl::SwitchHandHook(item); if (tmp != -1) { cRetTmp = 0; SetHSReturn(tmp); } EndHook(); - if (cRet > 0) { - return rets[0]; - } - return -1; + + return (cRet > 0) ? rets[0] : -1; } /* @@ -82,73 +99,58 @@ static int __stdcall SwitchHandHook2(fo::GameObject* item, fo::GameObject* itemR If switch_hand_ function is not called, item is not placed anywhere (it remains in main inventory) */ static void _declspec(naked) SwitchHandHook() { - _asm { + __asm { pushad; - mov ecx, eax; - mov eax, [esp+32]; // back address + mov ecx, eax; // item being moved + mov edx, [edx]; // other item + mov eax, [esp + 32]; // back address push eax; - mov edx, [edx]; - push edx; // other item - push ecx; // item being moved - call SwitchHandHook2; - cmp eax, -1; + call SwitchHandHook_Script; + cmp eax, -1; // ret value popad; - jne skip; + jne skip; call fo::funcoffs::switch_hand_; skip: retn; } } - -static const DWORD UseArmorHack_back = 0x4713A9; // normal operation +static const DWORD UseArmorHack_back = 0x4713AF; // normal operation (old 0x4713A9) static const DWORD UseArmorHack_skip = 0x471481; // skip code, prevent wearing armor // This hack is called when an armor is dropped into the armor slot at inventory screen static void _declspec(naked) UseArmorHack() { __asm { - cmp eax, 0; - jne skip; // not armor - hookbegin(3); - mov args[0], 3; - mov eax, [esp+24]; // item - mov args[4], eax; - mov eax, ds:[FO_VAR_i_worn] - mov args[8], eax; + mov ecx, ds:[FO_VAR_i_worn]; // replacement item (override code) + mov edx, [esp + 0x58 - 0x40]; // item pushad; - push HOOK_INVENTORYMOVE; - call RunHookScript; + push 3; // event: armor slot + call InventoryMoveHook_Script; + cmp eax, -1; // ret value popad; - cmp cRet, 1; - jl back; - cmp rets[0], -1; jne skip; -back: - hookend; jmp UseArmorHack_back; skip: - hookend; jmp UseArmorHack_skip; } } static void _declspec(naked) MoveInventoryHook() { __asm { - hookbegin(3); - mov args[0], 0; - mov args[4], edx; - mov args[8], 0; // no item being replaced here.. pushad; - push HOOK_INVENTORYMOVE; - call RunHookScript; + xor ecx, ecx; // no item replace + mov ebx, ecx; + cmp dword ptr ds:[FO_VAR_curr_stack], 0; + jle noCont; + mov ecx, eax; // contaner ptr + mov ebx, 5; +noCont: + push ebx; // event: 0 - main backpack, 5 - contaner + call InventoryMoveHook_Script; // edx - item + cmp eax, -1; // ret value popad; - cmp cRet, 1; - jl skipcheck; - cmp rets[0], -1; - jne skipcall; -skipcheck: - call fo::funcoffs::item_add_force_ -skipcall: - hookend; + jne skip; + call fo::funcoffs::item_add_force_; +skip: retn; } } @@ -157,152 +159,179 @@ static void _declspec(naked) MoveInventoryHook() { // - allows to prevent item being dropped if 0 is returned with set_sfall_return // - called for every item when dropping multiple items in stack (except caps) // - when dropping caps it called always once -// - if 0 is returned while dropping caps, selected amount - 1 will still disappear from inventory -/* -static void __declspec(naked) InvenActionCursor_ObjDrop_Hook() { +// - if 0 is returned while dropping caps, selected amount - 1 will still disappear from inventory (fixed) +static DWORD nextHookDropSkip = 0; +static int dropResult = -1; +static void __declspec(naked) InvenActionCursorObjDropHook() { + if (nextHookDropSkip) { + nextHookDropSkip = 0; + dropResult = -1; + } else { + __asm { + pushad; + xor ecx, ecx; // no itemReplace + push 6; // event: item drop ground + call InventoryMoveHook_Script; // edx - item + mov dropResult, eax; // ret value + popad; + cmp dword ptr [esp], 0x47379A + 5; // caps call address + jz capsMultiDrop; + } + } + + if (dropResult == -1) { + _asm call fo::funcoffs::obj_drop_; + } + _asm retn; + +/* for only caps multi drop */ +capsMultiDrop: + if (dropResult == -1) { + nextHookDropSkip = 1; + _asm call fo::funcoffs::item_remove_mult_; + } else { + _asm mov dword ptr [esp], 0x473874; // no caps drop + } + _asm retn; +} + +static int __fastcall DropIntoContainer(DWORD ptrCont, DWORD item, DWORD addrCall) { + int type = 5; // event: move to container (Crafty compatibility) + + if (addrCall == 0x47147C + 5) { // drop out contaner + ptrCont = 0; + type = 0; // event: move to main backpack + } + return InventoryMoveHook_Script(ptrCont, item, type); +} + +static const DWORD DropIntoContainer_back = 0x47649D; // normal operation +static const DWORD DropIntoContainer_skip = 0x476503; +static void __declspec(naked) DropIntoContainerHack() { __asm { - hookbegin(3); - mov args[0], 5; - mov args[4], edx; // item being dropped - mov args[8], 0; // no item being replaced here.. pushad; - push HOOK_INVENTORYMOVE; - call RunHookScript; + mov ecx, ebp; // contaner ptr + mov edx, esi; // item + mov eax, [esp + 0x10 + 32]; // call address + push eax; + call DropIntoContainer; + cmp eax, -1; // ret value popad; - cmp cRet, 1; - jl skipcheck; - cmp rets[0], -1; - jne skipcall; -skipcheck: - call fo::funcoffs::obj_drop_; -skipcall: - hookend; - retn; + jne skipdrop; + jmp DropIntoContainer_back; +skipdrop: + mov eax, -1; + jmp DropIntoContainer_skip; + } +} + +static const DWORD DropIntoContainerRet = 0x471481; +static void __declspec(naked) DropIntoContainerHandSlotHack() { + __asm { + call fo::funcoffs::drop_into_container_; + jmp DropIntoContainerRet; } } -*/ static const DWORD DropAmmoIntoWeaponHack_back = 0x47658D; // proceed with reloading static const DWORD DropAmmoIntoWeaponHack_return = 0x476643; static void _declspec(naked) DropAmmoIntoWeaponHack() { __asm { - hookbegin(3); - mov args[0], 4; - mov eax, [esp]; - mov args[4], eax; - mov args[8], ebp; pushad; - push HOOK_INVENTORYMOVE; - call RunHookScript; + mov ecx, ebp; // weapon ptr + mov edx, [esp + 32]; // item var: ammo_ + push 4; // event: weapon reloading + call InventoryMoveHook_Script; + cmp eax, -1; // ret value popad; - cmp cRet, 1; - jl proceedreloading; - cmp rets[0], -1; - jne donothing; -proceedreloading: - hookend; - mov ebx, 1; // overwritten code + jne donothing; + mov ebx, 1; // overwritten code jmp DropAmmoIntoWeaponHack_back; donothing: - hookend; - mov eax, 0; + xor eax, eax; // result 0 jmp DropAmmoIntoWeaponHack_return; } } +/* Common InvenWield hook */ +static void InvenWieldHook_Script(int flag) { + BeginHook(); + argCount = 4; + args[3] = flag; // invenwield flag + RunHookScript(HOOK_INVENWIELD); + EndHook(); +} -static void _declspec(naked) invenWieldFunc_Hook() { +static void _declspec(naked) InvenWieldFuncHook() { using namespace fo; __asm { - hookbegin(4); mov args[0], eax; // critter mov args[4], edx; // item mov args[8], ebx; // slot - mov args[12], 1; // wield flag pushad; - cmp ebx, 1; // right hand slot? - je skip; - mov eax, edx; - call fo::funcoffs::item_get_type_; - cmp eax, item_type_armor; - jz skip; - mov args[8], 2; // INVEN_TYPE_LEFT_HAND -skip: - push HOOK_INVENWIELD; - call RunHookScript; - popad; - cmp cRet, 1; - jl defaulthandler; - cmp rets[0], -1; - je defaulthandler; - jmp end - defaulthandler : - call fo::funcoffs::invenWieldFunc_ - end : - hookend; - retn; } + + // right hand slot? + if (args[2] != INVEN_TYPE_RIGHT_HAND && GetItemType((GameObject*)args[1]) != item_type_armor) { + args[2] = INVEN_TYPE_LEFT_HAND; + } + + InvenWieldHook_Script(1); // wield flag + + _asm popad; + if (cRet == 0 || rets[0] == -1) { + _asm call funcoffs::invenWieldFunc_; + } + _asm retn; } // called when unwielding weapons -static void _declspec(naked) invenUnwieldFunc_Hook() { +static void _declspec(naked) InvenUnwieldFuncHook() { __asm { - hookbegin(4); - mov args[0], eax; // critter - mov args[4], 0; // item - mov args[8], edx; // slot - mov args[12], 0; // wield flag - cmp edx, 0; // left hand slot? - jne notlefthand; - mov args[8], 2; // left hand -notlefthand: + mov args[0], eax; // critter + mov args[8], edx; // slot pushad; - push HOOK_INVENWIELD; - call RunHookScript; - popad; - cmp cRet, 1; - jl defaulthandler; - cmp rets[0], -1; - je defaulthandler; - jmp end - defaulthandler : - call fo::funcoffs::invenUnwieldFunc_; -end: - hookend; - retn; } + + // set slot + if (args[2] == 0) { // left hand slot? + args[2] = fo::INVEN_TYPE_LEFT_HAND; + } + args[1] = (DWORD)fo::GetItemPtrSlot((fo::GameObject*)args[0], (fo::InvenType)args[2]); // get item + + InvenWieldHook_Script(0); // unwield flag + + _asm popad; + if (cRet == 0 || rets[0] == -1) { + _asm call fo::funcoffs::invenUnwieldFunc_; + } + _asm retn; } -static void _declspec(naked) correctFidForRemovedItem_Hook() { +static void _declspec(naked) CorrectFidForRemovedItemHook() { __asm { - hookbegin(4); mov args[0], eax; // critter mov args[4], edx; // item - mov args[8], 0; // slot - mov args[12], 0; // wield flag (armor by default) - test ebx, 0x02000000; // right hand slot? - jz notrighthand; - mov args[8], 1; // right hand -notrighthand: - test ebx, 0x01000000; // left hand slot? - jz notlefthand; - mov args[8], 2; // left hand -notlefthand: + mov args[8], ebx; // item flag pushad; - push HOOK_INVENWIELD; - call RunHookScript; - popad; - cmp cRet, 1; - jl defaulthandler; - cmp rets[0], -1; - je defaulthandler; - jmp end; -defaulthandler: - call fo::funcoffs::correctFidForRemovedItem_; -end: - hookend; - retn; } + + // set slot + if (args[2] & fo::ObjectFlag::Right_Hand) { // right hand slot + args[2] = fo::INVEN_TYPE_RIGHT_HAND; + } else if (args[2] & fo::ObjectFlag::Left_Hand) { // left hand slot + args[2] = fo::INVEN_TYPE_LEFT_HAND; + } else { + args[2] = fo::INVEN_TYPE_WORN; // armor slot + } + + InvenWieldHook_Script(0); // unwield flag (armor by default) + + _asm popad; + if (cRet == 0 || rets[0] == -1) { + _asm call fo::funcoffs::correctFidForRemovedItem_; + } + _asm retn; } void AdjustFidHook(DWORD vanillaFid) { @@ -318,7 +347,7 @@ void AdjustFidHook(DWORD vanillaFid) { void InitInventoryHookScripts() { LoadHookScript("hs_removeinvenobj", HOOK_REMOVEINVENOBJ); - MakeJump(0x477490, RemoveObjHook); + MakeJump(0x477492, RemoveObjHook); // old 0x477490 LoadHookScript("hs_movecost", HOOK_MOVECOST); HookCalls(MoveCostHook, { 0x417665, 0x44B88A }); @@ -328,20 +357,21 @@ void InitInventoryHookScripts() { 0x4712E3, // left slot 0x47136D // right slot }); - MakeJump(0x4713A3, UseArmorHack); - //HookCall(0x4711B3, &DropIntoContainerHook); - //HookCall(0x47147C, &DropIntoContainerHook); + MakeJump(0x4713A9, UseArmorHack); // old 0x4713A3 + MakeJump(0x476491, DropIntoContainerHack); + MakeJump(0x471338, DropIntoContainerHandSlotHack); + MakeJump(0x4712AB, DropIntoContainerHandSlotHack); HookCall(0x471200, MoveInventoryHook); - //HookCall(0x4712C7, &DropAmmoIntoWeaponHook); - //HookCall(0x471351, &DropAmmoIntoWeaponHook); MakeJump(0x476588, DropAmmoIntoWeaponHack); - // TODO: consider adding item drop event into INVENTORYMOVE or different hook - //HookCalls(InvenActionCursor_ObjDrop_Hook, { 0x473851, 0x47386F }); + HookCalls(InvenActionCursorObjDropHook, { + 0x473851, 0x47386F, + 0x47379A // caps multi drop + }); LoadHookScript("hs_invenwield", HOOK_INVENWIELD); - HookCalls(invenWieldFunc_Hook, { 0x47275E, 0x495FDF }); - HookCalls(invenUnwieldFunc_Hook, { 0x45967D, 0x472A5A, 0x495F0B }); - HookCalls(correctFidForRemovedItem_Hook, { 0x45680C, 0x45C4EA }); + HookCalls(InvenWieldFuncHook, { 0x47275E, 0x495FDF }); + HookCalls(InvenUnwieldFuncHook, { 0x45967D, 0x472A5A, 0x495F0B }); + HookCalls(CorrectFidForRemovedItemHook, { 0x45680C, 0x45C4EA }); LoadHookScript("hs_adjustfid", HOOK_ADJUSTFID); Inventory::OnAdjustFid() += AdjustFidHook; diff --git a/sfall/Modules/Inventory.cpp b/sfall/Modules/Inventory.cpp index 4cad3ce69..2dd1c407e 100644 --- a/sfall/Modules/Inventory.cpp +++ b/sfall/Modules/Inventory.cpp @@ -35,6 +35,7 @@ static Delegate onAdjustFid; static DWORD mode; static DWORD maxItemSize; static DWORD reloadWeaponKey = 0; +static DWORD itemFastMoveKey = 0; long& GetActiveItemMode() { return fo::var::itemButtonItems[fo::var::itemCurrentItem].mode; @@ -794,6 +795,27 @@ void __declspec(naked) adjust_fid_replacement() { } } +void __declspec(naked) do_move_timer_hook() { + __asm { + cmp eax, 4; + jnz end; + pushad; + } + + KeyDown(itemFastMoveKey); + + __asm { + test eax, eax; + popad; + jz end; + mov dword ptr [esp], 0x476920; + retn; +end: + call fo::funcoffs::setup_move_timer_win_; + retn; + } +} + void InventoryReset() { invenapcost = GetConfigInt("Misc", "InventoryApCost", 4); } @@ -858,7 +880,7 @@ void Inventory::init() { } // Do not call the 'Move Items' window when using drap and drop to reload weapons in the inventory - int ReloadReserve = GetConfigInt("Misc", "ReloadReserve", 1); + int ReloadReserve = GetConfigInt("Misc", "ReloadReserve", -1); if (ReloadReserve >= 0) { SafeWrite32(0x47655F, ReloadReserve); // mov eax, ReloadReserve SafeWrite32(0x476563, 0x097EC139); // cmp ecx, eax; jle 0x476570 @@ -866,6 +888,15 @@ void Inventory::init() { SafeWrite8(0x476569, 0x91); // xchg ecx, eax }; + itemFastMoveKey = GetConfigInt("Input", "ItemFastMoveKey", DIK_LCONTROL); + if (itemFastMoveKey > 0) { + HookCall(0x476897, do_move_timer_hook); + } + + if (GetConfigInt("Misc", "ItemCounterDefaultMax", 0)) { + 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); diff --git a/sfall/Modules/Karma.cpp b/sfall/Modules/Karma.cpp index 18d55d0da..f302eb4cc 100644 --- a/sfall/Modules/Karma.cpp +++ b/sfall/Modules/Karma.cpp @@ -89,7 +89,7 @@ void ApplyDisplayKarmaChangesPatch() { void ApplyKarmaFRMsPatch() { auto karmaFrmList = GetConfigList("Misc", "KarmaFRMs", "", 512); if (karmaFrmList.size() > 0) { - dlog("Applying karma frm patch.", DL_INIT); + dlog("Applying karma FRM patch.", DL_INIT); auto karmaPointsList = GetConfigList("Misc", "KarmaPoints", "", 512); karmaFrms.resize(karmaFrmList.size()); diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index 7880ab748..039161336 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -37,11 +37,23 @@ 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 } + static Delegate<> onGameInit; static Delegate<> onGameReset; static Delegate<> onBeforeGameStart; static Delegate<> onAfterGameStarted; static Delegate<> onAfterNewGame; +static Delegate onGameModeChange; static DWORD inLoop = 0; static DWORD saveInCombatFix; @@ -54,11 +66,11 @@ bool IsMapLoaded() { } DWORD InWorldMap() { - return (inLoop&WORLDMAP) ? 1 : 0; + return (inLoop & WORLDMAP) ? 1 : 0; } DWORD InCombat() { - return (inLoop&COMBAT) ? 1 : 0; + return (inLoop & COMBAT) ? 1 : 0; } DWORD GetLoopFlags() { @@ -73,6 +85,19 @@ void ClearLoopFlag(LoopFlag flag) { inLoop &= ~flag; } +static void __stdcall GameModeChange(DWORD state) { + onGameModeChange.invoke(state); +} + +void _stdcall SetInLoop(DWORD mode, LoopFlag flag) { + if (mode) { + SetLoopFlag(flag); + } else { + ClearLoopFlag(flag); + } + GameModeChange(0); +} + void GetSavePath(char* buf, char* ftype) { sprintf(buf, "%s\\savegame\\slot%.2d\\sfall%s.sav", fo::var::patches, fo::var::slot_cursor + 1 + LSPageOffset, ftype); //add SuperSave Page offset } @@ -133,15 +158,16 @@ static void __declspec(naked) SaveGame_hook() { push eax; // save Mode parameter call CombatSaveTest; test eax, eax; - pop edx; // recall Mode parameter + pop edx; // recall Mode parameter (pop eax) jz end; mov eax, edx; - or inLoop, SAVEGAME; + _InLoop(1, SAVEGAME); call fo::funcoffs::SaveGame_; - and inLoop, (-1 ^ SAVEGAME); + _InLoop(0, SAVEGAME); cmp eax, 1; jne end; + // save sfall.sav call SaveGame2; mov eax, 1; end: @@ -185,6 +211,11 @@ static void _stdcall GameReset(DWORD isGameLoad) { 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 @@ -195,20 +226,21 @@ static void _stdcall LoadGame_After() { static void __declspec(naked) LoadGame_hook() { __asm { - push ebx; - push ecx; - push edx; - or inLoop, LOADGAME; + _InLoop(1, LOADGAME); call fo::funcoffs::LoadGame_; - and inLoop, (-1 ^ LOADGAME); + _InLoop(0, LOADGAME); cmp eax, 1; jne end; + // Invoked + push ebx; + push ecx; + push edx; call LoadGame_After; mov eax, 1; -end: pop edx; pop ecx; pop ebx; +end: retn; } } @@ -282,21 +314,31 @@ static void __declspec(naked) game_reset_on_load_hook() { } } +static void __declspec(naked) before_game_exit_hook() { + __asm { + pushad; + push 1; + call GameModeChange; + popad; + jmp fo::funcoffs::map_exit_; + } +} + static void __declspec(naked) WorldMapHook() { __asm { - or inLoop, WORLDMAP; + _InLoop(1, WORLDMAP); xor eax, eax; call fo::funcoffs::wmWorldMapFunc_; - and inLoop, (-1 ^ WORLDMAP); + _InLoop(0, WORLDMAP); retn; } } static void __declspec(naked) WorldMapHook2() { __asm { - or inLoop, WORLDMAP; + _InLoop(1, WORLDMAP); call fo::funcoffs::wmWorldMapFunc_; - and inLoop, (-1 ^ WORLDMAP); + _InLoop(0, WORLDMAP); retn; } } @@ -305,31 +347,31 @@ static void __declspec(naked) CombatHook() { __asm { pushad; call AICombatStart; - popad - or inLoop, COMBAT; + _InLoop2(1, COMBAT); + popad; call fo::funcoffs::combat_; pushad; call AICombatEnd; - popad - and inLoop, (-1 ^ COMBAT); + _InLoop2(0, COMBAT); + popad; retn; } } static void __declspec(naked) PlayerCombatHook() { __asm { - or inLoop, PCOMBAT; + _InLoop(1, PCOMBAT); call fo::funcoffs::combat_input_; - and inLoop, (-1 ^ PCOMBAT); + _InLoop(0, PCOMBAT); retn; } } static void __declspec(naked) EscMenuHook() { __asm { - or inLoop, ESCMENU; + _InLoop(1, ESCMENU); call fo::funcoffs::do_optionsFunc_; - and inLoop, (-1 ^ ESCMENU); + _InLoop(0, ESCMENU); retn; } } @@ -337,35 +379,35 @@ static void __declspec(naked) EscMenuHook() { static void __declspec(naked) EscMenuHook2() { //Bloody stupid watcom compiler optimizations... __asm { - or inLoop, ESCMENU; + _InLoop(1, ESCMENU); call fo::funcoffs::do_options_; - and inLoop, (-1 ^ ESCMENU); + _InLoop(0, ESCMENU); retn; } } static void __declspec(naked) OptionsMenuHook() { __asm { - or inLoop, OPTIONS; + _InLoop(1, OPTIONS); call fo::funcoffs::do_prefscreen_; - and inLoop, (-1 ^ OPTIONS); + _InLoop(0, OPTIONS); retn; } } static void __declspec(naked) HelpMenuHook() { __asm { - or inLoop, HELP; + _InLoop(1, HELP); call fo::funcoffs::game_help_; - and inLoop, (-1 ^ HELP); + _InLoop(0, HELP); retn; } } static void __declspec(naked) CharacterHook() { __asm { - or inLoop, CHARSCREEN; pushad; + _InLoop2(1, CHARSCREEN); call PerksEnterCharScreen; popad; call fo::funcoffs::editor_design_; @@ -377,81 +419,81 @@ static void __declspec(naked) CharacterHook() { success: call PerksAcceptCharScreen; end: + _InLoop2(0, CHARSCREEN); popad; - and inLoop, (-1 ^ CHARSCREEN); retn; } } static void __declspec(naked) DialogHook() { __asm { - or inLoop, DIALOG; + _InLoop(1, DIALOG); call fo::funcoffs::gdProcess_; - and inLoop, (-1 ^ DIALOG); + _InLoop(0, DIALOG); retn; } } static void __declspec(naked) PipboyHook() { __asm { - or inLoop, PIPBOY; + _InLoop(1, PIPBOY); call fo::funcoffs::pipboy_; - and inLoop, (-1 ^ PIPBOY); + _InLoop(0, PIPBOY); retn; } } static void __declspec(naked) SkilldexHook() { __asm { - or inLoop, SKILLDEX; + _InLoop(1, SKILLDEX); call fo::funcoffs::skilldex_select_; - and inLoop, (-1 ^ SKILLDEX); + _InLoop(0, SKILLDEX); retn; } } static void __declspec(naked) HandleInventoryHook() { __asm { - or inLoop, INVENTORY; + _InLoop(1, INVENTORY); call fo::funcoffs::handle_inventory_; - and inLoop, (-1 ^ INVENTORY); + _InLoop(0, INVENTORY); retn; } } static void __declspec(naked) UseInventoryOnHook() { __asm { - or inLoop, INTFACEUSE; + _InLoop(1, INTFACEUSE); call fo::funcoffs::use_inventory_on_; - and inLoop, (-1 ^ INTFACEUSE); + _InLoop(0, INTFACEUSE); retn; } } static void __declspec(naked) LootContainerHook() { __asm { - or inLoop, INTFACELOOT; + _InLoop(1, INTFACELOOT); call fo::funcoffs::loot_container_; - and inLoop, (-1 ^ INTFACELOOT); + _InLoop(0, INTFACELOOT); retn; } } static void __declspec(naked) BarterInventoryHook() { __asm { - or inLoop, BARTER; + _InLoop(1, BARTER); push [ESP + 4]; call fo::funcoffs::barter_inventory_; - and inLoop, (-1 ^ BARTER); + _InLoop(0, BARTER); retn 4; } } static void __declspec(naked) AutomapHook() { __asm { - or inLoop, AUTOMAP; + _InLoop(1, AUTOMAP); call fo::funcoffs::automap_; - and inLoop, (-1 ^ AUTOMAP); + _InLoop(0, AUTOMAP); retn; } } @@ -459,7 +501,7 @@ static void __declspec(naked) AutomapHook() { void LoadGameHook::init() { saveInCombatFix = GetConfigInt("Misc", "SaveInCombatFix", 1); if (saveInCombatFix > 2) saveInCombatFix = 0; - saveFailMsg = Translate("sfall", "SaveInCombat", "Cannot save at this time"); + saveFailMsg = Translate("sfall", "SaveInCombat", "Cannot save at this time."); saveSfallDataFailMsg = Translate("sfall", "SaveSfallDataFail", "ERROR saving extended savegame information! Check if other programs interfere with savegame files/folders and try again!"); @@ -471,10 +513,10 @@ void LoadGameHook::init() { HookCalls(game_reset_hook, { 0x47DD6B, // LoadSlot_ (on error) 0x47DDF3, // LoadSlot_ (on error) - 0x480708, // RestoreLoad_ (never called) + //0x480708, // RestoreLoad_ (never called) 0x480AD3, // gnw_main_ (game ended after playing via New Game) 0x480BCC, // gnw_main_ (game ended after playing via Load Game) - 0x480D0C, // main_reset_system_ (never called) + //0x480D0C, // main_reset_system_ (never called) 0x481028, // main_selfrun_record_ 0x481062, // main_selfrun_record_ 0x48110B, // main_selfrun_play_ @@ -483,6 +525,7 @@ void LoadGameHook::init() { HookCalls(game_reset_on_load_hook, { 0x47F491, // PrepLoad_ (the very first step during save game loading) }); + HookCalls(before_game_exit_hook, {0x480ACE, 0x480BC7}); HookCalls(WorldMapHook, {0x483668, 0x4A4073}); HookCalls(WorldMapHook2, {0x4C4855}); @@ -526,4 +569,8 @@ Delegate<>& LoadGameHook::OnAfterNewGame() { return onAfterNewGame; } +Delegate& LoadGameHook::OnGameModeChange() { + return onGameModeChange; +} + } diff --git a/sfall/Modules/LoadGameHook.h b/sfall/Modules/LoadGameHook.h index 804e7daf4..0ad5d8275 100644 --- a/sfall/Modules/LoadGameHook.h +++ b/sfall/Modules/LoadGameHook.h @@ -43,6 +43,9 @@ class LoadGameHook : public Module { // Invoked after new game has started static Delegate<>& OnAfterNewGame(); + + // Invoked when the game mode is changed. + static Delegate& OnGameModeChange(); }; // True if some map was loaded, false when on the main menu diff --git a/sfall/Modules/Message.h b/sfall/Modules/Message.h index 2ac52a491..bb8a8b974 100644 --- a/sfall/Modules/Message.h +++ b/sfall/Modules/Message.h @@ -28,7 +28,7 @@ namespace sfall { -typedef std::tr1::unordered_map> ExtraGameMessageListsMap; +typedef std::unordered_map> ExtraGameMessageListsMap; extern ExtraGameMessageListsMap gExtraGameMsgLists; class Message : public Module { diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 5d5b679cf..8b94b79b5 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -365,6 +365,31 @@ static void __declspec(naked) objCanSeeObj_ShootThru_Fix() {//(EAX *objStruct, E } } +static DWORD __fastcall GetWeaponSlotMode(DWORD itemPtr, DWORD mode) { + int slot = (mode > 0) ? 1 : 0; + auto itemButton = fo::var::itemButtonItems; + if ((DWORD)itemButton[slot].item == itemPtr) { + int slotMode = itemButton[slot].mode; + if (slotMode == 3 || slotMode == 4) { + mode++; + } + } + return mode; +} + +static void __declspec(naked) display_stats_hook() { + __asm { + push eax; + push ecx; + mov ecx, ds:[esp + edi + 0xA8 + 0xC]; // get itemPtr + call GetWeaponSlotMode; // ecx - itemPtr, edx - mode; + mov edx, eax; + pop ecx; + pop eax; + jmp fo::funcoffs::item_w_range_; + } +} + static const DWORD EncounterTableSize[] = { 0x4BD1A3, 0x4BD1D9, 0x4BD270, 0x4BD604, 0x4BDA14, 0x4BDA44, 0x4BE707, 0x4C0815, 0x4C0D4A, 0x4C0FD4, @@ -649,6 +674,7 @@ void DisablePipboyAlarmPatch() { if (GetConfigInt("Misc", "DisablePipboyAlarm", 0)) { dlog("Applying Disable Pip-Boy alarm button patch.", DL_INIT); SafeWrite8(0x499518, 0xC3); + SafeWrite8(0x443601, 0x0); dlogr(" Done", DL_INIT); } } @@ -754,6 +780,14 @@ void DisableHorriganPatch() { } } +void DisplaySecondWeaponRangePatch() { + if (GetConfigInt("Misc", "DisplaySecondWeaponRange", 1)) { + dlog("Applying display second weapon range patch.", DL_INIT); + HookCall(0x472201, display_stats_hook); + dlogr(" Done", DL_INIT); + } +} + void MiscPatches::init() { mapName[64] = 0; if (GetConfigString("Misc", "StartingMap", "", mapName, 64)) { @@ -825,6 +859,7 @@ void MiscPatches::init() { NumbersInDialoguePatch(); PipboyAvailableAtStartPatch(); DisableHorriganPatch(); + DisplaySecondWeaponRangePatch(); } void MiscPatches::exit() { diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index fdac394ad..ce97dbdf9 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -80,7 +80,7 @@ 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::tr1::unordered_map ExportedVarsMap; +typedef std::unordered_map ExportedVarsMap; static ExportedVarsMap globalExportedVars; DWORD isGlobalScriptLoading = 0; DWORD modifiedIni; diff --git a/sfall/Modules/Scripting/Arrays.h b/sfall/Modules/Scripting/Arrays.h index 9cefda2eb..1949db9ba 100644 --- a/sfall/Modules/Scripting/Arrays.h +++ b/sfall/Modules/Scripting/Arrays.h @@ -83,7 +83,7 @@ struct sArrayVarOld #define ARRAYFLAG_ASSOC (1) // is map -typedef std::tr1::unordered_map ArrayKeysMap; +typedef std::unordered_map ArrayKeysMap; /** This class represents sfall array @@ -128,7 +128,7 @@ class sArrayVar }; // arrays map: arrayId => arrayVar -typedef std::tr1::unordered_map ArraysMap; +typedef std::unordered_map ArraysMap; extern ArraysMap arrays; typedef ArraysMap::const_iterator array_citr; typedef ArraysMap::iterator array_itr; diff --git a/sfall/Modules/Scripting/Handlers/Anims.cpp b/sfall/Modules/Scripting/Handlers/Anims.cpp index 36c936c85..3be3cb917 100644 --- a/sfall/Modules/Scripting/Handlers/Anims.cpp +++ b/sfall/Modules/Scripting/Handlers/Anims.cpp @@ -108,10 +108,12 @@ void sf_reg_anim_turn_towards(OpcodeContext& ctx) { void sf_explosions_metarule(OpcodeContext& ctx) { int mode = ctx.arg(0).asInt(), - arg1 = ctx.arg(1).asInt(), - arg2 = ctx.arg(2).asInt(); + result = ExplosionsMetaruleFunc(mode, ctx.arg(1).asInt(), ctx.arg(2).asInt()); - ctx.setReturn(ExplosionsMetaruleFunc(mode, arg1, arg2)); + if (result == -1) { + ctx.printOpcodeError("ExplosionsMetarule() - mode (%d) is not supported for the function.", mode); + } + ctx.setReturn(result); } } diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index f41d5de43..3d503959a 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -114,20 +114,13 @@ void __declspec(naked) op_get_mouse_y() { } } -void __declspec(naked) op_get_mouse_buttons() { - __asm { - push ecx; - push edx; - mov ecx, eax; - mov edx, ds:[FO_VAR_last_buttons]; - call fo::funcoffs::interpretPushLong_; - mov eax, ecx; - mov edx, VAR_TYPE_INT; - call fo::funcoffs::interpretPushShort_ - pop edx; - pop ecx; - retn; +#define MOUSE_MIDDLE_BTN (4) +void sf_get_mouse_buttons(OpcodeContext& ctx) { + DWORD button = fo::var::last_buttons; + if (button == 0 && middleMouseDown) { + button = MOUSE_MIDDLE_BTN; } + ctx.setReturn(button); } void __declspec(naked) op_get_window_under_mouse() { @@ -453,7 +446,17 @@ void sf_set_cursor_mode(OpcodeContext& ctx) { void sf_display_stats(OpcodeContext& ctx) { // calling the function outside of inventory screen will crash the game if (GetLoopFlags() & INVENTORY) { - __asm call fo::funcoffs::display_stats_ + fo::func::display_stats(); + } +} + +void sf_set_iface_tag_text(OpcodeContext& ctx) { + int boxTag = ctx.arg(0).asInt(); + + if (boxTag > 4 && boxTag < 10) { + BarBoxes::SetText(boxTag, ctx.arg(1).asString(), ctx.arg(2).asInt()); + } else { + ctx.printOpcodeError("set_iface_tag_text() - tag value must be in the range of 5 to 9."); } } diff --git a/sfall/Modules/Scripting/Handlers/Interface.h b/sfall/Modules/Scripting/Handlers/Interface.h index 69ce8d1cb..01afe81c2 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.h +++ b/sfall/Modules/Scripting/Handlers/Interface.h @@ -38,8 +38,8 @@ void __declspec() op_get_mouse_x(); //Return mouse y position void __declspec() op_get_mouse_y(); -//Return pressed mouse button (1=left, 2=right, 3=left+right) -void __declspec() op_get_mouse_buttons(); +//Return pressed mouse button (1=left, 2=right, 3=left+right, 4=middle) +void sf_get_mouse_buttons(OpcodeContext&); //Return the window number under the mous void __declspec() op_get_window_under_mouse(); @@ -89,5 +89,7 @@ void sf_set_cursor_mode(OpcodeContext&); void sf_display_stats(OpcodeContext&); +void sf_set_iface_tag_text(OpcodeContext&); + } } diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index e3ab6ad4e..2414268dc 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -57,7 +57,7 @@ struct SfallMetarule { OpcodeArgumentType argValidation[OP_MAX_ARGUMENTS]; }; -typedef std::tr1::unordered_map MetaruleTableType; +typedef std::unordered_map MetaruleTableType; static MetaruleTableType metaruleTable; @@ -102,6 +102,7 @@ static const SfallMetarule metarules[] = { {"set_cursor_mode", sf_set_cursor_mode, 1, 1, {ARG_INT}}, {"set_dude_obj", sf_set_dude_obj, 1, 1, {ARG_OBJECT}}, {"set_flags", sf_set_flags, 2, 2, {ARG_OBJECT, ARG_INT}}, + {"set_iface_tag_text", sf_set_iface_tag_text, 3, 3, {ARG_INT, ARG_STRING, ARG_INT}}, {"set_ini_setting", sf_set_ini_setting, 2, 2, {ARG_STRING, ARG_INTSTR}}, {"set_map_enter_position", sf_set_map_enter_position, 3, 3, {ARG_INT, ARG_INT, ARG_INT}}, {"set_outline", sf_set_outline, 2, 2, {ARG_OBJECT, ARG_INT}}, diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index 4ccf7527b..9d2094025 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -51,7 +51,7 @@ static const short opcodeCount = 0x300; // Other half is filled by sfall here. static void* opcodes[opcodeCount]; -typedef std::tr1::unordered_map OpcodeInfoMapType; +typedef std::unordered_map OpcodeInfoMapType; // Opcode Table. Add additional (sfall) opcodes here. // Format: { @@ -83,12 +83,13 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x218, "set_weapon_ammo_pid", sf_set_weapon_ammo_pid, 2, false, {ARG_OBJECT, ARG_INT}}, {0x219, "get_weapon_ammo_count", sf_get_weapon_ammo_count, 1, true, {ARG_OBJECT}}, {0x21a, "set_weapon_ammo_count", sf_set_weapon_ammo_count, 2, false, {ARG_OBJECT, ARG_INT}}, + {0x21e, "get_mouse_buttons", sf_get_mouse_buttons, 0, true}, {0x22d, "create_array", sf_create_array, 2, true, {ARG_INT, ARG_INT}}, {0x22e, "set_array", sf_set_array, 3, false, {ARG_OBJECT, ARG_ANY, ARG_ANY}}, {0x22f, "get_array", sf_get_array, 2, true, {ARG_ANY, ARG_ANY}}, // can also be used on strings {0x230, "free_array", sf_free_array, 1, false, {ARG_OBJECT}}, - {0x231, "len_array", sf_len_array, 1, true, {ARG_OBJECT}}, + {0x231, "len_array", sf_len_array, 1, true, {ARG_INT}}, {0x232, "resize_array", sf_resize_array, 2, false, {ARG_OBJECT, ARG_INT}}, {0x233, "temp_array", sf_temp_array, 2, true, {ARG_INT, ARG_INT}}, {0x234, "fix_array", sf_fix_array, 1, false, {ARG_INT}}, @@ -375,7 +376,6 @@ void InitNewOpcodes() { } opcodes[0x21c] = op_get_mouse_x; opcodes[0x21d] = op_get_mouse_y; - opcodes[0x21e] = op_get_mouse_buttons; opcodes[0x21f] = op_get_window_under_mouse; opcodes[0x220] = op_get_screen_width; opcodes[0x221] = op_get_screen_height; diff --git a/sfall/Modules/Tiles.cpp b/sfall/Modules/Tiles.cpp index 50158f083..484d3da01 100644 --- a/sfall/Modules/Tiles.cpp +++ b/sfall/Modules/Tiles.cpp @@ -27,6 +27,28 @@ namespace sfall { +using namespace fo; + +static const DWORD Tiles_0E[] = { + 0x484255, 0x48429D, 0x484377, 0x484385, 0x48A897, 0x48A89A, 0x4B2231, + 0x4B2374, 0x4B2381, 0x4B2480, 0x4B248D, 0x4B2A7C, 0x4B2BDA, +}; + +static const DWORD Tiles_3F[] = { + 0x41875D, 0x4839E6, 0x483A2F, 0x484380, 0x48A803, 0x48A9F2, 0x48C96C, + 0x48C99F, 0x48C9D2, 0x4B2247, 0x4B2334, 0x4B2440, 0x4B2AB9, 0x4B2B8F, + 0x4B2BF4, +}; + +static const DWORD Tiles_40[] = { + 0x48C941, 0x48C955, 0x48CA5F, 0x48CA71, 0x48CA9B, 0x48CAAD, 0x48CADB, + 0x48CAEB, 0x48CAF7, 0x48CB20, 0x48CB32, 0x48CB61, +}; + +static const DWORD Tiles_C0[] = { + 0x48424E, 0x484296, 0x484372, 0x48A88D, 0x48A892, 0x4B222C, 0x4B236F, + 0x4B247B, 0x4B2A77, 0x4B2BD5, +}; struct OverrideEntry { //DWORD id; @@ -103,7 +125,7 @@ static int ProcessTile(fo::Art* tiles, int tile, int listpos) { for (int y2 = 0; y2 < 36; y2++) { for (int x2 = 0; x2 < 80; x2++) { if (mask[y2 * 80 + x2]) { - frame.pixels[y2 * 80 + x2] = pixeldata[(yoffset + y2)*width + xoffset + x2]; + frame.pixels[y2 * 80 + x2] = pixeldata[(yoffset + y2) * width + xoffset + x2]; } else { frame.pixels[y2 * 80 + x2] = 0; } @@ -189,7 +211,7 @@ static void _stdcall SquareLoadCheck(tilestruct* data) { for (DWORD x = 0; x < 100; x++) { for (DWORD z = 0; z < 2; z++) { DWORD tile = data[y * 100 + x].tile[z]; - if (tile > 1 && tile < origTileCount&&overrides[tile]) { + if (tile > 1 && tile < origTileCount && overrides[tile]) { DWORD newtile = overrides[tile]->replacementid - 1; for (DWORD y2 = 0; y2 < overrides[tile]->ytiles; y2++) { for (DWORD x2 = 0; x2 < overrides[tile]->xtiles; x2++) { @@ -219,11 +241,56 @@ static void __declspec(naked) SquareLoadHook() { } } +static void __declspec(naked) art_id_hack() { + __asm { + cmp esi, (OBJ_TYPE_TILE << 24); // 0x4000000 + jne end; + and eax, 0x3FFF; + retn; +end: + and eax, 0x0FFF; + retn; + } +} + +static void __declspec(naked) art_get_name_hack() { + __asm { + sar eax, 24; + cmp eax, OBJ_TYPE_TILE; + jne end; + mov esi, edx; + mov ebp, edx; + and esi, 0x3FFF; + and ebp, 0xC000; + sar ebp, 0x0E; +end: + test esi, esi; + retn; + } +} + void Tiles::init() { tileMode = GetConfigInt("Misc", "AllowLargeTiles", 0); if (tileMode) { + dlog("Applying allow large tiles patch.", DL_INIT); HookCall(0x481D72, &ArtInitHook); HookCall(0x48434C, SquareLoadHook); + dlogr(" Done", DL_INIT); + } + + if (GetConfigInt("Misc", "MoreTiles", 0)) { + dlog("Applying tile FRM limit patch.", DL_INIT); + MakeCall(0x419D46, art_id_hack); + MakeCall(0x419479, art_get_name_hack); + SafeWriteBatch(0x0E, Tiles_0E); + SafeWriteBatch(0x3F, Tiles_3F); + SafeWriteBatch(0x40, Tiles_40); + SafeWriteBatch(0xC0, Tiles_C0); + if (*(long*)0x1000E1BF == 0x1000 && *(long*)0x1000E1D9 == 0x0FFF) { // Check HRP 4.1.8 + SafeWrite8(0x1000E1C0, 0x40); + SafeWrite8(0x1000E1DA, 0x3F); + } + dlogr(" Done", DL_INIT); } } diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index ed91f07b0..99bc5e68d 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -225,6 +225,14 @@ static __declspec(naked) void PathfinderFix() { } } +static void __declspec(naked) wmInterfaceInit_text_font_hook() { + __asm { + mov eax, 0x65; // normal text font + call fo::funcoffs::text_font_; + retn; + } +} + static void RestRestore() { if (!restMode) return; @@ -293,13 +301,15 @@ void TimeLimitPatch() { limit = -1; addUnarmedStatToGetYear = 1; - HookCall(0x4392F8, &GetDateWrapper); - HookCall(0x443808, &GetDateWrapper); - HookCall(0x47E127, &GetDateWrapper); - HookCall(0x4975A2, &GetDateWrapper); - HookCall(0x497712, &GetDateWrapper); - HookCall(0x4979C9, &GetDateWrapper); - HookCall(0x4C3CB5, &GetDateWrapper); + HookCalls(GetDateWrapper, { + 0x4392F8, + 0x443808, + 0x47E127, + 0x4975A2, + 0x497712, + 0x4979C9, + 0x4C3CB5 + }); dlogr(" Done", DL_INIT); } @@ -419,6 +429,14 @@ void StartingStatePatches() { } } +void WorldMapFontPatch() { + if (GetConfigInt("Misc", "WorldMapFontPatch", 0)) { + dlog("Applying world map font patch.", DL_INIT); + HookCall(0x4C2343, wmInterfaceInit_text_font_hook); + dlogr(" Done", DL_INIT); + } +} + void Worldmap::init() { PathfinderFixInit(); StartingStatePatches(); @@ -426,6 +444,7 @@ void Worldmap::init() { TownMapsHotkeyFix(); WorldLimitsPatches(); WorldmapFpsPatch(); + WorldMapFontPatch(); LoadGameHook::OnGameReset() += []() { SetCarInterfaceArt(0x1B1); diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index cf705b756..bc8641c4b 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -24,7 +24,7 @@ DynamicLibrary NotSet true - v140 + v141 DynamicLibrary @@ -35,7 +35,7 @@ DynamicLibrary NotSet - v140 + v141 @@ -66,13 +66,16 @@ true - C:\Program Files %28x86%29\Microsoft DirectX SDK\Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)lib\x86\;$(LibraryPath) - C:\Program Files %28x86%29\Microsoft DirectX SDK\Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)lib\x86\;$(LibraryPath) - C:\Program Files %28x86%29\Microsoft DirectX SDK\Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)Include;$(IncludePath);$(ProjectDir) + $(DXSDK_DIR)lib\x86\;$(LibraryPath) diff --git a/sfall/version.h b/sfall/version.h index 806be3989..7357e3389 100644 --- a/sfall/version.h +++ b/sfall/version.h @@ -24,13 +24,13 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 0 -#define VERSION_BUILD 4 +#define VERSION_BUILD 5 #define VERSION_REV 0 #ifdef WIN2K -#define VERSION_STRING "4.0.4 win2k" +#define VERSION_STRING "4.0.5 win2k" #else -#define VERSION_STRING "4.0.4" +#define VERSION_STRING "4.0.5" #endif #define CHECK_VAL (4)