diff --git a/changelog.md b/changelog.md index 9b67268..9ba35c3 100644 --- a/changelog.md +++ b/changelog.md @@ -28,7 +28,15 @@ - Moved a few warnings to go through Python's system, so they get attributed to the right place. ### UI Utils v1.3 -- Linting fixes. +- Added several new helper functions: + - `show_blocking_message`, `hide_blocking_message` + - `show_button_prompt`, `hide_button_prompt` + - `show_coop_message`, `hide_coop_message` + - `show_discovery_message` + - `show_reward_popup` + - `show_second_wind_notification` + + [See examples here](https://bl-sdk.github.io/developing/ui_utils/willow2/). ### [unrealsdk v2.0.0](https://github.com/bl-sdk/unrealsdk/blob/master/changelog.md#v200) > - Now supports Borderlands 1. Big thanks to Ry for doing basically all the reverse engineering. diff --git a/src/ui_utils/__init__.py b/src/ui_utils/__init__.py index 9d3c3d0..610b3af 100644 --- a/src/ui_utils/__init__.py +++ b/src/ui_utils/__init__.py @@ -2,13 +2,28 @@ from .chat import show_chat_message from .clipboard import clipboard_copy, clipboard_paste -from .hud_message import show_hud_message +from .hud_message import ( + ERewardPopup, + hide_button_prompt, + show_button_prompt, + show_discovery_message, + show_hud_message, + show_reward_popup, + show_second_wind_notification, +) +from .online_message import ( + hide_blocking_message, + hide_coop_message, + show_blocking_message, + show_coop_message, +) from .option_box import OptionBox, OptionBoxButton from .reorder_box import ReorderBox from .training_box import EBackButtonScreen, TrainingBox __all__: tuple[str, ...] = ( "EBackButtonScreen", + "ERewardPopup", "OptionBox", "OptionBoxButton", "ReorderBox", @@ -18,11 +33,20 @@ "__version_info__", "clipboard_copy", "clipboard_paste", + "hide_blocking_message", + "hide_button_prompt", + "hide_coop_message", + "show_blocking_message", + "show_button_prompt", "show_chat_message", + "show_coop_message", + "show_discovery_message", "show_hud_message", + "show_reward_popup", + "show_second_wind_notification", ) -__version_info__: tuple[int, int] = (1, 3) +__version_info__: tuple[int, int] = (1, 4) __version__: str = f"{__version_info__[0]}.{__version_info__[1]}" __author__: str = "bl-sdk" diff --git a/src/ui_utils/hud_message.py b/src/ui_utils/hud_message.py index fb81a6e..9452363 100644 --- a/src/ui_utils/hud_message.py +++ b/src/ui_utils/hud_message.py @@ -1,8 +1,33 @@ +from types import EllipsisType +from typing import TYPE_CHECKING + import unrealsdk +from unrealsdk import unreal from mods_base import get_pc -__all__: tuple[str, ...] = ("show_hud_message",) +if TYPE_CHECKING: + from enum import IntEnum + + class ERewardPopup(IntEnum): + ERP_BadassToken = 0 + ERP_CharacterHead = 1 + ERP_CharacterSkin = 2 + ERP_VehicleSkin = 3 + ERP_MAX = 4 + +else: + ERewardPopup = unrealsdk.find_enum("ERewardPopup") + +__all__: tuple[str, ...] = ( + "ERewardPopup", + "hide_button_prompt", + "show_button_prompt", + "show_discovery_message", + "show_hud_message", + "show_reward_popup", + "show_second_wind_notification", +) def show_hud_message(title: str, msg: str, duration: float = 2.5) -> None: @@ -39,3 +64,115 @@ def show_hud_message(title: str, msg: str, duration: float = 2.5) -> None: True, 0, ) + + +def show_second_wind_notification( + msg: str, + ui_sound: unreal.UObject | None | EllipsisType = ..., +) -> None: + """ + Displays a big notification message in the main in game hud. + + Uses the message style of the Second Wind notification. + + Note this should not be used for critical messages, it may silently fail at any point. + + Args: + msg: The message to display. + ui_sound: An optional AkEvent to play when the message is displayed. + If Ellipsis, default sound will be used. + """ + if (hud_movie := get_pc().GetHUDMovie()) is None: + return + + sound_backup = None + sw_interaction = None + for interaction in hud_movie.InteractionOverrideSounds: + if interaction.Interaction == "SecondWind": + sound_backup = interaction.AkEvent + sw_interaction = interaction + break + + if ui_sound is not Ellipsis and sw_interaction: + sw_interaction.AkEvent = ui_sound + + backup_string = hud_movie.SecondWindString + hud_movie.SecondWindString = msg + hud_movie.DisplaySecondWind() + hud_movie.SecondWindString = backup_string + if sw_interaction: + sw_interaction.AkEvent = sound_backup + + +def show_discovery_message(msg: str, show_discovered_message: bool = False) -> None: + """ + Displays a message in the top center of the screen. + + Uses the style of the new area discovered message. + + Note this should not be used for critical messages, it may silently fail at any point. + + Args: + msg: The message to display. + show_discovered_message: If True, the message 'You have discovered' header will show. + """ + if (hud_movie := get_pc().GetHUDMovie()) is None: + return + hud_movie.ShowWorldDiscovery("", msg, show_discovered_message, False) + + +def show_reward_popup( + msg: str, + reward_type: ERewardPopup = ERewardPopup.ERP_BadassToken, +) -> None: + """ + Displays a reward popup with the given message and reward type. + + Note this should not be used for critical messages, it may silently fail at any point. + + Args: + msg: The message to display in the popup. + reward_type: The type of reward to display. Defaults to ERewardPopup.ERP_BadassToken. + """ + if (hud_movie := get_pc().GetHUDMovie()) is None: + return + + icon = { + ERewardPopup.ERP_BadassToken: "token", + ERewardPopup.ERP_CharacterHead: "head", + ERewardPopup.ERP_CharacterSkin: "playerSkin", + ERewardPopup.ERP_VehicleSkin: "vehicleSkin", + }.get(reward_type, "token") + + hud_movie.SingleArgInvokeS("p1.badassToken.gotoAndStop", "stop") + hud_movie.SingleArgInvokeS("p1.badassToken.gotoAndStop", "go") + hud_movie.SingleArgInvokeS("p1.badassToken.inner.gotoAndStop", icon) + hud_movie.SetVariableString("p1.badassToken.inner.dispText.text", msg) + + +def show_button_prompt(reason: str, button: str) -> None: + """ + Displays a contextual prompt with the given text and button string. + + This will stay visible until it is explicitly hidden, see `hide_contextual_prompt`. + + Note this should not be used for critical messages, it may silently fail at any point. + + Args: + reason: The text top to display in the prompt. + button: The button string to display in the prompt. + """ + + if (hud_movie := get_pc().GetHUDMovie()) is None: + return + contextual_prompt = hud_movie.ContextualPromptButtonString + hud_movie.ContextualPromptButtonString = button + hud_movie.ToggleContextualPrompt(reason, True) + hud_movie.ContextualPromptButtonString = contextual_prompt + + +def hide_button_prompt() -> None: + """Hides the currently displayed contextual prompt, if any.""" + if (hud_movie := get_pc().GetHUDMovie()) is None: + return + hud_movie.ToggleContextualPrompt("", False) diff --git a/src/ui_utils/online_message.py b/src/ui_utils/online_message.py new file mode 100644 index 0000000..1e74b63 --- /dev/null +++ b/src/ui_utils/online_message.py @@ -0,0 +1,61 @@ +from mods_base import get_pc + +__all__: tuple[str, ...] = ( + "hide_blocking_message", + "hide_coop_message", + "show_blocking_message", + "show_coop_message", +) + + +def show_blocking_message(msg: str, reason: str | None = None) -> None: + """ + Displays a blocking message with the given text. + + This message blocks all user input until it is hidden. + + This message will stay until it is explicitly hidden, see `hide_blocking_message`. + + Args: + msg: The message to display. + reason: An optional reason for the blocking message, which will be displayed as a subtitle. + If None, the default text will show. + """ + if (msg_movie := get_pc().GetOnlineMessageMovie()) is None: + return + + backup = msg_movie.BlockingSubtitle + msg_movie.BlockingSubtitle = reason if reason is not None else backup + msg_movie.DisplayBlockingMessage(msg) + msg_movie.BlockingSubtitle = backup + + +def hide_blocking_message() -> None: + """Hides the currently displayed blocking message, if any.""" + if (msg_movie := get_pc().GetOnlineMessageMovie()) is None: + return + + msg_movie.HideBlocking() + + +def show_coop_message(msg: str) -> None: + """ + Displays a short message on the left of the screen, like those used when coop players join. + + This message will stay until it is explicitly hidden, see `hide_coop_message`. + + Args: + msg: The message to display. + """ + if (msg_movie := get_pc().GetOnlineMessageMovie()) is None: + return + + msg_movie.DisplayMessage(msg) + + +def hide_coop_message() -> None: + """Hides the currently displayed coop message, if any.""" + if (msg_movie := get_pc().GetOnlineMessageMovie()) is None: + return + + msg_movie.Hide()