def Enable(self) -> None: def SpawnPopulationControlledActor(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller != self.CurrentPopMaster: self.CurrentPopMaster = caller self.OriginalLimit = caller.MaxActorCost if self.SpawnLimitSpinner.CurrentValue == "Linear": caller.MaxActorCost *= self.MultiplierSlider.CurrentValue elif self.SpawnLimitSpinner.CurrentValue == "Unlimited": caller.MaxActorCost = 0x7FFFFFFF return True def PostBeginPlay(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: unrealsdk.DoInjectedCallNext() caller.PostBeginPlay() self.MultiplyDenIfAble(caller, self.MultiplierSlider.CurrentValue) return False for den in unrealsdk.FindAll("PopulationOpportunityDen"): self.MultiplyDenIfAble(den, self.MultiplierSlider.CurrentValue) self.OldMultiplier = self.MultiplierSlider.CurrentValue unrealsdk.RunHook( "GearboxFramework.PopulationMaster.SpawnPopulationControlledActor", self.Name, SpawnPopulationControlledActor) unrealsdk.RunHook("WillowGame.PopulationOpportunityDen.PostBeginPlay", self.Name, PostBeginPlay)
def Enable(self) -> None: def GiveTo(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if params.Other != unrealsdk.GetEngine().GamePlayers[0].Actor.Pawn: return True if not self.CanItemBeEquipped(caller): if params.Other.InvManager is None: return True params.Other.InvManager.ClientConditionalIncrementPickupStats( caller) # Force bReady False so that you don't force equip params.Other.InvManager.AddInventory(caller, False, False, params.bPlayPickupSound) return False return True def CanBeUsedBy(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if params.Other != unrealsdk.GetEngine().GamePlayers[0].Actor.Pawn: return True return self.CanItemBeEquipped(caller) def SetItemCardEx(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if params.InventoryItem.ObjectPointer is None: return True if self.CanItemBeEquipped(params.InventoryItem.ObjectPointer): return True caller.SetItemCardEx(params.WPC, params.InventoryItem.ObjectPointer, params.CompareAgainstInventoryItem, params.CurrencyType, params.OverrideValue) caller.SetLevelRequirement(True, False, False, self.UnableToEquipMessage) return False def InventoryShouldBeReadiedWhenEquipped( caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller.Owner != unrealsdk.GetEngine().GamePlayers[0].Actor.Pawn: return True return self.CanItemBeEquipped(params.WillowInv) unrealsdk.RunHook("Engine.Inventory.GiveTo", self.Name, GiveTo) unrealsdk.RunHook("Engine.WillowInventory.CanBeUsedBy", self.Name, CanBeUsedBy) unrealsdk.RunHook("WillowGame.ItemCardGFxObject.SetItemCardEx", self.Name, SetItemCardEx) unrealsdk.RunHook( "WillowGame.WillowInventoryManager.InventoryShouldBeReadiedWhenEquipped", self.Name, InventoryShouldBeReadiedWhenEquipped)
def Enable(self) -> None: def UpdateOpportunityEnabledStates(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: # Seems to be -1 on map load - though I've never seen it get called at another time if params.nWave != -1: return True self.MultiplePopEncounterIfAble(caller, self.MultiplierSlider.CurrentValue) return True def SpawnPopulationControlledActor(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller != self.CurrentPopMaster: self.CurrentPopMaster = caller self.OriginalLimit = caller.MaxActorCost if self.SpawnLimitSpinner.CurrentValue == SpawnLimitType.Linear: caller.MaxActorCost *= self.MultiplierSlider.CurrentValue elif self.SpawnLimitSpinner.CurrentValue == SpawnLimitType.Unlimited: caller.MaxActorCost = 0x7FFFFFFF return True def PostBeginPlay(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: unrealsdk.DoInjectedCallNext() caller.PostBeginPlay() self.MultiplyDenIfAble(caller, self.MultiplierSlider.CurrentValue) return False for den in unrealsdk.FindAll("PopulationOpportunityDen"): self.MultiplyDenIfAble(den, self.MultiplierSlider.CurrentValue) for encounter in unrealsdk.FindAll("PopulationEncounter"): self.MultiplePopEncounterIfAble(encounter, self.MultiplierSlider.CurrentValue) self.OldMultiplier = self.MultiplierSlider.CurrentValue unrealsdk.RunHook( "GearboxFramework.PopulationEncounter.UpdateOpportunityEnabledStates", self.Name, UpdateOpportunityEnabledStates) unrealsdk.RunHook( "GearboxFramework.PopulationMaster.SpawnPopulationControlledActor", self.Name, SpawnPopulationControlledActor) unrealsdk.RunHook("WillowGame.PopulationOpportunityDen.PostBeginPlay", self.Name, PostBeginPlay)
def _FrontEndPopulate(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called to create the front end menu. We use it to replace the DLC caption and event id. """ def AddListItem(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called every time an item is added to *any* menu list - we obviously can't use a generic hook. Using it cause it simplifies the code to replace the caption. """ if params.Caption == "$WillowMenu.WillowScrollingListDataProviderFrontEnd.DLC": unrealsdk.DoInjectedCallNext() caller.AddListItem(_MODS_EVENT_ID, _MODS_MENU_NAME, False, False) return False return True unrealsdk.RunHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.MenuManager", AddListItem) unrealsdk.DoInjectedCallNext() caller.Populate(params.TheList) unrealsdk.RemoveHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.MenuManager") return False
def _TopLevelOptionsPopulate(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called to create the options menu. We use it to inject our `MODS` menu. """ # If not mods have accessable options, we want to disable the mods entry disabled = True for mod in ModObjects.Mods: if not mod.IsEnabled: continue if _is_anything_shown(mod.Options): disabled = False break def AddListItem(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called every time an item is added to *any* menu list - we obviously can't use a generic hook. Using it cause it simplifies the code to add our own entry. """ if params.Caption == "$WillowGame.WillowScrollingList.BackCaption": caller.AddListItem(_MOD_OPTIONS_EVENT_ID, _MOD_OPTIONS_MENU_NAME, disabled, False) return True unrealsdk.RunHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.OptionManager", AddListItem) unrealsdk.DoInjectedCallNext() caller.Populate(params.TheList) unrealsdk.RemoveHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.OptionManager") return False
def ApplyWeaponSaveGameData(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ We need to call `FixupSavedWeapons` at the start here, but it uses an out argument. If we block `ValidateWeaponDefinition` and `IsLocalPlayerController`, so they return false, this function will do nothing except make that call for us """ unrealsdk.RunHook( "WillowGame.WillowPlayerController.IsLocalPlayerController", __name__, Block) unrealsdk.RunHook( "WillowGame.WillowPlayerController.ValidateWeaponDefinition", __name__, Block) caller.ApplyWeaponSaveGameData(params.SaveGame) unrealsdk.RemoveHook( "WillowGame.WillowPlayerController.IsLocalPlayerController", __name__) unrealsdk.RemoveHook( "WillowGame.WillowPlayerController.ValidateWeaponDefinition", __name__) # Now we can recreate the call like normal inv_manager = caller.GetPawnInventoryManager() is_local = caller.IsLocalPlayerController() for idx, saved_weap in enumerate(params.SaveGame.WeaponData): if saved_weap.QuickSlot != 0: if caller.Role == 3: # ROLE_Authority ServerSetWeaponSaveGameData(caller, idx, saved_weap.WeaponDefinitionData, saved_weap.QuickSlot, saved_weap.Mark) continue if is_local: inv_manager.ClientAddWeaponToBackpack( expand_weapon_definition_data(saved_weap.WeaponDefinitionData), saved_weap.Mark, False, 14) if is_local: caller.UnloadableDlcWeaponData = list( params.SaveGame.UnloadableDlcWeaponData) caller.ServerItemSaveGameDataCompleted() return False
def OnStart(self, msg: JSON) -> None: self.ShowRedemption(msg) def BlockCall(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: return False unrealsdk.RunHook("WillowGame.WillowPlayerPawn.TakeDamage", "CCGodMode", BlockCall)
def _enqueue_message(message: _Message) -> None: """ Add a message to the message queue, sending it if message queue is empty. """ _message_queue.append(message) # If this was the first message to be added to the queue, send it now, and register our tick # hook to observe for its timeout. if len(_message_queue) == 1: message.send() unrealsdk.RunHook("Engine.PlayerController.PlayerTick", "ModMenu.NetworkManager", _PlayerTick)
def OnStart(self, msg: JSON) -> None: self.ShowRedemption(msg) def BlockCall(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: return False unrealsdk.RunHook( "WillowGame.WillowWeapon.ShouldAutoReloadWhileFiring", "CCManualReloads", BlockCall)
def Enable(self) -> None: def AddChatMessage(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if unrealsdk.GetEngine( ).SparkInterface.ObjectPointer.IsSparkEnabled(): return True time = caller.GetTimestampString( unrealsdk.FindAll("WillowSaveGameManager")[-1].TimeFormat) caller.AddChatMessageInternal(params.PRI.PlayerName + time, params.msg) return False def DisplayOkBoxTextFromSpark(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if not self.WarningOption.CurrentValue: return True if params.Section == "dlgCouldNotConnectSHiFT": caller.Close() return False return True def DoSparkAuthentication(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if self.ForceOption.CurrentValue: caller.ShouldStartSparkInitialization = False return True unrealsdk.RunHook("WillowGame.TextChatGFxMovie.AddChatMessage", self.Name, AddChatMessage) unrealsdk.RunHook( "WillowGame.WillowGFxDialogBox.DisplayOkBoxTextFromSpark", self.Name, DisplayOkBoxTextFromSpark) unrealsdk.RunHook( "WillowGame.WillowGFxMoviePressStart.DoSparkAuthentication", self.Name, DoSparkAuthentication)
def _KeyboardMouseOptionsPopulate(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called to create the kb/m settings menu. We use it to inject our own "MODDED KEYBINDS" menu. """ # If we have no modded binds, disable the menu disabled = True for mod in ModObjects.Mods: if not mod.IsEnabled: continue for input in mod.Keybinds: if isinstance(input, Keybind) and input.IsHidden: continue disabled = False break if not disabled: break def AddListItem(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called every time an item is added to *any* menu list - we obviously can't use a generic hook. Using it cause it simplifies the code to add our own entry. """ if params.Caption != "$WillowMenu.MenuOptionDisplayNames.KeyBinds": return True # Want ours to display after the normal keybinds option unrealsdk.DoInjectedCallNext() caller.AddListItem(params.EventID, params.Caption, params.bDisabled, params.bNew) caller.AddListItem(_MODDED_EVENT_ID, _MODDED_KEYBINDS_CAPTION, disabled, False) return False unrealsdk.RunHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.KeybindManager", AddListItem) unrealsdk.DoInjectedCallNext() caller.Populate(params.TheList) caller.AddDescription(_MODDED_EVENT_ID, "$WillowMenu.MenuOptionDisplayNames.KeyBindsDesc") unrealsdk.RemoveHook("WillowGame.WillowScrollingList.AddListItem", "ModMenu.KeybindManager") return False
def apply_hook(function: AnyHook) -> AnyHook: # If the function has four parameters, it should be a method. params = signature(function).parameters is_method = (len(params) == 4) # Retrieve the function's dictionary of targets. If it does not yet have one, we preform # initial setup on it now. hook_targets = getattr(function, "HookTargets", None) if hook_targets is None: param_exception = ValueError( "Hook functions must have the signature" " ([self,] caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct)" ) # If the function is an instance method, create a mutable list of the parameters and # remove the `self` one, so we may check the remaining ones same as a non-method. param_list = list(params.values()) if is_method: del param_list[0] # If the function has neither 4 nor 3 parameters, it is invalid. elif len(param_list) != 3: raise param_exception # If the functions parameters do not accept positional arguments, it is invalid. for param in param_list: if Parameter.POSITIONAL_ONLY != param.kind != Parameter.POSITIONAL_OR_KEYWORD: raise param_exception # If the function is a method, store the name format string on it for formatting with # future instances. If it's a simple function, format its name for use now. function.HookName = name if is_method else name.format( # type: ignore f"{function.__module__}.{function.__qualname__}", id(function) ) # With the function now known as valid, create its set of targets. hook_targets = function.HookTargets = set() # type: ignore hook_targets.add(target) if not is_method: unrealsdk.RunHook(target, function.HookName, function) # type: ignore return function
def OnStart(self, msg: JSON) -> None: user = "******" try: user = msg["data"]["redemption"]["user"]["login"] except KeyError: pass # Can't exactly show this on the hud, we'll let other messages stay hidden though :) ShowChatMessage("Crowd Control:", f"{user} redeemed '{self.Name}'", ShowTimestamp=False) unrealsdk.GetEngine().GamePlayers[0].Actor.HideHUD() def BlockCall(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: return False # Pausing/unpausing will make it show again so got to block that unrealsdk.RunHook("WillowGame.WillowHUDGFxMovie.Start", "CCHideHUD", BlockCall)
def RegisterHooks(obj: object) -> None: """ Registers all `@Hook` decorated methods for the object. Said methods will subsequently be called in response to the hooked Unreal Engine methods. Args: obj: The object for which to register method hooks. """ # Create a weak reference to the object which we may use in attributes on it without creating # cyclical references. Before destruction, `RemoveHooks` should be called on the object to # ensure there are no remaining hooks that reference it. obj_ref = weakref.ref(obj, RemoveHooks) # Iterate over each attribute on the object's class that contains a function. for attribute_name, function in type(obj).__dict__.items(): if not callable(function): continue # Attempt to get the set of hook targets from the function. If it doesn't have one, or if # its signature doesn't have 4 parameters, it is not a hook method. hook_targets = getattr(function, "HookTargets", None) if hook_targets is None or len(signature(function).parameters) != 4: continue # Create a wrapper to replace the descriptor of the attribute, "binding" the function to the # mod's weak reference, in a function that can be passed to `unrealsdk.RunHook`. method_wrapper = _create_method_wrapper(obj_ref, function) setattr(obj, attribute_name, method_wrapper) # Format the provided hook name. method_wrapper.HookName = function.HookName.format( # type: ignore f"{function.__module__}.{function.__qualname__}", id(obj) ) for target in hook_targets: unrealsdk.RunHook(target, method_wrapper.HookName, method_wrapper) # type: ignore
dh.PrintWarning(KeybindManager.Keybind._list_deprecation_warning) if input[0] in saved_keybinds: input[1] = saved_keybinds[input[0]] if settings.get(_ENABLED_CATEGORY_NAME, False): if mod.SaveEnabledState == ModObjects.EnabledSaveType.LoadWithSettings: if not mod.IsEnabled: mod.SettingsInputPressed("Enable") elif mod.SaveEnabledState == ModObjects.EnabledSaveType.LoadOnMainMenu: _mods_to_enable_on_main_menu.add(mod) def _FrontendGFxMovieStart(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called upon reaching the main menu, after hotfix objects already exist and all the main packages are loaded. We use it to enable all `LoadOnMainMenu` mods. """ for mod in _mods_to_enable_on_main_menu: if not mod.IsEnabled: mod.SettingsInputPressed("Enable") _mods_to_enable_on_main_menu.clear() return True unrealsdk.RunHook("WillowGame.FrontendGFxMovie.Start", "ModMenu.SettingsManager", _FrontendGFxMovieStart)
def Enable(self) -> None: for hook, func_list in ALL_HOOKS.items(): for i, func in enumerate(func_list): unrealsdk.RunHook(hook, f"{self.Name}{i}", func)
if in_option_list(option.Children): return True return False for mod in unrealsdk.Mods: if in_option_list(mod.Options): # Calling this before updating the value mod.ModOptionChanged(changed_option, new_value) changed_option.CurrentValue = new_value break return True unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderTopLevelOptions.Populate", "ModMenu.OptionManager", _TopLevelOptionsPopulate) unrealsdk.RunHook("WillowGame.WillowScrollingList.OnClikEvent", "ModMenu.OptionManager", _WillowScrollingListOnClikEvent) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderOptionsBase.Populate", "ModMenu.OptionManager", _DataProviderOptionsBasePopulate) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderOptionsBase.OnPop", "ModMenu.OptionManager", _DataProviderOptionsBaseOnPop) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderOptionsBase.HandleSpinnerChange", "ModMenu.OptionManager", _HandleSpinnerSliderChange) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderOptionsBase.HandleSliderChange", "ModMenu.OptionManager", _HandleSpinnerSliderChange)
if caller.CanShowSpectatorControls(): tooltip += caller.TooltipSpacing + caller.SpectatorTooltip if caller.CanShowCharacterSelect(-1): tooltip += caller.TooltipSpacing + caller.CharacterSelectTooltip if caller.WPCOwner.WorldInfo.NetMode != 3: tooltip += caller.TooltipSpacing + caller.NetworkOptionsTooltip # Only show on the main menu, not also the pause menu if caller.Class.Name == "FrontendGFxMovie": tooltip += caller.TooltipSpacing + "[M] Mods" if caller.MyFrontendDefinition is not None: caller.SetVariableString( caller.MyFrontendDefinition.TooltipPath, caller.ResolveDataStoreMarkup(tooltip) ) return False unrealsdk.RunHook("WillowGame.WillowScrollingListDataProviderFrontEnd.Populate", "ModMenu.MenuManager", _FrontEndPopulate) unrealsdk.RunHook("WillowGame.MarketplaceGFxMovie.RefreshDLC", "ModMenu.MenuManager", _RefreshDLC) unrealsdk.RunHook("WillowGame.MarketplaceGFxMovie.OnDownloadableContentListRead", "ModMenu.MenuManager", _OnDownloadableContentListRead) unrealsdk.RunHook("WillowGame.MarketplaceGFxMovie.ShopInputKey", "ModMenu.MenuManager", _ShopInputKey) unrealsdk.RunHook("WillowGame.MarketplaceGFxMovie.extOnOfferingChanged", "ModMenu.MenuManager", _extOnOfferingChanged) unrealsdk.RunHook("WillowGame.WillowScrollingListDataProviderFrontEnd.HandleClick", "ModMenu.MenuManager", _FrontEndHandleClick) unrealsdk.RunHook("WillowGame.FrontendGFxMovie.SharedHandleInputKey", "ModMenu.MenuManager", _SharedHandleInputKey) unrealsdk.RunHook("WillowGame.FrontendGFxMovie.UpdateTooltips", "ModMenu.MenuManager", _FrontEndUpdateTooltips)
unrealsdk.Log( f"Unable to deserialize arguments for '{message_type}'") tb = traceback.format_exc().split('\n') unrealsdk.Log(f" {tb[-4].strip()}") unrealsdk.Log(f" {tb[-3].strip()}") unrealsdk.Log(f" {tb[-2].strip()}") if arguments is not None: for method in methods: try: method(*arguments["args"], **arguments["kwargs"]) except Exception: unrealsdk.Log( f"Unable to call remotely requested {method}.") tb = traceback.format_exc().split('\n') unrealsdk.Log(f" {tb[-4].strip()}") unrealsdk.Log(f" {tb[-3].strip()}") unrealsdk.Log(f" {tb[-2].strip()}") caller.ServerSpeech(message_id, 0, "unrealsdk.__clientack__") return False unrealsdk.RunHook("Engine.PlayerController.ServerSpeech", "ModMenu.NetworkManager", _server_speech) unrealsdk.RunHook("WillowGame.WillowPlayerController.ClientMessage", "ModMenu.NetworkManager", _client_message) unrealsdk.RunHook("Engine.GameInfo.Logout", "ModMenu.NetworkManager", _Logout) unrealsdk.RunHook("Engine.GameViewportClient.GameSessionEnded", "ModMenu.NetworkManager", _GameSessionEnded)
if time < now: for callbk in callbk_list: try: callbk() except Exception: unrealsdk.Log("[AsyncUtil] Exception thrown:") for line in traceback.format_exc().split("\n"): unrealsdk.Log(line) if time != datetime.min: del _Callbacks[time] else: break return True unrealsdk.RunHook("WillowGame.WillowGameViewportClient.Tick", "AsyncUtil", _OnTick) def RunIn(time: float, callbk: Callback, key: str = "") -> None: """ Runs a callback in the provided amount of seconds. Args: time: The amount of time (in seconds) to wait before running the callback. callbk: The callback. key: An optional key to help identify the callback if you want to cancel it later. """ if time <= 0: raise ValueError("RunIn() requires a non-zero, positive, time!") if len(key) == 0:
def Open(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller.ChestIsOpen: return False # We'll set this later cause we re-call this function and don't want it to early exit PC = caller.Outer.GetOwningPlayerController() if PC is None: return False inv_manager = PC.GetPawnInventoryManager() save_manager: SaveManager if caller == inv_manager.TheBank: save_manager = SaveManager(PC.SaveGameName, True) elif caller == inv_manager.TheStash: save_manager = SaveManager(STASH_NAME) else: unrealsdk.Log("[SanitySaver] Could not identify opened container!") return False save_manager.load() PC.OnChestOpened(caller) """ This is a bit of a mess. All items are stored as serial number structs, which contain a static array. This means we can't ever pass one of these structs, or even whole the list, from Python back into UnrealScript. So how do we convert these to a useable format? `WillowInventory.CreateInventoryFromSerialNumber` We still can't call this ourself though, we have to get something else to. And unfortuantly it happens to only be called by the very function we have to overwrite to remove the sanity check in the first place. The only way we can actually extract item references is by hooking the sanity check functions. Hooking them means we have to force all items to get sanity checked and be destroyed, but luckily there's handy functions to recreate them from the definition with our changes. """ inv_list = [] def ValidateItemWeaponDefinition(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: is_weapon = function.Name == "ValidateWeaponDefinition" inv_list.append( (save_manager.apply_replacements(params.DefinitionData, is_weapon), is_weapon)) return False unrealsdk.RunHook("WillowGame.WillowPlayerController.OnChestOpened", __name__, Block) unrealsdk.RunHook( "WillowGame.WillowPlayerController.ValidateItemDefinition", __name__, ValidateItemWeaponDefinition) unrealsdk.RunHook( "WillowGame.WillowPlayerController.ValidateWeaponDefinition", __name__, ValidateItemWeaponDefinition) caller.Open() unrealsdk.RemoveHook("WillowGame.WillowPlayerController.OnChestOpened", __name__) unrealsdk.RemoveHook( "WillowGame.WillowPlayerController.ValidateItemDefinition", __name__) unrealsdk.RemoveHook( "WillowGame.WillowPlayerController.ValidateWeaponDefinition", __name__) save_manager.write() caller.ChestIsOpen = True for def_data, is_weapon in inv_list: if is_weapon: caller.AddWeaponFromDef(def_data, False, False) else: caller.AddItemFromDef(def_data, False, False) """ Technically we should get this function to return True, which should be doable by blocking more function calls and caching some stuff, but nothing that calls this function relies on that value anyway. """ return False
tries to exec your file without running Command Extensions. """[1:-1], formatter_class=argparse.RawDescriptionHelpFormatter) suppress_next_chat_parser.add_argument( "pattern", nargs="?", default="*", help="The glob pattern matching the message to suppress.") def ServerSay(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: global suppress_global_count for pattern, count in suppressed_patterns.items(): if fnmatch.fnmatch(params.msg, pattern): if count == 1: del suppressed_patterns[pattern] else: suppressed_patterns[pattern] -= 1 return False if suppress_global_count > 0: suppress_global_count -= 1 return False return True unrealsdk.RunHook("Engine.PlayerController.ServerSay", __name__, ServerSay)
# Prefer the callback function = (returned_input.OnPress or functools.partial( mod.GameInputPressed, returned_input)) if len(inspect.signature(function).parameters) == 0: if params.Event == InputEvent.Pressed: function() return False else: function(InputEvent(params.Event)) return False return True unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderKeyboardMouseOptions.Populate", "ModMenu.KeybindManager", _KeyboardMouseOptionsPopulate) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderKeyboardMouseOptions.extOnPopulateKeys", "ModMenu.KeybindManager", _extOnPopulateKeys) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderKeyboardMouseOptions.HandleSelectionChange", "ModMenu.KeybindManager", _HandleSelectionChangeRollover) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderKeyboardMouseOptions.HandleSelectionRollover", "ModMenu.KeybindManager", _HandleSelectionChangeRollover) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderKeyboardMouseOptions.DoBind", "ModMenu.KeybindManager", _DoBind) unrealsdk.RunHook( "WillowGame.WillowScrollingListDataProviderKeyboardMouseOptions.BindCurrentSelection",
def Enable(self) -> None: self.CreateIcons() def ConditionalReactToUse(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller.Class.Name != "WillowVendingMachine": return True if params.UsedType != 1: return True if params.User is None: return True PC = params.User.Controller PRI = PC.PlayerReplicationInfo wallet = PRI.GetCurrencyOnHand(0) cost = 0 if caller.ShopType == 2: cost = self.GetHealthCost(params.User, caller) elif caller.ShopType == 1: cost = self.GetAmmoCost(params.User, caller) else: return True if cost == 0 or wallet < cost: PC.NotifyUnableToAffordUsableObject(1) return True # If you have updating costs on, block payment so we can do it manually # This ensures that it always costs the right amount, even if it's displaying wrong # (e.g. if in coop a different player is closer to the vendor) if self.UpdatingOption.CurrentValue: vendor = caller def PayForUsedObject(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if params.UsedObject.ObjectPointer == vendor and params.UsabilityType == 1: unrealsdk.RemoveHook("WillowGame.WillowPlayerController.PayForUsedObject", self.Name) return False else: return True unrealsdk.RegisterHook("WillowGame.WillowPlayerController.PayForUsedObject", self.Name, PayForUsedObject) PRI.AddCurrencyOnHand(0, -cost) PC.SetPendingTransactionStatus(1) if caller.ShopType == 2: self.BuyHealth(params.User, caller) elif caller.ShopType == 1: self.BuyAmmo(params.User, caller) return True def InitializeFromDefinition(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller.Class.Name != "WillowVendingMachine": return True if caller.ShopType == 2: params.Definition.HUDIconDefSecondary = self.HealthIcon caller.SetUsability(True, 1) elif caller.ShopType == 1: params.Definition.HUDIconDefSecondary = self.AmmoIcon caller.SetUsability(True, 1) return True # Touch and UnTouch are called whenever a player gets close to an interactive object # We use them to disable the update loop when we don't need it, to reduce lag # Process it even if updating costs are off so that it switches seamlessly on option change def Touch(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller.Class.Name != "WillowVendingMachine": return True if params.Other.Class.Name != "WillowPlayerPawn": return True # If no one's currently near a vendor, but is about to be, and if updating costs are on, # start the update loop if self.UpdatingOption.CurrentValue and len(self.TouchingActors) == 0: AsyncUtil.RunEvery(self.UPDATE_DELAY, self.OnUpdate, self.Name) if caller not in self.TouchingActors: self.TouchingActors[caller] = set() self.TouchingActors[caller].add(params.Other) return True def UnTouch(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller.Class.Name != "WillowVendingMachine": return True if params.Other.Class.Name != "WillowPlayerPawn": return True try: self.TouchingActors[caller].remove(params.Other) if len(self.TouchingActors[caller]) == 0: del self.TouchingActors[caller] except (KeyError, ValueError): # If the player or vendor aren't in the dict pass if self.UpdatingOption.CurrentValue and len(self.TouchingActors) == 0: AsyncUtil.CancelFutureCallbacks(self.Name) return True def WillowClientDisableLoadingMovie(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: # On level change reset all our caching self.TouchingActors = {} self.VialCosts = {} self.AmmoCosts = {} self.PlayerAmmoPools = {} AsyncUtil.CancelFutureCallbacks(self.Name) return True def GenerateInventory(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: # Whenever a vendor inventory is generated, update our cached costs # Unfortuantly ShopInventory is a fixed array, which we can't iterate though, so we have # to do a findall to find the items unrealsdk.DoInjectedCallNext() caller.GenerateInventory() PC = unrealsdk.GetEngine().GamePlayers[0].Actor if caller.ShopType == 1: self.AmmoCosts[caller] = {} for item in unrealsdk.FindAll("WillowUsableItem"): if item.Owner != caller: continue if item.DefinitionData is None or item.DefinitionData.ItemDefinition is None: continue if caller.ShopType == 2: if item.DefinitionData.ItemDefinition.Name == "BuffDrink_HealingInstant": self.VialCosts[caller] = caller.GetSellingPriceForInventory(item, PC, 1) break elif caller.ShopType == 1: name = item.DefinitionData.ItemDefinition.Name if name not in self.AMMO_COUNTS: continue info = self.AMMO_COUNTS[name] price = caller.GetSellingPriceForInventory(item, PC, 1) / info.BulletsPerItem self.AmmoCosts[caller][info.ResourcePoolName] = price return False unrealsdk.RunHook("WillowGame.WillowInteractiveObject.ConditionalReactToUse", self.Name, ConditionalReactToUse) unrealsdk.RunHook("WillowGame.WillowInteractiveObject.InitializeFromDefinition", self.Name, InitializeFromDefinition) unrealsdk.RunHook("WillowGame.WillowInteractiveObject.Touch", self.Name, Touch) unrealsdk.RunHook("WillowGame.WillowInteractiveObject.UnTouch", self.Name, UnTouch) unrealsdk.RunHook("WillowGame.WillowPlayerController.WillowClientDisableLoadingMovie", self.Name, WillowClientDisableLoadingMovie) unrealsdk.RunHook("WillowGame.WillowVendingMachine.GenerateInventory", self.Name, GenerateInventory)
def _BindCurrentSelection(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called when a key is rebound. We basically entirely rewrite it, making sure to update modded binds, as well as adding the ability to unbind modded keys. Unbinding default binds won't save, so doing so has been disabled. If you want to look into it more, it's probably caused by how it gets saved to your profile, follow the trail from `OnPop()`. """ selected_idx = caller.CurrentKeyBindSelection selected_bind = caller.KeyBinds[selected_idx] translation_context = unrealsdk.GetEngine( ).GamePlayers[0].GetTranslationContext() if selected_bind.Tag.startswith(_TAG_SEPERATOR): return False key = params.Key if selected_bind.CurrentKey == key: # Don't allow unbinding defaults if selected_idx not in _modded_keybind_map: return False key = "None" if selected_idx in _modded_keybind_map: input = _modded_keybind_map[selected_idx] if isinstance(input, Keybind): input.Key = key else: dh.PrintWarning(Keybind._list_deprecation_warning) input[1] = key # Find if we have to swap the bind with anything for idx, bind in enumerate(caller.KeyBinds): if bind.CurrentKey != params.Key: continue if bind == selected_bind: continue # Allow multiple "None" binds # Using continue rather than a break so that it falls into the else if key == "None": continue # If you would swap a default bind to None if selected_bind.CurrentKey == "None" and idx not in _modded_keybind_map: # Show a small explanatory dialog. dialog = caller.WPCOwner.GFxUIManager.ShowDialog() title = dialog.Localize("dlgKeyBindSwap", "Caption", "WillowMenu") msg = ( f"Unable to bind \"{selected_bind.Caption}\" to \"{key}\".\n" f"\n" f"Doing so would cause the default bind \"{bind.Caption}\" to become unbound." ) dialog.SetText(title, msg) dialog.SetVariableString( "tooltips.text", "$<Strings:WillowMenu.TitleMenu.BackBar>") dialog.ApplyLayout() def HandleInputKey(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called on any key event on any `WillowGFxDialogBox`. Only using it to replicate adding `HandleKeySwapDialog` as a delegate, sdk can't quite do so yet. """ if caller != dialog: return True if (params.uevent == InputEvent.Released and params.ukey in ("Escape", "XboxTypeS_B", "XboxTypeS_Back")): dialog.Close() unrealsdk.RemoveHook( "WillowGame.WillowGFxDialogBox.HandleInputKey", "ModMenu.KeybindManager") return True unrealsdk.RunHook("WillowGame.WillowGFxDialogBox.HandleInputKey", "ModMenu.KeybindManager", HandleInputKey) return False if idx in _modded_keybind_map: input = _modded_keybind_map[idx] if isinstance(input, Keybind): input.Key = selected_bind.CurrentKey else: dh.PrintWarning(Keybind._list_deprecation_warning) input[1] = selected_bind.CurrentKey unrealsdk.DoInjectedCallNext() caller.BindCurrentSelection(key) bind.Object.SetString( "value", _get_fixed_localized_key_name(caller, bind.CurrentKey), translation_context) else: caller.bNeedsToSaveKeyBinds = True selected_bind.CurrentKey = key selected_bind.Object.SetString( "value", _get_fixed_localized_key_name(caller, selected_bind.CurrentKey), translation_context) caller.ControllerMappingClip.InvalidateKeyData() return False
def Enable(self) -> None: self.CreateIcons() def ConditionalReactToUse(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if params.UsedType != 1: return True if str(caller).split(" ")[0] != "WillowVendingMachine": return True # If you have updating costs off we have to take the off money ourselves if not self.UpdatingOption.CurrentValue: PC = params.User.Controller PRI = PC.PlayerReplicationInfo wallet = PRI.GetCurrencyOnHand(0) cost = 0 if caller.ShopType == 2: cost = self.GetHealthCost(params.User, caller) elif caller.ShopType == 1: cost = self.GetAmmoCost(params.User, caller) else: return True if cost == 0 or wallet < cost: PC.NotifyUnableToAffordUsableObject(1) return True PRI.AddCurrencyOnHand(0, -cost) PC.SetPendingTransactionStatus(1) if caller.ShopType == 2: self.BuyHealth(params.user, caller) elif caller.ShopType == 1: self.BuyAmmo(params.user, caller) return True def InitializeFromDefinition(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if str(caller).split(" ")[0] != "WillowVendingMachine": return True if caller.ShopType == 2: params.Definition.HUDIconDefSecondary = self.HealthIcon caller.SetUsability(True, 1) elif caller.ShopType == 1: params.Definition.HUDIconDefSecondary = self.AmmoIcon caller.SetUsability(True, 1) return True def Touch(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if not self.UpdatingOption.CurrentValue: return True if str(caller).split(" ")[0] != "WillowVendingMachine": return True self.TouchingActors.add(params.Other) if self.UpdatingOption.CurrentValue and len( self.TouchingActors) == 1: AsyncUtil.RunEvery(self.UPDATE_DELAY, self.OnUpdate, self.Name) return True def UnTouch(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if str(caller).split(" ")[0] != "WillowVendingMachine": return True try: self.TouchingActors.remove(params.Other) except KeyError: # If the player is not already in the set pass if self.UpdatingOption.CurrentValue and len( self.TouchingActors) == 0: AsyncUtil.CancelFutureCallbacks(self.Name) return True def WillowClientDisableLoadingMovie(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: self.TouchingActors.clear() return True unrealsdk.RunHook( "WillowGame.WillowInteractiveObject.ConditionalReactToUse", self.Name, ConditionalReactToUse) unrealsdk.RunHook( "WillowGame.WillowInteractiveObject.InitializeFromDefinition", self.Name, InitializeFromDefinition) unrealsdk.RunHook("WillowGame.WillowInteractiveObject.Touch", self.Name, Touch) unrealsdk.RunHook("WillowGame.WillowInteractiveObject.UnTouch", self.Name, UnTouch) unrealsdk.RunHook( "WillowGame.WillowPlayerController.WillowClientDisableLoadingMovie", self.Name, WillowClientDisableLoadingMovie)
def Enable(self) -> None: update_compression(self.CompressOption.CurrentValue) for func, hook in AllHooks.items(): unrealsdk.RunHook(func, self.Name, hook)