diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index cbf4ffe4..b966869c 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -370,8 +370,8 @@ Movie17=credits.mve ;LocalMapXLimit=480 ;LocalMapYLimit=400 -;Set to 1 if you want the pipboy to be available at the start of the game -;Set to 2 to make the pipboy available by only skipping the vault suit movie check +;Set to 1 to make the pipboy and the vault suit available when the game starts +;Set to 2 to make only the pipboy available PipBoyAvailableAtGameStart=0 ;Set to 1 to double the number of available kill types diff --git a/artifacts/scripting/sfall function notes.md b/artifacts/scripting/sfall function notes.md index c2945726..64db9e99 100644 --- a/artifacts/scripting/sfall function notes.md +++ b/artifacts/scripting/sfall function notes.md @@ -5,8 +5,7 @@ As well as the new functions, sfall also adds global scripts. These run independ To use a global script, the script must have a name which begins with `gl` and contains a procedure called `start`, `map_enter_p_proc`, `map_exit_p_proc`, or `map_update_p_proc`. The `start` procedure will be executed once when the player loads a saved game or starts a new game. The `map_*_p_proc` procedures will be executed once when a map is being entered/left/updated. If you wish the script to be executed repeatedly, call `set_global_script_repeat` on the first run of the `start` procedure using the number of frames between each run as the argument (0 disables the script, 1 runs it every frame, 2 runs it every other frame, etc.). -Global scripts have multiple modes, which can be set using the `set_global_script_type` function. In the default mode (i.e. mode 0) their execution is linked to the local map game loop, so the script will not run in dialogs or on the world map. In mode 1 their execution is linked to the player input, and so they will run whenever the mouse cursor is visible on screen, including the world map, character dialogs, etc. In mode 2, execution is linked to the world map loop, so the script will only be executed on the world map and not on the local map or in any dialog windows. Mode 3 is a combination of modes 0 and 2, so scripts will be executed on both local maps and the world map, but not in dialog windows.\ -_Using mode 1 requires the input wrapper to be enabled. Use available_global_script_types to check what is available._ - **Obsolete**. +Global scripts have multiple modes, which can be set using the `set_global_script_type` function. In the default mode (i.e. mode 0) their execution is linked to the local map game loop, so the script will not run in dialogs or on the world map. In mode 1 their execution is linked to the player input, and so they will run whenever the mouse cursor is visible on screen, including the world map, character dialogs, etc. In mode 2, execution is linked to the world map loop, so the script will only be executed on the world map and not on the local map or in any dialog windows. Mode 3 is a combination of modes 0 and 2, so scripts will be executed on both local maps and the world map, but not in dialog windows. ---------------------- @@ -24,8 +23,6 @@ The `get_pc_base_stat`, `set_pc_base_stat`, `get_pc_extra_stat` and `set_pc_extr The `set_stat_min` and `set_stat_max` functions can be used to set the valid ranges on stats. Values returned by `get_current_stat` will be clamped to this range. The `set_pc_stat_*` functions only affect the player, the `set_npc_stat_*` functions only affect other critters, and the `set_stat_*` functions affect both. -The input functions are only available if the user has the input hook turned on in **ddraw.ini**. Use `input_funcs_available` to check. - The graphics functions are only available if the user is using graphics mode 4 or 5. Use `graphics_funcs_available` to check; it returns 1 if you can use them or 0 if you can't. Calling graphics functions when `graphics_funcs_available` returns 0 will do nothing. The `load_shader` takes a path relative to the `\\shaders\` directory as an argument and returns a shader ID. That ID should be passed as the first argument to all other shader functions, and is valid until `free_shader` is called on the ID, the player loads a saved game or the player quits to the main menu. @@ -62,8 +59,6 @@ The `get_critical_table`, `set_critical_table` and `reset_critical_table` are us The `get_unspent_ap_bonus` and `set_unspent_ap_bonus` alter the AC bonus you receive per unused action point at the end of your turn in combat. To allow for fractional values, the value given if divided by 4. (Hence the default value is 4 and not 1.) The `get_unspent_ap_perk_bonus` and `set_unspent_ap_perk_bonus` are similar, but affect the extra AC granted by the HtH Evade perk. (The default value of this is also 4, equivalent to doubling the original bonus.) -The `nb_*` functions are reserved for the brotherhood tactical training mod, and should be avoided. - The `fs_*` functions are used to manipulate a virtual file system. Files saved here should have paths relative to the data folder, and use backslashes as the directory separator. They will take precedence over files stored in the normal data folder. They will also be saved into save games if you set a flag for them using `fs_resize(fileId, -1)`, so be avoid creating large files. Using `fs_copy` followed by `fs_read_xxx`, you can read the contents of existing files. The `get_proto_data` and `set_proto_data` are used to manipulate the in memory copies of the **.pro** files Fallout makes when they are loaded. The offset refers to the offset in memory from the start of the proto to the element you are reading. Changes are not stored on disc, and are not permanent. If you modify the protos, and then Fallout subsequently reloads the file your changes will be lost. @@ -71,7 +66,7 @@ The `get_proto_data` and `set_proto_data` are used to manipulate the in memory c The `list_xxx` functions can be used to loop over all items on a map. `list_begin` takes an argument telling sfall what you want to list (defined in **sfall.h**). It returns a list pointer, which you iterate through with `list_next`. Finally, when you've finished with the list use `list_end` on it. Not calling `list_end` will result in a memory leak. Alternatively, use `list_as_array` to get the whole list at once as a temp array variable, which can be looped over using `len_array` and which you don't need to remember to free afterwards. The `play_sfall_sound` and `stop_sfall_sound` are used to play **mp3/wav/wma** files. The path given is relative to the Fallout folder. Specify mode as 1 to loop the file continuously, 2 to replace the current background game music with playing the specified file in loop mode, or 0 to play the file once. If you don't wish to loop, `play_sfall_sound` returns 0. If you do loop, it returns an ID which can be passed back to `stop_sfall_sound` when you want to stop the effect. All sounds effects will be stopped on game reload, looping or not. These functions do not require **AllowDShowSound** to be set to 1 in **ddraw.ini**. -Starting from sfall 4.2.8/3.8.28, you can pass a value in the **mode** argument for a reduced sound volume. To set the volume, You need to convert the number to hexadecimal and use the argument format `0xZZZZ000Y`, where `ZZZZ` is the volume reduction value in range from 0 to 32767 (the value 32767 is muted), and `Y` is the playback mode. +Starting from sfall 4.2.8/3.8.28, you can pass a value in the **mode** argument for a reduced sound volume. To set the volume, you need to convert the number to hexadecimal and use the argument format `0xZZZZ000Y`, where `ZZZZ` is the volume reduction value in range from 0 to 32767 (the value 32767 is muted), and `Y` is the playback mode. Arrays are created and manipulated with the `xxx_array` functions. An array must first be created with `create_array` or `temp_array`, specifying how many data elements the array can hold. You can store any of ints, floats and strings in an array, and can mix all 3 in a single array. The ID returned by `create_array` or `temp_array` can then be used with the other array functions. Arrays are shared between all scripts. (i.e. you can call `create_array` from one script, and then use the returned ID from another script.) They are also saved across savegames. Arrays created with `temp_array` will be automatically freed at the end of the frame. These functions are safe, in that supplying a bad ID or trying to access out of range elements will not crash the script. `create_array` is the only function that returns a permanent array, all other functions which return arrays (`string_split`, `list_as_array`, etc,) all return temp arrays. You can use `fix_array` to make a temp array permanent.\ __NOTE:__ refer to **arrays.md** for detailed description of the array behavior and function usage. @@ -81,6 +76,17 @@ The `force_aimed_shots` and `disable_aimed_shots` allow overriding the normal ru The `get_critter_skill_points` and `set_critter_skill_points` will get or 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. +-------------------- +DEPRECATED FUNCTIONS +-------------------- + +Using global scripts in mode 1 requires the input wrapper to be enabled. Use `available_global_script_types` to check what is available. + +The input functions are only available if the user has the input hook turned on in **ddraw.ini**. Use `input_funcs_available` to check. + +The `nb_create_char` function is reserved for the brotherhood tactical training mod, and should be avoided. + + --------------- TYPES REFERENCE --------------- @@ -106,7 +112,9 @@ FUNCTION REFERENCE ----- ##### `void set_pipboy_available(int available)` -- Sets the availability of the pipboy in the game. Use 0 to disable the pipboy, and 1 or 2 to enable it (value 2 does not mark the `VSUIT_MOVIE` movie as "played"). +- Sets the availability of the pipboy in the game. Use 0 to disable the pipboy, and 1 or 2 to enable it (value 2 does not change the player's default appearance). +- The availability state will be reset each time the player reloads the game. +- __NOTE:__ Starting from sfall 4.4.6/3.8.46, value 1 no longer marks the `VSUIT_MOVIE` movie as "played". Use `mark_movie_played(VSUIT_MOVIE)` instead if you want the old behavior. ----- ##### `void inc_npc_level(int pid/string name)` diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index 0605fc94..92893039 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -80,8 +80,6 @@ namespace sfall static DWORD inLoop = 0; static DWORD saveInCombatFix; -static bool disableHorrigan = false; -static bool pipBoyAvailableAtGameStart = false; static bool gameLoaded = false; static bool onLoadingMap = false; @@ -141,7 +139,6 @@ void __stdcall SetInLoop(DWORD mode, LoopFlag flag) { static void __stdcall RunOnBeforeGameStart() { Criticals::ApplyCritTable(); ReadExtraGameMsgFiles(); - if (pipBoyAvailableAtGameStart) fo::ptr::gmovie_played_list[3] = true; // PipBoy aquiring video Combat::OnBeforeGameStart(); LoadGlobalScripts(); // loading sfall scripts } @@ -857,19 +854,6 @@ void LoadGameHook::init() { saveInCombatFix = IniReader::GetConfigInt("Misc", "SaveInCombatFix", 1); if (saveInCombatFix > 2) saveInCombatFix = 0; - switch (IniReader::GetConfigInt("Misc", "PipBoyAvailableAtGameStart", 0)) { - case 1: - pipBoyAvailableAtGameStart = true; - break; - case 2: - SafeWrite8(0x497011, CodeType::JumpShort); // skip the vault suit movie check - break; - } - - if (IniReader::GetConfigInt("Misc", "DisableHorrigan", 0)) { - disableHorrigan = true; - } - HookCall(0x482AEC, map_load_hook); HookCall(0x4809BA, main_init_system_hook); HookCall(0x4426A6, game_init_hook); diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 8dd64193..6ca5be03 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -37,6 +37,8 @@ static char versionString[65] = {}; static int* scriptDialog = nullptr; +bool disableHorrigan = false; + static void __stdcall Sleep2(DWORD dwMilliseconds) { Sleep(dwMilliseconds); } @@ -701,6 +703,22 @@ static void MusicInDialoguePatch() { } } +static void PipboyAvailableAtStartPatch() { + switch (IniReader::GetConfigInt("Misc", "PipBoyAvailableAtGameStart", 0)) { + case 1: + SafeWrite16(0x49F9AF, 0x9090); // skip the vault suit movie check (proto_dude_update_gender_) + case 2: + SafeWrite8(0x497011, CodeType::JumpShort); // skip the vault suit movie check (pipboy_) + break; + } +} + +static void DisableHorriganPatch() { + if (IniReader::GetConfigInt("Misc", "DisableHorrigan", 0)) { + disableHorrigan = true; + } +} + static void DisplaySecondWeaponRangePatch() { // Display the range of the secondary attack mode in the inventory when you switch weapon modes in active item slots //if (IniReader::GetConfigInt("Misc", "DisplaySecondWeaponRange", 1)) { @@ -1101,6 +1119,8 @@ void MiscPatches::init() { InstantWeaponEquipPatch(); NumbersInDialoguePatch(); + PipboyAvailableAtStartPatch(); + DisableHorriganPatch(); DisplaySecondWeaponRangePatch(); KeepSelectModePatch(); diff --git a/sfall/Modules/MiscPatches.h b/sfall/Modules/MiscPatches.h index 1a4d65f6..625c4011 100644 --- a/sfall/Modules/MiscPatches.h +++ b/sfall/Modules/MiscPatches.h @@ -32,4 +32,6 @@ public: static void OnGameReset(); }; +extern bool disableHorrigan; + } diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index a91ad6b2..f2a452cd 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -45,28 +45,32 @@ __declspec(naked) void op_input_funcs_available() { } } -static BYTE pipboyMovieCheck = 0; +static WORD pipboyMovieCheck = 0; +static WORD defAppMovieCheck = 0; void PipboyAvailableRestore() { if (pipboyMovieCheck) { - SafeWrite8(0x497011, pipboyMovieCheck); + SafeWrite16(0x497011, pipboyMovieCheck); pipboyMovieCheck = 0; + if (defAppMovieCheck) { + SafeWrite16(0x49F9AF, defAppMovieCheck); + defAppMovieCheck = 0; + } } } void op_set_pipboy_available(OpcodeContext& ctx) { - if (!pipboyMovieCheck) pipboyMovieCheck = *(BYTE*)0x497011; // should be either jnz or jmp(short) + if (!pipboyMovieCheck) pipboyMovieCheck = *(WORD*)0x497011; // should be either jnz or jmp(short) switch (ctx.arg(0).rawValue()) { case 0: - fo::ptr::gmovie_played_list[3] = false; - SafeWrite8(0x497011, CodeType::JumpNZ); // restore the vault suit movie check + SafeWrite16(0x497011, 0x9090); // disable the pipboy break; case 1: - fo::ptr::gmovie_played_list[3] = true; - break; + if (!defAppMovieCheck) defAppMovieCheck = *(WORD*)0x49F9AF; // should be either jz or nop + SafeWrite16(0x49F9AF, 0x9090); // skip the vault suit movie check (proto_dude_update_gender_) case 2: - SafeWrite8(0x497011, CodeType::JumpShort); // skip the vault suit movie check + SafeWrite16(0x497011, 0x37EB); // skip the vault suit movie check (pipboy_) break; } } diff --git a/sfall/Modules/Skills.cpp b/sfall/Modules/Skills.cpp index 67a28569..0fd55886 100644 --- a/sfall/Modules/Skills.cpp +++ b/sfall/Modules/Skills.cpp @@ -420,7 +420,7 @@ void Skills::init() { if (tagBonus != 20 && tagBonus >=0 && tagBonus <= 100) SafeWrite8(0x4AA61E, static_cast(tagBonus)); // skill_level_ int tagMode = IniReader::GetInt("Skills", "TagSkillMode", 0, file); - if (tagMode & 1) SafeWrite8(0x4AA612, 0xEB); // 4th tag skill can have initial skill bonus. skill_level_ (jz > jmp) + if (tagMode & 1) SafeWrite8(0x4AA612, CodeType::JumpShort); // 4th tag skill can have initial skill bonus. skill_level_ (jz > jmp) if (tagMode & 2) SafeWrite16(0x4AA60E, 0x9090); // disables double skill points bonus for tag skills. skill_level_ } }