def GetItemStatus(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if params.WPC != unrealsdk.GetEngine().GamePlayers[0].Actor: return True if not self.IsOn: return True unrealsdk.DoInjectedCallNext() status = caller.GetItemStatus(params.Item, params.WPC, params.ItemPrice) # If we get back SIS_ItemCanBePurchased (0) we don't have to do anything if status == 0: return True # Otherwise temporarily give all the money you'd need to purcahse it and check again PRI = params.WPC.PlayerReplicationInfo currency = caller.GetCurrencyTypeInventoryIsSoldIn(params.Item) wallet = PRI.GetCurrencyOnHand(currency) PRI.SetCurrencyOnHand(currency, params.ItemPrice) unrealsdk.DoInjectedCallNext() status = caller.GetItemStatus(params.Item, params.WPC, params.ItemPrice) # Revert your money back PRI.SetCurrencyOnHand(currency, wallet) # If the status now is SIS_ItemCanBePurchased (0) then we were just missing money, and # we want the actual status to be ignore that # We can't directly change the return value of the function, only if it executes # However, if the function doesn't execute then it's return value is None # As luck would have it this ends up casting to 0 - the exact value we want to set return bool(status != 0)
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 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
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 Loot(caller: UObject, function: UFunction, params: FStruct) -> bool: for _ in range(self.multiplier): unrealsdk.DoInjectedCallNext() caller.DropLootOnDeath(params.Killer, params.DamageType, params.DamageTypeDefinition) 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
def PostBeginPlay(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: # Re-run the function so we can modify it after it's done unrealsdk.DoInjectedCallNext() caller.PostBeginPlay() caller.bShowUndiscoveredMissions = False return False
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
def SetTopStat(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if params.LabelText == "Fire Rate": newRate = f"{float(params.ValueText) * 60:.1f}" aux = "" if params.AuxText is None else params.AuxText unrealsdk.DoInjectedCallNext() caller.SetTopStat(params.StatIndex, params.LabelText, newRate, params.CompareArrow, aux, params.IconName) return False return True
def _extOnPopulateKeys(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called to load the list of keybinds. We add our own binds onto the end of the list after it's called. """ global _modded_keybind_map unrealsdk.DoInjectedCallNext() caller.extOnPopulateKeys() _modded_keybind_map = {} for mod in MenuManager.GetOrderedModList(): if not mod.IsEnabled: continue if all(isinstance(k, Keybind) and k.IsHidden for k in mod.Keybinds): continue tag = f"{_TAG_SEPERATOR}.{mod.Name}" idx = caller.AddKeyBindEntry(tag, tag, mod.Name) caller.KeyBinds[idx].CurrentKey = "None" for input in mod.Keybinds: name: str key: str rebindable: bool if isinstance(input, Keybind): if input.IsHidden: continue name = input.Name key = input.Key rebindable = input.IsRebindable else: dh.PrintWarning(Keybind._list_deprecation_warning) name = input[0] key = input[1] rebindable = True tag = (_TAG_INPUT if rebindable else _TAG_UNREBINDABLE) + f".{mod.Name}.{name}" idx = caller.AddKeyBindEntry(tag, tag, " " * _INDENT + name) _modded_keybind_map[idx] = input if not rebindable: key = "[ ]" if key == "None" else f"[ {key} ]" caller.KeyBinds[idx].CurrentKey = key return False
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 SetHealth(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller != unrealsdk.GetEngine().GamePlayers[0].Actor.Pawn: return True if self.CurrentValue == GodMode.FULL: # The previous function should prevent getting here, but just in case return False elif self.CurrentValue == GodMode.ALLOWDAMAGE: # noqa: SIM102 if params.NewHealth < 1: unrealsdk.DoInjectedCallNext() caller.SetHealth(1) return False return True
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
def PlayerBuyBackInventory(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller != unrealsdk.GetEngine().GamePlayers[0].Actor: return True if not self.IsOn: return True # Exact same logic as above pri = caller.PlayerReplicationInfo wallet = pri.GetCurrencyOnHand(params.FormOfCurrency) pri.SetCurrencyOnHand(params.FormOfCurrency, params.Price) unrealsdk.DoInjectedCallNext() caller.PlayerBuyBackInventory(params.FormOfCurrency, params.Price, params.Quantity) pri.SetCurrencyOnHand(params.FormOfCurrency, wallet) return False
def SetGameStage(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: # This might do weird things if we let it through, visibly you'll stay at your level but # idk if there are any side effects if str(caller).startswith("WillowPlayerPawn"): return True default = max(0, params.NewGameStage + self.OffsetSlider.CurrentValue) min_val = max(0, default - self.MinSlider.CurrentValue) max_val = max(0, default + self.MaxSlider.CurrentValue) val = random.randrange(min_val, max_val + 1) unrealsdk.DoInjectedCallNext() caller.SetGameStage(val) return False
def _OnResetKeyBindsButtonClicked(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called when the user resets all keybinds - unsuprisingly we do the same. There is no easy way to store default binds for legacy mods that still provide them as lists, so they will not be reset. """ unrealsdk.DoInjectedCallNext() caller.OnResetKeyBindsButtonClicked(params.Dlg, params.ControllerId) if params.Dlg.DialogResult != "Yes": return False for input in _modded_keybind_map.values(): if isinstance(input, Keybind): input.Key = input.DefaultKey return False
def ServerPurchaseBlackMarketUpgrade(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller != unrealsdk.GetEngine().GamePlayers[0].Actor: return True if not self.IsOn: return True # This is a static method so any black market will do market = unrealsdk.FindAll("WillowVendingMachineBlackMarket")[-1] price = market.StaticGetSellingPriceForBlackMarketInventory(params.BalanceDef, caller) # And the same logic once again - this time we know currency is CURRENCY_Eridium, aka 1 pri = caller.PlayerReplicationInfo wallet = pri.GetCurrencyOnHand(1) pri.SetCurrencyOnHand(1, price) unrealsdk.DoInjectedCallNext() caller.ServerPurchaseBlackMarketUpgrade(params.BalanceDef) pri.SetCurrencyOnHand(1, wallet) return False
def EndClimbLadder(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: pawn = unrealsdk.GetEngine().GamePlayers[0].Actor.Pawn if caller != pawn: return True if pawn.MyActionSkill is None: return True if pawn.MyActionSkill.Class.Name != "DualWieldActionSkill": return True unrealsdk.DoInjectedCallNext() caller.EndClimbLadder(params.OldLadder) if pawn.Weapon is not None: pawn.InvManager.SetCurrentWeapon(self.DupeWeapon(pawn.Weapon), True) return False
def ServerPlayerBoughtItem(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller != unrealsdk.GetEngine().GamePlayers[0].Actor: return True if not self.IsOn: return True # Copy a bit of logic to get the right shop shop = params.Shop if shop is None: shop = caller.ActiveShop # If it's still none then just let the game deal with it if shop is None: return True else: # This is an FScriptInterface, need to get the object shop = shop.ObjectPointer currency = shop.GetCurrencyTypeInventoryIsSoldIn(params.InventoryObject) price = shop.GetSellingPriceForInventory( params.InventoryObject, caller, params.Quantity ) pri = caller.PlayerReplicationInfo # Save your wallet beforehand wallet = pri.GetCurrencyOnHand(currency) # Make sure you can afford the item + buy it pri.SetCurrencyOnHand(currency, price) unrealsdk.DoInjectedCallNext() caller.ServerPlayerBoughtItem( params.InventoryObject, params.Quantity, params.bReadyItem, params.Shop.ObjectPointer, params.bWasItemOfTheDay ) pri.SetCurrencyOnHand(currency, wallet) return False
def TossInventory(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: pawn = unrealsdk.GetEngine().GamePlayers[0].Actor.Pawn if caller != pawn: return True if pawn.MyActionSkill is None: return True if pawn.MyActionSkill.Class.Name != "DualWieldActionSkill": return True unrealsdk.DoInjectedCallNext() caller.TossInventory( params.Inv, (params.ForceVelocity.X, params.ForceVelocity.Y, params.ForceVelocity.Z)) if pawn.Weapon is not None: pawn.InvManager.SetCurrentWeapon(self.DupeWeapon(pawn.Weapon), True) return False
def ServerPurchaseBlackMarketUpgrade( caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: if caller != unrealsdk.GetEngine().GamePlayers[0].Actor: return True if self == self.OFF: return True # This is a static method so any black market will do BM = unrealsdk.FindAll("WillowVendingMachineBlackMarket")[0] price = BM.StaticGetSellingPriceForBlackMarketInventory( params.BalanceDef, caller) # And the same logic once again PRI = caller.PlayerReplicationInfo wallet = PRI.GetCurrencyOnHand(1) PRI.SetCurrencyOnHand(1, price) unrealsdk.DoInjectedCallNext() caller.ServerPurchaseBlackMarketUpgrade(params.BalanceDef) PRI.SetCurrencyOnHand(1, wallet) return False
def _extOnPopulateKeys(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: """ This function is called to load the list of keybinds. We add our own binds onto the end of the list after it's called. """ global _modded_keybind_map unrealsdk.DoInjectedCallNext() caller.extOnPopulateKeys() _modded_keybind_map = {} for mod in MenuManager.GetOrderedModList(): if not mod.IsEnabled: continue if len(mod.Keybinds) > 0: tag = f"{_TAG_SEPERATOR}.{mod.Name}" idx = caller.AddKeyBindEntry(tag, tag, mod.Name) caller.KeyBinds[idx].CurrentKey = "None" for input in mod.Keybinds: name: str key: str if isinstance(input, Keybind): name = input.Name key = input.Key else: dh.PrintWarning(Keybind._list_deprecation_warning) name = input[0] key = input[1] tag = f"{_TAG_INPUT}.{mod.Name}.{name}" idx = caller.AddKeyBindEntry(tag, tag, " " * _INDENT + name) _modded_keybind_map[idx] = input caller.KeyBinds[idx].CurrentKey = key return False
def PostBeginPlay(caller: unrealsdk.UObject, function: unrealsdk.UFunction, params: unrealsdk.FStruct) -> bool: unrealsdk.DoInjectedCallNext() caller.PostBeginPlay() caller.bShowUndiscoveredMissions = False return False
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