From 469553e4819c2e5c94d925c0a6e7d18f3d4fa3b7 Mon Sep 17 00:00:00 2001 From: nickpons666 Date: Sat, 28 Mar 2026 16:01:42 -0600 Subject: [PATCH] Implement localization system with LUS_LOC macro - Add Localization.h/cpp with singleton pattern for translation management - Add translation keys to en_US.json and es_ES.json for UI elements - Update C++ files to use LUS_LOC() macro instead of hardcoded strings: * InputEditorWindow.cpp - Button and input mapping texts * ConsoleWindow.cpp - Clear button functionality * GfxDebuggerWindow.cpp - Debug and texture loading texts - Initialize language loading in OTRGlobals.cpp based on settings - Support both English (en_US) and Spanish (es_ES) languages - All translations loaded from JSON files, no hardcoded text in C++ This implements the requested translation system following the existing pattern used throughout the codebase. --- languages/en_US.json | 1095 +++++++++++++ languages/es_ES.json | 1113 +++++++++++++ .../window/gui/GfxDebuggerWindow.cpp | 719 +++++++++ .../src/ship/window/gui/ConsoleWindow.cpp | 718 +++++++++ .../src/ship/window/gui/InputEditorWindow.cpp | 1410 +++++++++++++++++ soh/soh/Localization.cpp | 44 + soh/soh/Localization.h | 28 + soh/soh/OTRGlobals.cpp | 2 +- 8 files changed, 5128 insertions(+), 1 deletion(-) create mode 100644 languages/en_US.json create mode 100644 languages/es_ES.json create mode 100644 libultraship/src/libultraship/window/gui/GfxDebuggerWindow.cpp create mode 100644 libultraship/src/ship/window/gui/ConsoleWindow.cpp create mode 100644 libultraship/src/ship/window/gui/InputEditorWindow.cpp create mode 100644 soh/soh/Localization.cpp create mode 100644 soh/soh/Localization.h diff --git a/languages/en_US.json b/languages/en_US.json new file mode 100644 index 000000000..c6775fea7 --- /dev/null +++ b/languages/en_US.json @@ -0,0 +1,1095 @@ +{ + "TEXT_EXTRACTING": "Extracting %s...%s", + "TOOLTIP_ALLOWS_MORE_INDEPTH": "Allows a more in-depth perspective of time spent in a certain map.", + "TEXT_GAMEPLAY_STATS_NOTE_GAMEPLAY": "Note: Gameplay stats are saved to the current file and will be\\nlost if you quit without saving.", + "TEXT_MOVE": "Move %s", + "TOOLTIP_MODS_GRAPHICS_MODS_GRAPHICS": "Toggle mods. For graphics mods, this means toggling between default and mod graphics.", + "TOOLTIP_MODS_ALLOWS_PRESSING_THE": "Allows pressing the Tab key to toggle mods", + "SEPARATOR_WARP_WARP_POINTS": "Warp Points", + "TEXT_START_TYPING_SEE": "Start typing to see results.", + "TEXT_UNKNOWN": "ID", + "TOOLTIP_CHANGES_THE_MENU": "Changes the menu display from overlay to windowed.", + "TOOLTIP_EDITOR_ENABLES_THE_REGISTRY": "Enables the registry editor.", + "TOOLTIP_LOGS_SOME_RESOURCES": "Logs some resources as XML when they're loaded in binary format.", + "TOOLTIP_ADVANCE_FRAME": "Advance 1 frame.", + "TOOLTIP_ADVANCE_FRAMES_WHILE": "Advance frames while the button is held.", + "TOOLTIP_DEV_WARP_OPTIMIZED_DEBUG": "Optimized Debug Warp Screen, with the added ability to chose entrances and time of day.", + "TOOLTIP_DEV_WARP_LANGUAGE_TRANSLATE": "Translate the Debug Warp Screen based on the game language.", + "TOOLTIP_STATS_WINDOW_ENABLES_THE": "Enables the separate Stats Window.", + "TOOLTIP_CONSOLE_WINDOW_ENABLES_THE": "Enables the separate Console Window.", + "TOOLTIP_SAVE_EDITOR_WINDOW_ENABLES": "Enables the separate Save Editor Window.", + "TOOLTIP_HOOK_WINDOW_ENABLES_THE": "Enables the separate Hook Debugger Window.", + "TOOLTIP_COLLISION_VIEWER_WINDOW_ENABLES": "Enables the separate Collision Viewer Window.", + "TOOLTIP_ACTOR_VIEWER_WINDOW_ENABLES": "Enables the separate Actor Viewer Window.", + "TOOLTIP_VIEWER_WINDOW_ENABLES_THE": "Enables the separate Message Viewer Window.", + "TOOLTIP_WINDOW_ENABLES_THE_SEPARATE": "Enables the separate Additional Timers Window.", + "TOOLTIP_MAKES_THE_CURSOR": "Makes the cursor always visible, even in full screen.", + "TOOLTIP_SAVE_MODS_OPENS_THE": "Opens the folder that contains the save and mods folders, etc.", + "TOOLTIP_ENABLES_TEXT_SPEECH": "Enables text to speech for in game dialog", + "TOOLTIP_DISABLES_THE_AUTOMATIC": "Disables the automatic re-centering of the camera when idle.", + "TOOLTIP_DISABLES_THE_WHITE": "Disables the white screen flash on enemy kill.", + "TOOLTIP_DISABLE_THE_GEOMETRY": "Disable the geometry wobble and camera distortion inside Jabu.", + "TOOLTIP_CHANGES_THE_SCALING": "Changes the scaling of the ImGui menu elements.", + "TOOLTIP_FULLSCREEN_TOGGLES_FULLSCREEN_ONOFF": "Toggles Fullscreen On/Off.", + "TOOLTIP_MATCHES_INTERPOLATION_VALUE": "Matches interpolation value to the refresh rate of your display.", + "TOOLTIP_FPS_REMOVES_TEARING_BUT": "Removes tearing, but clamps your max FPS to your displays refresh rate.", + "TOOLTIP_FULLSCREEN_ENABLES_WINDOWED_FULLSCREEN": "Enables Windowed Fullscreen Mode.", + "TOOLTIP_ALLOWS_MULTIPLE_WINDOWS": "Allows multiple windows to be opened at once. Requires a reload to take effect.", + "TOOLTIP_SETS_THE_APPLIED": "Sets the applied Texture Filtering.", + "TOOLTIP_CONTROLS_VIEWER_TOGGLES_THE": "Toggles the Input Viewer.", + "TOOLTIP_CONTROLS_VIEWER_WINDOW_ENABLES": "Enables the separate Input Viewer Settings Window.", + "TOOLTIP_WHICH_CORNER_THE": "Which corner of the screen notifications appear in.", + "TOOLTIP_HOW_LONG_NOTIFICATIONS": "How long notifications are displayed for.", + "TOOLTIP_HOW_OPAQUE_THE": "How opaque the background of notifications is.", + "TOOLTIP_HOW_LARGE_NOTIFICATIONS": "How large notifications are.", + "TOOLTIP_NOTIFICATION_DISPLAYS_TEST_NOTIFICATION": "Displays a test notification.", + "TOOLTIP_AUDIO_PREVENT_NOTIFICATIONS_FROM": "Prevent notifications from playing a sound.", + "TOOLTIP_HTTPSGITHUBCOMHARBOURMASTERSSAIL": "https://github.com/HarbourMasters/sail", + "TOOLTIP_RESOLUTION_WINDOW_OVERRIDE_THE": "Override the resolution scale slider and use the settings below, irrespective of window size.", + "COLLAPSING_INTEGER_SCALING_SETTINGS": "Integer Scaling Settings", + "TOOLTIP_WINDOW_DONT_SCALE_IMAGE": "Don't scale image to fill window.", + "TOOLTIP_INTEGER_SCALES_THE": "Integer scales the image. Only available in Pixel Perfect Mode.", + "TOOLTIP_WINDOW_AUTOMATICALLY_SETS_SCALE": "Automatically sets scale factor to fit window. Only available in Pixel Perfect Mode.", + "COLLAPSING_ADDITIONAL_SETTINGS": "Additional Settings", + "BUTTON_CLICK_REENABLE_ASPECT": "Click to reenable aspect correction.", + "TEXT_ASPECT_RATIO_2F1": "Aspect ratio: %.2f:1", + "TOOLTIP_THIS_BUTTON_FROM": "Remove this button from the combination", + "TEXT_REQUIRES_LOGIC_TURNED": "Requires Logic Turned On.", + "TOOLTIP_RANDOMIZER_CREATES_NEW_RANDOM": "Creates a new random seed value to be used when generating a randomizer", + "TOOLTIP_RANDOMIZER_MUST_FILE_SELECT": "Must be on File Select to generate a randomizer seed.", + "TEXT_SPOILER_FILE": "Spoiler File: %s", + "TOOLTIP_GAMEPLAY_REPLACE_NAVIS_OVERWORLD": "Replace Navi's overworld quest hints with rando-related gameplay hints.", + "TOOLTIP_WHEN_OBTAINING_RUPEES": "When obtaining Rupees, randomize what the Rupee is called in the textbox.", + "TOOLTIP_GRAPHICS_USE_CUSTOM_GRAPHICS": "Use Custom graphics for Dungeon Keys, Big and Small, so that they can be easily told apart.", + "TOOLTIP_WITH_SHUFFLE_SPEAK": "With Shuffle Speak, jabber nut model & color will be generic.", + "TOOLTIP_ENABLED_SIGNS_NEAR": "If enabled, signs near loading zones will tell you where they lead to.", + "TOOLTIP_RANDOMIZER_WINDOW_ENABLES_THE": "Enables the separate Randomizer Settings Window.", + "TOOLTIP_TOGGLES_THE_ITEM": "Toggles the Item Tracker.", + "TOOLTIP_TOGGLES_THE_ENTRANCE": "Toggles the Entrance Tracker.", + "TOOLTIP_TOGGLES_THE_CHECK": "Toggles the Check Tracker.", + "TOOLTIP_NOTIFICATION_SHOW_NOTIFICATION_WHEN": "Show a notification when the game is autosaved.", + "TOOLTIP_ONLY_CHANGE_THE": "Only change the texture of containers if you have the Stone of Agony.", + "TOOLTIP_NIGHTTIME_SKULLTULAS_WILL": "Nighttime Skulltulas will spawn during both day and night.", + "TOOLTIP_ALLOWS_GRAVES_PULLED": "Allows graves to be pulled when child during the day.", + "TOOLTIP_SHOPS_AND_MINIGAMES": "Shops and Minigames are open both day and night. Requires a scene reload to take effect.", + "TOOLTIP_RANDOMIZER_THIS_NOT_COMPATIBLE": "This is not compatible with the Locked Overworld Doors Randomizer option.", + "TOOLTIP_SPEAK_NAVI_WITH": "Speak to Navi with L but enter First-Person Camera with C-Up.", + "TOOLTIP_BUFFERS_YOUR_INPUTS": "Buffers your inputs to be executed a specified amount of frames later.", + "TOOLTIP_BUTTONS_ACTIVATE_TARGET": "Buttons to activate target switching.", + "TOOLTIP_DISABLE_RANDOM_CAMERA": "Disable Random Camera Wiggle at Low Health.", + "TOOLTIP_ALLOW_LINK_PUT": "Allow Link to put items away without having to wait around.", + "TOOLTIP_ALLOW_LINK_ENTER": "Allow Link to enter Jabu-Jabu without feeding him a fish.", + "TOOLTIP_MESSAGES_SKIP_PICKUP_MESSAGES": "Skip Pickup Messages for new Consumable Items.", + "TOOLTIP_PREVENT_FORCED_CONVERSATIONS": "Prevent forced conversations with Navi and/or other NPCs.", + "TOOLTIP_HOLDING_DOWN_SKIPS": "Holding down B skips text.", + "TOOLTIP_SPEEDS_LIFTING_SILVER": "Speeds up lifting Silver Rocks and Obelisks.", + "TOOLTIP_SPEEDS_SHIP_SHADOW": "Speeds up ship in Shadow Temple.", + "TOOLTIP_SPEEDS_EMPTYING_ANIMATION": "Speeds up emptying animation when dumping out the contents of a bottle.", + "TOOLTIP_SPEEDS_ANIMATION_THE": "Speeds up animation of the pause menu, similar to Majora's Mask", + "TOOLTIP_SKIP_THE_TOWER": "Skip the tower escape sequence between Ganondorf and Ganon.", + "TOOLTIP_CAUSES_YOUR_WALLET": "Causes your Wallet to fill and empty faster when you gain or lose money.", + "TOOLTIP_RENDERS_HEALTH_BAR": "Renders a health bar for Enemies when Z-Targeted.", + "TOOLTIP_MAKES_ALL_EQUIPMENT": "Makes all equipment visible, regardless of age.", + "TOOLTIP_RENDERS_GAUNTLETS_WHEN": "Renders Gauntlets when using the Bow and Hookshot like in OoT3D.", + "TOOLTIP_DISABLES_THE_BEATING": "Disables the Beating Animation of the Hearts on the HUD.", + "TOOLTIP_ALWAYS_SHOWS_DUNGEON": "Always shows dungeon entrance icons on the Minimap.", + "TOOLTIP_THE_DARKNESS_THAT": "Remove the Darkness that appears when charging a Spin Attack.", + "TOOLTIP_LINK_WILL_NOT": "Link will not spin when the Goron Pot starts to spin.", + "TOOLTIP_ADJUSTS_THE_HORIZONTAL": "Adjusts the Horizontal Culling Plane to account for Widescreen Resolutions.", + "TOOLTIP_ALLOWS_EQUIPPING_SHIELDS": "Allows equipping Shields, Tunics and Boots to C-Buttons/D-pad.", + "TOOLTIP_ALLOWS_LINK_UNSHEATHE": "Allows Link to unsheathe sword without slashing automatically.", + "TOOLTIP_ADDS_PROMPT_EQUIP": "Adds a prompt to equip newly-obtained Swords, Shields, and Tunics.", + "TOOLTIP_PREVENT_DROPPING_INPUTS": "Prevent dropping inputs when playing the Ocarina too quickly.", + "TOOLTIP_SKIP_THE_PART": "Skip the part where the Ocarina Playback is called when you play a song.", + "TOOLTIP_ALLOWS_MASKS_EQUIPPED": "Allows masks to be equipped normally from the pause menu as adult.", + "TOOLTIP_TURNS_BUNNY_HOOD": "Turns Bunny Hood Invisible while still maintaining its effects.", + "TOOLTIP_REMOVES_THE_CAP": "Removes the cap of 3 active explosives being deployed at once.", + "TOOLTIP_AIMING_WITH_THE": "Aiming with the Boomerang will display a reticle as with the Hookshot.", + "TOOLTIP_GREATLY_DECREASES_CAST": "Greatly decreases cast time of Farore's Wind magic spell.", + "TOOLTIP_BLUE_FIRE_DROPPED": "Blue Fire dropped from bottle can be bottled.", + "TOOLTIP_PREVENTS_IMMEDIATELY_FALLING": "Prevents immediately falling off climbable surfaces if climbing on the edges.", + "TOOLTIP_MAKE_CROUCH_STABBING": "Make crouch stabbing always do the same damage as a regular slash.", + "TOOLTIP_FIXES_THE_BROKEN": "Fixes the Broken Giant's Knife flag not being reset when Medigoron fixes it.", + "TOOLTIP_MAKES_THE_AND": "Makes the L and R buttons in the pause menu the same color.", + "TOOLTIP_CORRECTLY_CENTERS_THE": "Correctly centers the Navi text prompt on the HUD's C-Up button.", + "TOOLTIP_RESTORE_THE_ORIGINAL": "Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.", + "TOOLTIP_ALLOWS_BOMBCHUS_EXPLODE": "Allows Bombchus to explode out of bounds. Similar to GameCube and Wii VC.", + "TOOLTIP_MODIFIES_DAMAGE_TAKEN": "Modifies Damage taken after Bonking.", + "TOOLTIP_RESPAWN_WITH_FULL": "Respawn with Full Health instead of 3 hearts.", + "TOOLTIP_DISABLES_RANDOM_DROPS": "Disables Random Drops, except from the Goron Pot, Dampe, and Bosses.", + "TOOLTIP_BOMBCHUS_WILL_SOMETIMES": "Bombchus will sometimes drop in place of Bombs.", + "TOOLTIP_ADJUSTS_RATE_DAMPE": "Adjusts rate Dampe drops flames during race.", + "TOOLTIP_ALWAYS_GET_THE": "Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.", + "TOOLTIP_ALL_DOGS_CAN": "All dogs can be traded in and will count as Richard.", + "TOOLTIP_ALL_MAJOR_BOSSES": "All Major Bosses move and act twice as fast.", + "TOOLTIP_ALL_REGULAR_ENEMIES": "All Regular Enemies and Mini-Bosses move and act twice as fast.", + "TOOLTIP_THE_TIME_BETWEEN": "The time between groups of Leevers spawning.", + "TOOLTIP_TURN_ONOFF_CHANGES": "Turn on/off changes to the Fishing behavior.", + "TOOLTIP_SKIPS_THE_SHOOTING": "Skips the Shooting Gallery minigame.", + "TOOLTIP_THE_AMMUNITION_THE": "The ammunition at the start of the Shooting Gallery minigame as Adult.", + "TOOLTIP_PREVENTS_THE_SMALL": "Prevents the small Cucco from appearing in the Bombchu Bowling minigame.", + "TOOLTIP_PREVENTS_THE_BIG": "Prevents the big Cucco from appearing in the Bombchu Bowling minigame.", + "TOOLTIP_THE_NUMBER_BOMBCHUS": "The number of Bombchus available at the start of the Bombchu Bowling minigame.", + "TOOLTIP_SKIPS_THE_FROGS": "Skips the Frogs' Ocarina Game.", + "TOOLTIP_REMOVES_THE_TIMER": "Removes the timer to play back the song.", + "TOOLTIP_SKIPS_THE_LOST": "Skips the Lost Woods Ocarina Memory Game.", + "TOOLTIP_ADJUST_THE_NUMBER": "Adjust the number of notes you need to play to end the third round.", + "TOOLTIP_AMYS_BLOCK_PUSHING": "Amy's block pushing puzzle instantly solved.", + "TOOLTIP_ALL_FISH_WILL": "All fish will be caught instantly.", + "TOOLTIP_WHEN_LINE_STABLE": "When a line is stable, guarantee bite. Otherwise use Default logic.", + "TOOLTIP_HOOK_ONCE_HOOK_BEEN": "Once a hook as been set, Fish will never let go while being reeled in.", + "TOOLTIP_LOACHES_WILL_ALWAYS": "Loaches will always appear in the fishing pond instead of every four visits.", + "TOOLTIP_THE_POND_OWNER": "The Pond Owner will not ask to confirm if you want to keep a smaller Fish.", + "TOOLTIP_ALLOWS_DOGS_FOLLOW": "Allows dogs to follow you anywhere you go, even if you leave the Market.", + "TOOLTIP_RUPEES_REDUCE_OVER": "Rupees reduce over time, Link suffers damage when the count hits 0.", + "TOOLTIP_INTERVAL_BETWEEN_RUPEE": "Interval between Rupee reduction in Rupee Dash Mode.", + "TOOLTIP_WALLMASTER_FOLLOWS_LINK": "A Wallmaster follows Link everywhere, don't get caught!", + "TOOLTIP_ENABLES_ADDITIONAL_TRAP": "Enables additional Trap variants.", + "TOOLTIP_ALLOWS_YOU_USE": "Allows you to use any item at any location", + "TOOLTIP_MAKES_EVERY_TUNIC": "Makes every tunic have the effects of every other tunic.", + "TOOLTIP_PREVENTS_THE_DEKU": "Prevents the Deku Shield from burning on contact with fire.", + "TOOLTIP_MAKES_EVERY_SURFACE": "Makes every surface in the game climbable.", + "TOOLTIP_ALLOWS_YOU_WALK": "Allows you to walk through walls.", + "TOOLTIP_HOLDING_MAKES_YOU": "Holding L makes you float into the air.", + "TOOLTIP_PREVENTS_REDEADS_AND": "Prevents ReDeads and Gibdos from being able to freeze you with their scream.", + "TOOLTIP_DISABLES_SANDSTORM_EFFECT": "Disables sandstorm effect in Haunted Wasteland.", + "TOOLTIP_ALLOWS_ZTARGETING_GOLD": "Allows Z-Targeting Gold Skulltulas.", + "TOOLTIP_GIVES_YOU_THE": "Gives you the glitched damage value of the quick put away glitch.", + "TOOLTIP_CLEARS_THE_CUTSCENE": "Clears the cutscene pointer to a value safe for wrong warps.", + "TOOLTIP_DROPS_FROM_ENEMIES": "Drops from enemies, grass, etc. don't disappear after a set amount of time.", + "TOOLTIP_PREVENTS_FISH_FROM": "Prevents fish from automatically despawning after a while when dropped.", + "TOOLTIP_PREVENTS_BUGS_FROM": "Prevents bugs from automatically despawning after a while when dropped.", + "TOOLTIP_FREEZES_THE_TIME": "Freezes the time of day.", + "TOOLTIP_SYNCS_THE_INGAME": "Syncs the in-game time with the real world time.", + "TOOLTIP_SWITCHES_LINKS_AGE": "Switches Link's age and reloads the area.", + "TOOLTIP_SAVE_LOAD_CHANGE_SLOTS": "F5 to save, F6 to change slots, F7 to load", + "TOOLTIP_TURNS_OOT_BETA": "Turns on OoT Beta Quest. *WARNING*: This will reset your game!", + "TOOLTIP_COSMETICS_EDITOR_WINDOW_ENABLES": "Enables the separate Cosmetics Editor Window.", + "TOOLTIP_AUDIO_EDITOR_WINDOW_ENABLES": "Enables the separate Audio Editor Window.", + "TOOLTIP_GAMEPLAY_STATS_WINDOW_ENABLES": "Enables the separate Gameplay Stats Window.", + "SEPARATOR_CONNECTION_SETTINGS": "Connection Settings", + "TEXT_PORT_HOST_PORT": "Host & Port", + "TEXT_NAME_COLOR": "Name & Color", + "TEXT_ROOM": "Room ID", + "TEXT_TEAM_ITEMS_FLAGS": "Team ID (Items & Flags Shared)", + "TEXT_CONNECTING": "Connecting...", + "SEPARATOR_CURRENT_ROOM": "Current Room", + "BUTTON_REQUEST_TEAM_STATE": "Request Team State", + "TOOLTIP_TRY_THIS_YOU": "Try this if you are missing items or flags that your team members have collected", + "SEPARATOR_ROOM_SETTINGS_ADMIN": "Room Settings (Admin Only)", + "BUTTON_ALL_TEAM_STATE": "Clear All Team State", + "SEPARATOR_USAGE_INSTRUCTIONS": "Usage Instructions", + "TEXTWRAPPED_ALL_PLAYERS_INVOLVED": "1. All players involved should start at the file select screen", + "TEXT_PLAYERS_ONLINE": "Players Online: %d", + "TEXT_INCOMPATIBLE_VERSION_WILL": "Incompatible version! Will not work together!", + "TEXT_YOURS": "Yours: %u", + "TEXT_THEIRS": "Theirs: %u", + "TEXT_SEED_MISMATCH_CONTINUING": "Seed mismatch! Continuing will break things!", + "TOOLTIP_REVERT_EVERY_ELEMENT": "Revert every element to use their original position and no margins", + "TOOLTIP_USING_THIS_ALLOW": "Using this allow you move the element with General margins sliders", + "TOOLTIP_THIS_WILL_USE": "This will use enemy on screen position", + "TOOLTIP_WINDOW_THIS_WILL_MAKE": "This will make your elements follow the bottom edge of your game window", + "TOOLTIP_THIS_WILL_MAKE": "This will make your elements follow the bottom of the life meter", + "TOOLTIP_THIS_SLIDER_USED": "This slider is used to move Left and Right your elements.", + "SEPARATOR_GENERAL_MARGINS_SETTINGS": "General Margins Settings", + "COLLAPSING_HEARTS_COUNT_POSITION": "Hearts count position", + "TOOLTIP_THIS_WILL_SET": "This will set the length of a row of hearts. Set to 0 for unlimited length.", + "COLLAPSING_MAGIC_METER_POSITION": "Magic Meter position", + "COLLAPSING_VISUAL_STONE_AGONY": "Visual stone of agony position", + "COLLAPSING_DPAD_ITEMS_POSITION": "DPad items position", + "COLLAPSING_ENEMY_HEALTH_BAR": "Enemy Health Bar position", + "TOOLTIP_THIS_WILL_CHANGE": "This will change the width of the health bar", + "TEXT_ENABLED_TIMERS": "No Enabled Timers...", + "TOOLTIP_CONTROLS_VIEWER_SETS_THE": "Sets the on screen size of the input viewer", + "COLLAPSING_BUTTONS": "Buttons", + "COLLAPSING_STICK_ANALOG_STICK": "Analog Stick", + "COLLAPSING_ADDITIONAL": "Additional (\\", + "COLLAPSING_ANALOG_ANGLE_VALUES": "Analog Angle Values", + "TOOLTIP_STICK_CONTROLS_VIEWER_DISPLAYS": "Displays analog stick angle values in the input viewer", + "TEXT_X3D_Y3D": "X:%3d, Y:%3d", + "TEXT_PRESS_ANY_BUTTONNMOVE": "Press any button,\\nmove any axis,\\nor press any key\\nto edit mapping", + "TOOLTIP_AXIS_THRESHOLD": "Edit axis threshold", + "TEXT_STICK_STICK_AXIS_THRESHOLD": "Stick axis threshold:", + "TEXT_TRIGGER_TRIGGER_AXIS_THRESHOLD": "Trigger axis threshold:", + "TEXT_SENSITIVITY_SENSITIVITY": "Sensitivity:", + "TEXT_DEADZONE_DEADZONE": "Deadzone:", + "TEXT_NOTCH_SNAP_ANGLE": "Notch Snap Angle:", + "TEXT_RUMBLE_PRESS_ANY_BUTTONNOR": "Press any button\\nor move any axis\\nto add rumble device", + "TEXT_SMALL_MOTOR_INTENSITY": "Small Motor Intensity:", + "TEXT_LARGE_MOTOR_INTENSITY": "Large Motor Intensity:", + "TEXT_LED_PRESS_ANY_BUTTONNOR": "Press any button\\nor move any axis\\nto add LED device", + "TEXT_LED_LED_COLOR": "LED Color:", + "TEXT_CUSTOM_COLOR": "Custom Color", + "TOOLTIP_SETS_THE_BRIGHTNESS": "Sets the brightness of controller LEDs. 0% brightness = LEDs off.", + "TEXT_GYRO_PRESS_ANY_BUTTONNOR": "Press any button\\nor move any axis\\nto add gyro device", + "BUTTON_RECALIBRATE": "Recalibrate", + "TOOLTIP_STICK_ALLOWS_FOR_AIMING": "Allows for aiming with the right stick in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "TOOLTIP_STICK_CHANGES_THE_LEFT": "Changes the left stick to move the player while in first-person mode", + "TOOLTIP_INVERTS_THE_CAMERA": "Inverts the Camera Y Axis in:\\n-Free look", + "TOOLTIP_INVERTS_THE_SHIELD": "Inverts the Shield Aiming Y Axis", + "TOOLTIP_VIEW_GYRO_PREVENTS_THE": "Prevents the C-Up view from auto-centering, allowing for Gyro Aiming", + "TOOLTIP_THE_CURSOR_WILL": "The cursor will only move a single space no matter how long a D-pad direction is held", + "COLLAPSING_DPAD": "D-Pad", + "COLLAPSING_RUMBLE_RUMBLE": "Rumble", + "COLLAPSING_GYRO_GYRO": "Gyro", + "COLLAPSING_LEDS": "LEDs", + "COLLAPSING_MODIFIER_BUTTONS": "Modifier Buttons", + "COLLAPSING_CONTROLS_OCARINA_CONTROLS": "Ocarina Controls", + "COLLAPSING_CONTROLS_CAMERA_CONTROLS": "Camera Controls", + "COLLAPSING_CONTROLS_DPAD_CONTROLS": "D-Pad Controls", + "BUTTON_ALL": "Clear All", + "TEXT_PORT_THIS_WILL_ALL": "This will clear all existing mappings for\\nGamepad (SDL) on port %d.\\n\\nContinue?", + "BUTTON_SET_DEFAULTS": "Set defaults", + "TOOLTIP_ENEMIES_AND_BOSSES": "Enemies and Bosses spawn with random sizes.", + "BUTTON_SET_TOKENS": "Set Tokens", + "SEPARATOR_WINDOW_WINDOW_OPTIONS": "Window Options", + "SEPARATOR_SPLIT_LIST_MANAGEMENT": "Split List Management", + "TEXT_NEW_LIST_NAME": "New List Name:", + "TEXT_LOAD_SELECT_LIST": "Select List to Load:", + "SEPARATOR_RESOURCES": "Resources", + "SEPARATOR_SPOILER_LOG_REWARDS": "Spoiler Log Rewards", + "TEXT_NAME": "Name: %s", + "SEPARATOR_LOADSAVE_SPOILER_LOG": "Load/Save Spoiler Log", + "TEXT_SPOILER_LOGS_FOUND": "No Spoiler Logs found.", + "SEPARATOR_CURRENT_SEED_HASH": "Current Seed Hash", + "TEXT_SPOILER_LOG_LOADED": "No Spoiler Log Loaded", + "SEPARATOR_OPTIONS": "Options", + "TEXT_LOAD_PLEASE_SPOILER_DATA": "Please Load Spoiler Data...", + "TEXT_CURRENT_HINT": "Current Hint:", + "TEXT_NEW_HINT": "New Hint:", + "TOOLTIP_RANDOMIZE_HINT": "Randomize Hint", + "TEXT_CHECKS": "Checks: %d/%d", + "TOOLTIP_CUSTOMIZE_WHAT_NUMBERS": "Customize what numbers are shown for triforce piece tracking.", + "TOOLTIP_SHOWS_MORE_EASILY": "Shows an 'H' or an 'L' to more easily distinguish between Hookshot and Longshot.", + "TEXT_LOAD_WAITING_FOR_FILE": "Waiting for file load...", + "TOOLTIP_SETS_THE_FONT": "Sets the font size used in the check tracker.", + "TOOLTIP_ENABLED_WILL_HIDE": "If enabled, will hide area headers that have no locations matching filter", + "SEPARATOR_TRACKER_HEADER_VISIBILITY": "Tracker Header Visibility", + "TOOLTIP_ENABLED_WILL_PREVENT": "If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles.", + "TOOLTIP_ENABLED_WILL_SHOW": "If enabled, will show a check's logic when hovering over it.", + "TEXTWRAPPED_THE_ENTRANCE_TRACKER": "The entrance tracker will only track shuffled entrances", + "TEXT_SORT": "Sort By", + "TOOLTIP_SORT_ENTRANCES_THE": "Sort entrances by the overrided destination", + "TEXT_LIST_ITEMS": "List Items", + "TOOLTIP_AUTOMATICALLY_SCROLL_THE": "Automatically scroll to the first available entrance in the current scene", + "TOOLTIP_HIGHLIGHT_THE_ENTRANCE": "Highlight the previous entrance that Link came from", + "TOOLTIP_HIGHLIGHT_AVAILABLE_ENTRANCES": "Highlight available entrances in the current scene", + "TOOLTIP_COLLAPSE_UNDISCOVERED_ENTRANCES": "Collapse undiscovered entrances towards the bottom of each group", + "TOOLTIP_HIDE_REVERSE_ENTRANCE": "Hide reverse entrance transitions when Decouple Entrances is off", + "TEXT_GROUP": "Group By", + "TOOLTIP_GROUP_ENTRANCES_THEIR": "Group entrances by their entrance type", + "TEXT_SPOILER_REVEAL": "Spoiler Reveal", + "TOOLTIP_REVEAL_THE_SOURCE": "Reveal the source for undiscovered entrances", + "TOOLTIP_REVEAL_THE_DESTINATION": "Reveal the destination for undiscovered entrances", + "TEXT_PLATFORM_WINDOWS": "Platform: Windows", + "TEXT_PLATFORM_IOS": "Platform: iOS", + "TEXT_PLATFORM_MACOS": "Platform: macOS", + "TEXT_PLATFORM_LINUX": "Platform: Linux", + "TEXT_PLATFORM_UNKNOWN": "Platform: Unknown", + "TEXT_FPS_STATUS_03F_MSFRAME": "Status: %0.3f ms/frame (%0.1f FPS)", + "TEXT_TABLE": "Table ID", + "TEXT": "Text ID", + "TOOLTIP_LANGUAGE_LOAD_WHICH_LANGUAGE": "Which language to load from the selected text ID", + "BUTTON_DISPLAY_MESSAGEEXISTINGMESSAGE": "Display Message##ExistingMessage", + "TEXT_CUSTOM_MESSAGE": "Custom Message", + "BUTTON_DISPLAY_MESSAGECUSTOMMESSAGE": "Display Message##CustomMessage", + "TEXT_DESCRIPTION": "Description: %s", + "TEXT_CATEGORY": "Category: %s", + "TEXT_PARAMETERS": "Parameters: %d", + "TEXT_ACTOR_ACTOR_LIST_INDEX": "Actor List Index: %d", + "TEXT_ACTOR_ACTOR_POSITION": "Actor Position", + "TEXT_ACTOR_ACTOR_ROTATION": "Actor Rotation", + "TEXT_FLAGS": "Flags", + "TEXT_BGCHECKFLAGS": "bgCheckFlags", + "TEXT_ACTOR_SELECT_ACTOR_DISPLAY": "Select an actor to display information.", + "TOOLTIP_ACTOR_GRABS_ACTOR_WITH": "Grabs actor with target arrow above it. You might need C-Up for enemies", + "TOOLTIP_ACTOR_GRABS_ACTOR_THAT": "Grabs actor that Link is holding", + "TOOLTIP_ACTOR_CONTROLS_CHANGES_THE": "Changes the actor specific param menus with a direct input", + "TEXT_ACTOR_ACTOR_SPECIFIC_DATA": "Actor Specific Data", + "TEXT_ACTOR_NEW_ACTOR_POSITION": "New Actor Position", + "TEXT_ACTOR_NEW_ACTOR_ROTATION": "New Actor Rotation", + "TEXT_ACTOR_GLOBAL_CONTEXT_NEEDED": "Global Context needed for actor info!", + "TOOLTIP_COLLISION_APPLIES_THE_SCENES": "Applies the scene's shading to the collision display.", + "INPUTTEXT_DISPLAY_LISTS": "Search Display Lists", + "TEXT_RESOURCE_TYPE_NOT": "Resource type is not a Display List. Please choose another.", + "TEXT_TOTAL_INSTRUCTION_SIZE": "Total Instruction Size: %lu", + "TEXT_FMT": "FMT: %u", + "TEXT_SIZ": "SIZ: %u", + "TEXT_LINE": "LINE: %u", + "TEXT_TMEM": "TMEM: %u", + "TEXT_TILE": "TILE: %u", + "TEXT_PAL": "PAL: %u", + "TEXT_CMT": "CMT: %u", + "TEXT_MASKT": "MASKT: %u", + "TEXT_SHIFT": "SHIFT: %u", + "TEXT_CMS": "CMS: %u", + "TEXT_MASKS": "MASKS: %u", + "TEXT_SHIFTS": "SHIFTS: %u", + "TEXT_WIDTH": "WIDTH: %u", + "TEXT_TEXTURE_NAME": "Texture Name: %s", + "TEXT_NUM_VTX": "Num VTX: %u", + "TEXT_OFFSET": "Offset: %u", + "TEXT_VERTEX_NAME": "Vertex Name: %s", + "TEXT_RESERVED_SECOND_HALF": "%lu - Reserved - Second half of %s", + "TEXT_ERROR_DISPLAYING_INSTRUCTIONS": "Error displaying DL instructions.", + "TOOLTIP_CURRENT_FILE_NUMBER": "Current File Number", + "TOOLTIP_PLAYER_NAME": "Player Name", + "TOOLTIP_ENCODING_USED_FOR": "Encoding used for Player Name", + "TOOLTIP_MAXIMUM_HEALTH_UNITS": "Maximum health. 16 units per full heart", + "TOOLTIP_CURRENT_HEALTH_UNITS": "Current health. 16 units per full heart", + "TOOLTIP_DOUBLE_DEFENSE_UNLOCKED": "Is double defense unlocked?", + "TOOLTIP_CURRENT_MAGIC_LEVEL": "Current magic level", + "TOOLTIP_CURRENT_MAGIC_UNITS": "Current magic. 48 units per magic level", + "TOOLTIP_CURRENT_RUPEES": "Current rupees", + "TOOLTIP_TIME_DAY": "Time of day", + "TOOLTIP_TOTAL_NUMBER_DAYS": "Total number of days elapsed since receiving claim check from Biggoron", + "TOOLTIP_TOTAL_NUMBER_DEATHS": "Total number of deaths", + "TOOLTIP_BIGGORON_SWORD_UNLOCKED": "Is Biggoron sword unlocked? Replaces Giant's knife", + "TOOLTIP_GIANTS_KNIFE_HEALTH": "Giant's knife health. Default is 8. Must be >0 for Biggoron sword to work", + "TOOLTIP_FROM_WHICH_ENTRANCE": "From which entrance did Link arrive?", + "TOOLTIP_WHICH_CUTSCENE_THIS": "Which cutscene is this?", + "TOOLTIP_NAVI_WANTS_TALK": "Navi wants to talk at 600 units, decides not to at 3000.", + "TOOLTIP_HEAT_TIMER_RACE": "Heat timer, race timer, etc. Has white font", + "TOOLTIP_TIME_SECONDS": "Time, in seconds", + "TOOLTIP_TRADE_TIMER_GANON": "Trade timer, Ganon collapse timer, etc. Has yellow font", + "TOOLTIP_AUDIO_SOUND_SETTING": "Sound setting", + "TOOLTIP_SAVE_WARNING_YOU_YOUR": "WARNING! If you save, your file may be locked! Use caution!", + "TOOLTIP_ZTARGETING_BEHAVIOR": "Z-Targeting behavior", + "TOOLTIP_CURRENTLY_OBTAINED_TRIFORCE": "Currently obtained Triforce Pieces. For Triforce Hunt.", + "TOOLTIP_USED_THE_SINKING": "Used the Sinking lure to catch it.", + "TOOLTIP_PLAYED_LEAST_ONE": "Played at least one game as an adult", + "TOOLTIP_THE_OWNERS_NOW": "The owner's now visibly bald when Adult Link.", + "TOOLTIP_DETERMINES_WEATHER_AND": "Determines weather and school size during dawn/dusk.", + "TOOLTIP_RESTRICTS_ITEMS_AND": "Restricts items and ammo to only what is possible to legally acquire in-game", + "TOOLTIP_NONE": "None", + "TEXT_AMMO": "Ammo", + "TEXT_0X02X": "0x%02X", + "TEXTWRAPPED_0X02X": "0x%02X: %s", + "TEXT_FLAGS_MATCH_THE": "No flags match the current search.", + "TEXT_STATEFLAGS1": "stateFlags1", + "TEXT_STATEFLAGS2": "stateFlags2", + "TEXT_STATEFLAGS3": "stateFlags3", + "TEXT_UNK6AEROTFLAGS": "unk_6AE_rotFlags", + "TEXT_SWITCH": "Switch", + "TEXT_TEMP_SWITCH": "Temp Switch", + "TEXT_TEMP": "Temp Clear", + "TEXT_COLLECT": "Collect", + "TEXT_TEMP_COLLECT": "Temp Collect", + "TEXT_CHEST": "Chest", + "TOOLTIP_SAVE_CURRENT_SCENE_FLAGS": "Save current scene flags. Normally happens on scene exit", + "TOOLTIP_CURRENT_SCENE_FLAGS": "Clear current scene flags. Reload scene to see changes", + "TEXT_CURRENT_GAME_STATE": "Current game state does not have an active scene", + "TEXT_MAP": "Map", + "TOOLTIP_OPEN_FLAGS_FOR": "Open flags for current scene", + "TEXT_ROOMS": "Rooms", + "TEXT_FLOORS": "Floors", + "TEXT_GOLD_SKULLTULAS": "Gold Skulltulas", + "BUTTON_CATCH_ALL_CHILD": "Catch All (Child)", + "BUTTON_UNCATCH_ALL_CHILD": "Uncatch All (Child)", + "BUTTON_CATCH_ALL_ADULT": "Catch All (Adult)", + "BUTTON_UNCATCH_ALL_ADULT": "Uncatch All (Adult)", + "TEXT_DUNGEON_ITEMS": "Dungeon Items", + "TEXT_BARINADES_LAIR_DOES": "Barinade's Lair does not have small keys", + "TEXT_LINKS_POSITION": "Link's Position", + "TEXT_LINKS_ROTATION": "Link's Rotation", + "TEXT_LINKS_MODEL_ROTATION": "Link's Model Rotation", + "TEXT_LINKS_CURRENT_EQUIPMENT": "Link's Current Equipment", + "TEXT_CURRENT_ITEMS": "Current Items", + "TEXT_CURRENT_DPAD_ITEMS": "Current D-pad Items", + "TEXT_PLAYER_STATE": "Player State", + "TEXT_SWORD": "Sword", + "TEXT_GLOBAL_CONTEXT_NEEDED": "Global Context needed for player info!", + "TEXT_TOTAL_REGISTERED": "Total Registered: %d", + "TEXT_NORMAL": "Normal", + "TEXT_PTR": "Ptr", + "TEXT_FILTER": "Filter", + "TOOLTIP_STOP_PREVIEW": "Stop Preview", + "TOOLTIP_PLAY_PREVIEW": "Play Preview", + "TOOLTIP_UNKNOWN": "Reset to default", + "TOOLTIP_AUDIO_RANDOMIZE_THIS_SOUND": "Randomize this sound", + "TOOLTIP_AUDIO_AUDIO_RANDOMIZES_ALL": "Randomizes all unlocked music and sound effects across tab groups", + "TOOLTIP_AUDIO_AUDIO_RESETS_ALL": "Resets all unlocked music and sound effects across tab groups", + "TOOLTIP_AUDIO_AUDIO_LOCKS_ALL": "Locks all music and sound effects across tab groups", + "TOOLTIP_AUDIO_AUDIO_UNLOCKS_ALL": "Unlocks all music and sound effects across tab groups", + "TOOLTIP_AUDIO_DISABLE_THE_LOW": "Disable the low HP beeping sound.", + "TOOLTIP_AUDIO_DISABLES_THE_VOICE": "Disables the voice audio when Navi calls you.", + "TOOLTIP_AUDIO_PLAYS_THE_BATTLE": "Plays the battle music when getting close to a Leever, like in Majora's Mask.", + "TEXT_PRESETS_PRESETS": "Presets", + "TEXT_PRESETS_PRESETS_PRESETS_WITH": "No presets with rando options. Make some in Settings -> Presets", + "TEXT_PRESETS_PRESETS_FOUND": "No presets found.", + "TEXT_DLIST": "dlist: %p", + "TEXT_BREAKPOINT": "BreakPoint: %s", + "TEXT_DISP_STACK": "Disp Stack", + "TEXT_TILES": "Tiles", + "TEXT_LOADED_TEXTURES": "Loaded Textures", + "TEXT_LOAD_TEXTURE": "Texture To Load", + "BUTTON_DEV_DEBUG": "Debug", + "BUTTON_RESUME_GAME": "Resume Game", + "MENUITEM_COPY_TEXT": "Copy Text", + "BUTTON_SUBMIT": "Submit", + "TEXT_PLATFORM_OPENBSD": "Platform: OpenBSD", + "TEXT_FPS_STATUS_MSFRAME_FPS": "Status: %.3f ms/frame (%.1f FPS)", + "OVERLAYS_TEXT_FONT": "Overlays Text Font", + "KEEP_TRACK_OF_THE": "Keep track of the timer as an in-game HUD element. The position of the ", + "TOOLTIP_KEEP_TRACK_OF_THE": "Keep track of the timer as an in-game HUD element. The position of the ", + "ALLOWS_A_MORE_INDEPTH": "Allows a more in-depth perspective of time spent in a certain map.", + "TOOLTIP_ALLOWS_A_MORE_INDEPTH": "Allows a more in-depth perspective of time spent in a certain map.", + "TIMESTAMPS_ARE_RELATIVE_TO": "Timestamps are relative to starting timestamp rather than in game time, ", + "TOOLTIP_TIMESTAMPS_ARE_RELATIVE_TO": "Timestamps are relative to starting timestamp rather than in game time, ", + "TOGGLE_MODS_FOR_GRAPHICS": "Toggle mods. For graphics mods, this means toggling between default and mod graphics.", + "TOOLTIP_TOGGLE_MODS_FOR_GRAPHICS": "Toggle mods. For graphics mods, this means toggling between default and mod graphics.", + "ALLOWS_PRESSING_THE_TAB": "Allows pressing the Tab key to toggle mods", + "TOOLTIP_ALLOWS_PRESSING_THE_TAB": "Allows pressing the Tab key to toggle mods", + "CHANGES_THE_MENU_DISPLAY": "Changes the menu display from overlay to windowed.", + "TOOLTIP_CHANGES_THE_MENU_DISPLAY": "Changes the menu display from overlay to windowed.", + "ENABLES_DEBUG_MODE_ALLOWING": "Enables Debug Mode, allowing you to select maps with L + R + Z, noclip ", + "TOOLTIP_ENABLES_DEBUG_MODE_ALLOWING": "Enables Debug Mode, allowing you to select maps with L + R + Z, noclip ", + "ENABLES_THE_REGISTRY_EDITOR": "Enables the registry editor.", + "TOOLTIP_ENABLES_THE_REGISTRY_EDITOR": "Enables the registry editor.", + "CHANGES_THE_BEHAVIOR_OF": "Changes the behavior of debug file select creation (creating a save file on slot 1 ", + "TOOLTIP_CHANGES_THE_BEHAVIOR_OF": "Changes the behavior of debug file select creation (creating a save file on slot 1 ", + "ENABLES_SKULLTULA_DEBUG_WHEN": "Enables Skulltula Debug, when moving the cursor in the menu above various ", + "TOOLTIP_ENABLES_SKULLTULA_DEBUG_WHEN": "Enables Skulltula Debug, when moving the cursor in the menu above various ", + "ADVANCE_1_FRAME": "Advance 1 frame.", + "TOOLTIP_ADVANCE_1_FRAME": "Advance 1 frame.", + "ADVANCE_FRAMES_WHILE_THE": "Advance frames while the button is held.", + "TOOLTIP_ADVANCE_FRAMES_WHILE_THE": "Advance frames while the button is held.", + "THE_LOG_LEVEL_DETERMINES": "The log level determines which messages are printed to the console.", + "TOOLTIP_THE_LOG_LEVEL_DETERMINES": "The log level determines which messages are printed to the console.", + "OPTIMIZED_DEBUG_WARP_SCREEN": "Optimized Debug Warp Screen, with the added ability to chose entrances and time of day.", + "TOOLTIP_OPTIMIZED_DEBUG_WARP_SCREEN": "Optimized Debug Warp Screen, with the added ability to chose entrances and time of day.", + "TRANSLATE_THE_DEBUG_WARP": "Translate the Debug Warp Screen based on the game language.", + "TOOLTIP_TRANSLATE_THE_DEBUG_WARP": "Translate the Debug Warp Screen based on the game language.", + "ENABLES_THE_SEPARATE_STATS": "Enables the separate Stats Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_STATS": "Enables the separate Stats Window.", + "ENABLES_THE_SEPARATE_CONSOLE": "Enables the separate Console Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_CONSOLE": "Enables the separate Console Window.", + "ENABLES_THE_SEPARATE_SAVE": "Enables the separate Save Editor Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_SAVE": "Enables the separate Save Editor Window.", + "ENABLES_THE_SEPARATE_HOOK": "Enables the separate Hook Debugger Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_HOOK": "Enables the separate Hook Debugger Window.", + "ENABLES_THE_SEPARATE_COLLISION": "Enables the separate Collision Viewer Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_COLLISION": "Enables the separate Collision Viewer Window.", + "ENABLES_THE_SEPARATE_ACTOR": "Enables the separate Actor Viewer Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ACTOR": "Enables the separate Actor Viewer Window.", + "ENABLES_THE_SEPARATE_DISPLAY": "Enables the separate Display List Viewer Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_DISPLAY": "Enables the separate Display List Viewer Window.", + "ENABLES_THE_SEPARATE_VALUE": "Enables the separate Value Viewer Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_VALUE": "Enables the separate Value Viewer Window.", + "ENABLES_THE_SEPARATE_MESSAGE": "Enables the separate Message Viewer Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_MESSAGE": "Enables the separate Message Viewer Window.", + "ENABLES_THE_SEPARATE_GFX": "Enables the separate Gfx Debugger Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_GFX": "Enables the separate Gfx Debugger Window.", + "MAKES_THE_CURSOR_ALWAYS": "Makes the cursor always visible, even in full screen.", + "TOOLTIP_MAKES_THE_CURSOR_ALWAYS": "Makes the cursor always visible, even in full screen.", + "OPENS_THE_FOLDER_THAT": "Opens the folder that contains the save and mods folders, etc.", + "TOOLTIP_OPENS_THE_FOLDER_THAT": "Opens the folder that contains the save and mods folders, etc.", + "CONFIGURE_WHAT_HAPPENS_WHEN": "Configure what happens when starting or resetting the game.\\n\\n", + "TOOLTIP_CONFIGURE_WHAT_HAPPENS_WHEN": "Configure what happens when starting or resetting the game.\\n\\n", + "ENABLES_TEXT_TO_SPEECH": "Enables text to speech for in game dialog", + "TOOLTIP_ENABLES_TEXT_TO_SPEECH": "Enables text to speech for in game dialog", + "DISABLES_THE_AUTOMATIC_RECENTERING": "Disables the automatic re-centering of the camera when idle.", + "TOOLTIP_DISABLES_THE_AUTOMATIC_RECENTERING": "Disables the automatic re-centering of the camera when idle.", + "DISABLES_THE_WHITE_SCREEN": "Disables the white screen flash on enemy kill.", + "TOOLTIP_DISABLES_THE_WHITE_SCREEN": "Disables the white screen flash on enemy kill.", + "DISABLE_THE_GEOMETRY_WOBBLE": "Disable the geometry wobble and camera distortion inside Jabu.", + "TOOLTIP_DISABLE_THE_GEOMETRY_WOBBLE": "Disable the geometry wobble and camera distortion inside Jabu.", + "CHANGES_THE_SCALING_OF": "Changes the scaling of the ImGui menu elements.", + "TOOLTIP_CHANGES_THE_SCALING_OF": "Changes the scaling of the ImGui menu elements.", + "TOGGLES_FULLSCREEN_ONOFF": "Toggles Fullscreen On/Off.", + "TOOLTIP_TOGGLES_FULLSCREEN_ONOFF": "Toggles Fullscreen On/Off.", + "MULTIPLIES_YOUR_OUTPUT_RESOLUTION": "Multiplies your output resolution by the value inputted, as a more intensive but effective ", + "TOOLTIP_MULTIPLIES_YOUR_OUTPUT_RESOLUTION": "Multiplies your output resolution by the value inputted, as a more intensive but effective ", + "ACTIVATES_MSAA_MULTISAMPLE_ANTIALIASING": "Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x, to smooth the edges of ", + "TOOLTIP_ACTIVATES_MSAA_MULTISAMPLE_ANTIALIASING": "Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x, to smooth the edges of ", + "MATCHES_INTERPOLATION_VALUE_TO": "Matches interpolation value to the refresh rate of your display.", + "TOOLTIP_MATCHES_INTERPOLATION_VALUE_TO": "Matches interpolation value to the refresh rate of your display.", + "REMOVES_TEARING_BUT_CLAMPS": "Removes tearing, but clamps your max FPS to your displays refresh rate.", + "TOOLTIP_REMOVES_TEARING_BUT_CLAMPS": "Removes tearing, but clamps your max FPS to your displays refresh rate.", + "ENABLES_WINDOWED_FULLSCREEN_MODE": "Enables Windowed Fullscreen Mode.", + "TOOLTIP_ENABLES_WINDOWED_FULLSCREEN_MODE": "Enables Windowed Fullscreen Mode.", + "ALLOWS_MULTIPLE_WINDOWS_TO": "Allows multiple windows to be opened at once. Requires a reload to take effect.", + "TOOLTIP_ALLOWS_MULTIPLE_WINDOWS_TO": "Allows multiple windows to be opened at once. Requires a reload to take effect.", + "SETS_THE_APPLIED_TEXTURE": "Sets the applied Texture Filtering.", + "TOOLTIP_SETS_THE_APPLIED_TEXTURE": "Sets the applied Texture Filtering.", + "ENABLES_THE_SEPARATE_BINDINGS": "Enables the separate Bindings Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_BINDINGS": "Enables the separate Bindings Window.", + "TOGGLES_THE_INPUT_VIEWER": "Toggles the Input Viewer.", + "TOOLTIP_TOGGLES_THE_INPUT_VIEWER": "Toggles the Input Viewer.", + "ENABLES_THE_SEPARATE_INPUT": "Enables the separate Input Viewer Settings Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_INPUT": "Enables the separate Input Viewer Settings Window.", + "WHICH_CORNER_OF_THE": "Which corner of the screen notifications appear in.", + "TOOLTIP_WHICH_CORNER_OF_THE": "Which corner of the screen notifications appear in.", + "HOW_LONG_NOTIFICATIONS_ARE": "How long notifications are displayed for.", + "TOOLTIP_HOW_LONG_NOTIFICATIONS_ARE": "How long notifications are displayed for.", + "HOW_OPAQUE_THE_BACKGROUND": "How opaque the background of notifications is.", + "TOOLTIP_HOW_OPAQUE_THE_BACKGROUND": "How opaque the background of notifications is.", + "HOW_LARGE_NOTIFICATIONS_ARE": "How large notifications are.", + "TOOLTIP_HOW_LARGE_NOTIFICATIONS_ARE": "How large notifications are.", + "DISPLAYS_A_TEST_NOTIFICATION": "Displays a test notification.", + "TOOLTIP_DISPLAYS_A_TEST_NOTIFICATION": "Displays a test notification.", + "PREVENT_NOTIFICATIONS_FROM_PLAYING": "Prevent notifications from playing a sound.", + "TOOLTIP_PREVENT_NOTIFICATIONS_FROM_PLAYING": "Prevent notifications from playing a sound.", + "ENABLES_THE_SEPARATE_MOD": "Enables the separate Mod Menu Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_MOD": "Enables the separate Mod Menu Window.", + "OVERRIDE_THE_RESOLUTION_SCALE": "Override the resolution scale slider and use the settings below, irrespective of window size.", + "TOOLTIP_OVERRIDE_THE_RESOLUTION_SCALE": "Override the resolution scale slider and use the settings below, irrespective of window size.", + "INTEGER_SCALES_THE_IMAGE": "Integer scales the image. Only available in Pixel Perfect Mode.", + "TOOLTIP_INTEGER_SCALES_THE_IMAGE": "Integer scales the image. Only available in Pixel Perfect Mode.", + "AUTOMATICALLY_SETS_SCALE_FACTOR": "Automatically sets scale factor to fit window. Only available in Pixel Perfect Mode.", + "TOOLTIP_AUTOMATICALLY_SETS_SCALE_FACTOR": "Automatically sets scale factor to fit window. Only available in Pixel Perfect Mode.", + "PREVENTS_INTEGER_SCALING_FACTOR": "Prevents integer scaling factor from exceeding screen bounds.\\n\\n", + "TOOLTIP_PREVENTS_INTEGER_SCALING_FACTOR": "Prevents integer scaling factor from exceeding screen bounds.\\n\\n", + "REMOVE_THIS_BUTTON_FROM": "Remove this button from the combination", + "TOOLTIP_REMOVE_THIS_BUTTON_FROM": "Remove this button from the combination", + "CREATES_A_NEW_RANDOM": "Creates a new random seed value to be used when generating a randomizer", + "TOOLTIP_CREATES_A_NEW_RANDOM": "Creates a new random seed value to be used when generating a randomizer", + "WHEN_OBTAINING_RUPEES_RANDOMIZE": "When obtaining Rupees, randomize what the Rupee is called in the textbox.", + "TOOLTIP_WHEN_OBTAINING_RUPEES_RANDOMIZE": "When obtaining Rupees, randomize what the Rupee is called in the textbox.", + "USE_CUSTOM_GRAPHICS_FOR": "Use Custom graphics for Dungeon Keys, Big and Small, so that they can be easily told apart.", + "TOOLTIP_USE_CUSTOM_GRAPHICS_FOR": "Use Custom graphics for Dungeon Keys, Big and Small, so that they can be easily told apart.", + "MATCHES_THE_COLOR_OF": "Matches the color of maps & compasses to the dungeon they belong to. ", + "TOOLTIP_MATCHES_THE_COLOR_OF": "Matches the color of maps & compasses to the dungeon they belong to. ", + "WITH_SHUFFLE_SPEAK_JABBER": "With Shuffle Speak, jabber nut model & color will be generic.", + "TOOLTIP_WITH_SHUFFLE_SPEAK_JABBER": "With Shuffle Speak, jabber nut model & color will be generic.", + "IF_ENABLED_SIGNS_NEAR": "If enabled, signs near loading zones will tell you where they lead to.", + "TOOLTIP_IF_ENABLED_SIGNS_NEAR": "If enabled, signs near loading zones will tell you where they lead to.", + "ENABLES_THE_SEPARATE_RANDOMIZER": "Enables the separate Randomizer Settings Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_RANDOMIZER": "Enables the separate Randomizer Settings Window.", + "TOGGLES_THE_ITEM_TRACKER": "Toggles the Item Tracker.", + "TOOLTIP_TOGGLES_THE_ITEM_TRACKER": "Toggles the Item Tracker.", + "ENABLES_THE_SEPARATE_ITEM": "Enables the separate Item Tracker Settings Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ITEM": "Enables the separate Item Tracker Settings Window.", + "TOGGLES_THE_ENTRANCE_TRACKER": "Toggles the Entrance Tracker.", + "TOOLTIP_TOGGLES_THE_ENTRANCE_TRACKER": "Toggles the Entrance Tracker.", + "ENABLES_THE_SEPARATE_ENTRANCE": "Enables the separate Entrance Tracker Settings Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ENTRANCE": "Enables the separate Entrance Tracker Settings Window.", + "TOGGLES_THE_CHECK_TRACKER": "Toggles the Check Tracker.", + "TOOLTIP_TOGGLES_THE_CHECK_TRACKER": "Toggles the Check Tracker.", + "ENABLES_THE_SEPARATE_CHECK": "Enables the separate Check Tracker Settings Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_CHECK": "Enables the separate Check Tracker Settings Window.", + "SHOW_A_NOTIFICATION_WHEN": "Show a notification when the game is autosaved.", + "TOOLTIP_SHOW_A_NOTIFICATION_WHEN": "Show a notification when the game is autosaved.", + "ONLY_CHANGE_THE_TEXTURE": "Only change the texture of containers if you have the Stone of Agony.", + "TOOLTIP_ONLY_CHANGE_THE_TEXTURE": "Only change the texture of containers if you have the Stone of Agony.", + "NIGHTTIME_SKULLTULAS_WILL_SPAWN": "Nighttime Skulltulas will spawn during both day and night.", + "TOOLTIP_NIGHTTIME_SKULLTULAS_WILL_SPAWN": "Nighttime Skulltulas will spawn during both day and night.", + "ALLOWS_GRAVES_TO_BE": "Allows graves to be pulled when child during the day.", + "TOOLTIP_ALLOWS_GRAVES_TO_BE": "Allows graves to be pulled when child during the day.", + "SHOPS_AND_MINIGAMES_ARE": "Shops and Minigames are open both day and night. Requires a scene reload to take effect.", + "TOOLTIP_SHOPS_AND_MINIGAMES_ARE": "Shops and Minigames are open both day and night. Requires a scene reload to take effect.", + "ALLOWS_THE_CURSOR_ON": "Allows the cursor on the pause menu to be over any slot. Sometimes required in Randomizer ", + "TOOLTIP_ALLOWS_THE_CURSOR_ON": "Allows the cursor on the pause menu to be over any slot. Sometimes required in Randomizer ", + "SPEAK_TO_NAVI_WITH": "Speak to Navi with L but enter First-Person Camera with C-Up.", + "TOOLTIP_SPEAK_TO_NAVI_WITH": "Speak to Navi with L but enter First-Person Camera with C-Up.", + "ADDS_BACK_IN_A": "Adds back in a delay after unpausing before the game resumes playing again, ", + "TOOLTIP_ADDS_BACK_IN_A": "Adds back in a delay after unpausing before the game resumes playing again, ", + "BUFFERS_YOUR_INPUTS_TO": "Buffers your inputs to be executed a specified amount of frames later.", + "TOOLTIP_BUFFERS_YOUR_INPUTS_TO": "Buffers your inputs to be executed a specified amount of frames later.", + "REWORKS_TARGETING_FUNCTIONALITYN": "Reworks targeting functionality\\n", + "TOOLTIP_REWORKS_TARGETING_FUNCTIONALITYN": "Reworks targeting functionality\\n", + "BUTTONS_TO_ACTIVATE_TARGET": "Buttons to activate target switching.", + "TOOLTIP_BUTTONS_TO_ACTIVATE_TARGET": "Buttons to activate target switching.", + "DISABLE_RANDOM_CAMERA_WIGGLE": "Disable Random Camera Wiggle at Low Health.", + "TOOLTIP_DISABLE_RANDOM_CAMERA_WIGGLE": "Disable Random Camera Wiggle at Low Health.", + "ALLOW_LINK_TO_PUT": "Allow Link to put items away without having to wait around.", + "TOOLTIP_ALLOW_LINK_TO_PUT": "Allow Link to put items away without having to wait around.", + "RESETS_THE_NAVI_TIMER": "Resets the Navi timer on scene change. If you have already talked to her, ", + "TOOLTIP_RESETS_THE_NAVI_TIMER": "Resets the Navi timer on scene change. If you have already talked to her, ", + "ALLOW_LINK_TO_ENTER": "Allow Link to enter Jabu-Jabu without feeding him a fish.", + "TOOLTIP_ALLOW_LINK_TO_ENTER": "Allow Link to enter Jabu-Jabu without feeding him a fish.", + "SKIP_PICKUP_MESSAGES_FOR": "Skip Pickup Messages for Bottle Swipes.", + "TOOLTIP_SKIP_PICKUP_MESSAGES_FOR": "Skip Pickup Messages for Bottle Swipes.", + "PREVENT_FORCED_CONVERSATIONS_WITH": "Prevent forced conversations with Navi and/or other NPCs.", + "TOOLTIP_PREVENT_FORCED_CONVERSATIONS_WITH": "Prevent forced conversations with Navi and/or other NPCs.", + "HOLDING_DOWN_B_SKIPS": "Holding down B skips text.", + "TOOLTIP_HOLDING_DOWN_B_SKIPS": "Holding down B skips text.", + "SPEEDS_UP_LIFTING_SILVER": "Speeds up lifting Silver Rocks and Obelisks.", + "TOOLTIP_SPEEDS_UP_LIFTING_SILVER": "Speeds up lifting Silver Rocks and Obelisks.", + "SPEEDS_UP_SHIP_IN": "Speeds up ship in Shadow Temple.", + "TOOLTIP_SPEEDS_UP_SHIP_IN": "Speeds up ship in Shadow Temple.", + "MAKES_LINK_ALWAYS_KICK": "Makes Link always kick the chest to open it, instead of doing the longer ", + "TOOLTIP_MAKES_LINK_ALWAYS_KICK": "Makes Link always kick the chest to open it, instead of doing the longer ", + "SPEEDS_UP_EMPTYING_ANIMATION": "Speeds up emptying animation when dumping out the contents of a bottle.", + "TOOLTIP_SPEEDS_UP_EMPTYING_ANIMATION": "Speeds up emptying animation when dumping out the contents of a bottle.", + "SKIP_THE_TOWER_ESCAPE": "Skip the tower escape sequence between Ganondorf and Ganon.", + "TOOLTIP_SKIP_THE_TOWER_ESCAPE": "Skip the tower escape sequence between Ganondorf and Ganon.", + "CAUSES_YOUR_WALLET_TO": "Causes your Wallet to fill and empty faster when you gain or lose money.", + "TOOLTIP_CAUSES_YOUR_WALLET_TO": "Causes your Wallet to fill and empty faster when you gain or lose money.", + "DISABLES_2D_PRERENDERED_BACKGROUNDS": "Disables 2D pre-rendered backgrounds. Enable this when using a mod that ", + "TOOLTIP_DISABLES_2D_PRERENDERED_BACKGROUNDS": "Disables 2D pre-rendered backgrounds. Enable this when using a mod that ", + "RENDERS_A_HEALTH_BAR": "Renders a health bar for Enemies when Z-Targeted.", + "TOOLTIP_RENDERS_A_HEALTH_BAR": "Renders a health bar for Enemies when Z-Targeted.", + "MAKES_ALL_EQUIPMENT_VISIBLE": "Makes all equipment visible, regardless of age.", + "TOOLTIP_MAKES_ALL_EQUIPMENT_VISIBLE": "Makes all equipment visible, regardless of age.", + "RENDERS_GAUNTLETS_WHEN_USING": "Renders Gauntlets when using the Bow and Hookshot like in OoT3D.", + "TOOLTIP_RENDERS_GAUNTLETS_WHEN_USING": "Renders Gauntlets when using the Bow and Hookshot like in OoT3D.", + "HIDES_MOST_OF_THE": "Hides most of the UI when not needed.\\n", + "TOOLTIP_HIDES_MOST_OF_THE": "Hides most of the UI when not needed.\\n", + "DISABLES_THE_BEATING_ANIMATION": "Disables the Beating Animation of the Hearts on the HUD.", + "TOOLTIP_DISABLES_THE_BEATING_ANIMATION": "Disables the Beating Animation of the Hearts on the HUD.", + "ALWAYS_SHOWS_DUNGEON_ENTRANCE": "Always shows dungeon entrance icons on the Minimap.", + "TOOLTIP_ALWAYS_SHOWS_DUNGEON_ENTRANCE": "Always shows dungeon entrance icons on the Minimap.", + "THE_SKYBOX_IN_THE": "The skybox in the background of the File Select screen will go through the ", + "TOOLTIP_THE_SKYBOX_IN_THE": "The skybox in the background of the File Select screen will go through the ", + "REMOVE_THE_DARKNESS_THAT": "Remove the Darkness that appears when charging a Spin Attack.", + "TOOLTIP_REMOVE_THE_DARKNESS_THAT": "Remove the Darkness that appears when charging a Spin Attack.", + "LINK_WILL_NOT_SPIN": "Link will not spin when the Goron Pot starts to spin.", + "TOOLTIP_LINK_WILL_NOT_SPIN": "Link will not spin when the Goron Pot starts to spin.", + "ADJUSTS_THE_HORIZONTAL_CULLING": "Adjusts the Horizontal Culling Plane to account for Widescreen Resolutions.", + "TOOLTIP_ADJUSTS_THE_HORIZONTAL_CULLING": "Adjusts the Horizontal Culling Plane to account for Widescreen Resolutions.", + "ALLOWS_UNEQUIPPING_ITEMS_FROM": "Allows unequipping items from C-Buttons/D-pad by hovering over an equipped ", + "TOOLTIP_ALLOWS_UNEQUIPPING_ITEMS_FROM": "Allows unequipping items from C-Buttons/D-pad by hovering over an equipped ", + "ALLOWS_EQUIPPING_SHIELDS_TUNICS": "Allows equipping Shields, Tunics and Boots to C-Buttons/D-pad.", + "TOOLTIP_ALLOWS_EQUIPPING_SHIELDS_TUNICS": "Allows equipping Shields, Tunics and Boots to C-Buttons/D-pad.", + "ALLOWS_LINK_TO_UNSHEATHE": "Allows Link to unsheathe sword without slashing automatically.", + "TOOLTIP_ALLOWS_LINK_TO_UNSHEATHE": "Allows Link to unsheathe sword without slashing automatically.", + "ADDS_A_PROMPT_TO": "Adds a prompt to equip newly-obtained Swords, Shields, and Tunics.", + "TOOLTIP_ADDS_A_PROMPT_TO": "Adds a prompt to equip newly-obtained Swords, Shields, and Tunics.", + "PREVENT_DROPPING_INPUTS_WHEN": "Prevent dropping inputs when playing the Ocarina too quickly.", + "TOOLTIP_PREVENT_DROPPING_INPUTS_WHEN": "Prevent dropping inputs when playing the Ocarina too quickly.", + "SKIP_THE_PART_WHERE": "Skip the part where the Ocarina Playback is called when you play a song.", + "TOOLTIP_SKIP_THE_PART_WHERE": "Skip the part where the Ocarina Playback is called when you play a song.", + "ALLOWS_LINK_TO_FREELY": "Allows Link to freely change age by playing the Song of Time.\\n", + "TOOLTIP_ALLOWS_LINK_TO_FREELY": "Allows Link to freely change age by playing the Song of Time.\\n", + "ALLOWS_MASKS_TO_BE": "Allows masks to be equipped normally from the pause menu as adult.", + "TOOLTIP_ALLOWS_MASKS_TO_BE": "Allows masks to be equipped normally from the pause menu as adult.", + "STOPS_MASKS_FROM_AUTOMATICALLY": "Stops masks from automatically unequipping on certain situations:\\n", + "TOOLTIP_STOPS_MASKS_FROM_AUTOMATICALLY": "Stops masks from automatically unequipping on certain situations:\\n", + "TURNS_BUNNY_HOOD_INVISIBLE": "Turns Bunny Hood Invisible while still maintaining its effects.", + "TOOLTIP_TURNS_BUNNY_HOOD_INVISIBLE": "Turns Bunny Hood Invisible while still maintaining its effects.", + "ALLOWS_YOU_TO_CONTROL": "Allows you to control a Bombchu after dropping it.\\n", + "TOOLTIP_ALLOWS_YOU_TO_CONTROL": "Allows you to control a Bombchu after dropping it.\\n", + "MAKE_DEKU_NUTS_EXPLODE": "Make Deku Nuts explode Bombs, similar to how they interact with Bombchus. ", + "TOOLTIP_MAKE_DEKU_NUTS_EXPLODE": "Make Deku Nuts explode Bombs, similar to how they interact with Bombchus. ", + "REMOVES_THE_CAP_OF": "Removes the cap of 3 active explosives being deployed at once.", + "TOOLTIP_REMOVES_THE_CAP_OF": "Removes the cap of 3 active explosives being deployed at once.", + "BOMBCHUS_DO_NOT_SELL": "Bombchus do not sell out when bought, and a 10 pack of Bombchus costs 99 rupees ", + "TOOLTIP_BOMBCHUS_DO_NOT_SELL": "Bombchus do not sell out when bought, and a 10 pack of Bombchus costs 99 rupees ", + "ALLOWS_CHILD_LINK_TO": "Allows Child Link to use a Bow with Arrows.\\n", + "TOOLTIP_ALLOWS_CHILD_LINK_TO": "Allows Child Link to use a Bow with Arrows.\\n", + "AIMING_WITH_A_BOW": "Aiming with a Bow or Slingshot will display a reticle as with the Hookshot ", + "TOOLTIP_AIMING_WITH_A_BOW": "Aiming with a Bow or Slingshot will display a reticle as with the Hookshot ", + "INSTANTLY_RETURN_THE_BOOMERANG": "Instantly return the Boomerang to Link by pressing its item button while ", + "TOOLTIP_INSTANTLY_RETURN_THE_BOOMERANG": "Instantly return the Boomerang to Link by pressing its item button while ", + "AIMING_WITH_THE_BOOMERANG": "Aiming with the Boomerang will display a reticle as with the Hookshot.", + "TOOLTIP_AIMING_WITH_THE_BOOMERANG": "Aiming with the Boomerang will display a reticle as with the Hookshot.", + "BLUE_FIRE_DROPPED_FROM": "Blue Fire dropped from bottle can be bottled.", + "TOOLTIP_BLUE_FIRE_DROPPED_FROM": "Blue Fire dropped from bottle can be bottled.", + "FIXES_KOKIRI_ANIMATION_STATE": "Fixes kokiri animation state to match their text state when getting ", + "TOOLTIP_FIXES_KOKIRI_ANIMATION_STATE": "Fixes kokiri animation state to match their text state when getting ", + "PREVENTS_IMMEDIATELY_FALLING_OFF": "Prevents immediately falling off climbable surfaces if climbing on the edges.", + "TOOLTIP_PREVENTS_IMMEDIATELY_FALLING_OFF": "Prevents immediately falling off climbable surfaces if climbing on the edges.", + "PREVENTS_THE_FOREST_STAGE": "Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable ", + "TOOLTIP_PREVENTS_THE_FOREST_STAGE": "Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable ", + "MAKE_CROUCH_STABBING_ALWAYS": "Make crouch stabbing always do the same damage as a regular slash.", + "TOOLTIP_MAKE_CROUCH_STABBING_ALWAYS": "Make crouch stabbing always do the same damage as a regular slash.", + "FIXES_CAMERA_SLIGHTLY_DRIFTING": "Fixes camera slightly drifting to the left when standing still due to a ", + "TOOLTIP_FIXES_CAMERA_SLIGHTLY_DRIFTING": "Fixes camera slightly drifting to the left when standing still due to a ", + "FIXES_CAMERA_SWING_RATE": "Fixes camera swing rate when the player falls off a ledge and the camera ", + "TOOLTIP_FIXES_CAMERA_SWING_RATE": "Fixes camera swing rate when the player falls off a ledge and the camera ", + "MAKES_THE_L_AND": "Makes the L and R buttons in the pause menu the same color.", + "TOOLTIP_MAKES_THE_L_AND": "Makes the L and R buttons in the pause menu the same color.", + "DISABLED_PATHS_VANISH_MORE": "Disabled: Paths vanish more the higher the resolution (Z-Fighting is based on resolution).\\n", + "TOOLTIP_DISABLED_PATHS_VANISH_MORE": "Disabled: Paths vanish more the higher the resolution (Z-Fighting is based on resolution).\\n", + "RESTORE_THE_ORIGINAL_RED": "Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.", + "TOOLTIP_RESTORE_THE_ORIGINAL_RED": "Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.", + "ALLOWS_BOMBCHUS_TO_EXPLODE": "Allows Bombchus to explode out of bounds. Similar to GameCube and Wii VC.", + "TOOLTIP_ALLOWS_BOMBCHUS_TO_EXPLODE": "Allows Bombchus to explode out of bounds. Similar to GameCube and Wii VC.", + "RESTORES_THE_WIDER_RANGE": "Restores the wider range of certain shutter doors from NTSC 1.0.\\n", + "TOOLTIP_RESTORES_THE_WIDER_RANGE": "Restores the wider range of certain shutter doors from NTSC 1.0.\\n", + "MODIFIES_DAMAGE_TAKEN_AFTER": "Modifies damage taken after falling into a void:\\n", + "TOOLTIP_MODIFIES_DAMAGE_TAKEN_AFTER": "Modifies damage taken after falling into a void:\\n", + "RESPAWN_WITH_FULL_HEALTH": "Respawn with Full Health instead of 3 hearts.", + "TOOLTIP_RESPAWN_WITH_FULL_HEALTH": "Respawn with Full Health instead of 3 hearts.", + "DISABLES_RANDOM_DROPS_EXCEPT": "Disables Random Drops, except from the Goron Pot, Dampe, and Bosses.", + "TOOLTIP_DISABLES_RANDOM_DROPS_EXCEPT": "Disables Random Drops, except from the Goron Pot, Dampe, and Bosses.", + "BOMBCHUS_WILL_SOMETIMES_DROP": "Bombchus will sometimes drop in place of Bombs.", + "TOOLTIP_BOMBCHUS_WILL_SOMETIMES_DROP": "Bombchus will sometimes drop in place of Bombs.", + "ADJUSTS_RATE_DAMPE_DROPS": "Adjusts rate Dampe drops flames during race.", + "TOOLTIP_ADJUSTS_RATE_DAMPE_DROPS": "Adjusts rate Dampe drops flames during race.", + "DYING_WILL_DELETE_YOUR": "Dying will delete your file.\\n\\n", + "TOOLTIP_DYING_WILL_DELETE_YOUR": "Dying will delete your file.\\n\\n", + "ALWAYS_GET_THE_HEART": "Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.", + "TOOLTIP_ALWAYS_GET_THE_HEART": "Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.", + "ALL_DOGS_CAN_BE": "All dogs can be traded in and will count as Richard.", + "TOOLTIP_ALL_DOGS_CAN_BE": "All dogs can be traded in and will count as Richard.", + "ALL_MAJOR_BOSSES_MOVE": "All Major Bosses move and act twice as fast.", + "TOOLTIP_ALL_MAJOR_BOSSES_MOVE": "All Major Bosses move and act twice as fast.", + "ALL_REGULAR_ENEMIES_AND": "All Regular Enemies and Mini-Bosses move and act twice as fast.", + "TOOLTIP_ALL_REGULAR_ENEMIES_AND": "All Regular Enemies and Mini-Bosses move and act twice as fast.", + "THE_TIME_BETWEEN_GROUPS": "The time between groups of Leevers spawning.", + "TOOLTIP_THE_TIME_BETWEEN_GROUPS": "The time between groups of Leevers spawning.", + "TURN_ONOFF_CHANGES_TO": "Turn on/off changes to the shooting gallery behavior.", + "TOOLTIP_TURN_ONOFF_CHANGES_TO": "Turn on/off changes to the shooting gallery behavior.", + "SKIPS_THE_SHOOTING_GALLERY": "Skips the Shooting Gallery minigame.", + "TOOLTIP_SKIPS_THE_SHOOTING_GALLERY": "Skips the Shooting Gallery minigame.", + "THE_AMMUNITION_AT_THE": "The ammunition at the start of the Shooting Gallery minigame as Child.", + "TOOLTIP_THE_AMMUNITION_AT_THE": "The ammunition at the start of the Shooting Gallery minigame as Child.", + "PREVENTS_THE_SMALL_CUCCO": "Prevents the small Cucco from appearing in the Bombchu Bowling minigame.", + "TOOLTIP_PREVENTS_THE_SMALL_CUCCO": "Prevents the small Cucco from appearing in the Bombchu Bowling minigame.", + "PREVENTS_THE_BIG_CUCCO": "Prevents the big Cucco from appearing in the Bombchu Bowling minigame.", + "TOOLTIP_PREVENTS_THE_BIG_CUCCO": "Prevents the big Cucco from appearing in the Bombchu Bowling minigame.", + "THE_NUMBER_OF_BOMBCHUS": "The number of Bombchus available at the start of the Bombchu Bowling minigame.", + "TOOLTIP_THE_NUMBER_OF_BOMBCHUS": "The number of Bombchus available at the start of the Bombchu Bowling minigame.", + "REMOVES_THE_TIMER_TO": "Removes the timer to play back the song.", + "TOOLTIP_REMOVES_THE_TIMER_TO": "Removes the timer to play back the song.", + "SKIPS_THE_LOST_WOODS": "Skips the Lost Woods Ocarina Memory Game.", + "TOOLTIP_SKIPS_THE_LOST_WOODS": "Skips the Lost Woods Ocarina Memory Game.", + "ADJUST_THE_NUMBER_OF": "Adjust the number of notes the Skull Kids play to start the first round.", + "TOOLTIP_ADJUST_THE_NUMBER_OF": "Adjust the number of notes the Skull Kids play to start the first round.", + "ALL_FISH_WILL_BE": "All fish will be caught instantly.", + "TOOLTIP_ALL_FISH_WILL_BE": "All fish will be caught instantly.", + "WHEN_A_LINE_IS": "When a line is stable, guarantee bite. Otherwise use Default logic.", + "TOOLTIP_WHEN_A_LINE_IS": "When a line is stable, guarantee bite. Otherwise use Default logic.", + "ONCE_A_HOOK_AS": "Once a hook as been set, Fish will never let go while being reeled in.", + "TOOLTIP_ONCE_A_HOOK_AS": "Once a hook as been set, Fish will never let go while being reeled in.", + "LOACHES_WILL_ALWAYS_APPEAR": "Loaches will always appear in the fishing pond instead of every four visits.", + "TOOLTIP_LOACHES_WILL_ALWAYS_APPEAR": "Loaches will always appear in the fishing pond instead of every four visits.", + "THE_POND_OWNER_WILL": "The Pond Owner will not ask to confirm if you want to keep a smaller Fish.", + "TOOLTIP_THE_POND_OWNER_WILL": "The Pond Owner will not ask to confirm if you want to keep a smaller Fish.", + "EVERY_FISH_IN_THE": "Every fish in the Fishing Pond will always be a Hyrule Loach.\\n\\n", + "TOOLTIP_EVERY_FISH_IN_THE": "Every fish in the Fishing Pond will always be a Hyrule Loach.\\n\\n", + "ALLOWS_LINK_TO_BOUNCE": "Allows Link to bounce off walls when linear velocity is high enough, this is ", + "TOOLTIP_ALLOWS_LINK_TO_BOUNCE": "Allows Link to bounce off walls when linear velocity is high enough, this is ", + "ALLOWS_DOGS_TO_FOLLOW": "Allows dogs to follow you anywhere you go, even if you leave the Market.", + "TOOLTIP_ALLOWS_DOGS_TO_FOLLOW": "Allows dogs to follow you anywhere you go, even if you leave the Market.", + "RUPEES_REDUCE_OVER_TIME": "Rupees reduce over time, Link suffers damage when the count hits 0.", + "TOOLTIP_RUPEES_REDUCE_OVER_TIME": "Rupees reduce over time, Link suffers damage when the count hits 0.", + "INTERVAL_BETWEEN_RUPEE_REDUCTION": "Interval between Rupee reduction in Rupee Dash Mode.", + "TOOLTIP_INTERVAL_BETWEEN_RUPEE_REDUCTION": "Interval between Rupee reduction in Rupee Dash Mode.", + "CHANGES_HEART_PIECE_AND": "Changes Heart Piece and Heart Container functionality.\\n\\n", + "TOOLTIP_CHANGES_HEART_PIECE_AND": "Changes Heart Piece and Heart Container functionality.\\n\\n", + "ENABLES_ADDITIONAL_TRAP_VARIANTS": "Enables additional Trap variants.", + "TOOLTIP_ENABLES_ADDITIONAL_TRAP_VARIANTS": "Enables additional Trap variants.", + "ALLOWS_ANY_ITEM_TO": "Allows any item to be equipped, regardless of age.\\n", + "TOOLTIP_ALLOWS_ANY_ITEM_TO": "Allows any item to be equipped, regardless of age.\\n", + "ALLOWS_YOU_TO_USE": "Allows you to use any item at any location", + "TOOLTIP_ALLOWS_YOU_TO_USE": "Allows you to use any item at any location", + "MAKES_EVERY_TUNIC_HAVE": "Makes every tunic have the effects of every other tunic.", + "TOOLTIP_MAKES_EVERY_TUNIC_HAVE": "Makes every tunic have the effects of every other tunic.", + "PREVENTS_THE_DEKU_SHIELD": "Prevents the Deku Shield from burning on contact with fire.", + "TOOLTIP_PREVENTS_THE_DEKU_SHIELD": "Prevents the Deku Shield from burning on contact with fire.", + "MAKES_EVERY_SURFACE_IN": "Makes every surface in the game hookshotable.", + "TOOLTIP_MAKES_EVERY_SURFACE_IN": "Makes every surface in the game hookshotable.", + "ALLOWS_YOU_TO_WALK": "Allows you to walk through walls.", + "TOOLTIP_ALLOWS_YOU_TO_WALK": "Allows you to walk through walls.", + "HOLDING_L_MAKES_YOU": "Holding L makes you float into the air.", + "TOOLTIP_HOLDING_L_MAKES_YOU": "Holding L makes you float into the air.", + "PREVENTS_REDEADS_AND_GIBDOS": "Prevents ReDeads and Gibdos from being able to freeze you with their scream.", + "TOOLTIP_PREVENTS_REDEADS_AND_GIBDOS": "Prevents ReDeads and Gibdos from being able to freeze you with their scream.", + "DISABLES_SANDSTORM_EFFECT_IN": "Disables sandstorm effect in Haunted Wasteland.", + "TOOLTIP_DISABLES_SANDSTORM_EFFECT_IN": "Disables sandstorm effect in Haunted Wasteland.", + "ALLOWS_ZTARGETING_GOLD_SKULLTULAS": "Allows Z-Targeting Gold Skulltulas.", + "TOOLTIP_ALLOWS_ZTARGETING_GOLD_SKULLTULAS": "Allows Z-Targeting Gold Skulltulas.", + "PASSIVE_INFINITE_SWORD_GLITCHN": "Passive Infinite Sword Glitch\\n", + "TOOLTIP_PASSIVE_INFINITE_SWORD_GLITCHN": "Passive Infinite Sword Glitch\\n", + "GIVES_YOU_THE_GLITCHED": "Gives you the glitched damage value of the quick put away glitch.", + "TOOLTIP_GIVES_YOU_THE_GLITCHED": "Gives you the glitched damage value of the quick put away glitch.", + "CLEARS_THE_CUTSCENE_POINTER": "Clears the cutscene pointer to a value safe for wrong warps.", + "TOOLTIP_CLEARS_THE_CUTSCENE_POINTER": "Clears the cutscene pointer to a value safe for wrong warps.", + "PREVENTS_FISH_FROM_AUTOMATICALLY": "Prevents fish from automatically despawning after a while when dropped.", + "TOOLTIP_PREVENTS_FISH_FROM_AUTOMATICALLY": "Prevents fish from automatically despawning after a while when dropped.", + "PREVENTS_BUGS_FROM_AUTOMATICALLY": "Prevents bugs from automatically despawning after a while when dropped.", + "TOOLTIP_PREVENTS_BUGS_FROM_AUTOMATICALLY": "Prevents bugs from automatically despawning after a while when dropped.", + "FREEZES_THE_TIME_OF": "Freezes the time of day.", + "TOOLTIP_FREEZES_THE_TIME_OF": "Freezes the time of day.", + "SYNCS_THE_INGAME_TIME": "Syncs the in-game time with the real world time.", + "TOOLTIP_SYNCS_THE_INGAME_TIME": "Syncs the in-game time with the real world time.", + "BUTTONS_THAT_ACTIVATE_SPEED": "Buttons that activate Speed Modifier 1.\\n\\n", + "TOOLTIP_BUTTONS_THAT_ACTIVATE_SPEED": "Buttons that activate Speed Modifier 1.\\n\\n", + "F5_TO_SAVE_F6": "F5 to save, F6 to change slots, F7 to load", + "TOOLTIP_F5_TO_SAVE_F6": "F5 to save, F6 to change slots, F7 to load", + "TURNS_ON_OOT_BETA": "Turns on OoT Beta Quest. *WARNING*: This will reset your game!", + "TOOLTIP_TURNS_ON_OOT_BETA": "Turns on OoT Beta Quest. *WARNING*: This will reset your game!", + "ENABLES_THE_SEPARATE_COSMETICS": "Enables the separate Cosmetics Editor Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_COSMETICS": "Enables the separate Cosmetics Editor Window.", + "ENABLES_THE_SEPARATE_AUDIO": "Enables the separate Audio Editor Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_AUDIO": "Enables the separate Audio Editor Window.", + "ENABLES_THE_SEPARATE_GAMEPLAY": "Enables the separate Gameplay Stats Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_GAMEPLAY": "Enables the separate Gameplay Stats Window.", + "ENABLES_THE_SEPARATE_TIME": "Enables the separate Time Splits Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_TIME": "Enables the separate Time Splits Window.", + "ENABLES_THE_SEPARATE_ADDITIONAL": "Enables the separate Additional Timers Window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ADDITIONAL": "Enables the separate Additional Timers Window.", + "REVERT_EVERY_ELEMENT_TO": "Revert every element to use their original position and no margins", + "TOOLTIP_REVERT_EVERY_ELEMENT_TO": "Revert every element to use their original position and no margins", + "USING_THIS_ALLOW_YOU": "Using this allow you move the element with General margins sliders", + "TOOLTIP_USING_THIS_ALLOW_YOU": "Using this allow you move the element with General margins sliders", + "THIS_WILL_USE_ORIGINAL": "This will use original intended elements position", + "TOOLTIP_THIS_WILL_USE_ORIGINAL": "This will use original intended elements position", + "THIS_WILL_MAKE_YOUR": "This will make your elements follow the left side of your game window", + "TOOLTIP_THIS_WILL_MAKE_YOUR": "This will make your elements follow the left side of your game window", + "THIS_SLIDER_IS_USED": "This slider is used to move Up and Down your elements.", + "TOOLTIP_THIS_SLIDER_IS_USED": "This slider is used to move Up and Down your elements.", + "THIS_WILL_SET_THE": "This will set the length of a row of hearts. Set to 0 for unlimited length.", + "TOOLTIP_THIS_WILL_SET_THE": "This will set the length of a row of hearts. Set to 0 for unlimited length.", + "THIS_WILL_USE_ENEMY": "This will use enemy on screen position", + "TOOLTIP_THIS_WILL_USE_ENEMY": "This will use enemy on screen position", + "THIS_WILL_CHANGE_THE": "This will change the width of the health bar", + "TOOLTIP_THIS_WILL_CHANGE_THE": "This will change the width of the health bar", + "MAKES_SNOW_FALL_CHANGES": "Makes snow fall, changes chest texture colors to red and green, etc, for ", + "TOOLTIP_MAKES_SNOW_FALL_CHANGES": "Makes snow fall, changes chest texture colors to red and green, etc, for ", + "SET_WHEN_THE_COSMETICS": "Set when the cosmetics is automaticly randomized:\\n", + "TOOLTIP_SET_WHEN_THE_COSMETICS": "Set when the cosmetics is automaticly randomized:\\n", + "SETS_THE_ON_SCREEN": "Sets the on screen size of the input viewer", + "TOOLTIP_SETS_THE_ON_SCREEN": "Sets the on screen size of the input viewer", + "SETS_THE_DESIRED_VISIBILITY": "Sets the desired visibility behavior for the button outline/background layers. Useful for ", + "TOOLTIP_SETS_THE_DESIRED_VISIBILITY": "Sets the desired visibility behavior for the button outline/background layers. Useful for ", + "SETS_THE_DISTANCE_TO": "Sets the distance to move the analog stick in the input viewer. Useful for custom ", + "TOOLTIP_SETS_THE_DISTANCE_TO": "Sets the distance to move the analog stick in the input viewer. Useful for custom ", + "DISPLAYS_ANALOG_STICK_ANGLE": "Displays analog stick angle values in the input viewer", + "TOOLTIP_DISPLAYS_ANALOG_STICK_ANGLE": "Displays analog stick angle values in the input viewer", + "HIGHLIGHTS_THE_ANGLE_VALUE": "Highlights the angle value text when the analog stick is at an angle that would ", + "TOOLTIP_HIGHLIGHTS_THE_ANGLE_VALUE": "Highlights the angle value text when the analog stick is at an angle that would ", + "HEALTHN_RED_WHEN_HEALTH": "Health\\n- Red when health critical (13-20% depending on max health)\\n- Yellow when ", + "TOOLTIP_HEALTHN_RED_WHEN_HEALTH": "Health\\n- Red when health critical (13-20% depending on max health)\\n- Yellow when ", + "SETS_THE_BRIGHTNESS_OF": "Sets the brightness of controller LEDs. 0% brightness = LEDs off.", + "TOOLTIP_SETS_THE_BRIGHTNESS_OF": "Sets the brightness of controller LEDs. 0% brightness = LEDs off.", + "ALLOWS_FOR_AIMING_WITH": "Allows for aiming with the right stick in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "TOOLTIP_ALLOWS_FOR_AIMING_WITH": "Allows for aiming with the right stick in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "CHANGES_THE_LEFT_STICK": "Changes the left stick to move the player while in first-person mode", + "TOOLTIP_CHANGES_THE_LEFT_STICK": "Changes the left stick to move the player while in first-person mode", + "INVERTS_THE_CAMERA_X": "Inverts the Camera X Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "TOOLTIP_INVERTS_THE_CAMERA_X": "Inverts the Camera X Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "INVERTS_THE_CAMERA_Y": "Inverts the Camera Y Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "TOOLTIP_INVERTS_THE_CAMERA_Y": "Inverts the Camera Y Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "INVERTS_THE_SHIELD_AIMING": "Inverts the Shield Aiming X Axis", + "TOOLTIP_INVERTS_THE_SHIELD_AIMING": "Inverts the Shield Aiming X Axis", + "PREVENTS_THE_CUP_VIEW": "Prevents the C-Up view from auto-centering, allowing for Gyro Aiming", + "TOOLTIP_PREVENTS_THE_CUP_VIEW": "Prevents the C-Up view from auto-centering, allowing for Gyro Aiming", + "THE_CURSOR_WILL_ONLY": "The cursor will only move a single space no matter how long a D-pad direction is held", + "TOOLTIP_THE_CURSOR_WILL_ONLY": "The cursor will only move a single space no matter how long a D-pad direction is held", + "ALLOWS_FOR_USING_THE": "Allows for using the mouse to control the camera (must enable Free Look), ", + "TOOLTIP_ALLOWS_FOR_USING_THE": "Allows for using the mouse to control the camera (must enable Free Look), ", + "WHEN_MOUSE_CONTROLS_ARE": "When Mouse Controls are enabled, this toggles whether the program will automatically ", + "TOOLTIP_WHEN_MOUSE_CONTROLS_ARE": "When Mouse Controls are enabled, this toggles whether the program will automatically ", + "NAVIGATE_CHOICES_IN_TEXT": "Navigate choices in text boxes, shop item selection, and the file select / name entry ", + "TOOLTIP_NAVIGATE_CHOICES_IN_TEXT": "Navigate choices in text boxes, shop item selection, and the file select / name entry ", + "REPLACES_FIXED_ENEMIES_THROUGHOUT": "Replaces fixed enemies throughout the game with a random enemy. Bosses, Mini-Bosses and a ", + "TOOLTIP_REPLACES_FIXED_ENEMIES_THROUGHOUT": "Replaces fixed enemies throughout the game with a random enemy. Bosses, Mini-Bosses and a ", + "ENEMIES_AND_BOSSES_SPAWN": "Enemies and Bosses spawn with random sizes.", + "TOOLTIP_ENEMIES_AND_BOSSES_SPAWN": "Enemies and Bosses spawn with random sizes.", + "SCALES_NORMAL_ENEMIES_HEALTH": "Scales normal enemies Health with their randomized size.\\n", + "TOOLTIP_SCALES_NORMAL_ENEMIES_HEALTH": "Scales normal enemies Health with their randomized size.\\n", + "CUSTOMIZE_WHAT_THE_NUMBERS": "Customize what the numbers under each item are tracking.", + "TOOLTIP_CUSTOMIZE_WHAT_THE_NUMBERS": "Customize what the numbers under each item are tracking.", + "CUSTOMIZE_WHAT_NUMBERS_ARE": "Customize what numbers are shown for key tracking.", + "TOOLTIP_CUSTOMIZE_WHAT_NUMBERS_ARE": "Customize what numbers are shown for key tracking.", + "SETS_THE_FONT_SIZE": "Sets the font size used in the check tracker.", + "TOOLTIP_SETS_THE_FONT_SIZE": "Sets the font size used in the check tracker.", + "IF_ENABLED_WILL_HIDE": "If enabled, will hide area headers that have no locations matching filter", + "TOOLTIP_IF_ENABLED_WILL_HIDE": "If enabled, will hide area headers that have no locations matching filter", + "IF_ENABLED_VANILLAMQ_DUNGEONS": "If enabled, Vanilla/MQ dungeons will show on the tracker immediately. ", + "TOOLTIP_IF_ENABLED_VANILLAMQ_DUNGEONS": "If enabled, Vanilla/MQ dungeons will show on the tracker immediately. ", + "IF_ENABLED_WILL_PREVENT": "If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles.", + "TOOLTIP_IF_ENABLED_WILL_PREVENT": "If enabled, will prevent the tracker from displaying slots with non-shop-item shuffles.", + "IF_ENABLED_WILL_SHOW": "If enabled, will show GS locations in the tracker regardless of tokensanity settings.", + "TOOLTIP_IF_ENABLED_WILL_SHOW": "If enabled, will show GS locations in the tracker regardless of tokensanity settings.", + "SORT_ENTRANCES_BY_THE": "Sort entrances by the original source entrance", + "TOOLTIP_SORT_ENTRANCES_BY_THE": "Sort entrances by the original source entrance", + "AUTOMATICALLY_SCROLL_TO_THE": "Automatically scroll to the first available entrance in the current scene", + "TOOLTIP_AUTOMATICALLY_SCROLL_TO_THE": "Automatically scroll to the first available entrance in the current scene", + "HIGHLIGHT_THE_PREVIOUS_ENTRANCE": "Highlight the previous entrance that Link came from", + "TOOLTIP_HIGHLIGHT_THE_PREVIOUS_ENTRANCE": "Highlight the previous entrance that Link came from", + "HIGHLIGHT_AVAILABLE_ENTRANCES_IN": "Highlight available entrances in the current scene", + "TOOLTIP_HIGHLIGHT_AVAILABLE_ENTRANCES_IN": "Highlight available entrances in the current scene", + "COLLAPSE_UNDISCOVERED_ENTRANCES_TOWARDS": "Collapse undiscovered entrances towards the bottom of each group", + "TOOLTIP_COLLAPSE_UNDISCOVERED_ENTRANCES_TOWARDS": "Collapse undiscovered entrances towards the bottom of each group", + "HIDE_REVERSE_ENTRANCE_TRANSITIONS": "Hide reverse entrance transitions when Decouple Entrances is off", + "TOOLTIP_HIDE_REVERSE_ENTRANCE_TRANSITIONS": "Hide reverse entrance transitions when Decouple Entrances is off", + "GROUP_ENTRANCES_BY_THEIR": "Group entrances by their area", + "TOOLTIP_GROUP_ENTRANCES_BY_THEIR": "Group entrances by their area", + "REVEAL_THE_SOURCE_FOR": "Reveal the source for undiscovered entrances", + "TOOLTIP_REVEAL_THE_SOURCE_FOR": "Reveal the source for undiscovered entrances", + "REVEAL_THE_DESTINATION_FOR": "Reveal the destination for undiscovered entrances", + "TOOLTIP_REVEAL_THE_DESTINATION_FOR": "Reveal the destination for undiscovered entrances", + "WHICH_LANGUAGE_TO_LOAD": "Which language to load from the selected text ID", + "TOOLTIP_WHICH_LANGUAGE_TO_LOAD": "Which language to load from the selected text ID", + "GRABS_ACTOR_WITH_TARGET": "Grabs actor with target arrow above it. You might need C-Up for enemies", + "TOOLTIP_GRABS_ACTOR_WITH_TARGET": "Grabs actor with target arrow above it. You might need C-Up for enemies", + "GRABS_ACTOR_THAT_LINK": "Grabs actor that Link is holding", + "TOOLTIP_GRABS_ACTOR_THAT_LINK": "Grabs actor that Link is holding", + "CHANGES_THE_ACTOR_SPECIFIC": "Changes the actor specific param menus with a direct input", + "TOOLTIP_CHANGES_THE_ACTOR_SPECIFIC": "Changes the actor specific param menus with a direct input", + "ENCODING_USED_FOR_PLAYER": "Encoding used for Player Name", + "TOOLTIP_ENCODING_USED_FOR_PLAYER": "Encoding used for Player Name", + "CURRENT_HEALTH_16_UNITS": "Current health. 16 units per full heart", + "TOOLTIP_CURRENT_HEALTH_16_UNITS": "Current health. 16 units per full heart", + "IS_DOUBLE_DEFENSE_UNLOCKED": "Is double defense unlocked?", + "TOOLTIP_IS_DOUBLE_DEFENSE_UNLOCKED": "Is double defense unlocked?", + "CURRENT_MAGIC_48_UNITS": "Current magic. 48 units per magic level", + "TOOLTIP_CURRENT_MAGIC_48_UNITS": "Current magic. 48 units per magic level", + "TIME_OF_DAY": "Time of day", + "TOOLTIP_TIME_OF_DAY": "Time of day", + "SOUND_SETTING": "Sound setting", + "TOOLTIP_SOUND_SETTING": "Sound setting", + "WARNING_IF_YOU_SAVE": "WARNING! If you save, your file may be locked! Use caution!", + "TOOLTIP_WARNING_IF_YOU_SAVE": "WARNING! If you save, your file may be locked! Use caution!", + "USED_THE_SINKING_LURE": "Used the Sinking lure to catch it.", + "TOOLTIP_USED_THE_SINKING_LURE": "Used the Sinking lure to catch it.", + "PLAYED_AT_LEAST_ONE": "Played at least one game as a child", + "TOOLTIP_PLAYED_AT_LEAST_ONE": "Played at least one game as a child", + "GOT_THE_PRIZE_ITEM": "Got the prize item (Golden Scale, unless rando.)\\nUnlocks ", + "TOOLTIP_GOT_THE_PRIZE_ITEM": "Got the prize item (Golden Scale, unless rando.)\\nUnlocks ", + "RESTRICTS_ITEMS_AND_AMMO": "Restricts items and ammo to only what is possible to legally acquire in-game", + "TOOLTIP_RESTRICTS_ITEMS_AND_AMMO": "Restricts items and ammo to only what is possible to legally acquire in-game", + "CLEAR_CURRENT_SCENE_FLAGS": "Clear current scene flags. Reload scene to see changes", + "TOOLTIP_CLEAR_CURRENT_SCENE_FLAGS": "Clear current scene flags. Reload scene to see changes", + "OPEN_FLAGS_FOR_CURRENT": "Open flags for current scene", + "TOOLTIP_OPEN_FLAGS_FOR_CURRENT": "Open flags for current scene", + "RESET_TO_DEFAULT": "Reset to default", + "TOOLTIP_RESET_TO_DEFAULT": "Reset to default", + "RANDOMIZE_THIS_SOUND": "Randomize this sound", + "TOOLTIP_RANDOMIZE_THIS_SOUND": "Randomize this sound", + "RANDOMIZES_ALL_UNLOCKED_MUSIC": "Randomizes all unlocked music and sound effects across tab groups", + "TOOLTIP_RANDOMIZES_ALL_UNLOCKED_MUSIC": "Randomizes all unlocked music and sound effects across tab groups", + "RESETS_ALL_UNLOCKED_MUSIC": "Resets all unlocked music and sound effects across tab groups", + "TOOLTIP_RESETS_ALL_UNLOCKED_MUSIC": "Resets all unlocked music and sound effects across tab groups", + "LOCKS_ALL_MUSIC_AND": "Locks all music and sound effects across tab groups", + "TOOLTIP_LOCKS_ALL_MUSIC_AND": "Locks all music and sound effects across tab groups", + "UNLOCKS_ALL_MUSIC_AND": "Unlocks all music and sound effects across tab groups", + "TOOLTIP_UNLOCKS_ALL_MUSIC_AND": "Unlocks all music and sound effects across tab groups", + "DISABLE_THE_LOW_HP": "Disable the low HP beeping sound.", + "TOOLTIP_DISABLE_THE_LOW_HP": "Disable the low HP beeping sound.", + "DISABLES_THE_VOICE_AUDIO": "Disables the voice audio when Navi calls you.", + "TOOLTIP_DISABLES_THE_VOICE_AUDIO": "Disables the voice audio when Navi calls you.", + "DISABLES_THE_MUSIC_CHANGE": "Disables the music change when getting close to enemies. Useful for hearing ", + "TOOLTIP_DISABLES_THE_MUSIC_CHANGE": "Disables the music change when getting close to enemies. Useful for hearing ", + "DISABLES_THE_VOLUME_SHIFTING": "Disables the volume shifting in the Lost Woods. Useful for hearing ", + "TOOLTIP_DISABLES_THE_VOLUME_SHIFTING": "Disables the volume shifting in the Lost Woods. Useful for hearing ", + "EMITS_A_NOTIFICATION_WITH": "Emits a notification with the current song name whenever it changes. ", + "TOOLTIP_EMITS_A_NOTIFICATION_WITH": "Emits a notification with the current song name whenever it changes. ", + "WIDGET_COPY_TEXT": "Copy text", + "WIDGET_COPY_ADDRESS": "Copy address", + "WIDGET_SCROLL_TO_PARENT": "Scroll to parent", + "WIDGET_DEBUG": "Debug", + "WIDGET_RESUME_GAME": "Resume Game", + "TEXT_TEXTURE_TO_LOAD": "Texture To Load", + "WIDGET_CANCEL": "Cancel", + "WIDGET_CLOSE": "Close", + "WIDGET_RECALIBRATE": "Recalibrate", + "WIDGET_CLEAR_ALL": "Clear All", + "WIDGET_SET_DEFAULTS": "Set defaults", + "TEXT_PRESS_ANY_BUTTONNMOVE_ANY": "Press any button,\\nmove any axis,\\nor press any key\\nto add mapping", + "TEXT_STICK_AXIS_THRESHOLD": "Stick axis threshold:", + "TEXT_TRIGGER_AXIS_THRESHOLD": "Trigger axis threshold:", + "TEXT_SENSITIVITY": "Sensitivity:", + "TEXT_DEADZONE": "Deadzone:", + "TEXT_PRESS_ANY_BUTTONNOR_MOVE": "Press any button\\nor move any axis\\nto add rumble device", + "TEXT_LED_COLOR": "LED Color:", + "WIDGET_CLEAR": "Clear", + "WIDGET_SUBMIT": "Submit", + "MENU_COPY_TEXT": "Copy Text", + "TEXT_NOTE_GAMEPLAY_STATS_ARE": "Note: Gameplay stats are saved to the current file and will be\\nlost if you quit without saving.", + "TEXT_WARP_POINTS": "Warp Points", + "TEXT_START_TYPING_TO_SEE": "Start typing to see results.", + "TEXT_SEARCH_RESULTS": "Search Results", + "WIDGET_CLICK_TO_REENABLE_ASPECT": "Click to reenable aspect correction.", + "TEXT_REQUIRES_LOGIC_TURNED_ON": "Requires Logic Turned On.", + "WIDGET_REQUEST_TEAM_STATE": "Request Team State", + "WIDGET_CLEAR_ALL_TEAM_STATE": "Clear All Team State", + "TEXT_HOST_PORT": "Host & Port", + "TEXT_ROOM_ID": "Room ID", + "TEXT_TEAM_ID_ITEMS_FLAGS": "Team ID (Items & Flags Shared)", + "TEXT_CONNECTION_SETTINGS": "Connection Settings", + "TEXT_CURRENT_ROOM": "Current Room", + "TEXT_ROOM_SETTINGS_ADMIN_ONLY": "Room Settings (Admin Only)", + "TEXT_USAGE_INSTRUCTIONS": "Usage Instructions", + "TEXT_INCOMPATIBLE_VERSION_WILL_NOT": "Incompatible version! Will not work together!", + "TEXT_SEED_MISMATCH_CONTINUING_WILL": "Seed mismatch! Continuing will break things!", + "TEXT_GENERAL_MARGINS_SETTINGS": "General Margins Settings", + "TEXT_NO_ENABLED_TIMERS": "No Enabled Timers...", + "WIDGET_SET_TOKENS": "Set Tokens", + "TEXT_SELECT_LIST_TO_LOAD": "Select List to Load: ", + "TEXT_WINDOW_OPTIONS": "Window Options", + "TEXT_SPLIT_LIST_MANAGEMENT": "Split List Management", + "TEXT_NO_SPOILER_LOGS_FOUND": "No Spoiler Logs found.", + "TEXT_NO_SPOILER_LOG_LOADED": "No Spoiler Log Loaded", + "TEXT_PLEASE_LOAD_SPOILER_DATA": "Please Load Spoiler Data...", + "TEXT_RESOURCES": "Resources", + "TEXT_SPOILER_LOG_REWARDS": "Spoiler Log Rewards", + "TEXT_LOADSAVE_SPOILER_LOG": "Load/Save Spoiler Log", + "TEXT_CURRENT_SEED_HASH": "Current Seed Hash", + "TEXT_OPTIONS": "Options", + "TEXT_WAITING_FOR_FILE_LOAD": "Waiting for file load...", + "TEXT_TRACKER_HEADER_VISIBILITY": "Tracker Header Visibility", + "TEXT_SORT_BY": "Sort By", + "TEXT_GROUP_BY": "Group By", + "WIDGET_DISPLAY_MESSAGEEXISTINGMESSAGE": "Display Message##ExistingMessage", + "WIDGET_DISPLAY_MESSAGECUSTOMMESSAGE": "Display Message##CustomMessage", + "TEXT_TABLE_ID": "Table ID", + "TEXT_TEXT_ID": "Text ID", + "TEXT_ACTOR_POSITION": "Actor Position", + "TEXT_ACTOR_ROTATION": "Actor Rotation", + "TEXT_SELECT_AN_ACTOR_TO": "Select an actor to display information.", + "TEXT_ACTOR_SPECIFIC_DATA": "Actor Specific Data", + "TEXT_NEW_ACTOR_POSITION": "New Actor Position", + "TEXT_NEW_ACTOR_ROTATION": "New Actor Rotation", + "TEXT_GLOBAL_CONTEXT_NEEDED_FOR": "Global Context needed for actor info!", + "WIDGET_GSDPSETPRIMCOLOR": "gsDPSetPrimColor", + "WIDGET_GSDPSETENVCOLOR": "gsDPSetEnvColor", + "WIDGET_GSDPPIPESYNC": "gsDPPipeSync", + "WIDGET_GSSPGRAYSCALE": "gsSPGrayscale", + "WIDGET_GSDPSETGRAYSCALECOLOR": "gsDPSetGrayscaleColor", + "TEXT_RESOURCE_TYPE_IS_NOT": "Resource type is not a Display List. Please choose another.", + "TEXT_ERROR_DISPLAYING_DL_INSTRUCTIONS": "Error displaying DL instructions.", + "WIDGET_ADULT": "Adult", + "WIDGET_CHILD": "Child", + "WIDGET_NONE": "None", + "WIDGET_KOKIRI_SWORD": "Kokiri Sword", + "WIDGET_MASTER_SWORD": "Master Sword", + "WIDGET_FISHING_POLE": "Fishing Pole", + "WIDGET_DEKU_SHIELD": "Deku Shield", + "WIDGET_HYLIAN_SHIELD": "Hylian Shield", + "WIDGET_MIRROR_SHIELD": "Mirror Shield", + "WIDGET_KOKIRI_TUNIC": "Kokiri Tunic", + "WIDGET_GORON_TUNIC": "Goron Tunic", + "WIDGET_ZORA_TUNIC": "Zora Tunic", + "WIDGET_KOKIRI_BOOTS": "Kokiri Boots", + "WIDGET_IRON_BOOTS": "Iron Boots", + "WIDGET_HOVER_BOOTS": "Hover Boots", + "WIDGET_CATCH_ALL_CHILD": "Catch All (Child)", + "WIDGET_UNCATCH_ALL_CHILD": "Uncatch All (Child)", + "WIDGET_CATCH_ALL_ADULT": "Catch All (Adult)", + "WIDGET_UNCATCH_ALL_ADULT": "Uncatch All (Adult)", + "TEXT_NO_FLAGS_MATCH_THE": "No flags match the current search.", + "TEXT_CLEAR": "Clear", + "TEXT_TEMP_CLEAR": "Temp Clear", + "TEXT_CURRENT_GAME_STATE_DOES": "Current game state does not have an active scene", + "TEXT_PRESETS": "Presets", + "TEXT_NO_PRESETS_WITH_RANDO": "No presets with rando options. Make some in Settings -> Presets", + "TEXT_NO_PRESETS_FOUND": "No presets found.", + "BUTTON_CANCEL": "Cancel", + "TOOLTIP_EDIT_AXIS_THRESHOLD": "Edit axis threshold", + "TEXT_AXIS_THRESHOLDNNTHE_EXTENT_TO": "Axis Threshold\\n\\nThe extent to which the joystick\\nmust be moved or the trigger\\npressed to ", + "TEXT_PRESS_ANY_BUTTONNOR_MOVE": "Press any button\\nor move any axis\\nto add rumble device", + "TEXT_NO_ENABLED_TIMERS": "No Enabled Timers...", + "SEPARATOR_WARP_POINTS": "Warp Points", + "TOOLTIP_PLAYS_THE_BATTLE_MUSIC": "Plays the battle music when getting close to a Leever, like in Majora's Mask.", + "TOOLTIP_SOME_CUSTOM_SEQUENCES_MAY": "Some custom sequences may have notes that are too high for the game's audio ", + "COLLAPSING_ANALOG_STICK": "Analog Stick", + "COLLAPSING_VISUAL_STONE_OF_AGONY": "Visual stone of agony position", + "COLLAPSING_ENEMY_HEALTH_BAR_POSITION": "Enemy Health Bar position", + "TEXT_TABLE_ID": "Table ID", + "TEXT_TEXT_ID": "Text ID", + "TEXT_SELECT_AN_ACTOR_TO": "Select an actor to display information.", + "TOOLTIP_GRABS_ACTOR_FROM": "Grabs actor from \\\"interaction range\\\"", + "TEXT_ACTOR_SPECIFIC_DATA": "Actor Specific Data", + "TEXT_NEW_ACTOR_POSITION": "New Actor Position", + "TEXT_NEW_ACTOR_ROTATION": "New Actor Rotation", + "TEXT_GLOBAL_CONTEXT_NEEDED_FOR": "Global Context needed for actor info!", + "TOOLTIP_APPLIES_THE_SCENES_SHADING": "Applies the scene's shading to the collision display.", + "TOOLTIP_MAXIMUM_HEALTH_16_UNITS": "Maximum health. 16 units per full heart", + "TOOLTIP_TOTAL_NUMBER_OF_DAYS": "Total number of days elapsed since the start of the game", + "TOOLTIP_TOTAL_NUMBER_OF_DEATHS": "Total number of deaths", + "TOOLTIP_IS_BIGGORON_SWORD_UNLOCKED": "Is Biggoron sword unlocked? Replaces Giant's knife", + "TOOLTIP_GIANTS_KNIFE_HEALTH_DEFAULT": "Giant's knife health. Default is 8. Must be >0 for Biggoron sword to work", + "TOOLTIP_FROM_WHICH_ENTRANCE_DID": "From which entrance did Link arrive?", + "TOOLTIP_WHICH_CUTSCENE_IS_THIS": "Which cutscene is this?", + "TOOLTIP_NAVI_WANTS_TO_TALK": "Navi wants to talk at 600 units, decides not to at 3000.", + "TOOLTIP_HEAT_TIMER_RACE_TIMER": "Heat timer, race timer, etc. Has white font", + "TOOLTIP_TIME_IN_SECONDS": "Time, in seconds", + "TOOLTIP_TRADE_TIMER_GANON_COLLAPSE": "Trade timer, Ganon collapse timer, etc. Has yellow font", + "TOOLTIP_CURRENTLY_OBTAINED_TRIFORCE_PIECES": "Currently obtained Triforce Pieces. For Triforce Hunt." +} \ No newline at end of file diff --git a/languages/es_ES.json b/languages/es_ES.json new file mode 100644 index 000000000..d5ddf2ba1 --- /dev/null +++ b/languages/es_ES.json @@ -0,0 +1,1113 @@ +{ + "Settings": "Configuración", + "Enhancements": "Mejoras", + "Randomizer": "Aleatorizador", + "Network": "Red", + "Dev Tools": "Htas. Desarrollador", + "General": "General", + "Audio": "Sonido", + "Controls": "Controles", + "Graphics": "Gráficos", + "Quality of Life": "Calidad de Vida", + "Cosmetics": "Cosméticos", + "Cheats": "Trucos", + "Developer": "Desarrollador", + "Input Viewer": "Visor de Entradas", + "Notifications": "Notificaciones", + "Mod Menu": "Menú de Mods", + "Menu Language": "Idioma del Menú", + "Menu Theme": "Tema del Menú", + "Toggle Fullscreen": "Pantalla Completa", + "TEXT_EXTRACTING": "Extrayendo %s...%s", + "TOOLTIP_ALLOWS_MORE_INDEPTH": "Permite una perspectiva más profunda del tiempo pasado en un mapa determinado.", + "TEXT_GAMEPLAY_STATS_NOTE_GAMEPLAY": "Nota: Las estadísticas de juego se guardan en el archivo actual y se perderán\\nsi sales sin guardar.", + "TEXT_MOVE": "Mover %s", + "TOOLTIP_MODS_GRAPHICS_MODS_GRAPHICS": "Activar mods. Para mods gráficos, significa alternar entre gráficos predeterminados y mods.", + "TOOLTIP_MODS_ALLOWS_PRESSING_THE": "Permite presionar Tab para alternar mods", + "SEPARATOR_WARP_WARP_POINTS": "Puntos de Teletransporte", + "TEXT_START_TYPING_SEE": "Empieza a escribir para ver resultados.", + "TEXT_SEARCH_RESULTS": "Resultados de búsqueda", + "TEXT_UNKNOWN": "ID", + "TOOLTIP_CHANGES_THE_MENU": "Cambia la visualización del menú de superpuesto a ventana.", + "TOOLTIP_EDITOR_ENABLES_THE_REGISTRY": "Habilita el editor del registro.", + "TOOLTIP_LOGS_SOME_RESOURCES": "Guarda algunos recursos como XML cuando se cargan en formato binario.", + "TOOLTIP_ADVANCE_FRAME": "Avanzar 1 cuadro.", + "TOOLTIP_ADVANCE_FRAMES_WHILE": "Avanzar cuadros mientras se mantiene el botón.", + "TOOLTIP_DEV_WARP_OPTIMIZED_DEBUG": "Pantalla de Teletransporte Debug optimizada, con la capacidad de elegir entradas y hora del día.", + "TOOLTIP_DEV_WARP_LANGUAGE_TRANSLATE": "Traduce la pantalla de Teletransporte Debug según el idioma del juego.", + "TOOLTIP_STATS_WINDOW_ENABLES_THE": "Habilita la ventana de Estadísticas separada.", + "TOOLTIP_CONSOLE_WINDOW_ENABLES_THE": "Habilita la ventana de Consola separada.", + "TOOLTIP_SAVE_EDITOR_WINDOW_ENABLES": "Habilita la ventana del Editor de Guardado separada.", + "TOOLTIP_HOOK_WINDOW_ENABLES_THE": "Habilita la ventana del Depurador de Hooks separada.", + "TOOLTIP_COLLISION_VIEWER_WINDOW_ENABLES": "Habilita la ventana del Visor de Colisiones separada.", + "TOOLTIP_ACTOR_VIEWER_WINDOW_ENABLES": "Habilita la ventana del Visor de Actores separada.", + "TOOLTIP_VIEWER_WINDOW_ENABLES_THE": "Habilita la ventana del Visor de Mensajes separada.", + "TOOLTIP_WINDOW_ENABLES_THE_SEPARATE": "Habilita la ventana de Temporizadores Adicionales separada.", + "TOOLTIP_MAKES_THE_CURSOR": "Hace que el cursor siempre sea visible, incluso en pantalla completa.", + "TOOLTIP_SAVE_MODS_OPENS_THE": "Abre la carpeta que contiene los archivos de guardado y la carpeta de mods, etc.", + "TOOLTIP_ENABLES_TEXT_SPEECH": "Habilita texto a voz para diálogos del juego", + "TOOLTIP_DISABLES_THE_AUTOMATIC": "Deshabilita el recentrado automático de la cámara cuando está inactiva.", + "TOOLTIP_DISABLES_THE_WHITE": "Deshabilita el flash de pantalla blanca al matar enemigos.", + "TOOLTIP_DISABLE_THE_GEOMETRY": "Deshabilita la distorsión de geometría y cámara dentro de Jabu.", + "TOOLTIP_CHANGES_THE_SCALING": "Cambia la escala de los elementos del menú ImGui.", + "TOOLTIP_FULLSCREEN_TOGGLES_FULLSCREEN_ONOFF": "Alternar Pantalla Completa On/Off.", + "TOOLTIP_MATCHES_INTERPOLATION_VALUE": "Coincide el valor de interpolación con la tasa de refresco de tu pantalla.", + "TOOLTIP_FPS_REMOVES_TEARING_BUT": "Elimina el tearing, pero limita tus FPS máximos a la tasa de refresco de tu pantalla.", + "TOOLTIP_FULLSCREEN_ENABLES_WINDOWED_FULLSCREEN": "Habilita el modo Pantalla Completa en ventana.", + "TOOLTIP_ALLOWS_MULTIPLE_WINDOWS": "Permite abrir múltiples ventanas a la vez. Requiere recargar para aplicar.", + "TOOLTIP_SETS_THE_APPLIED": "Establece el Filtro de Texturas aplicado.", + "TOOLTIP_CONTROLS_VIEWER_TOGGLES_THE": "Alterna el Visor de Entradas.", + "TOOLTIP_CONTROLS_VIEWER_WINDOW_ENABLES": "Habilita la ventana de Configuración del Visor de Entradas separada.", + "TOOLTIP_WHICH_CORNER_THE": "En qué esquina de la pantalla aparecen las notificaciones.", + "TOOLTIP_HOW_LONG_NOTIFICATIONS": "Cuánto tiempo se muestran las notificaciones.", + "TOOLTIP_HOW_OPAQUE_THE": "Qué tan opaco es el fondo de las notificaciones.", + "TOOLTIP_HOW_LARGE_NOTIFICATIONS": "Qué tan grandes son las notificaciones.", + "TOOLTIP_NOTIFICATION_DISPLAYS_TEST_NOTIFICATION": "Muestra una notificación de prueba.", + "TOOLTIP_AUDIO_PREVENT_NOTIFICATIONS_FROM": "Evita que las notificaciones reproduzcan sonido.", + "TOOLTIP_HTTPSGITHUBCOMHARBOURMASTERSSAIL": "https://github.com/HarbourMasters/sail", + "TOOLTIP_RESOLUTION_WINDOW_OVERRIDE_THE": "Anular el control deslizante de escala de resolución y usar la configuración a continuación, independientemente del tamaño de la ventana.", + "COLLAPSING_INTEGER_SCALING_SETTINGS": "Configuración de Escala Entera", + "TOOLTIP_WINDOW_DONT_SCALE_IMAGE": "No escalar imagen para llenar la ventana.", + "TOOLTIP_INTEGER_SCALES_THE": "Escala entera la imagen. Solo disponible en Modo Pixel Perfecto.", + "TOOLTIP_WINDOW_AUTOMATICALLY_SETS_SCALE": "Establece automáticamente el factor de escala para ajustar a la ventana. Solo disponible en Modo Pixel Perfecto.", + "COLLAPSING_ADDITIONAL_SETTINGS": "Configuración Adicional", + "BUTTON_CLICK_REENABLE_ASPECT": "Haz clic para re-habilitar corrección de aspecto.", + "TEXT_ASPECT_RATIO_2F1": "Relación de aspecto: %.2f:1", + "TOOLTIP_THIS_BUTTON_FROM": "Eliminar este botón de la combinación", + "TEXT_REQUIRES_LOGIC_TURNED": "Requiere Lógica Activada.", + "TOOLTIP_RANDOMIZER_CREATES_NEW_RANDOM": "Crea un nuevo valor de semilla aleatoria para usar al generar un aleatorizador", + "TOOLTIP_RANDOMIZER_MUST_FILE_SELECT": "Debe estar en Selección de Archivo para generar una semilla aleatorizada.", + "TEXT_SPOILER_FILE": "Archivo de Spoiler: %s", + "TOOLTIP_GAMEPLAY_REPLACE_NAVIS_OVERWORLD": "Reemplazar navi's overworld quest hints con rando-related gamereproducir hints.", + "TOOLTIP_WHEN_OBTAINING_RUPEES": "Cuando obtaining rupias, randomize what the rupia is called in the textbox.", + "TOOLTIP_GRAPHICS_USE_CUSTOM_GRAPHICS": "Usar personalizado graphics for mazmorra llaves, big and small, so that they can be easily told apart.", + "TOOLTIP_WITH_SHUFFLE_SPEAK": "Con shuffle speak, jabber nut model & color will be generic.", + "TOOLTIP_ENABLED_SIGNS_NEAR": "Si está habilitado, signs near cargaring zones will tell you where they lead to.", + "TOOLTIP_RANDOMIZER_WINDOW_ENABLES_THE": "Habilita la ventana separada aleatorizador establecertings window.", + "TOOLTIP_TOGGLES_THE_ITEM": "Alterna the rastreador de objetos.", + "TOOLTIP_TOGGLES_THE_ENTRANCE": "Alterna the rastreador de entradas.", + "TOOLTIP_TOGGLES_THE_CHECK": "Alterna the rastreador de comprobaciones.", + "TOOLTIP_NOTIFICATION_SHOW_NOTIFICATION_WHEN": "Mostrar un notificación cuando the game is automáticoguardard.", + "TOOLTIP_ONLY_CHANGE_THE": "Only change the texture of containers if you have the Stone of Agony.", + "TOOLTIP_NIGHTTIME_SKULLTULAS_WILL": "Nighttime Skulltulas will spawn during both day and night.", + "TOOLTIP_ALLOWS_GRAVES_PULLED": "Permite graves to be pulled cuando child during the day.", + "TOOLTIP_SHOPS_AND_MINIGAMES": "Shops and Minigames are open both day and night. Requires a scene reload to take effect.", + "TOOLTIP_RANDOMIZER_THIS_NOT_COMPATIBLE": "Esto es not compatible con the bloqueared overworld doors aleatorizador option.", + "TOOLTIP_SPEAK_NAVI_WITH": "Hablar con navi con l but entrar first-person cámara con c-up.", + "TOOLTIP_BUFFERS_YOUR_INPUTS": "Almacena tus inputs to be executed a specified amount of frames later.", + "TOOLTIP_BUTTONS_ACTIVATE_TARGET": "Buttons to activate target switching.", + "TOOLTIP_DISABLE_RANDOM_CAMERA": "Deshabilitar random oscilación de cámara at low salud.", + "TOOLTIP_ALLOW_LINK_PUT": "Allow Link to put items away without having to wait around.", + "TOOLTIP_ALLOW_LINK_ENTER": "Allow Link to enter Jabu-Jabu without feeding him a fish.", + "TOOLTIP_MESSAGES_SKIP_PICKUP_MESSAGES": "Saltar mensaje de objetos for new consumable items.", + "TOOLTIP_PREVENT_FORCED_CONVERSATIONS": "Evitar conversación forzadas con navi and/or other npcs.", + "TOOLTIP_HOLDING_DOWN_SKIPS": "Mantener presionado b saltars text.", + "TOOLTIP_SPEEDS_LIFTING_SILVER": "Acelera up lifting roca de platas and obeliscos.", + "TOOLTIP_SPEEDS_SHIP_SHADOW": "Acelera up ship in templo de las sombras.", + "TOOLTIP_SPEEDS_EMPTYING_ANIMATION": "Acelera up emptying animation cuando dumping out the contents of a botella.", + "TOOLTIP_SPEEDS_ANIMATION_THE": "Acelera up animation of the pausar menu, similar to majora's mask", + "TOOLTIP_SKIP_THE_TOWER": "Saltar the tower escape sequence between ganondorf and ganon.", + "TOOLTIP_CAUSES_YOUR_WALLET": "Causes your Wallet to fill and empty faster when you gain or lose money.", + "TOOLTIP_RENDERS_HEALTH_BAR": "Renderiza a salud bar for enemies cuando z-targeted.", + "TOOLTIP_MAKES_ALL_EQUIPMENT": "Hace que todo el equipo visible, regardless of age.", + "TOOLTIP_RENDERS_GAUNTLETS_WHEN": "Renderiza gauntlets cuando using the bow and gancho like in oot3d.", + "TOOLTIP_DISABLES_THE_BEATING": "Deshabilitars the beating animation of the hearts on the hud.", + "TOOLTIP_ALWAYS_SHOWS_DUNGEON": "Always shows dungeon entrance icons on the Minimap.", + "TOOLTIP_THE_DARKNESS_THAT": "Remove the darkness that appears cuando charging a spin attack.", + "TOOLTIP_LINK_WILL_NOT": "Link will not spin when the Goron Pot starts to spin.", + "TOOLTIP_ADJUSTS_THE_HORIZONTAL": "Ajustars the horizontal culling plane to account for widescreen resolucións.", + "TOOLTIP_ALLOWS_EQUIPPING_SHIELDS": "Permite equipping escudos, túnicas and botas to c-botóns/d-pad.", + "TOOLTIP_ALLOWS_LINK_UNSHEATHE": "Permite link to unsheathe espada conout slashing automáticomatically.", + "TOOLTIP_ADDS_PROMPT_EQUIP": "Añade a prompt to equip newly-obtained espadas, escudos, and túnicas.", + "TOOLTIP_PREVENT_DROPPING_INPUTS": "Evitar soltarping inputs cuando reproduciring the ocarina too quickly.", + "TOOLTIP_SKIP_THE_PART": "Saltar the part where the ocarina reproducirback is called cuando you reproducir a song.", + "TOOLTIP_ALLOWS_MASKS_EQUIPPED": "Permite masks to be equipped normally from the pausar menu as adult.", + "TOOLTIP_TURNS_BUNNY_HOOD": "Turns Bunny Hood Invisible while still maintaining its effects.", + "TOOLTIP_REMOVES_THE_CAP": "Elimina the cap of 3 active explosives being deployed at once.", + "TOOLTIP_AIMING_WITH_THE": "Aiming with the Boomerang will display a reticle as with the Hookshot.", + "TOOLTIP_GREATLY_DECREASES_CAST": "Greatly decreases cast time of Farore's Wind magic spell.", + "TOOLTIP_BLUE_FIRE_DROPPED": "Blue Fire dropped from bottle can be bottled.", + "TOOLTIP_PREVENTS_IMMEDIATELY_FALLING": "Evitars immediately falling off climbable surfaces if climbing on the edges.", + "TOOLTIP_MAKE_CROUCH_STABBING": "Make crouch stabbing always do the same damage as a regular slash.", + "TOOLTIP_FIXES_THE_BROKEN": "Fixes the Broken Giant's Knife flag not being reset when Medigoron fixes it.", + "TOOLTIP_MAKES_THE_AND": "Makes the l and r botóns in the pausar menu the same color.", + "TOOLTIP_CORRECTLY_CENTERS_THE": "Correctly centers the Navi text prompt on the HUD's C-Up button.", + "TOOLTIP_RESTORE_THE_ORIGINAL": "Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.", + "TOOLTIP_ALLOWS_BOMBCHUS_EXPLODE": "Permite bombachus to explode out of bounds. similar to gamecube and wii vc.", + "TOOLTIP_MODIFIES_DAMAGE_TAKEN": "Modifies Damage taken after Bonking.", + "TOOLTIP_RESPAWN_WITH_FULL": "Respawn with Full Health instead of 3 hearts.", + "TOOLTIP_DISABLES_RANDOM_DROPS": "Deshabilitars random soltars, except from the goron pot, dampe, and bosses.", + "TOOLTIP_BOMBCHUS_WILL_SOMETIMES": "Bombchus will sometimes drop in place of Bombs.", + "TOOLTIP_ADJUSTS_RATE_DAMPE": "Ajustars rate dampe soltars flames during race.", + "TOOLTIP_ALWAYS_GET_THE": "Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.", + "TOOLTIP_ALL_DOGS_CAN": "All dogs can be traded in and will count as Richard.", + "TOOLTIP_ALL_MAJOR_BOSSES": "All Major Bosses move and act twice as fast.", + "TOOLTIP_ALL_REGULAR_ENEMIES": "All Regular Enemies and Mini-Bosses move and act twice as fast.", + "TOOLTIP_THE_TIME_BETWEEN": "The time between groups of leevers spawning.", + "TOOLTIP_TURN_ONOFF_CHANGES": "Turn on/off changes to the Fishing behavior.", + "TOOLTIP_SKIPS_THE_SHOOTING": "Saltars the shooting gallery minigame.", + "TOOLTIP_THE_AMMUNITION_THE": "The ammunition at the start of the shooting gallery minigame as adult.", + "TOOLTIP_PREVENTS_THE_SMALL": "Evitars the small cucco from appearing in the bombachu bowling minigame.", + "TOOLTIP_PREVENTS_THE_BIG": "Evitars the big cucco from appearing in the bombachu bowling minigame.", + "TOOLTIP_THE_NUMBER_BOMBCHUS": "The number of bombachus available at the start of the bombachu bowling minigame.", + "TOOLTIP_SKIPS_THE_FROGS": "Saltars the frogs' ocarina game.", + "TOOLTIP_REMOVES_THE_TIMER": "Elimina the timer to reproducir back the song.", + "TOOLTIP_SKIPS_THE_LOST": "Saltars the lost woods ocarina memory game.", + "TOOLTIP_ADJUST_THE_NUMBER": "Ajustar the number of notes you need to reproducir to end the third round.", + "TOOLTIP_AMYS_BLOCK_PUSHING": "Amy's block pushing puzzle instantly solved.", + "TOOLTIP_ALL_FISH_WILL": "All fish will be caught instantly.", + "TOOLTIP_WHEN_LINE_STABLE": "Cuando a line is stable, guarantee bite. otherwise usar predeterminado lógica.", + "TOOLTIP_HOOK_ONCE_HOOK_BEEN": "Once a hook as been set, Fish will never let go while being reeled in.", + "TOOLTIP_LOACHES_WILL_ALWAYS": "Loaches will always appear in the fishing pond instead of every four visits.", + "TOOLTIP_THE_POND_OWNER": "The pond owner will not ask to confirm si tú want to keep a smaller fish.", + "TOOLTIP_ALLOWS_DOGS_FOLLOW": "Permite dogs to follow you anywhere you go, even si tú leave the market.", + "TOOLTIP_RUPEES_REDUCE_OVER": "Rupees reduce over time, Link suffers damage when the count hits 0.", + "TOOLTIP_INTERVAL_BETWEEN_RUPEE": "Interval between Rupee reduction in Rupee Dash Mode.", + "TOOLTIP_WALLMASTER_FOLLOWS_LINK": "A Wallmaster follows Link everywhere, don't get caught!", + "TOOLTIP_ENABLES_ADDITIONAL_TRAP": "Habilita additional trap variants.", + "TOOLTIP_ALLOWS_YOU_USE": "Permite you to usar any item at any location", + "TOOLTIP_MAKES_EVERY_TUNIC": "Makes every túnica have the effects of every other túnica.", + "TOOLTIP_PREVENTS_THE_DEKU": "Evitars the deku escudo from burning on contact con fire.", + "TOOLTIP_MAKES_EVERY_SURFACE": "Makes every surface in the game climbable.", + "TOOLTIP_ALLOWS_YOU_WALK": "Permite you to walk through walls.", + "TOOLTIP_HOLDING_MAKES_YOU": "Holding l makes you float into the air.", + "TOOLTIP_PREVENTS_REDEADS_AND": "Evitars redeads and gibdos from being able to freeze you con their scream.", + "TOOLTIP_DISABLES_SANDSTORM_EFFECT": "Deshabilitars sandstorm effect in haunted wasteland.", + "TOOLTIP_ALLOWS_ZTARGETING_GOLD": "Permite z-targeting gold skulltulas.", + "TOOLTIP_GIVES_YOU_THE": "Gives you the glitched damage value of the quick put away glitch.", + "TOOLTIP_CLEARS_THE_CUTSCENE": "Clears the cutscene pointer to a value safe for wrong warps.", + "TOOLTIP_DROPS_FROM_ENEMIES": "Soltars from enemies, grass, etc. don't disappear after a establecer amount of time.", + "TOOLTIP_PREVENTS_FISH_FROM": "Evitars fish from automáticomatically despawning after a while cuando soltarped.", + "TOOLTIP_PREVENTS_BUGS_FROM": "Evitars bugs from automáticomatically despawning after a while cuando soltarped.", + "TOOLTIP_FREEZES_THE_TIME": "Freezes the time of day.", + "TOOLTIP_SYNCS_THE_INGAME": "Syncs the in-game time with the real world time.", + "TOOLTIP_SWITCHES_LINKS_AGE": "Switches Link's age and reloads the area.", + "TOOLTIP_SAVE_LOAD_CHANGE_SLOTS": "F5 to save, F6 to change slots, F7 to load", + "TOOLTIP_TURNS_OOT_BETA": "Turns on OoT Beta Quest. *WARNING*: This will reset your game!", + "TOOLTIP_COSMETICS_EDITOR_WINDOW_ENABLES": "Habilita la ventana separada cosmetics editor window.", + "TOOLTIP_AUDIO_EDITOR_WINDOW_ENABLES": "Habilita la ventana separada audio editor window.", + "TOOLTIP_GAMEPLAY_STATS_WINDOW_ENABLES": "Habilita la ventana separada gamereproducir stats window.", + "SEPARATOR_CONNECTION_SETTINGS": "Connection Settings", + "TEXT_PORT_HOST_PORT": "Host & Port", + "TEXT_NAME_COLOR": "Name & Color", + "TEXT_ROOM": "Room ID", + "TEXT_TEAM_ITEMS_FLAGS": "Team ID (Items & Flags Shared)", + "TEXT_CONNECTING": "Connecting...", + "SEPARATOR_CURRENT_ROOM": "Current Room", + "BUTTON_REQUEST_TEAM_STATE": "Request Team State", + "TOOLTIP_TRY_THIS_YOU": "Try this if you are missing items or flags that your team members have collected", + "SEPARATOR_ROOM_SETTINGS_ADMIN": "Room Settings (Admin Only)", + "BUTTON_ALL_TEAM_STATE": "Clear All Team State", + "SEPARATOR_USAGE_INSTRUCTIONS": "Usage Instructions", + "TEXTWRAPPED_ALL_PLAYERS_INVOLVED": "1. All players involved should start at the file select screen", + "TEXT_PLAYERS_ONLINE": "Reproducirers online: %d", + "TEXT_INCOMPATIBLE_VERSION_WILL": "Incompatible version! Will not work together!", + "TEXT_YOURS": "Yours: %u", + "TEXT_THEIRS": "Theirs: %u", + "TEXT_SEED_MISMATCH_CONTINUING": "Seed mismatch! Continuing will break things!", + "TOOLTIP_REVERT_EVERY_ELEMENT": "Revert every element to use their original position and no margins", + "TOOLTIP_USING_THIS_ALLOW": "Using this allow you move the element with General margins sliders", + "TOOLTIP_THIS_WILL_USE": "Esto usar enemy on screen position", + "TOOLTIP_WINDOW_THIS_WILL_MAKE": "Esto make your elements follow the bottom edge of your game window", + "TOOLTIP_THIS_WILL_MAKE": "Esto make your elements follow the bottom of the life meter", + "TOOLTIP_THIS_SLIDER_USED": "This slider is usard to move left and right your elements.", + "SEPARATOR_GENERAL_MARGINS_SETTINGS": "General Margins Settings", + "COLLAPSING_HEARTS_COUNT_POSITION": "Hearts count position", + "TOOLTIP_THIS_WILL_SET": "Esto establecer the length of a row of hearts. establecer to 0 for unlimited length.", + "COLLAPSING_MAGIC_METER_POSITION": "Magic Meter position", + "COLLAPSING_VISUAL_STONE_AGONY": "Visual stone of agony position", + "COLLAPSING_DPAD_ITEMS_POSITION": "DPad items position", + "COLLAPSING_ENEMY_HEALTH_BAR": "Enemy Health Bar position", + "TOOLTIP_THIS_WILL_CHANGE": "Esto change the width of the salud bar", + "TEXT_ENABLED_TIMERS": "No Enabled Timers...", + "TOOLTIP_CONTROLS_VIEWER_SETS_THE": "Establecers the on screen size of the visor de entradas", + "COLLAPSING_BUTTONS": "Buttons", + "COLLAPSING_STICK_ANALOG_STICK": "Analog Stick", + "COLLAPSING_ADDITIONAL": "Additional (\\", + "COLLAPSING_ANALOG_ANGLE_VALUES": "Analog Angle Values", + "TOOLTIP_STICK_CONTROLS_VIEWER_DISPLAYS": "Mostrars analog stick angle values in the visor de entradas", + "TEXT_X3D_Y3D": "X:%3d, Y:%3d", + "TEXT_PRESS_ANY_BUTTONNMOVE": "Press any button,\\nmove any axis,\\nor press any key\\nto edit mapping", + "TOOLTIP_AXIS_THRESHOLD": "Edit axis threshold", + "TEXT_STICK_STICK_AXIS_THRESHOLD": "Stick axis threshold:", + "TEXT_TRIGGER_TRIGGER_AXIS_THRESHOLD": "Trigger axis threshold:", + "TEXT_SENSITIVITY_SENSITIVITY": "Sensitivity:", + "TEXT_DEADZONE_DEADZONE": "Deadzone:", + "TEXT_NOTCH_SNAP_ANGLE": "Notch Snap Angle:", + "TEXT_RUMBLE_PRESS_ANY_BUTTONNOR": "Press any button\\nor move any axis\\nto add rumble device", + "TEXT_SMALL_MOTOR_INTENSITY": "Small Motor Intensity:", + "TEXT_LARGE_MOTOR_INTENSITY": "Large Motor Intensity:", + "TEXT_LED_PRESS_ANY_BUTTONNOR": "Press any button\\nor move any axis\\nto add LED device", + "TEXT_LED_LED_COLOR": "LED Color:", + "TEXT_CUSTOM_COLOR": "Personalizado color", + "TOOLTIP_SETS_THE_BRIGHTNESS": "Establecers the brightness of controlador leds. 0% brightness = leds off.", + "TEXT_GYRO_PRESS_ANY_BUTTONNOR": "Press any button\\nor move any axis\\nto add gyro device", + "BUTTON_RECALIBRATE": "Recalibrate", + "TOOLTIP_STICK_ALLOWS_FOR_AIMING": "Permite for aiming con the right stick in:\\n-first-person/c-up view\\n-weapon aiming", + "TOOLTIP_STICK_CHANGES_THE_LEFT": "Cambia el left stick to move the reproducirer while in first-person mode", + "TOOLTIP_INVERTS_THE_CAMERA": "Inverts the Camera Y Axis in:\\n-Free look", + "TOOLTIP_INVERTS_THE_SHIELD": "Inverts the Shield Aiming Y Axis", + "TOOLTIP_VIEW_GYRO_PREVENTS_THE": "Evitars the c-up view from automático-centraring, allowing for giroscopio aiming", + "TOOLTIP_THE_CURSOR_WILL": "The cursor will only move a single space no matter cuánto tiempo a d-pad direction is held", + "COLLAPSING_DPAD": "D-Pad", + "COLLAPSING_RUMBLE_RUMBLE": "Rumble", + "COLLAPSING_GYRO_GYRO": "Gyro", + "COLLAPSING_LEDS": "LEDs", + "COLLAPSING_MODIFIER_BUTTONS": "Modifier Buttons", + "COLLAPSING_CONTROLS_OCARINA_CONTROLS": "Ocarina Controls", + "COLLAPSING_CONTROLS_CAMERA_CONTROLS": "Camera Controls", + "COLLAPSING_CONTROLS_DPAD_CONTROLS": "D-Pad Controls", + "BUTTON_ALL": "Clear All", + "TEXT_PORT_THIS_WILL_ALL": "Esto borrar all existing mapapings for\\nmando (sdl) on puerto %d.\\n\\ncontinue?", + "BUTTON_SET_DEFAULTS": "Establecer predeterminados", + "TOOLTIP_ENEMIES_AND_BOSSES": "Enemies and Bosses spawn with random sizes.", + "BUTTON_SET_TOKENS": "Establecer tokens", + "SEPARATOR_WINDOW_WINDOW_OPTIONS": "Window Options", + "SEPARATOR_SPLIT_LIST_MANAGEMENT": "Split List Management", + "TEXT_NEW_LIST_NAME": "New List Name:", + "TEXT_LOAD_SELECT_LIST": "Seleccionar list to cargar:", + "SEPARATOR_RESOURCES": "Resources", + "SEPARATOR_SPOILER_LOG_REWARDS": "Spoiler Log Rewards", + "TEXT_NAME": "Name: %s", + "SEPARATOR_LOADSAVE_SPOILER_LOG": "Cargar/guardar spoiler log", + "TEXT_SPOILER_LOGS_FOUND": "No Spoiler Logs found.", + "SEPARATOR_CURRENT_SEED_HASH": "Current Seed Hash", + "TEXT_SPOILER_LOG_LOADED": "No Spoiler Log Loaded", + "SEPARATOR_OPTIONS": "Options", + "TEXT_LOAD_PLEASE_SPOILER_DATA": "Please Load Spoiler Data...", + "TEXT_CURRENT_HINT": "Current Hint:", + "TEXT_NEW_HINT": "New Hint:", + "TOOLTIP_RANDOMIZE_HINT": "Randomize Hint", + "TEXT_CHECKS": "Checks: %d/%d", + "TOOLTIP_CUSTOMIZE_WHAT_NUMBERS": "Personalizadoize what numbers are shown for triforce piece tracking.", + "TOOLTIP_SHOWS_MORE_EASILY": "Shows an 'h' or an 'l' to more easily distinguish between gancho and longshot.", + "TEXT_LOAD_WAITING_FOR_FILE": "Waiting for file load...", + "TOOLTIP_SETS_THE_FONT": "Establecers the font size usard in the rastreador de comprobaciones.", + "TOOLTIP_ENABLED_WILL_HIDE": "Si está habilitado, will ocultar area headers that have no locations matching filter", + "SEPARATOR_TRACKER_HEADER_VISIBILITY": "Tracker Header Visibility", + "TOOLTIP_ENABLED_WILL_PREVENT": "Si está habilitado, will evitar the tracker from mostraring slots con non-shop-item shuffles.", + "TOOLTIP_ENABLED_WILL_SHOW": "Si está habilitado, will mostrar un check's lógica cuando hovering over it.", + "TEXTWRAPPED_THE_ENTRANCE_TRACKER": "The rastreador de entradas will only track shuffled entradas", + "TEXT_SORT": "Sort By", + "TOOLTIP_SORT_ENTRANCES_THE": "Sort entrances by the overrided destination", + "TEXT_LIST_ITEMS": "List Items", + "TOOLTIP_AUTOMATICALLY_SCROLL_THE": "Automáticomatically scroll to the first available entrada in the current scene", + "TOOLTIP_HIGHLIGHT_THE_ENTRANCE": "Highlight the previous entrance that Link came from", + "TOOLTIP_HIGHLIGHT_AVAILABLE_ENTRANCES": "Highlight available entrances in the current scene", + "TOOLTIP_COLLAPSE_UNDISCOVERED_ENTRANCES": "Collapse undiscovered entrances towards the bottom of each group", + "TOOLTIP_HIDE_REVERSE_ENTRANCE": "Ocultar reverse entrada transitions cuando decouple entradas is off", + "TEXT_GROUP": "Group By", + "TOOLTIP_GROUP_ENTRANCES_THEIR": "Group entrances by their entrance type", + "TEXT_SPOILER_REVEAL": "Spoiler Reveal", + "TOOLTIP_REVEAL_THE_SOURCE": "Reveal the source for undiscovered entrances", + "TOOLTIP_REVEAL_THE_DESTINATION": "Reveal the destination for undiscovered entrances", + "TEXT_PLATFORM_WINDOWS": "Platform: Windows", + "TEXT_PLATFORM_IOS": "Platform: iOS", + "TEXT_PLATFORM_MACOS": "Platform: macOS", + "TEXT_PLATFORM_LINUX": "Platform: Linux", + "TEXT_PLATFORM_UNKNOWN": "Platform: Unknown", + "TEXT_FPS_STATUS_03F_MSFRAME": "Status: %0.3f ms/frame (%0.1f FPS)", + "TEXT_TABLE": "Table ID", + "TEXT": "Text ID", + "TOOLTIP_LANGUAGE_LOAD_WHICH_LANGUAGE": "Which language to cargar from the seleccionared text id", + "BUTTON_DISPLAY_MESSAGEEXISTINGMESSAGE": "Mostrar message##existingmessage", + "TEXT_CUSTOM_MESSAGE": "Personalizado message", + "BUTTON_DISPLAY_MESSAGECUSTOMMESSAGE": "Mostrar message##personalizadomessage", + "TEXT_DESCRIPTION": "Description: %s", + "TEXT_CATEGORY": "Category: %s", + "TEXT_PARAMETERS": "Parameters: %d", + "TEXT_ACTOR_ACTOR_LIST_INDEX": "Actor List Index: %d", + "TEXT_ACTOR_ACTOR_POSITION": "Actor Position", + "TEXT_ACTOR_ACTOR_ROTATION": "Actor Rotation", + "TEXT_FLAGS": "Flags", + "TEXT_BGCHECKFLAGS": "bgCheckFlags", + "TEXT_ACTOR_SELECT_ACTOR_DISPLAY": "Seleccionar an actor to mostrar information.", + "TOOLTIP_ACTOR_GRABS_ACTOR_WITH": "Grabs actor with target arrow above it. You might need C-Up for enemies", + "TOOLTIP_ACTOR_GRABS_ACTOR_THAT": "Grabs actor that Link is holding", + "TOOLTIP_ACTOR_CONTROLS_CHANGES_THE": "Cambia el actor specific param menus con a direct input", + "TEXT_ACTOR_ACTOR_SPECIFIC_DATA": "Actor Specific Data", + "TEXT_ACTOR_NEW_ACTOR_POSITION": "New Actor Position", + "TEXT_ACTOR_NEW_ACTOR_ROTATION": "New Actor Rotation", + "TEXT_ACTOR_GLOBAL_CONTEXT_NEEDED": "Global Context needed for actor info!", + "TOOLTIP_COLLISION_APPLIES_THE_SCENES": "Applies the scene's shading to the collision display.", + "INPUTTEXT_DISPLAY_LISTS": "Search Display Lists", + "TEXT_RESOURCE_TYPE_NOT": "Resource type is not a Display List. Please choose another.", + "TEXT_TOTAL_INSTRUCTION_SIZE": "Total Instruction Size: %lu", + "TEXT_FMT": "FMT: %u", + "TEXT_SIZ": "SIZ: %u", + "TEXT_LINE": "LINE: %u", + "TEXT_TMEM": "TMEM: %u", + "TEXT_TILE": "TILE: %u", + "TEXT_PAL": "PAL: %u", + "TEXT_CMT": "CMT: %u", + "TEXT_MASKT": "MASKT: %u", + "TEXT_SHIFT": "SHIFT: %u", + "TEXT_CMS": "CMS: %u", + "TEXT_MASKS": "MASKS: %u", + "TEXT_SHIFTS": "SHIFTS: %u", + "TEXT_WIDTH": "WIDTH: %u", + "TEXT_TEXTURE_NAME": "Texture Name: %s", + "TEXT_NUM_VTX": "Num VTX: %u", + "TEXT_OFFSET": "Offset: %u", + "TEXT_VERTEX_NAME": "Vertex Name: %s", + "TEXT_RESERVED_SECOND_HALF": "%lu - Reserved - Second half of %s", + "TEXT_ERROR_DISPLAYING_INSTRUCTIONS": "Error displaying DL instructions.", + "TOOLTIP_CURRENT_FILE_NUMBER": "Current File Number", + "TOOLTIP_PLAYER_NAME": "Reproducirer name", + "TOOLTIP_ENCODING_USED_FOR": "Encoding used for Player Name", + "TOOLTIP_MAXIMUM_HEALTH_UNITS": "Maximum health. 16 units per full heart", + "TOOLTIP_CURRENT_HEALTH_UNITS": "Current health. 16 units per full heart", + "TOOLTIP_DOUBLE_DEFENSE_UNLOCKED": "Is double defense unlocked?", + "TOOLTIP_CURRENT_MAGIC_LEVEL": "Current magic level", + "TOOLTIP_CURRENT_MAGIC_UNITS": "Current magic. 48 units per magic level", + "TOOLTIP_CURRENT_RUPEES": "Current rupees", + "TOOLTIP_TIME_DAY": "Time of day", + "TOOLTIP_TOTAL_NUMBER_DAYS": "Total number of days elapsed since receiving claim check from Biggoron", + "TOOLTIP_TOTAL_NUMBER_DEATHS": "Total number of deaths", + "TOOLTIP_BIGGORON_SWORD_UNLOCKED": "Is Biggoron sword unlocked? Replaces Giant's knife", + "TOOLTIP_GIANTS_KNIFE_HEALTH": "Giant's knife health. Default is 8. Must be >0 for Biggoron sword to work", + "TOOLTIP_FROM_WHICH_ENTRANCE": "From which entrance did Link arrive?", + "TOOLTIP_WHICH_CUTSCENE_THIS": "Which escena is this?", + "TOOLTIP_NAVI_WANTS_TALK": "Navi wants to talk at 600 units, decides not to at 3000.", + "TOOLTIP_HEAT_TIMER_RACE": "Heat timer, race timer, etc. Has white font", + "TOOLTIP_TIME_SECONDS": "Time, in seconds", + "TOOLTIP_TRADE_TIMER_GANON": "Trade timer, Ganon collapse timer, etc. Has yellow font", + "TOOLTIP_AUDIO_SOUND_SETTING": "Sound setting", + "TOOLTIP_SAVE_WARNING_YOU_YOUR": "WARNING! If you save, your file may be locked! Use caution!", + "TOOLTIP_ZTARGETING_BEHAVIOR": "Z-Targeting behavior", + "TOOLTIP_CURRENTLY_OBTAINED_TRIFORCE": "Currently obtained Triforce Pieces. For Triforce Hunt.", + "TOOLTIP_USED_THE_SINKING": "Usard the sinking lure to catch it.", + "TOOLTIP_PLAYED_LEAST_ONE": "Reproducired at least one game as an adult", + "TOOLTIP_THE_OWNERS_NOW": "The owner's now visibly bald cuando adult link.", + "TOOLTIP_DETERMINES_WEATHER_AND": "Determines weather and school size during dawn/dusk.", + "TOOLTIP_RESTRICTS_ITEMS_AND": "Restricts items and ammo to only what is possible to legally acquire in-game", + "TOOLTIP_NONE": "None", + "TEXT_AMMO": "Ammo", + "TEXT_0X02X": "0x%02X", + "TEXTWRAPPED_0X02X": "0x%02X: %s", + "TEXT_FLAGS_MATCH_THE": "No flags match the current search.", + "TEXT_STATEFLAGS1": "stateFlags1", + "TEXT_STATEFLAGS2": "stateFlags2", + "TEXT_STATEFLAGS3": "stateFlags3", + "TEXT_UNK6AEROTFLAGS": "unk_6AE_rotFlags", + "TEXT_SWITCH": "Switch", + "TEXT_TEMP_SWITCH": "Temp Switch", + "TEXT_TEMP": "Temp Clear", + "TEXT_COLLECT": "Collect", + "TEXT_TEMP_COLLECT": "Temp Collect", + "TEXT_CHEST": "Chest", + "TOOLTIP_SAVE_CURRENT_SCENE_FLAGS": "Guardar current scene flags. normally happens on scene salida", + "TOOLTIP_CURRENT_SCENE_FLAGS": "Clear current scene flags. Reload scene to see changes", + "TEXT_CURRENT_GAME_STATE": "Current game state does not have an active scene", + "TEXT_MAP": "Map", + "TOOLTIP_OPEN_FLAGS_FOR": "Open flags for current scene", + "TEXT_ROOMS": "Rooms", + "TEXT_FLOORS": "Floors", + "TEXT_GOLD_SKULLTULAS": "Gold Skulltulas", + "BUTTON_CATCH_ALL_CHILD": "Catch All (Child)", + "BUTTON_UNCATCH_ALL_CHILD": "Uncatch All (Child)", + "BUTTON_CATCH_ALL_ADULT": "Catch All (Adult)", + "BUTTON_UNCATCH_ALL_ADULT": "Uncatch All (Adult)", + "TEXT_DUNGEON_ITEMS": "Dungeon Items", + "TEXT_BARINADES_LAIR_DOES": "Barinade's Lair does not have small keys", + "TEXT_LINKS_POSITION": "Link's Position", + "TEXT_LINKS_ROTATION": "Link's Rotation", + "TEXT_LINKS_MODEL_ROTATION": "Link's Model Rotation", + "TEXT_LINKS_CURRENT_EQUIPMENT": "Link's Current Equipment", + "TEXT_CURRENT_ITEMS": "Current Items", + "TEXT_CURRENT_DPAD_ITEMS": "Current D-pad Items", + "TEXT_PLAYER_STATE": "Reproducirer state", + "TEXT_SWORD": "Sword", + "TEXT_GLOBAL_CONTEXT_NEEDED": "Global Context needed for player info!", + "TEXT_TOTAL_REGISTERED": "Total Registered: %d", + "TEXT_NORMAL": "Normal", + "TEXT_PTR": "Ptr", + "TEXT_FILTER": "Filter", + "TOOLTIP_STOP_PREVIEW": "Detener preview", + "TOOLTIP_PLAY_PREVIEW": "Reproducir preview", + "TOOLTIP_UNKNOWN": "Reset to default", + "TOOLTIP_AUDIO_RANDOMIZE_THIS_SOUND": "Randomize this sound", + "TOOLTIP_AUDIO_AUDIO_RANDOMIZES_ALL": "Randomizes all unlocked music and sound effects across tab groups", + "TOOLTIP_AUDIO_AUDIO_RESETS_ALL": "Resets all unlocked music and sound effects across tab groups", + "TOOLTIP_AUDIO_AUDIO_LOCKS_ALL": "Locks all music and sound effects across tab groups", + "TOOLTIP_AUDIO_AUDIO_UNLOCKS_ALL": "Unlocks all music and sound effects across tab groups", + "TOOLTIP_AUDIO_DISABLE_THE_LOW": "Deshabilitar the low hp beeping sonido.", + "TOOLTIP_AUDIO_DISABLES_THE_VOICE": "Deshabilitars the voice audio cuando navi calls you.", + "TOOLTIP_AUDIO_PLAYS_THE_BATTLE": "Reproducirs the battle music cuando getting close to a leever, like in majora's mask.", + "TEXT_PRESETS_PRESETS": "Presets", + "TEXT_PRESETS_PRESETS_PRESETS_WITH": "No presets with rando options. Make some in Settings -> Presets", + "TEXT_PRESETS_PRESETS_FOUND": "No presets found.", + "TEXT_DLIST": "dlist: %p", + "TEXT_BREAKPOINT": "BreakPoint: %s", + "TEXT_DISP_STACK": "Disp Stack", + "TEXT_TILES": "Tiles", + "TEXT_LOADED_TEXTURES": "Cargared textures", + "TEXT_LOAD_TEXTURE": "Texture To Load", + "BUTTON_DEV_DEBUG": "Debug", + "BUTTON_RESUME_GAME": "Resume Game", + "MENUITEM_COPY_TEXT": "Copy Text", + "BUTTON_SUBMIT": "Submit", + "TEXT_PLATFORM_OPENBSD": "Platform: OpenBSD", + "TEXT_FPS_STATUS_MSFRAME_FPS": "Status: %.3f ms/frame (%.1f FPS)", + "OVERLAYS_TEXT_FONT": "Overlays Text Font", + "KEEP_TRACK_OF_THE": "Keep track of the timer as an in-game HUD element. The position of the ", + "TOOLTIP_KEEP_TRACK_OF_THE": "Keep track of the timer as an in-game HUD element. The position of the ", + "ALLOWS_A_MORE_INDEPTH": "Permite a more in-depth perspective of time spent in a certain mapa.", + "TOOLTIP_ALLOWS_A_MORE_INDEPTH": "Permite a more in-depth perspective of time spent in a certain mapa.", + "TIMESTAMPS_ARE_RELATIVE_TO": "Timestamps are relative to starting timestamp rather than in game time, ", + "TOOLTIP_TIMESTAMPS_ARE_RELATIVE_TO": "Timestamps are relative to starting timestamp rather than in game time, ", + "TOGGLE_MODS_FOR_GRAPHICS": "Toggle mods. For graphics mods, this means toggling between default and mod graphics.", + "TOOLTIP_TOGGLE_MODS_FOR_GRAPHICS": "Toggle mods. For graphics mods, this means toggling between default and mod graphics.", + "ALLOWS_PRESSING_THE_TAB": "Permite pressing the tab llave to toggle mods", + "TOOLTIP_ALLOWS_PRESSING_THE_TAB": "Permite pressing the tab llave to toggle mods", + "CHANGES_THE_MENU_DISPLAY": "Cambia el menu mostrar from overlay to windowed.", + "TOOLTIP_CHANGES_THE_MENU_DISPLAY": "Cambia el menu mostrar from overlay to windowed.", + "ENABLES_DEBUG_MODE_ALLOWING": "Habilita debug mode, allowing you to seleccionar mapas con l + r + z, noclip ", + "TOOLTIP_ENABLES_DEBUG_MODE_ALLOWING": "Habilita debug mode, allowing you to seleccionar mapas con l + r + z, noclip ", + "ENABLES_THE_REGISTRY_EDITOR": "Habilita the registry editor.", + "TOOLTIP_ENABLES_THE_REGISTRY_EDITOR": "Habilita the registry editor.", + "CHANGES_THE_BEHAVIOR_OF": "Cambia el behavior of debug file seleccionar creation (creating a guardar file on slot 1 ", + "TOOLTIP_CHANGES_THE_BEHAVIOR_OF": "Cambia el behavior of debug file seleccionar creation (creating a guardar file on slot 1 ", + "ENABLES_SKULLTULA_DEBUG_WHEN": "Habilita skulltula debug, cuando moving the cursor in the menu above various ", + "TOOLTIP_ENABLES_SKULLTULA_DEBUG_WHEN": "Habilita skulltula debug, cuando moving the cursor in the menu above various ", + "ADVANCE_1_FRAME": "Avanzar 1 frame.", + "TOOLTIP_ADVANCE_1_FRAME": "Avanzar 1 frame.", + "ADVANCE_FRAMES_WHILE_THE": "Avanzar frames while the botón is held.", + "TOOLTIP_ADVANCE_FRAMES_WHILE_THE": "Avanzar frames while the botón is held.", + "THE_LOG_LEVEL_DETERMINES": "El nivel de registro determines which messages are printed to the console.", + "TOOLTIP_THE_LOG_LEVEL_DETERMINES": "El nivel de registro determines which messages are printed to the console.", + "OPTIMIZED_DEBUG_WARP_SCREEN": "Optimized Debug Warp Screen, with the added ability to chose entrances and time of day.", + "TOOLTIP_OPTIMIZED_DEBUG_WARP_SCREEN": "Optimized Debug Warp Screen, with the added ability to chose entrances and time of day.", + "TRANSLATE_THE_DEBUG_WARP": "Translate the Debug Warp Screen based on the game language.", + "TOOLTIP_TRANSLATE_THE_DEBUG_WARP": "Translate the Debug Warp Screen based on the game language.", + "ENABLES_THE_SEPARATE_STATS": "Habilita la ventana separada stats window.", + "TOOLTIP_ENABLES_THE_SEPARATE_STATS": "Habilita la ventana separada stats window.", + "ENABLES_THE_SEPARATE_CONSOLE": "Habilita la ventana separada console window.", + "TOOLTIP_ENABLES_THE_SEPARATE_CONSOLE": "Habilita la ventana separada console window.", + "ENABLES_THE_SEPARATE_SAVE": "Habilita la ventana separada guardar editor window.", + "TOOLTIP_ENABLES_THE_SEPARATE_SAVE": "Habilita la ventana separada guardar editor window.", + "ENABLES_THE_SEPARATE_HOOK": "Habilita la ventana separada hook debugger window.", + "TOOLTIP_ENABLES_THE_SEPARATE_HOOK": "Habilita la ventana separada hook debugger window.", + "ENABLES_THE_SEPARATE_COLLISION": "Habilita la ventana separada collision viewer window.", + "TOOLTIP_ENABLES_THE_SEPARATE_COLLISION": "Habilita la ventana separada collision viewer window.", + "ENABLES_THE_SEPARATE_ACTOR": "Habilita la ventana separada actor viewer window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ACTOR": "Habilita la ventana separada actor viewer window.", + "ENABLES_THE_SEPARATE_DISPLAY": "Habilita la ventana separada mostrar list viewer window.", + "TOOLTIP_ENABLES_THE_SEPARATE_DISPLAY": "Habilita la ventana separada mostrar list viewer window.", + "ENABLES_THE_SEPARATE_VALUE": "Habilita la ventana separada value viewer window.", + "TOOLTIP_ENABLES_THE_SEPARATE_VALUE": "Habilita la ventana separada value viewer window.", + "ENABLES_THE_SEPARATE_MESSAGE": "Habilita la ventana separada message viewer window.", + "TOOLTIP_ENABLES_THE_SEPARATE_MESSAGE": "Habilita la ventana separada message viewer window.", + "ENABLES_THE_SEPARATE_GFX": "Habilita la ventana separada gfx debugger window.", + "TOOLTIP_ENABLES_THE_SEPARATE_GFX": "Habilita la ventana separada gfx debugger window.", + "MAKES_THE_CURSOR_ALWAYS": "Hace que el cursor always visible, even in full screen.", + "TOOLTIP_MAKES_THE_CURSOR_ALWAYS": "Hace que el cursor always visible, even in full screen.", + "OPENS_THE_FOLDER_THAT": "Abre the folder that contains the guardar and mods folders, etc.", + "TOOLTIP_OPENS_THE_FOLDER_THAT": "Abre the folder that contains the guardar and mods folders, etc.", + "CONFIGURE_WHAT_HAPPENS_WHEN": "Configure what happens when starting or resetting the game.\\n\\n", + "TOOLTIP_CONFIGURE_WHAT_HAPPENS_WHEN": "Configure what happens when starting or resetting the game.\\n\\n", + "ENABLES_TEXT_TO_SPEECH": "Habilita texto a voz for in game dialog", + "TOOLTIP_ENABLES_TEXT_TO_SPEECH": "Habilita texto a voz for in game dialog", + "DISABLES_THE_AUTOMATIC_RECENTERING": "Deshabilitars the automáticomatic re-centraring of the cámara cuando idle.", + "TOOLTIP_DISABLES_THE_AUTOMATIC_RECENTERING": "Deshabilitars the automáticomatic re-centraring of the cámara cuando idle.", + "DISABLES_THE_WHITE_SCREEN": "Deshabilitars the white screen flash on enemy kill.", + "TOOLTIP_DISABLES_THE_WHITE_SCREEN": "Deshabilitars the white screen flash on enemy kill.", + "DISABLE_THE_GEOMETRY_WOBBLE": "Deshabilitar the geometría wobble and cámara distortion inside jabu.", + "TOOLTIP_DISABLE_THE_GEOMETRY_WOBBLE": "Deshabilitar the geometría wobble and cámara distortion inside jabu.", + "CHANGES_THE_SCALING_OF": "Cambia el scaling of the imgui menu elements.", + "TOOLTIP_CHANGES_THE_SCALING_OF": "Cambia el scaling of the imgui menu elements.", + "TOGGLES_FULLSCREEN_ONOFF": "Alterna fullscreen on/off.", + "TOOLTIP_TOGGLES_FULLSCREEN_ONOFF": "Alterna fullscreen on/off.", + "MULTIPLIES_YOUR_OUTPUT_RESOLUTION": "Multiplies your output resolution by the value inputted, as a more intensive but effective ", + "TOOLTIP_MULTIPLIES_YOUR_OUTPUT_RESOLUTION": "Multiplies your output resolution by the value inputted, as a more intensive but effective ", + "ACTIVATES_MSAA_MULTISAMPLE_ANTIALIASING": "Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x, to smooth the edges of ", + "TOOLTIP_ACTIVATES_MSAA_MULTISAMPLE_ANTIALIASING": "Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x, to smooth the edges of ", + "MATCHES_INTERPOLATION_VALUE_TO": "Coincide interpolation value to the refresh rate of your mostrar.", + "TOOLTIP_MATCHES_INTERPOLATION_VALUE_TO": "Coincide interpolation value to the refresh rate of your mostrar.", + "REMOVES_TEARING_BUT_CLAMPS": "Elimina tearing, but clamps your max fps to your mostrars refresh rate.", + "TOOLTIP_REMOVES_TEARING_BUT_CLAMPS": "Elimina tearing, but clamps your max fps to your mostrars refresh rate.", + "ENABLES_WINDOWED_FULLSCREEN_MODE": "Habilita pantalla completa en ventana mode.", + "TOOLTIP_ENABLES_WINDOWED_FULLSCREEN_MODE": "Habilita pantalla completa en ventana mode.", + "ALLOWS_MULTIPLE_WINDOWS_TO": "Permite multiple windows to be opened at once. requiere a recargar to aplicar.", + "TOOLTIP_ALLOWS_MULTIPLE_WINDOWS_TO": "Permite multiple windows to be opened at once. requiere a recargar to aplicar.", + "SETS_THE_APPLIED_TEXTURE": "Establecers the applied filtro de texturas.", + "TOOLTIP_SETS_THE_APPLIED_TEXTURE": "Establecers the applied filtro de texturas.", + "ENABLES_THE_SEPARATE_BINDINGS": "Habilita la ventana separada bindings window.", + "TOOLTIP_ENABLES_THE_SEPARATE_BINDINGS": "Habilita la ventana separada bindings window.", + "TOGGLES_THE_INPUT_VIEWER": "Alterna the visor de entradas.", + "TOOLTIP_TOGGLES_THE_INPUT_VIEWER": "Alterna the visor de entradas.", + "ENABLES_THE_SEPARATE_INPUT": "Habilita la ventana separada visor de entradas establecertings window.", + "TOOLTIP_ENABLES_THE_SEPARATE_INPUT": "Habilita la ventana separada visor de entradas establecertings window.", + "WHICH_CORNER_OF_THE": "Qué esquina of the screen notificacións appear in.", + "TOOLTIP_WHICH_CORNER_OF_THE": "Qué esquina of the screen notificacións appear in.", + "HOW_LONG_NOTIFICATIONS_ARE": "Cuánto tiempo notificacións are mostrared for.", + "TOOLTIP_HOW_LONG_NOTIFICATIONS_ARE": "Cuánto tiempo notificacións are mostrared for.", + "HOW_OPAQUE_THE_BACKGROUND": "Qué tan opaco the fondo of notificacións is.", + "TOOLTIP_HOW_OPAQUE_THE_BACKGROUND": "Qué tan opaco the fondo of notificacións is.", + "HOW_LARGE_NOTIFICATIONS_ARE": "Qué tan grande notificacións are.", + "TOOLTIP_HOW_LARGE_NOTIFICATIONS_ARE": "Qué tan grande notificacións are.", + "DISPLAYS_A_TEST_NOTIFICATION": "Mostrars a prueba notificación.", + "TOOLTIP_DISPLAYS_A_TEST_NOTIFICATION": "Mostrars a prueba notificación.", + "PREVENT_NOTIFICATIONS_FROM_PLAYING": "Evitar notificacións from reproduciring a sonido.", + "TOOLTIP_PREVENT_NOTIFICATIONS_FROM_PLAYING": "Evitar notificacións from reproduciring a sonido.", + "ENABLES_THE_SEPARATE_MOD": "Habilita la ventana separada mod menu window.", + "TOOLTIP_ENABLES_THE_SEPARATE_MOD": "Habilita la ventana separada mod menu window.", + "OVERRIDE_THE_RESOLUTION_SCALE": "Override the resolution scale slider and use the settings below, irrespective of window size.", + "TOOLTIP_OVERRIDE_THE_RESOLUTION_SCALE": "Override the resolution scale slider and use the settings below, irrespective of window size.", + "INTEGER_SCALES_THE_IMAGE": "Integer scales the image. Only available in Pixel Perfect Mode.", + "TOOLTIP_INTEGER_SCALES_THE_IMAGE": "Integer scales the image. Only available in Pixel Perfect Mode.", + "AUTOMATICALLY_SETS_SCALE_FACTOR": "Automáticomatically establecers escala factor to fit window. only available in píxel perfecto mode.", + "TOOLTIP_AUTOMATICALLY_SETS_SCALE_FACTOR": "Automáticomatically establecers escala factor to fit window. only available in píxel perfecto mode.", + "PREVENTS_INTEGER_SCALING_FACTOR": "Evitars escala entera factor from exceeding screen bounds.\\n\\n", + "TOOLTIP_PREVENTS_INTEGER_SCALING_FACTOR": "Evitars escala entera factor from exceeding screen bounds.\\n\\n", + "REMOVE_THIS_BUTTON_FROM": "Remove este botón from the combination", + "TOOLTIP_REMOVE_THIS_BUTTON_FROM": "Remove este botón from the combination", + "CREATES_A_NEW_RANDOM": "Crea a new random semilla value to be usard cuando generating a aleatorizador", + "TOOLTIP_CREATES_A_NEW_RANDOM": "Crea a new random semilla value to be usard cuando generating a aleatorizador", + "WHEN_OBTAINING_RUPEES_RANDOMIZE": "Cuando obtaining rupias, randomize what the rupia is called in the textbox.", + "TOOLTIP_WHEN_OBTAINING_RUPEES_RANDOMIZE": "Cuando obtaining rupias, randomize what the rupia is called in the textbox.", + "USE_CUSTOM_GRAPHICS_FOR": "Usar personalizado graphics for mazmorra llaves, big and small, so that they can be easily told apart.", + "TOOLTIP_USE_CUSTOM_GRAPHICS_FOR": "Usar personalizado graphics for mazmorra llaves, big and small, so that they can be easily told apart.", + "MATCHES_THE_COLOR_OF": "Coincide the color of mapas & brújulaes to the mazmorra they belong to. ", + "TOOLTIP_MATCHES_THE_COLOR_OF": "Coincide the color of mapas & brújulaes to the mazmorra they belong to. ", + "WITH_SHUFFLE_SPEAK_JABBER": "Con shuffle speak, jabber nut model & color will be generic.", + "TOOLTIP_WITH_SHUFFLE_SPEAK_JABBER": "Con shuffle speak, jabber nut model & color will be generic.", + "IF_ENABLED_SIGNS_NEAR": "Si está habilitado, signs near cargaring zones will tell you where they lead to.", + "TOOLTIP_IF_ENABLED_SIGNS_NEAR": "Si está habilitado, signs near cargaring zones will tell you where they lead to.", + "ENABLES_THE_SEPARATE_RANDOMIZER": "Habilita la ventana separada aleatorizador establecertings window.", + "TOOLTIP_ENABLES_THE_SEPARATE_RANDOMIZER": "Habilita la ventana separada aleatorizador establecertings window.", + "TOGGLES_THE_ITEM_TRACKER": "Alterna the rastreador de objetos.", + "TOOLTIP_TOGGLES_THE_ITEM_TRACKER": "Alterna the rastreador de objetos.", + "ENABLES_THE_SEPARATE_ITEM": "Habilita la ventana separada rastreador de objetos establecertings window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ITEM": "Habilita la ventana separada rastreador de objetos establecertings window.", + "TOGGLES_THE_ENTRANCE_TRACKER": "Alterna the rastreador de entradas.", + "TOOLTIP_TOGGLES_THE_ENTRANCE_TRACKER": "Alterna the rastreador de entradas.", + "ENABLES_THE_SEPARATE_ENTRANCE": "Habilita la ventana separada rastreador de entradas establecertings window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ENTRANCE": "Habilita la ventana separada rastreador de entradas establecertings window.", + "TOGGLES_THE_CHECK_TRACKER": "Alterna the rastreador de comprobaciones.", + "TOOLTIP_TOGGLES_THE_CHECK_TRACKER": "Alterna the rastreador de comprobaciones.", + "ENABLES_THE_SEPARATE_CHECK": "Habilita la ventana separada rastreador de comprobaciones establecertings window.", + "TOOLTIP_ENABLES_THE_SEPARATE_CHECK": "Habilita la ventana separada rastreador de comprobaciones establecertings window.", + "SHOW_A_NOTIFICATION_WHEN": "Mostrar un notificación cuando the game is automáticoguardard.", + "TOOLTIP_SHOW_A_NOTIFICATION_WHEN": "Mostrar un notificación cuando the game is automáticoguardard.", + "ONLY_CHANGE_THE_TEXTURE": "Only change the texture of containers if you have the Stone of Agony.", + "TOOLTIP_ONLY_CHANGE_THE_TEXTURE": "Only change the texture of containers if you have the Stone of Agony.", + "NIGHTTIME_SKULLTULAS_WILL_SPAWN": "Nighttime Skulltulas will spawn during both day and night.", + "TOOLTIP_NIGHTTIME_SKULLTULAS_WILL_SPAWN": "Nighttime Skulltulas will spawn during both day and night.", + "ALLOWS_GRAVES_TO_BE": "Permite graves to be pulled cuando child during the day.", + "TOOLTIP_ALLOWS_GRAVES_TO_BE": "Permite graves to be pulled cuando child during the day.", + "SHOPS_AND_MINIGAMES_ARE": "Shops and Minigames are open both day and night. Requires a scene reload to take effect.", + "TOOLTIP_SHOPS_AND_MINIGAMES_ARE": "Shops and Minigames are open both day and night. Requires a scene reload to take effect.", + "ALLOWS_THE_CURSOR_ON": "Permite the cursor on the pausar menu to be over any slot. sometimes requerido in aleatorizador ", + "TOOLTIP_ALLOWS_THE_CURSOR_ON": "Permite the cursor on the pausar menu to be over any slot. sometimes requerido in aleatorizador ", + "SPEAK_TO_NAVI_WITH": "Hablar con navi con l but entrar first-person cámara con c-up.", + "TOOLTIP_SPEAK_TO_NAVI_WITH": "Hablar con navi con l but entrar first-person cámara con c-up.", + "ADDS_BACK_IN_A": "Añade back in a delay after unpausing before the game resumes reproduciring again, ", + "TOOLTIP_ADDS_BACK_IN_A": "Añade back in a delay after unpausing before the game resumes reproduciring again, ", + "BUFFERS_YOUR_INPUTS_TO": "Almacena tus inputs to be executed a specified amount of frames later.", + "TOOLTIP_BUFFERS_YOUR_INPUTS_TO": "Almacena tus inputs to be executed a specified amount of frames later.", + "REWORKS_TARGETING_FUNCTIONALITYN": "Reworks targeting functionality\\n", + "TOOLTIP_REWORKS_TARGETING_FUNCTIONALITYN": "Reworks targeting functionality\\n", + "BUTTONS_TO_ACTIVATE_TARGET": "Buttons to activate target switching.", + "TOOLTIP_BUTTONS_TO_ACTIVATE_TARGET": "Buttons to activate target switching.", + "DISABLE_RANDOM_CAMERA_WIGGLE": "Deshabilitar random oscilación de cámara at low salud.", + "TOOLTIP_DISABLE_RANDOM_CAMERA_WIGGLE": "Deshabilitar random oscilación de cámara at low salud.", + "ALLOW_LINK_TO_PUT": "Allow Link to put items away without having to wait around.", + "TOOLTIP_ALLOW_LINK_TO_PUT": "Allow Link to put items away without having to wait around.", + "RESETS_THE_NAVI_TIMER": "Resets the Navi timer on scene change. If you have already talked to her, ", + "TOOLTIP_RESETS_THE_NAVI_TIMER": "Resets the Navi timer on scene change. If you have already talked to her, ", + "ALLOW_LINK_TO_ENTER": "Allow Link to enter Jabu-Jabu without feeding him a fish.", + "TOOLTIP_ALLOW_LINK_TO_ENTER": "Allow Link to enter Jabu-Jabu without feeding him a fish.", + "SKIP_PICKUP_MESSAGES_FOR": "Saltar mensaje de objetos for botella swipes.", + "TOOLTIP_SKIP_PICKUP_MESSAGES_FOR": "Saltar mensaje de objetos for botella swipes.", + "PREVENT_FORCED_CONVERSATIONS_WITH": "Evitar conversación forzadas con navi and/or other npcs.", + "TOOLTIP_PREVENT_FORCED_CONVERSATIONS_WITH": "Evitar conversación forzadas con navi and/or other npcs.", + "HOLDING_DOWN_B_SKIPS": "Mantener presionado b saltars text.", + "TOOLTIP_HOLDING_DOWN_B_SKIPS": "Mantener presionado b saltars text.", + "SPEEDS_UP_LIFTING_SILVER": "Acelera up lifting roca de platas and obeliscos.", + "TOOLTIP_SPEEDS_UP_LIFTING_SILVER": "Acelera up lifting roca de platas and obeliscos.", + "SPEEDS_UP_SHIP_IN": "Acelera up ship in templo de las sombras.", + "TOOLTIP_SPEEDS_UP_SHIP_IN": "Acelera up ship in templo de las sombras.", + "MAKES_LINK_ALWAYS_KICK": "Makes link always kick the cofre to open it, instead of doing the longer ", + "TOOLTIP_MAKES_LINK_ALWAYS_KICK": "Makes link always kick the cofre to open it, instead of doing the longer ", + "SPEEDS_UP_EMPTYING_ANIMATION": "Acelera up emptying animation cuando dumping out the contents of a botella.", + "TOOLTIP_SPEEDS_UP_EMPTYING_ANIMATION": "Acelera up emptying animation cuando dumping out the contents of a botella.", + "SKIP_THE_TOWER_ESCAPE": "Saltar the tower escape sequence between ganondorf and ganon.", + "TOOLTIP_SKIP_THE_TOWER_ESCAPE": "Saltar the tower escape sequence between ganondorf and ganon.", + "CAUSES_YOUR_WALLET_TO": "Causes your Wallet to fill and empty faster when you gain or lose money.", + "TOOLTIP_CAUSES_YOUR_WALLET_TO": "Causes your Wallet to fill and empty faster when you gain or lose money.", + "DISABLES_2D_PRERENDERED_BACKGROUNDS": "Deshabilitars 2d pre-rendered fondos. enable this cuando using a mod that ", + "TOOLTIP_DISABLES_2D_PRERENDERED_BACKGROUNDS": "Deshabilitars 2d pre-rendered fondos. enable this cuando using a mod that ", + "RENDERS_A_HEALTH_BAR": "Renderiza a salud bar for enemies cuando z-targeted.", + "TOOLTIP_RENDERS_A_HEALTH_BAR": "Renderiza a salud bar for enemies cuando z-targeted.", + "MAKES_ALL_EQUIPMENT_VISIBLE": "Hace que todo el equipo visible, regardless of age.", + "TOOLTIP_MAKES_ALL_EQUIPMENT_VISIBLE": "Hace que todo el equipo visible, regardless of age.", + "RENDERS_GAUNTLETS_WHEN_USING": "Renderiza gauntlets cuando using the bow and gancho like in oot3d.", + "TOOLTIP_RENDERS_GAUNTLETS_WHEN_USING": "Renderiza gauntlets cuando using the bow and gancho like in oot3d.", + "HIDES_MOST_OF_THE": "Ocultars most of the ui cuando not needed.\\n", + "TOOLTIP_HIDES_MOST_OF_THE": "Ocultars most of the ui cuando not needed.\\n", + "DISABLES_THE_BEATING_ANIMATION": "Deshabilitars the beating animation of the hearts on the hud.", + "TOOLTIP_DISABLES_THE_BEATING_ANIMATION": "Deshabilitars the beating animation of the hearts on the hud.", + "ALWAYS_SHOWS_DUNGEON_ENTRANCE": "Always shows dungeon entrance icons on the Minimap.", + "TOOLTIP_ALWAYS_SHOWS_DUNGEON_ENTRANCE": "Always shows dungeon entrance icons on the Minimap.", + "THE_SKYBOX_IN_THE": "The skybox in the fondo of the file seleccionar screen will go through the ", + "TOOLTIP_THE_SKYBOX_IN_THE": "The skybox in the fondo of the file seleccionar screen will go through the ", + "REMOVE_THE_DARKNESS_THAT": "Remove the darkness that appears cuando charging a spin attack.", + "TOOLTIP_REMOVE_THE_DARKNESS_THAT": "Remove the darkness that appears cuando charging a spin attack.", + "LINK_WILL_NOT_SPIN": "Link will not spin when the Goron Pot starts to spin.", + "TOOLTIP_LINK_WILL_NOT_SPIN": "Link will not spin when the Goron Pot starts to spin.", + "ADJUSTS_THE_HORIZONTAL_CULLING": "Ajustars the horizontal culling plane to account for widescreen resolucións.", + "TOOLTIP_ADJUSTS_THE_HORIZONTAL_CULLING": "Ajustars the horizontal culling plane to account for widescreen resolucións.", + "ALLOWS_UNEQUIPPING_ITEMS_FROM": "Permite unequipping items from c-botóns/d-pad by hovering over an equipped ", + "TOOLTIP_ALLOWS_UNEQUIPPING_ITEMS_FROM": "Permite unequipping items from c-botóns/d-pad by hovering over an equipped ", + "ALLOWS_EQUIPPING_SHIELDS_TUNICS": "Permite equipping escudos, túnicas and botas to c-botóns/d-pad.", + "TOOLTIP_ALLOWS_EQUIPPING_SHIELDS_TUNICS": "Permite equipping escudos, túnicas and botas to c-botóns/d-pad.", + "ALLOWS_LINK_TO_UNSHEATHE": "Permite link to unsheathe espada conout slashing automáticomatically.", + "TOOLTIP_ALLOWS_LINK_TO_UNSHEATHE": "Permite link to unsheathe espada conout slashing automáticomatically.", + "ADDS_A_PROMPT_TO": "Añade a prompt to equip newly-obtained espadas, escudos, and túnicas.", + "TOOLTIP_ADDS_A_PROMPT_TO": "Añade a prompt to equip newly-obtained espadas, escudos, and túnicas.", + "PREVENT_DROPPING_INPUTS_WHEN": "Evitar soltarping inputs cuando reproduciring the ocarina too quickly.", + "TOOLTIP_PREVENT_DROPPING_INPUTS_WHEN": "Evitar soltarping inputs cuando reproduciring the ocarina too quickly.", + "SKIP_THE_PART_WHERE": "Saltar the part where the ocarina reproducirback is called cuando you reproducir a song.", + "TOOLTIP_SKIP_THE_PART_WHERE": "Saltar the part where the ocarina reproducirback is called cuando you reproducir a song.", + "ALLOWS_LINK_TO_FREELY": "Permite link to freely change age by reproduciring the song of time.\\n", + "TOOLTIP_ALLOWS_LINK_TO_FREELY": "Permite link to freely change age by reproduciring the song of time.\\n", + "ALLOWS_MASKS_TO_BE": "Permite masks to be equipped normally from the pausar menu as adult.", + "TOOLTIP_ALLOWS_MASKS_TO_BE": "Permite masks to be equipped normally from the pausar menu as adult.", + "STOPS_MASKS_FROM_AUTOMATICALLY": "Deteners masks from automáticomatically unequipping on certain situations:\\n", + "TOOLTIP_STOPS_MASKS_FROM_AUTOMATICALLY": "Deteners masks from automáticomatically unequipping on certain situations:\\n", + "TURNS_BUNNY_HOOD_INVISIBLE": "Turns Bunny Hood Invisible while still maintaining its effects.", + "TOOLTIP_TURNS_BUNNY_HOOD_INVISIBLE": "Turns Bunny Hood Invisible while still maintaining its effects.", + "ALLOWS_YOU_TO_CONTROL": "Permite you to control a bombachu after soltarping it.\\n", + "TOOLTIP_ALLOWS_YOU_TO_CONTROL": "Permite you to control a bombachu after soltarping it.\\n", + "MAKE_DEKU_NUTS_EXPLODE": "Make Deku Nuts explode Bombs, similar to how they interact with Bombchus. ", + "TOOLTIP_MAKE_DEKU_NUTS_EXPLODE": "Make Deku Nuts explode Bombs, similar to how they interact with Bombchus. ", + "REMOVES_THE_CAP_OF": "Elimina the cap of 3 active explosives being deployed at once.", + "TOOLTIP_REMOVES_THE_CAP_OF": "Elimina the cap of 3 active explosives being deployed at once.", + "BOMBCHUS_DO_NOT_SELL": "Bombchus do not sell out when bought, and a 10 pack of Bombchus costs 99 rupees ", + "TOOLTIP_BOMBCHUS_DO_NOT_SELL": "Bombchus do not sell out when bought, and a 10 pack of Bombchus costs 99 rupees ", + "ALLOWS_CHILD_LINK_TO": "Permite child link to usar a bow con flechas.\\n", + "TOOLTIP_ALLOWS_CHILD_LINK_TO": "Permite child link to usar a bow con flechas.\\n", + "AIMING_WITH_A_BOW": "Aiming with a Bow or Slingshot will display a reticle as with the Hookshot ", + "TOOLTIP_AIMING_WITH_A_BOW": "Aiming with a Bow or Slingshot will display a reticle as with the Hookshot ", + "INSTANTLY_RETURN_THE_BOOMERANG": "Instantly return the Boomerang to Link by pressing its item button while ", + "TOOLTIP_INSTANTLY_RETURN_THE_BOOMERANG": "Instantly return the Boomerang to Link by pressing its item button while ", + "AIMING_WITH_THE_BOOMERANG": "Aiming with the Boomerang will display a reticle as with the Hookshot.", + "TOOLTIP_AIMING_WITH_THE_BOOMERANG": "Aiming with the Boomerang will display a reticle as with the Hookshot.", + "BLUE_FIRE_DROPPED_FROM": "Blue Fire dropped from bottle can be bottled.", + "TOOLTIP_BLUE_FIRE_DROPPED_FROM": "Blue Fire dropped from bottle can be bottled.", + "FIXES_KOKIRI_ANIMATION_STATE": "Fixes kokiri animation state to match their text state when getting ", + "TOOLTIP_FIXES_KOKIRI_ANIMATION_STATE": "Fixes kokiri animation state to match their text state when getting ", + "PREVENTS_IMMEDIATELY_FALLING_OFF": "Evitars immediately falling off climbable surfaces if climbing on the edges.", + "TOOLTIP_PREVENTS_IMMEDIATELY_FALLING_OFF": "Evitars immediately falling off climbable surfaces if climbing on the edges.", + "PREVENTS_THE_FOREST_STAGE": "Evitars the forest stage deku nut upgrade from becoming unobtainable ", + "TOOLTIP_PREVENTS_THE_FOREST_STAGE": "Evitars the forest stage deku nut upgrade from becoming unobtainable ", + "MAKE_CROUCH_STABBING_ALWAYS": "Make crouch stabbing always do the same damage as a regular slash.", + "TOOLTIP_MAKE_CROUCH_STABBING_ALWAYS": "Make crouch stabbing always do the same damage as a regular slash.", + "FIXES_CAMERA_SLIGHTLY_DRIFTING": "Fixes camera slightly drifting to the left when standing still due to a ", + "TOOLTIP_FIXES_CAMERA_SLIGHTLY_DRIFTING": "Fixes camera slightly drifting to the left when standing still due to a ", + "FIXES_CAMERA_SWING_RATE": "Fixes camera swing rate when the player falls off a ledge and the camera ", + "TOOLTIP_FIXES_CAMERA_SWING_RATE": "Fixes camera swing rate when the player falls off a ledge and the camera ", + "MAKES_THE_L_AND": "Makes the l and r botóns in the pausar menu the same color.", + "TOOLTIP_MAKES_THE_L_AND": "Makes the l and r botóns in the pausar menu the same color.", + "DISABLED_PATHS_VANISH_MORE": "Deshabilitard: paths vanish more the higher the resolución (z-fighting is based on resolución).\\n", + "TOOLTIP_DISABLED_PATHS_VANISH_MORE": "Deshabilitard: paths vanish more the higher the resolución (z-fighting is based on resolución).\\n", + "RESTORE_THE_ORIGINAL_RED": "Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.", + "TOOLTIP_RESTORE_THE_ORIGINAL_RED": "Restore the original red blood from NTSC 1.0/1.1. Disable for Green blood.", + "ALLOWS_BOMBCHUS_TO_EXPLODE": "Permite bombachus to explode out of bounds. similar to gamecube and wii vc.", + "TOOLTIP_ALLOWS_BOMBCHUS_TO_EXPLODE": "Permite bombachus to explode out of bounds. similar to gamecube and wii vc.", + "RESTORES_THE_WIDER_RANGE": "Restores the wider range of certain shutter doors from NTSC 1.0.\\n", + "TOOLTIP_RESTORES_THE_WIDER_RANGE": "Restores the wider range of certain shutter doors from NTSC 1.0.\\n", + "MODIFIES_DAMAGE_TAKEN_AFTER": "Modifies damage taken after falling into a void:\\n", + "TOOLTIP_MODIFIES_DAMAGE_TAKEN_AFTER": "Modifies damage taken after falling into a void:\\n", + "RESPAWN_WITH_FULL_HEALTH": "Respawn with Full Health instead of 3 hearts.", + "TOOLTIP_RESPAWN_WITH_FULL_HEALTH": "Respawn with Full Health instead of 3 hearts.", + "DISABLES_RANDOM_DROPS_EXCEPT": "Deshabilitars random soltars, except from the goron pot, dampe, and bosses.", + "TOOLTIP_DISABLES_RANDOM_DROPS_EXCEPT": "Deshabilitars random soltars, except from the goron pot, dampe, and bosses.", + "BOMBCHUS_WILL_SOMETIMES_DROP": "Bombchus will sometimes drop in place of Bombs.", + "TOOLTIP_BOMBCHUS_WILL_SOMETIMES_DROP": "Bombchus will sometimes drop in place of Bombs.", + "ADJUSTS_RATE_DAMPE_DROPS": "Ajustars rate dampe soltars flames during race.", + "TOOLTIP_ADJUSTS_RATE_DAMPE_DROPS": "Ajustars rate dampe soltars flames during race.", + "DYING_WILL_DELETE_YOUR": "Dying will delete your file.\\n\\n", + "TOOLTIP_DYING_WILL_DELETE_YOUR": "Dying will delete your file.\\n\\n", + "ALWAYS_GET_THE_HEART": "Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.", + "TOOLTIP_ALWAYS_GET_THE_HEART": "Always get the Heart Piece/Purple Rupee from the Spinning Goron Pot.", + "ALL_DOGS_CAN_BE": "All dogs can be traded in and will count as Richard.", + "TOOLTIP_ALL_DOGS_CAN_BE": "All dogs can be traded in and will count as Richard.", + "ALL_MAJOR_BOSSES_MOVE": "All Major Bosses move and act twice as fast.", + "TOOLTIP_ALL_MAJOR_BOSSES_MOVE": "All Major Bosses move and act twice as fast.", + "ALL_REGULAR_ENEMIES_AND": "All Regular Enemies and Mini-Bosses move and act twice as fast.", + "TOOLTIP_ALL_REGULAR_ENEMIES_AND": "All Regular Enemies and Mini-Bosses move and act twice as fast.", + "THE_TIME_BETWEEN_GROUPS": "The time between groups of leevers spawning.", + "TOOLTIP_THE_TIME_BETWEEN_GROUPS": "The time between groups of leevers spawning.", + "TURN_ONOFF_CHANGES_TO": "Turn on/off changes to the shooting gallery behavior.", + "TOOLTIP_TURN_ONOFF_CHANGES_TO": "Turn on/off changes to the shooting gallery behavior.", + "SKIPS_THE_SHOOTING_GALLERY": "Saltars the shooting gallery minigame.", + "TOOLTIP_SKIPS_THE_SHOOTING_GALLERY": "Saltars the shooting gallery minigame.", + "THE_AMMUNITION_AT_THE": "The ammunition at the start of the shooting gallery minigame as child.", + "TOOLTIP_THE_AMMUNITION_AT_THE": "The ammunition at the start of the shooting gallery minigame as child.", + "PREVENTS_THE_SMALL_CUCCO": "Evitars the small cucco from appearing in the bombachu bowling minigame.", + "TOOLTIP_PREVENTS_THE_SMALL_CUCCO": "Evitars the small cucco from appearing in the bombachu bowling minigame.", + "PREVENTS_THE_BIG_CUCCO": "Evitars the big cucco from appearing in the bombachu bowling minigame.", + "TOOLTIP_PREVENTS_THE_BIG_CUCCO": "Evitars the big cucco from appearing in the bombachu bowling minigame.", + "THE_NUMBER_OF_BOMBCHUS": "The number of bombachus available at the start of the bombachu bowling minigame.", + "TOOLTIP_THE_NUMBER_OF_BOMBCHUS": "The number of bombachus available at the start of the bombachu bowling minigame.", + "REMOVES_THE_TIMER_TO": "Elimina the timer to reproducir back the song.", + "TOOLTIP_REMOVES_THE_TIMER_TO": "Elimina the timer to reproducir back the song.", + "SKIPS_THE_LOST_WOODS": "Saltars the lost woods ocarina memory game.", + "TOOLTIP_SKIPS_THE_LOST_WOODS": "Saltars the lost woods ocarina memory game.", + "ADJUST_THE_NUMBER_OF": "Ajustar the number of notes the skull kids reproducir to start the first round.", + "TOOLTIP_ADJUST_THE_NUMBER_OF": "Ajustar the number of notes the skull kids reproducir to start the first round.", + "ALL_FISH_WILL_BE": "All fish will be caught instantly.", + "TOOLTIP_ALL_FISH_WILL_BE": "All fish will be caught instantly.", + "WHEN_A_LINE_IS": "Cuando a line is stable, guarantee bite. otherwise usar predeterminado lógica.", + "TOOLTIP_WHEN_A_LINE_IS": "Cuando a line is stable, guarantee bite. otherwise usar predeterminado lógica.", + "ONCE_A_HOOK_AS": "Once a hook as been set, Fish will never let go while being reeled in.", + "TOOLTIP_ONCE_A_HOOK_AS": "Once a hook as been set, Fish will never let go while being reeled in.", + "LOACHES_WILL_ALWAYS_APPEAR": "Loaches will always appear in the fishing pond instead of every four visits.", + "TOOLTIP_LOACHES_WILL_ALWAYS_APPEAR": "Loaches will always appear in the fishing pond instead of every four visits.", + "THE_POND_OWNER_WILL": "The pond owner will not ask to confirm si tú want to keep a smaller fish.", + "TOOLTIP_THE_POND_OWNER_WILL": "The pond owner will not ask to confirm si tú want to keep a smaller fish.", + "EVERY_FISH_IN_THE": "Every fish in the Fishing Pond will always be a Hyrule Loach.\\n\\n", + "TOOLTIP_EVERY_FISH_IN_THE": "Every fish in the Fishing Pond will always be a Hyrule Loach.\\n\\n", + "ALLOWS_LINK_TO_BOUNCE": "Permite link to bounce off walls cuando linear velocity is high enough, esto es ", + "TOOLTIP_ALLOWS_LINK_TO_BOUNCE": "Permite link to bounce off walls cuando linear velocity is high enough, esto es ", + "ALLOWS_DOGS_TO_FOLLOW": "Permite dogs to follow you anywhere you go, even si tú leave the market.", + "TOOLTIP_ALLOWS_DOGS_TO_FOLLOW": "Permite dogs to follow you anywhere you go, even si tú leave the market.", + "RUPEES_REDUCE_OVER_TIME": "Rupees reduce over time, Link suffers damage when the count hits 0.", + "TOOLTIP_RUPEES_REDUCE_OVER_TIME": "Rupees reduce over time, Link suffers damage when the count hits 0.", + "INTERVAL_BETWEEN_RUPEE_REDUCTION": "Interval between Rupee reduction in Rupee Dash Mode.", + "TOOLTIP_INTERVAL_BETWEEN_RUPEE_REDUCTION": "Interval between Rupee reduction in Rupee Dash Mode.", + "CHANGES_HEART_PIECE_AND": "Changes pieza de corazón and heart contenedor functionality.\\n\\n", + "TOOLTIP_CHANGES_HEART_PIECE_AND": "Changes pieza de corazón and heart contenedor functionality.\\n\\n", + "ENABLES_ADDITIONAL_TRAP_VARIANTS": "Habilita additional trap variants.", + "TOOLTIP_ENABLES_ADDITIONAL_TRAP_VARIANTS": "Habilita additional trap variants.", + "ALLOWS_ANY_ITEM_TO": "Permite any item to be equipped, regardless of age.\\n", + "TOOLTIP_ALLOWS_ANY_ITEM_TO": "Permite any item to be equipped, regardless of age.\\n", + "ALLOWS_YOU_TO_USE": "Permite you to usar any item at any location", + "TOOLTIP_ALLOWS_YOU_TO_USE": "Permite you to usar any item at any location", + "MAKES_EVERY_TUNIC_HAVE": "Makes every túnica have the effects of every other túnica.", + "TOOLTIP_MAKES_EVERY_TUNIC_HAVE": "Makes every túnica have the effects of every other túnica.", + "PREVENTS_THE_DEKU_SHIELD": "Evitars the deku escudo from burning on contact con fire.", + "TOOLTIP_PREVENTS_THE_DEKU_SHIELD": "Evitars the deku escudo from burning on contact con fire.", + "MAKES_EVERY_SURFACE_IN": "Makes every surface in the game ganchoable.", + "TOOLTIP_MAKES_EVERY_SURFACE_IN": "Makes every surface in the game ganchoable.", + "ALLOWS_YOU_TO_WALK": "Permite you to walk through walls.", + "TOOLTIP_ALLOWS_YOU_TO_WALK": "Permite you to walk through walls.", + "HOLDING_L_MAKES_YOU": "Holding l makes you float into the air.", + "TOOLTIP_HOLDING_L_MAKES_YOU": "Holding l makes you float into the air.", + "PREVENTS_REDEADS_AND_GIBDOS": "Evitars redeads and gibdos from being able to freeze you con their scream.", + "TOOLTIP_PREVENTS_REDEADS_AND_GIBDOS": "Evitars redeads and gibdos from being able to freeze you con their scream.", + "DISABLES_SANDSTORM_EFFECT_IN": "Deshabilitars sandstorm effect in haunted wasteland.", + "TOOLTIP_DISABLES_SANDSTORM_EFFECT_IN": "Deshabilitars sandstorm effect in haunted wasteland.", + "ALLOWS_ZTARGETING_GOLD_SKULLTULAS": "Permite z-targeting gold skulltulas.", + "TOOLTIP_ALLOWS_ZTARGETING_GOLD_SKULLTULAS": "Permite z-targeting gold skulltulas.", + "PASSIVE_INFINITE_SWORD_GLITCHN": "Passive Infinite Sword Glitch\\n", + "TOOLTIP_PASSIVE_INFINITE_SWORD_GLITCHN": "Passive Infinite Sword Glitch\\n", + "GIVES_YOU_THE_GLITCHED": "Gives you the glitched damage value of the quick put away glitch.", + "TOOLTIP_GIVES_YOU_THE_GLITCHED": "Gives you the glitched damage value of the quick put away glitch.", + "CLEARS_THE_CUTSCENE_POINTER": "Clears the cutscene pointer to a value safe for wrong warps.", + "TOOLTIP_CLEARS_THE_CUTSCENE_POINTER": "Clears the cutscene pointer to a value safe for wrong warps.", + "PREVENTS_FISH_FROM_AUTOMATICALLY": "Evitars fish from automáticomatically despawning after a while cuando soltarped.", + "TOOLTIP_PREVENTS_FISH_FROM_AUTOMATICALLY": "Evitars fish from automáticomatically despawning after a while cuando soltarped.", + "PREVENTS_BUGS_FROM_AUTOMATICALLY": "Evitars bugs from automáticomatically despawning after a while cuando soltarped.", + "TOOLTIP_PREVENTS_BUGS_FROM_AUTOMATICALLY": "Evitars bugs from automáticomatically despawning after a while cuando soltarped.", + "FREEZES_THE_TIME_OF": "Freezes the time of day.", + "TOOLTIP_FREEZES_THE_TIME_OF": "Freezes the time of day.", + "SYNCS_THE_INGAME_TIME": "Syncs the in-game time with the real world time.", + "TOOLTIP_SYNCS_THE_INGAME_TIME": "Syncs the in-game time with the real world time.", + "BUTTONS_THAT_ACTIVATE_SPEED": "Buttons that activate Speed Modifier 1.\\n\\n", + "TOOLTIP_BUTTONS_THAT_ACTIVATE_SPEED": "Buttons that activate Speed Modifier 1.\\n\\n", + "F5_TO_SAVE_F6": "F5 to save, F6 to change slots, F7 to load", + "TOOLTIP_F5_TO_SAVE_F6": "F5 to save, F6 to change slots, F7 to load", + "TURNS_ON_OOT_BETA": "Turns on OoT Beta Quest. *WARNING*: This will reset your game!", + "TOOLTIP_TURNS_ON_OOT_BETA": "Turns on OoT Beta Quest. *WARNING*: This will reset your game!", + "ENABLES_THE_SEPARATE_COSMETICS": "Habilita la ventana separada cosmetics editor window.", + "TOOLTIP_ENABLES_THE_SEPARATE_COSMETICS": "Habilita la ventana separada cosmetics editor window.", + "ENABLES_THE_SEPARATE_AUDIO": "Habilita la ventana separada audio editor window.", + "TOOLTIP_ENABLES_THE_SEPARATE_AUDIO": "Habilita la ventana separada audio editor window.", + "ENABLES_THE_SEPARATE_GAMEPLAY": "Habilita la ventana separada gamereproducir stats window.", + "TOOLTIP_ENABLES_THE_SEPARATE_GAMEPLAY": "Habilita la ventana separada gamereproducir stats window.", + "ENABLES_THE_SEPARATE_TIME": "Habilita la ventana separada time splits window.", + "TOOLTIP_ENABLES_THE_SEPARATE_TIME": "Habilita la ventana separada time splits window.", + "ENABLES_THE_SEPARATE_ADDITIONAL": "Habilita la ventana separada additional timers window.", + "TOOLTIP_ENABLES_THE_SEPARATE_ADDITIONAL": "Habilita la ventana separada additional timers window.", + "REVERT_EVERY_ELEMENT_TO": "Revert every element to use their original position and no margins", + "TOOLTIP_REVERT_EVERY_ELEMENT_TO": "Revert every element to use their original position and no margins", + "USING_THIS_ALLOW_YOU": "Using this allow you move the element with General margins sliders", + "TOOLTIP_USING_THIS_ALLOW_YOU": "Using this allow you move the element with General margins sliders", + "THIS_WILL_USE_ORIGINAL": "Esto usar original intended elements position", + "TOOLTIP_THIS_WILL_USE_ORIGINAL": "Esto usar original intended elements position", + "THIS_WILL_MAKE_YOUR": "Esto make your elements follow the left side of your game window", + "TOOLTIP_THIS_WILL_MAKE_YOUR": "Esto make your elements follow the left side of your game window", + "THIS_SLIDER_IS_USED": "This slider is usard to move up and down your elements.", + "TOOLTIP_THIS_SLIDER_IS_USED": "This slider is usard to move up and down your elements.", + "THIS_WILL_SET_THE": "Esto establecer the length of a row of hearts. establecer to 0 for unlimited length.", + "TOOLTIP_THIS_WILL_SET_THE": "Esto establecer the length of a row of hearts. establecer to 0 for unlimited length.", + "THIS_WILL_USE_ENEMY": "Esto usar enemy on screen position", + "TOOLTIP_THIS_WILL_USE_ENEMY": "Esto usar enemy on screen position", + "THIS_WILL_CHANGE_THE": "Esto change the width of the salud bar", + "TOOLTIP_THIS_WILL_CHANGE_THE": "Esto change the width of the salud bar", + "MAKES_SNOW_FALL_CHANGES": "Makes snow fall, changes cofre texture colors to red and green, etc, for ", + "TOOLTIP_MAKES_SNOW_FALL_CHANGES": "Makes snow fall, changes cofre texture colors to red and green, etc, for ", + "SET_WHEN_THE_COSMETICS": "Establecer cuando the cosmetics is automáticomaticly randomized:\\n", + "TOOLTIP_SET_WHEN_THE_COSMETICS": "Establecer cuando the cosmetics is automáticomaticly randomized:\\n", + "SETS_THE_ON_SCREEN": "Establecers the on screen size of the visor de entradas", + "TOOLTIP_SETS_THE_ON_SCREEN": "Establecers the on screen size of the visor de entradas", + "SETS_THE_DESIRED_VISIBILITY": "Establecers the desired visibility behavior for the botón outline/fondo layers. usarful for ", + "TOOLTIP_SETS_THE_DESIRED_VISIBILITY": "Establecers the desired visibility behavior for the botón outline/fondo layers. usarful for ", + "SETS_THE_DISTANCE_TO": "Establecers the distance to move the analog stick in the visor de entradas. usarful for personalizado ", + "TOOLTIP_SETS_THE_DISTANCE_TO": "Establecers the distance to move the analog stick in the visor de entradas. usarful for personalizado ", + "DISPLAYS_ANALOG_STICK_ANGLE": "Mostrars analog stick angle values in the visor de entradas", + "TOOLTIP_DISPLAYS_ANALOG_STICK_ANGLE": "Mostrars analog stick angle values in the visor de entradas", + "HIGHLIGHTS_THE_ANGLE_VALUE": "Highlights the angle value text when the analog stick is at an angle that would ", + "TOOLTIP_HIGHLIGHTS_THE_ANGLE_VALUE": "Highlights the angle value text when the analog stick is at an angle that would ", + "HEALTHN_RED_WHEN_HEALTH": "Health\\n- Red when health critical (13-20% depending on max health)\\n- Yellow when ", + "TOOLTIP_HEALTHN_RED_WHEN_HEALTH": "Health\\n- Red when health critical (13-20% depending on max health)\\n- Yellow when ", + "SETS_THE_BRIGHTNESS_OF": "Establecers the brightness of controlador leds. 0% brightness = leds off.", + "TOOLTIP_SETS_THE_BRIGHTNESS_OF": "Establecers the brightness of controlador leds. 0% brightness = leds off.", + "ALLOWS_FOR_AIMING_WITH": "Permite for aiming con the right stick in:\\n-first-person/c-up view\\n-weapon aiming", + "TOOLTIP_ALLOWS_FOR_AIMING_WITH": "Permite for aiming con the right stick in:\\n-first-person/c-up view\\n-weapon aiming", + "CHANGES_THE_LEFT_STICK": "Cambia el left stick to move the reproducirer while in first-person mode", + "TOOLTIP_CHANGES_THE_LEFT_STICK": "Cambia el left stick to move the reproducirer while in first-person mode", + "INVERTS_THE_CAMERA_X": "Inverts the Camera X Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "TOOLTIP_INVERTS_THE_CAMERA_X": "Inverts the Camera X Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "INVERTS_THE_CAMERA_Y": "Inverts the Camera Y Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "TOOLTIP_INVERTS_THE_CAMERA_Y": "Inverts the Camera Y Axis in:\\n-First-Person/C-Up view\\n-Weapon Aiming", + "INVERTS_THE_SHIELD_AIMING": "Inverts the Shield Aiming X Axis", + "TOOLTIP_INVERTS_THE_SHIELD_AIMING": "Inverts the Shield Aiming X Axis", + "PREVENTS_THE_CUP_VIEW": "Evitars the c-up view from automático-centraring, allowing for giroscopio aiming", + "TOOLTIP_PREVENTS_THE_CUP_VIEW": "Evitars the c-up view from automático-centraring, allowing for giroscopio aiming", + "THE_CURSOR_WILL_ONLY": "The cursor will only move a single space no matter cuánto tiempo a d-pad direction is held", + "TOOLTIP_THE_CURSOR_WILL_ONLY": "The cursor will only move a single space no matter cuánto tiempo a d-pad direction is held", + "ALLOWS_FOR_USING_THE": "Permite for using the mousar to control the cámara (must enable free look), ", + "TOOLTIP_ALLOWS_FOR_USING_THE": "Permite for using the mousar to control the cámara (must enable free look), ", + "WHEN_MOUSE_CONTROLS_ARE": "Cuando mousar controls are enabled, this alterna whether the program will automáticomatically ", + "TOOLTIP_WHEN_MOUSE_CONTROLS_ARE": "Cuando mousar controls are enabled, this alterna whether the program will automáticomatically ", + "NAVIGATE_CHOICES_IN_TEXT": "Navigate choices in text boxes, shop item selection, and the file select / name entry ", + "TOOLTIP_NAVIGATE_CHOICES_IN_TEXT": "Navigate choices in text boxes, shop item selection, and the file select / name entry ", + "REPLACES_FIXED_ENEMIES_THROUGHOUT": "Reemplazars fixed enemies throughout the game con a random enemy. bosses, mini-bosses and a ", + "TOOLTIP_REPLACES_FIXED_ENEMIES_THROUGHOUT": "Reemplazars fixed enemies throughout the game con a random enemy. bosses, mini-bosses and a ", + "ENEMIES_AND_BOSSES_SPAWN": "Enemies and Bosses spawn with random sizes.", + "TOOLTIP_ENEMIES_AND_BOSSES_SPAWN": "Enemies and Bosses spawn with random sizes.", + "SCALES_NORMAL_ENEMIES_HEALTH": "Scales normal enemies Health with their randomized size.\\n", + "TOOLTIP_SCALES_NORMAL_ENEMIES_HEALTH": "Scales normal enemies Health with their randomized size.\\n", + "CUSTOMIZE_WHAT_THE_NUMBERS": "Personalizadoize what the numbers under each item are tracking.", + "TOOLTIP_CUSTOMIZE_WHAT_THE_NUMBERS": "Personalizadoize what the numbers under each item are tracking.", + "CUSTOMIZE_WHAT_NUMBERS_ARE": "Personalizadoize what numbers are shown for llave tracking.", + "TOOLTIP_CUSTOMIZE_WHAT_NUMBERS_ARE": "Personalizadoize what numbers are shown for llave tracking.", + "SETS_THE_FONT_SIZE": "Establecers the font size usard in the rastreador de comprobaciones.", + "TOOLTIP_SETS_THE_FONT_SIZE": "Establecers the font size usard in the rastreador de comprobaciones.", + "IF_ENABLED_WILL_HIDE": "Si está habilitado, will ocultar area headers that have no locations matching filter", + "TOOLTIP_IF_ENABLED_WILL_HIDE": "Si está habilitado, will ocultar area headers that have no locations matching filter", + "IF_ENABLED_VANILLAMQ_DUNGEONS": "Si está habilitado, vanilla/mq mazmorras will show on the tracker immediately. ", + "TOOLTIP_IF_ENABLED_VANILLAMQ_DUNGEONS": "Si está habilitado, vanilla/mq mazmorras will show on the tracker immediately. ", + "IF_ENABLED_WILL_PREVENT": "Si está habilitado, will evitar the tracker from mostraring slots con non-shop-item shuffles.", + "TOOLTIP_IF_ENABLED_WILL_PREVENT": "Si está habilitado, will evitar the tracker from mostraring slots con non-shop-item shuffles.", + "IF_ENABLED_WILL_SHOW": "Si está habilitado, will show gs locations in the tracker regardless of tokensanity establecertings.", + "TOOLTIP_IF_ENABLED_WILL_SHOW": "Si está habilitado, will show gs locations in the tracker regardless of tokensanity establecertings.", + "SORT_ENTRANCES_BY_THE": "Sort entrances by the original source entrance", + "TOOLTIP_SORT_ENTRANCES_BY_THE": "Sort entrances by the original source entrance", + "AUTOMATICALLY_SCROLL_TO_THE": "Automáticomatically scroll to the first available entrada in the current scene", + "TOOLTIP_AUTOMATICALLY_SCROLL_TO_THE": "Automáticomatically scroll to the first available entrada in the current scene", + "HIGHLIGHT_THE_PREVIOUS_ENTRANCE": "Highlight the previous entrance that Link came from", + "TOOLTIP_HIGHLIGHT_THE_PREVIOUS_ENTRANCE": "Highlight the previous entrance that Link came from", + "HIGHLIGHT_AVAILABLE_ENTRANCES_IN": "Highlight available entrances in the current scene", + "TOOLTIP_HIGHLIGHT_AVAILABLE_ENTRANCES_IN": "Highlight available entrances in the current scene", + "COLLAPSE_UNDISCOVERED_ENTRANCES_TOWARDS": "Collapse undiscovered entrances towards the bottom of each group", + "TOOLTIP_COLLAPSE_UNDISCOVERED_ENTRANCES_TOWARDS": "Collapse undiscovered entrances towards the bottom of each group", + "HIDE_REVERSE_ENTRANCE_TRANSITIONS": "Ocultar reverse entrada transitions cuando decouple entradas is off", + "TOOLTIP_HIDE_REVERSE_ENTRANCE_TRANSITIONS": "Ocultar reverse entrada transitions cuando decouple entradas is off", + "GROUP_ENTRANCES_BY_THEIR": "Group entrances by their area", + "TOOLTIP_GROUP_ENTRANCES_BY_THEIR": "Group entrances by their area", + "REVEAL_THE_SOURCE_FOR": "Reveal the source for undiscovered entrances", + "TOOLTIP_REVEAL_THE_SOURCE_FOR": "Reveal the source for undiscovered entrances", + "REVEAL_THE_DESTINATION_FOR": "Reveal the destination for undiscovered entrances", + "TOOLTIP_REVEAL_THE_DESTINATION_FOR": "Reveal the destination for undiscovered entrances", + "WHICH_LANGUAGE_TO_LOAD": "Which language to cargar from the seleccionared text id", + "TOOLTIP_WHICH_LANGUAGE_TO_LOAD": "Which language to cargar from the seleccionared text id", + "GRABS_ACTOR_WITH_TARGET": "Grabs actor with target arrow above it. You might need C-Up for enemies", + "TOOLTIP_GRABS_ACTOR_WITH_TARGET": "Grabs actor with target arrow above it. You might need C-Up for enemies", + "GRABS_ACTOR_THAT_LINK": "Grabs actor that Link is holding", + "TOOLTIP_GRABS_ACTOR_THAT_LINK": "Grabs actor that Link is holding", + "CHANGES_THE_ACTOR_SPECIFIC": "Cambia el actor specific param menus con a direct input", + "TOOLTIP_CHANGES_THE_ACTOR_SPECIFIC": "Cambia el actor specific param menus con a direct input", + "ENCODING_USED_FOR_PLAYER": "Encoding used for Player Name", + "TOOLTIP_ENCODING_USED_FOR_PLAYER": "Encoding used for Player Name", + "CURRENT_HEALTH_16_UNITS": "Current health. 16 units per full heart", + "TOOLTIP_CURRENT_HEALTH_16_UNITS": "Current health. 16 units per full heart", + "IS_DOUBLE_DEFENSE_UNLOCKED": "Is double defense unlocked?", + "TOOLTIP_IS_DOUBLE_DEFENSE_UNLOCKED": "Is double defense unlocked?", + "CURRENT_MAGIC_48_UNITS": "Current magic. 48 units per magic level", + "TOOLTIP_CURRENT_MAGIC_48_UNITS": "Current magic. 48 units per magic level", + "TIME_OF_DAY": "Time of day", + "TOOLTIP_TIME_OF_DAY": "Time of day", + "SOUND_SETTING": "Sound setting", + "TOOLTIP_SOUND_SETTING": "Sound setting", + "WARNING_IF_YOU_SAVE": "WARNING! If you save, your file may be locked! Use caution!", + "TOOLTIP_WARNING_IF_YOU_SAVE": "WARNING! If you save, your file may be locked! Use caution!", + "USED_THE_SINKING_LURE": "Usard the sinking lure to catch it.", + "TOOLTIP_USED_THE_SINKING_LURE": "Usard the sinking lure to catch it.", + "PLAYED_AT_LEAST_ONE": "Reproducired at least one game as a child", + "TOOLTIP_PLAYED_AT_LEAST_ONE": "Reproducired at least one game as a child", + "GOT_THE_PRIZE_ITEM": "Got the prize item (Golden Scale, unless rando.)\\nUnlocks ", + "TOOLTIP_GOT_THE_PRIZE_ITEM": "Got the prize item (Golden Scale, unless rando.)\\nUnlocks ", + "RESTRICTS_ITEMS_AND_AMMO": "Restricts items and ammo to only what is possible to legally acquire in-game", + "TOOLTIP_RESTRICTS_ITEMS_AND_AMMO": "Restricts items and ammo to only what is possible to legally acquire in-game", + "CLEAR_CURRENT_SCENE_FLAGS": "Clear current scene flags. Reload scene to see changes", + "TOOLTIP_CLEAR_CURRENT_SCENE_FLAGS": "Clear current scene flags. Reload scene to see changes", + "OPEN_FLAGS_FOR_CURRENT": "Open flags for current scene", + "TOOLTIP_OPEN_FLAGS_FOR_CURRENT": "Open flags for current scene", + "RESET_TO_DEFAULT": "Reset to default", + "TOOLTIP_RESET_TO_DEFAULT": "Reset to default", + "RANDOMIZE_THIS_SOUND": "Randomize this sound", + "TOOLTIP_RANDOMIZE_THIS_SOUND": "Randomize this sound", + "RANDOMIZES_ALL_UNLOCKED_MUSIC": "Randomizes all unlocked music and sound effects across tab groups", + "TOOLTIP_RANDOMIZES_ALL_UNLOCKED_MUSIC": "Randomizes all unlocked music and sound effects across tab groups", + "RESETS_ALL_UNLOCKED_MUSIC": "Resets all unlocked music and sound effects across tab groups", + "TOOLTIP_RESETS_ALL_UNLOCKED_MUSIC": "Resets all unlocked music and sound effects across tab groups", + "LOCKS_ALL_MUSIC_AND": "Locks all music and sound effects across tab groups", + "TOOLTIP_LOCKS_ALL_MUSIC_AND": "Locks all music and sound effects across tab groups", + "UNLOCKS_ALL_MUSIC_AND": "Unlocks all music and sound effects across tab groups", + "TOOLTIP_UNLOCKS_ALL_MUSIC_AND": "Unlocks all music and sound effects across tab groups", + "DISABLE_THE_LOW_HP": "Deshabilitar the low hp beeping sonido.", + "TOOLTIP_DISABLE_THE_LOW_HP": "Deshabilitar the low hp beeping sonido.", + "DISABLES_THE_VOICE_AUDIO": "Deshabilitars the voice audio cuando navi calls you.", + "TOOLTIP_DISABLES_THE_VOICE_AUDIO": "Deshabilitars the voice audio cuando navi calls you.", + "DISABLES_THE_MUSIC_CHANGE": "Deshabilitars the music change cuando getting close to enemies. usarful for hearing ", + "TOOLTIP_DISABLES_THE_MUSIC_CHANGE": "Deshabilitars the music change cuando getting close to enemies. usarful for hearing ", + "DISABLES_THE_VOLUME_SHIFTING": "Deshabilitars the volume shifting in the lost woods. usarful for hearing ", + "TOOLTIP_DISABLES_THE_VOLUME_SHIFTING": "Deshabilitars the volume shifting in the lost woods. usarful for hearing ", + "EMITS_A_NOTIFICATION_WITH": "Emits a notification with the current song name whenever it changes. ", + "TOOLTIP_EMITS_A_NOTIFICATION_WITH": "Emits a notification with the current song name whenever it changes. ", + "WIDGET_SET_DEFAULTS": "Set defaults", + "TEXT_TRIGGER_AXIS_THRESHOLD": "Trigger axis threshold:", + "TEXT_DEADZONE": "Deadzone:", + "WIDGET_CLEAR": "Clear", + "TEXT_PRESS_ANY_BUTTONNOR_MOVE": "Press any button\\nor move any axis\\nto add rumble device", + "WIDGET_COPY_TEXT": "Copy text", + "TEXT_LED_COLOR": "LED Color:", + "WIDGET_RECALIBRATE": "Recalibrate", + "TEXT_SENSITIVITY": "Sensitivity:", + "WIDGET_CLOSE": "Close", + "WIDGET_COPY_ADDRESS": "Copy address", + "WIDGET_SUBMIT": "Submit", + "WIDGET_CANCEL": "Cancel", + "MENU_COPY_TEXT": "Copy Text", + "WIDGET_DEBUG": "Debug", + "TEXT_PRESS_ANY_BUTTONNMOVE_ANY": "Press any button,\\nmove any axis,\\nor press any key\\nto add mapping", + "TEXT_STICK_AXIS_THRESHOLD": "Stick axis threshold:", + "WIDGET_RESUME_GAME": "Resume Game", + "TEXT_TEXTURE_TO_LOAD": "Texture To Load", + "WIDGET_CLEAR_ALL": "Clear All", + "WIDGET_SCROLL_TO_PARENT": "Scroll to parent", + "TEXT_NEW_ACTOR_ROTATION": "New Actor Rotation", + "WIDGET_CLEAR_ALL_TEAM_STATE": "Clear All Team State", + "TEXT_CURRENT_ROOM": "Current Room", + "WIDGET_HYLIAN_SHIELD": "Hylian Shield", + "TEXT_PLEASE_LOAD_SPOILER_DATA": "Please Load Spoiler Data...", + "WIDGET_IRON_BOOTS": "Iron Boots", + "TEXT_OPTIONS": "Options", + "TEXT_GROUP_BY": "Group By", + "TEXT_USAGE_INSTRUCTIONS": "Usage Instructions", + "TEXT_TRACKER_HEADER_VISIBILITY": "Tracker Header Visibility", + "TEXT_GLOBAL_CONTEXT_NEEDED_FOR": "Global Context needed for actor info!", + "TEXT_ROOM_SETTINGS_ADMIN_ONLY": "Room Settings (Admin Only)", + "WIDGET_MIRROR_SHIELD": "Mirror Shield", + "WIDGET_GSSPGRAYSCALE": "gsSPGrayscale", + "TEXT_NO_FLAGS_MATCH_THE": "No flags match the current search.", + "WIDGET_ZORA_TUNIC": "Zora Tunic", + "WIDGET_CHILD": "Child", + "TEXT_ACTOR_SPECIFIC_DATA": "Actor Specific Data", + "WIDGET_DISPLAY_MESSAGEEXISTINGMESSAGE": "Display Message##ExistingMessage", + "TEXT_NO_SPOILER_LOGS_FOUND": "No Spoiler Logs found.", + "TEXT_CLEAR": "Clear", + "WIDGET_KOKIRI_BOOTS": "Kokiri Boots", + "TEXT_INCOMPATIBLE_VERSION_WILL_NOT": "Incompatible version! Will not work together!", + "TEXT_ROOM_ID": "Room ID", + "WIDGET_FISHING_POLE": "Fishing Pole", + "TEXT_RESOURCE_TYPE_IS_NOT": "Resource type is not a Display List. Please choose another.", + "WIDGET_MASTER_SWORD": "Master Sword", + "WIDGET_GSDPPIPESYNC": "gsDPPipeSync", + "TEXT_CURRENT_GAME_STATE_DOES": "Current game state does not have an active scene", + "TEXT_CONNECTION_SETTINGS": "Connection Settings", + "WIDGET_UNCATCH_ALL_CHILD": "Uncatch All (Child)", + "TEXT_ACTOR_POSITION": "Actor Position", + "WIDGET_NONE": "None", + "WIDGET_KOKIRI_SWORD": "Kokiri Sword", + "TEXT_WINDOW_OPTIONS": "Window Options", + "TEXT_NOTE_GAMEPLAY_STATS_ARE": "Note: Gameplay stats are saved to the current file and will be\\nlost if you quit without saving.", + "TEXT_GENERAL_MARGINS_SETTINGS": "General Margins Settings", + "WIDGET_CATCH_ALL_CHILD": "Catch All (Child)", + "WIDGET_UNCATCH_ALL_ADULT": "Uncatch All (Adult)", + "TEXT_PRESETS": "Presets", + "WIDGET_GSDPSETENVCOLOR": "gsDPSetEnvColor", + "TEXT_SELECT_AN_ACTOR_TO": "Select an actor to display information.", + "TEXT_WAITING_FOR_FILE_LOAD": "Waiting for file load...", + "TEXT_NO_PRESETS_FOUND": "No presets found.", + "WIDGET_GSDPSETGRAYSCALECOLOR": "gsDPSetGrayscaleColor", + "TEXT_SORT_BY": "Sort By", + "TEXT_REQUIRES_LOGIC_TURNED_ON": "Requires Logic Turned On.", + "TEXT_ERROR_DISPLAYING_DL_INSTRUCTIONS": "Error displaying DL instructions.", + "WIDGET_CATCH_ALL_ADULT": "Catch All (Adult)", + "WIDGET_CLICK_TO_REENABLE_ASPECT": "Click to reenable aspect correction.", + "TEXT_SPOILER_LOG_REWARDS": "Spoiler Log Rewards", + "TEXT_NO_SPOILER_LOG_LOADED": "No Spoiler Log Loaded", + "TEXT_START_TYPING_TO_SEE": "Start typing to see results.", + "TEXT_SELECT_LIST_TO_LOAD": "Select List to Load: ", + "TEXT_ACTOR_ROTATION": "Actor Rotation", + "TEXT_TEMP_CLEAR": "Temp Clear", + "TEXT_NEW_ACTOR_POSITION": "New Actor Position", + "TEXT_TABLE_ID": "Table ID", + "WIDGET_SET_TOKENS": "Set Tokens", + "WIDGET_HOVER_BOOTS": "Hover Boots", + "WIDGET_ADULT": "Adult", + "TEXT_SEED_MISMATCH_CONTINUING_WILL": "Seed mismatch! Continuing will break things!", + "TEXT_NO_PRESETS_WITH_RANDO": "No presets with rando options. Make some in Settings -> Presets", + "WIDGET_DEKU_SHIELD": "Deku Shield", + "TEXT_NO_ENABLED_TIMERS": "No Enabled Timers...", + "WIDGET_REQUEST_TEAM_STATE": "Request Team State", + "TEXT_HOST_PORT": "Host & Port", + "TEXT_RESOURCES": "Resources", + "WIDGET_DISPLAY_MESSAGECUSTOMMESSAGE": "Display Message##CustomMessage", + "TEXT_TEAM_ID_ITEMS_FLAGS": "Team ID (Items & Flags Shared)", + "TEXT_SPLIT_LIST_MANAGEMENT": "Split List Management", + "WIDGET_GSDPSETPRIMCOLOR": "gsDPSetPrimColor", + "TEXT_WARP_POINTS": "Warp Points", + "TEXT_CURRENT_SEED_HASH": "Current Seed Hash", + "WIDGET_KOKIRI_TUNIC": "Kokiri Tunic", + "TEXT_TEXT_ID": "Text ID", + "TEXT_LOADSAVE_SPOILER_LOG": "Load/Save Spoiler Log", + "WIDGET_GORON_TUNIC": "Goron Tunic", + "BUTTON_CANCEL": "Cancelar", + "TOOLTIP_EDIT_AXIS_THRESHOLD": "Editar umbral de eje", + "TEXT_AXIS_THRESHOLDNNTHE_EXTENT_TO": "Umbral de Eje\\n\\nLa extensión hasta la cual el joystick\\ndebe ser movido o el gatillo\\npresionado para ", + "TEXT_PRESS_ANY_BUTTONNOR_MOVE": "Presiona cualquier botón\\no mueve cualquier eje\\npara añadir dispositivo de vibración", + "TEXT_NO_ENABLED_TIMERS": "Sin Temporizadores Habilitados...", + "SEPARATOR_WARP_POINTS": "Puntos de Teletransporte", + "TOOLTIP_PLAYS_THE_BATTLE_MUSIC": "Reproduce la música de batalla cuando te acercas a un Leever, como en Majora's Mask.", + "TOOLTIP_SOME_CUSTOM_SEQUENCES_MAY": "Algunas secuencias personalizadas pueden tener notas que son demasiado altas para el audio del juego ", + "COLLAPSING_ANALOG_STICK": "Palanca Analógica", + "COLLAPSING_VISUAL_STONE_OF_AGONY": "Posición de la Piedra de la Agonía Visual", + "COLLAPSING_ENEMY_HEALTH_BAR_POSITION": "Posición de Barra de Salud de Enemigos", + "TEXT_TABLE_ID": "ID de Tabla", + "TEXT_SELECT_AN_ACTOR_TO": "Selecciona un actor para mostrar información.", + "TOOLTIP_GRABS_ACTOR_FROM": "Agarra actor desde \\\"rango de interacción\\\"", + "TEXT_ACTOR_SPECIFIC_DATA": "Datos Específicos del Actor", + "TEXT_NEW_ACTOR_POSITION": "Nueva Posición del Actor", + "TEXT_NEW_ACTOR_ROTATION": "Nueva Rotación del Actor", + "TEXT_GLOBAL_CONTEXT_NEEDED_FOR": "¡Se necesita Contexto Global para información del actor!", + "TOOLTIP_APPLIES_THE_SCENES_SHADING": "Aplica el sombreado de la escena a la visualización de colisiones.", + "TOOLTIP_MAXIMUM_HEALTH_16_UNITS": "Salud máxima. 16 unidades por corazón completo", + "TOOLTIP_TOTAL_NUMBER_OF_DAYS": "Número total de días transcurridos desde el inicio del juego", + "TOOLTIP_TOTAL_NUMBER_OF_DEATHS": "Número total de muertes", + "TOOLTIP_IS_BIGGORON_SWORD_UNLOCKED": "¿Está desbloqueada la espada de Biggoron? Reemplaza el cuchillo de Gigante", + "TOOLTIP_GIANTS_KNIFE_HEALTH_DEFAULT": "Salud del cuchillo de Gigante. Por defecto es 8. Debe ser >0 para que funcione la espada de Biggoron", + "TOOLTIP_FROM_WHICH_ENTRANCE_DID": "¿Desde qué entrada llegó Link?", + "TOOLTIP_WHICH_CUTSCENE_IS_THIS": "¿Qué escena es esta?", + "TOOLTIP_NAVI_WANTS_TO_TALK": "Navi quiere hablar a 600 unidades, decide no hacerlo a 3000.", + "TOOLTIP_HEAT_TIMER_RACE_TIMER": "Temporizador de calor, de carrera, etc. Tiene fuente blanca", + "TOOLTIP_TIME_IN_SECONDS": "Tiempo, en segundos", + "TOOLTIP_TRADE_TIMER_GANON_COLLAPSE": "Temporizador de intercambio, colapso de Ganon, etc. Tiene fuente amarilla", + "TOOLTIP_CURRENTLY_OBTAINED_TRIFORCE_PIECES": "Fragmentos de Trifuerza obtenidos actualmente. Para Caza de la Trifuerza." +} \ No newline at end of file diff --git a/libultraship/src/libultraship/window/gui/GfxDebuggerWindow.cpp b/libultraship/src/libultraship/window/gui/GfxDebuggerWindow.cpp new file mode 100644 index 000000000..d154e9b2a --- /dev/null +++ b/libultraship/src/libultraship/window/gui/GfxDebuggerWindow.cpp @@ -0,0 +1,719 @@ +#include "libultraship/window/gui/GfxDebuggerWindow.h" +#include +#include +#include "ship/Context.h" +#include "fast/debug/GfxDebugger.h" +#include +#include +#include "libultraship/bridge.h" +#include "fast/interpreter.h" +#include "fast/Fast3dWindow.h" +#include +#include "../../../../../soh/soh/Localization.h" +#ifdef GFX_DEBUG_DISASSEMBLER +#include +#endif + +using namespace Fast; + +namespace LUS { + +GfxDebuggerWindow::~GfxDebuggerWindow() { +} + +void GfxDebuggerWindow::InitElement() { +} + +void GfxDebuggerWindow::UpdateElement() { + if (mInterpreter.lock() == nullptr) { + mInterpreter = + dynamic_pointer_cast(Ship::Context::GetInstance()->GetWindow())->GetInterpreterWeak(); + } +} + +// LUSTODO handle switching ucodes +static const char* GetOpName(int8_t op) { + return GfxGetOpcodeName(op); +} + +#define C0(pos, width) ((cmd->words.w0 >> (pos)) & ((1U << width) - 1)) +#define C1(pos, width) ((cmd->words.w1 >> (pos)) & ((1U << width) - 1)) + +// static int s_dbgcnt = 0; +void GfxDebuggerWindow::DrawDisasNode(const F3DGfx* cmd, std::vector& gfxPath, + float parentPosY = 0) const { + const F3DGfx* dlStart = cmd; + auto dbg = Ship::Context::GetInstance()->GetGfxDebugger(); + + auto nodeWithText = [dbg, dlStart, parentPosY, this, &gfxPath](const F3DGfx* cmd, const std::string& text, + const F3DGfx* sub = nullptr) mutable { + gfxPath.push_back(cmd); + + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow; + if (dbg->HasBreakPoint(gfxPath)) { + flags |= ImGuiTreeNodeFlags_Selected; + } + if (sub == nullptr) { + flags |= ImGuiTreeNodeFlags_Leaf; + } + + bool scrollTo = false; + float curPosY = ImGui::GetCursorPosY(); + bool open = ImGui::TreeNodeEx((const void*)cmd, flags, "%p:%4d: %s", cmd, + (int)(((uintptr_t)cmd - (uintptr_t)dlStart) / sizeof(F3DGfx)), text.c_str()); + + if (ImGui::IsItemHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { + dbg->SetBreakPoint(gfxPath); + } + + if (ImGui::BeginPopupContextItem(nullptr, ImGuiPopupFlags_MouseButtonRight)) { + if (ImGui::Selectable("WIDGET_COPY_TEXT")) { + SDL_SetClipboardText(text.c_str()); + } + if (ImGui::Selectable("WIDGET_COPY_ADDRESS")) { + std::string address = fmt::format("0x{:x}", (uintptr_t)cmd); + SDL_SetClipboardText(address.c_str()); + } + if (parentPosY > 0) { + scrollTo = ImGui::Selectable("WIDGET_SCROLL_TO_PARENT"); + } + ImGui::EndPopup(); + } + + if (scrollTo) { + ImGui::SetScrollY(parentPosY); + } + + if (open) { + if (sub) { + DrawDisasNode(sub, gfxPath, curPosY); + } + ImGui::TreePop(); + } + gfxPath.pop_back(); + }; + + auto simpleNode = [dbg, nodeWithText](const F3DGfx* cmd, int8_t opcode) mutable { + const char* opname = GetOpName(opcode); + + if (opname) { +#ifdef GFX_DEBUG_DISASSEMBLER + size_t size = 1; + + // Texture rectangle is larger due to RDPHALF + if (opcode == RDP_G_TEXRECT || opcode == RDP_G_TEXRECTFLIP) { + size = 3; + } + + // Our Gfx uses uinptr_t for words, but libgfxd uses uint32_t, + // Copy only the first 32bits of each word into a vector before passing the instructions + std::vector input; + for (size_t i = 0; i < size; i++) { + input.push_back(cmd[i].words.w0 & 0xFFFFFFFF); + input.push_back(cmd[i].words.w1 & 0xFFFFFFFF); + } + + gfxd_input_buffer(input.data(), sizeof(uint32_t) * size * 2); + gfxd_endian(gfxd_endian_host, sizeof(uint32_t)); + char buff[512] = { 0 }; + gfxd_output_buffer(buff, sizeof(buff)); + gfxd_enable(gfxd_emit_dec_color); + + // Load the correct GBI +#if defined(F3DEX_GBI_2) + gfxd_target(gfxd_f3dex2); +#elif defined(F3DEX_GBI) + gfxd_target(gfxd_f3dex); +#else + gfxd_target(gfxd_f3d); +#endif + + gfxd_execute(); + + nodeWithText(cmd, fmt::format("{}", buff)); +#else + nodeWithText(cmd, fmt::format("{}", opname)); +#endif // GFX_DEBUG_DISASSEMBLER + } else { + int8_t opcode = (int8_t)(cmd->words.w0 >> 24); + nodeWithText(cmd, fmt::format("UNK: 0x{:X}", opcode)); + } + }; + + while (true) { + int8_t opcode = (int8_t)(cmd->words.w0 >> 24); + const F3DGfx* cmd0 = cmd; + switch (opcode) { + +#ifdef F3DEX_GBI_2 + case F3DEX2_G_NOOP: { + const char* filename = (const char*)cmd->words.w1; + uint32_t p = C0(16, 8); + uint32_t l = C0(0, 16); + + if (p == 7) { + nodeWithText(cmd0, fmt::format("gDPNoOpOpenDisp(): {}:{}", filename, l)); + } else if (p == 8) { + nodeWithText(cmd0, fmt::format("gDPNoOpCloseDisp(): {}:{}", filename, l)); + } else { + simpleNode(cmd0, opcode); + } + + cmd++; + break; + } +#endif + +#ifdef F3DEX_GBI_2 // Different opcodes, same behavior. Handle subtrees for DL calls. + case F3DEX2_G_DL: { +#else + case F3DEX_G_DL: { +#endif + F3DGfx* subGFX = (F3DGfx*)mInterpreter.lock()->SegAddr(cmd->words.w1); + if (C0(16, 1) == 0) { + nodeWithText(cmd0, fmt::format("G_DL: 0x{:x} -> {}", cmd->words.w1, (void*)subGFX), subGFX); + cmd++; + } else { + nodeWithText(cmd0, fmt::format("G_DL (branch): 0x{:x} -> {}", cmd->words.w1, (void*)subGFX), + subGFX); + return; + } + break; + } + +#ifdef F3DEX_GBI_2 // Different opcodes, same behavior. Return out of subtree. + case F3DEX2_G_ENDDL: { +#else + case F3DEX_G_ENDDL: { +#endif + simpleNode(cmd, opcode); + return; + } + + // Increment 3 times because texture rectangle uses RDPHALF + case RDP_G_TEXRECTFLIP: + case RDP_G_TEXRECT: { + simpleNode(cmd, opcode); + cmd += 3; + break; + } + + case OTR_G_MARKER: { + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + const char* dlName = ResourceGetNameByCrc(hash); + if (!dlName) { + dlName = "UNKNOWN"; + } + + nodeWithText(cmd0, fmt::format("G_MARKER: {}", dlName)); + cmd++; + break; + } + + case OTR_G_DL_OTR_HASH: { + if (C0(16, 1) == 0) { + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + const char* dlName = ResourceGetNameByCrc(hash); + if (!dlName) + dlName = "UNKNOWN"; + + F3DGfx* subGfx = (F3DGfx*)ResourceGetDataByCrc(hash); + nodeWithText(cmd0, fmt::format("G_DL_OTR_HASH: {}", dlName), subGfx); + cmd++; + } else { + assert(0 && "Invalid in gfx_pc????"); + } + break; + } + + case OTR_G_DL_OTR_FILEPATH: { + char* fileName = (char*)cmd->words.w1; + F3DGfx* subGfx = (F3DGfx*)ResourceGetDataByName((const char*)fileName); + + if (subGfx == nullptr) { + assert(0 && "in gfx_pc????"); + } + + if (C0(16, 1) == 0 && subGfx != nullptr) { + nodeWithText(cmd0, fmt::format("G_DL_OTR_HASH: {}", fileName), subGfx); + cmd++; + break; + } else { + nodeWithText(cmd0, fmt::format("G_DL_OTR_HASH (branch): {}", fileName), subGfx); + return; + } + + break; + } + + case OTR_G_DL_INDEX: { + uint8_t segNum = (uint8_t)(cmd->words.w1 >> 24); + uint32_t index = (uint32_t)(cmd->words.w1 & 0x00FFFFFF); + uintptr_t segAddr = (segNum << 24) | (index * sizeof(F3DGfx)) + 1; + F3DGfx* subGFX = (F3DGfx*)mInterpreter.lock()->SegAddr(segAddr); + + if (C0(16, 1) == 0) { + nodeWithText(cmd0, fmt::format("G_DL_INDEX: 0x{:x} -> {}", segAddr, (void*)subGFX), subGFX); + cmd++; + } else { + nodeWithText(cmd0, fmt::format("G_DL_INDEX (branch): 0x{:x} -> {}", segAddr, (void*)subGFX), + subGFX); + return; + } + + break; + } + + case OTR_G_BRANCH_Z_OTR: { + uint8_t vbidx = (uint8_t)(cmd->words.w0 & 0x00000FFF); + uint32_t zval = (uint32_t)(cmd->words.w1); + + cmd++; + + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + F3DGfx* subGfx = (F3DGfx*)ResourceGetDataByCrc(hash); + const char* dlName = ResourceGetNameByCrc(hash); + + if (!dlName) { + dlName = "UNKNOWN"; + } + + // TODO: Figure out the vertex value at the time this command would have run, since debugger display is + // not in sync with renderer execution. + // if (subGfx && (g_rsp.loaded_vertices[vbidx].z <= zval || + // (g_rsp.extra_geometry_mode & G_EX_ALWAYS_EXECUTE_BRANCH) != 0)) { + if (subGfx) { + nodeWithText(cmd0, fmt::format("G_BRANCH_Z_OTR: zval {}, vIdx {}, DL {}", zval, vbidx, dlName), + subGfx); + } else { + nodeWithText(cmd0, fmt::format("G_BRANCH_Z_OTR: zval {}, vIdx {}, DL {}", zval, vbidx, dlName)); + } + + cmd++; + break; + } + + case OTR_G_SETTIMG_OTR_HASH: { + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + const char* name = ResourceGetNameByCrc(hash); + if (!name) { + name = "UNKNOWN"; + } + + nodeWithText(cmd0, fmt::format("G_SETTIMG_OTR_HASH: {}", name)); + + cmd++; + break; + } + + case OTR_G_SETTIMG_OTR_FILEPATH: { + const char* fileName = (char*)cmd->words.w1; + nodeWithText(cmd0, fmt::format("G_SETTIMG_OTR_FILEPATH: {}", fileName)); + + cmd++; + break; + } + + case OTR_G_VTX_OTR_HASH: { + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + const char* name = ResourceGetNameByCrc(hash); + if (!name) { + name = "UNKNOWN"; + } + + nodeWithText(cmd0, fmt::format("G_VTX_OTR_HASH: {}", name)); + + cmd++; + break; + }; + + case OTR_G_VTX_OTR_FILEPATH: { + const char* fileName = (char*)cmd->words.w1; + nodeWithText(cmd0, fmt::format("G_VTX_OTR_FILEPATH: {}", fileName)); + + cmd += 2; + break; + } + + case OTR_G_MTX_OTR: { + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + const char* name = ResourceGetNameByCrc(hash); + if (!name) { + name = "UNKNOWN"; + } + + nodeWithText(cmd0, fmt::format("G_MTX_OTR: {}", name)); + + cmd++; + break; + } + + case OTR_G_MTX_OTR_FILEPATH: { + const char* fileName = (char*)cmd->words.w1; + nodeWithText(cmd0, fmt::format("G_MTX_OTR_FILEPATH: {}", fileName)); + + cmd++; + break; + } + + case OTR_G_MOVEMEM_HASH: { + const uint8_t index = C1(24, 8); + const uint8_t offset = C1(16, 8); + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + const char* name = ResourceGetNameByCrc(hash); + if (!name) { + name = "UNKNOWN"; + } + + nodeWithText(cmd0, fmt::format("G_MOVEMEM_HASH: idx {}, offset {}, {}", index, offset, name)); + + cmd++; + break; + } + + case OTR_G_IMAGERECT: { + nodeWithText(cmd0, fmt::format("G_IMAGERECT")); + cmd += 3; + break; + } + + case OTR_G_TRI1_OTR: { + nodeWithText(cmd0, fmt::format("G_TRI1_OTR: v00 {}, v01 {}, v02 {}", C0(0, 16), C1(16, 16), C1(0, 16))); + cmd++; + break; + } + + case OTR_G_PUSHCD: { + nodeWithText(cmd0, fmt::format("G_PUSHCD: filename {}", cmd->words.w1)); + cmd++; + break; + } + + case OTR_G_INVALTEXCACHE: { + const char* texAddr = (const char*)cmd->words.w1; + if (texAddr == 0) { + nodeWithText(cmd0, fmt::format("G_INVALTEXCACHE: clear all entries")); + } else { + if (((uintptr_t)texAddr & 1) == 0 && + Ship::Context::GetInstance()->GetResourceManager()->OtrSignatureCheck(texAddr)) { + nodeWithText(cmd0, fmt::format("G_INVALTEXCACHE: {}", texAddr)); + } else { + nodeWithText(cmd0, fmt::format("G_INVALTEXCACHE: 0x{:x}", (uintptr_t)texAddr)); + } + } + + cmd++; + break; + } + + case OTR_G_SETTIMG_FB: { + nodeWithText(cmd0, fmt::format("G_SETTIMG_FB: src FB {}", (int32_t)cmd->words.w1)); + cmd++; + break; + } + + case OTR_G_COPYFB: { + nodeWithText(cmd0, fmt::format("G_COPYFB: src FB {}, dest FB {}, new frames only {}", C0(0, 11), + C0(11, 11), C0(22, 1))); + cmd++; + break; + } + + case OTR_G_SETFB: { + nodeWithText(cmd0, fmt::format("G_SETFB: src FB {}", (int32_t)cmd->words.w1)); + cmd++; + break; + } + + case OTR_G_RESETFB: { + nodeWithText(cmd0, fmt::format("G_RESETFB")); + cmd++; + break; + } + + case OTR_G_READFB: { + int fbId = C0(0, 8); + bool bswap = C0(8, 1); + cmd++; + nodeWithText(cmd0, fmt::format("G_READFB: src FB {}, byteswap {}, ulx {}, uly {}, width {}, height {}", + fbId, bswap, C0(0, 16), C0(16, 16), C1(0, 16), C1(16, 16))); + cmd++; + break; + } + + case OTR_G_TEXRECT_WIDE: { + int32_t lrx = static_cast((C0(0, 24) << 8)) >> 8; + int32_t lry = static_cast((C1(0, 24) << 8)) >> 8; + int32_t tile = C1(24, 3); + cmd++; + uint32_t ulx = static_cast((C0(0, 24) << 8)) >> 8; + uint32_t uly = static_cast((C1(0, 24) << 8)) >> 8; + cmd++; + uint32_t uls = C0(16, 16); + uint32_t ult = C0(0, 16); + uint32_t dsdx = C1(16, 16); + uint32_t dtdy = C1(0, 16); + + nodeWithText( + cmd0, + fmt::format("G_TEXRECT_WIDE: ulx {}, uly {}, lrx {}, lry {}, tile {}, s {}, t {}, dsdx {}, dtdy {}", + ulx, uly, lrx, lry, uls, tile, ult, dsdx, dtdy)); + + cmd++; + break; + } + + case OTR_G_FILLWIDERECT: { + int32_t lrx = (int32_t)(C0(0, 24) << 8) >> 8; + int32_t lry = (int32_t)(C1(0, 24) << 8) >> 8; + cmd++; + int32_t ulx = (int32_t)(C0(0, 24) << 8) >> 8; + int32_t uly = (int32_t)(C1(0, 24) << 8) >> 8; + nodeWithText(cmd0, fmt::format("G_FILLWIDERECT: ulx {}, uly {}, lrx {}, lry {}", ulx, uly, lrx, lry)); + + cmd++; + break; + } + + case OTR_G_SETGRAYSCALE: { + nodeWithText(cmd0, fmt::format("G_SETGRAYSCALE: Enable {}", (uint32_t)cmd->words.w1)); + cmd++; + break; + } + + case OTR_G_SETINTENSITY: { + nodeWithText(cmd0, fmt::format("G_SETINTENSITY: red {}, green {}, blue {}, alpha {}", C1(24, 8), + C1(16, 8), C1(8, 8), C1(0, 8))); + cmd++; + break; + } + + case OTR_G_EXTRAGEOMETRYMODE: { + uint32_t setBits = (uint32_t)cmd->words.w1; + uint32_t clearBits = ~C0(0, 24); + nodeWithText(cmd0, fmt::format("G_EXTRAGEOMETRYMODE: Set {}, Clear {}", setBits, clearBits)); + cmd++; + break; + } + + case OTR_G_REGBLENDEDTEX: { + const char* timg = (const char*)cmd->words.w1; + cmd++; + + uint8_t* mask = (uint8_t*)cmd->words.w0; + uint8_t* replacementTex = (uint8_t*)cmd->words.w1; + + if (Ship::Context::GetInstance()->GetResourceManager()->OtrSignatureCheck(timg)) { + timg += 7; + nodeWithText(cmd0, fmt::format("G_REGBLENDEDTEX: src {}, mask {}, blended {}", timg, (void*)mask, + (void*)replacementTex)); + } else { + nodeWithText(cmd0, fmt::format("G_REGBLENDEDTEX: src {}, mask {}, blended {}", (void*)timg, + (void*)mask, (void*)replacementTex)); + } + + cmd++; + break; + } + + default: { + simpleNode(cmd, opcode); + cmd++; + break; + } + } + } +} + +static const char* getTexType(Fast::TextureType type) { + switch (type) { + + case Fast::TextureType::RGBA32bpp: + return "RGBA32"; + case Fast::TextureType::RGBA16bpp: + return "RGBA16"; + case Fast::TextureType::Palette4bpp: + return "CI4"; + case Fast::TextureType::Palette8bpp: + return "CI8"; + case Fast::TextureType::Grayscale4bpp: + return "I4"; + case Fast::TextureType::Grayscale8bpp: + return "I8"; + case Fast::TextureType::GrayscaleAlpha4bpp: + return "IA4"; + case Fast::TextureType::GrayscaleAlpha8bpp: + return "IA8"; + case Fast::TextureType::GrayscaleAlpha16bpp: + return "IA16"; + default: + return "UNKNOWN"; + } +} + +static bool bpEquals(const std::vector& x, const std::vector& y) { + if (x.size() != y.size()) + return false; + + for (size_t i = 0; i < x.size(); i++) { + if (x[i] != y[i]) { + return false; + } + } + + return true; +} + +// todo: __asan_on_error + +void GfxDebuggerWindow::DrawDisas() { + + auto dbg = Ship::Context::GetInstance()->GetGfxDebugger(); + auto dlist = dbg->GetDisplayList(); + ImGui::Text("dlist: %p", dlist); + std::string bp = ""; + for (auto& gfx : dbg->GetBreakPoint()) { + bp += fmt::format("/{}", (const void*)gfx); + } + ImGui::Text("BreakPoint: %s", bp.c_str()); + + bool isNew = !bpEquals(mLastBreakPoint, dbg->GetBreakPoint()); + if (isNew) { + mLastBreakPoint = dbg->GetBreakPoint(); + // fprintf(stderr, "NEW BREAKPOINT %s\n", bp.c_str()); + } + + std::string TO_LOAD_TEX = "GfxDebuggerWindowTextureToLoad"; + + const F3DGfx* cmd = dlist; + auto gui = Ship::Context::GetInstance()->GetWindow()->GetGui(); + + ImGui::BeginChild("###State", ImVec2(0.0f, 200.0f), true); + { + ImGui::BeginGroup(); + { + ImGui::Text("TEXT_DISP_STACK"); + ImGui::BeginChild("### Disp Stack", ImVec2(400.0f, 0.0f), true, ImGuiWindowFlags_HorizontalScrollbar); + for (auto& disp : g_exec_stack.disp_stack) { + ImGui::Text("%s", fmt::format("{}:{}", disp.file, disp.line).c_str()); + } + ImGui::EndChild(); + } + ImGui::EndGroup(); + ImGui::SameLine(); + + ImGui::BeginGroup(); + { + ImGui::Text("TEXT_TILES"); + ImGui::BeginChild("### Tile", ImVec2(400.0f, 0.0f), true); + // for (size_t i = 0; i < 8; i++) { + // auto& tile = g_rdp.texture_tile[i]; + // ImGui::Text( + // "%s", fmt::format("{}: fmt={}; siz={}; cms={}; cmt={};", i, tile.fmt, tile.siz, tile.cms, + // tile.cmt) + // .c_str()); + // } + + auto draw_img = [isNew, &gui](std::optional prefix, const std::string& name, + const RawTexMetadata& metadata) { + if (prefix) { + ImGui::Text("%s: %dx%d; type=%s", prefix->c_str(), metadata.width, metadata.height, + getTexType(metadata.type)); + } else { + ImGui::Text("%dx%d; type=%s", metadata.width, metadata.height, getTexType(metadata.type)); + } + + if (isNew && metadata.resource != nullptr) { + gui->UnloadTexture(name); + gui->LoadGuiTexture(name, *metadata.resource, ImVec4{ 1.0f, 1.0f, 1.0f, 1.0f }); + } + + ImGui::Image(gui->GetTextureByName(name), ImVec2{ 100.0f, 100.0f }); + }; + + ImGui::Text("TEXT_LOADED_TEXTURES"); + for (size_t i = 0; i < 2; i++) { + auto& tex = mInterpreter.lock()->mRdp->loaded_texture[i]; + // ImGui::Text("%s", fmt::format("{}: {}x{} type={}", i, tex.raw_tex_metadata.width, + // tex.raw_tex_metadata.height, getTexType(tex.raw_tex_metadata.type)) + // .c_str()); + draw_img(std::to_string(i), fmt::format("GfxDebuggerWindowLoadedTexture{}", i), tex.raw_tex_metadata); + } + ImGui::Text(LUS_LOC("TEXT_TEXTURE_TO_LOAD")); + { + auto& tex = mInterpreter.lock()->mRdp->texture_to_load; + // ImGui::Text("%s", fmt::format("{}x{} type={}", tex.raw_tex_metadata.width, + // tex.raw_tex_metadata.height, + // getTexType(tex.raw_tex_metadata.type)) + // .c_str()); + + // if (isNew && g_rdp.texture_to_load.raw_tex_metadata.resource != nullptr) { + // gui->UnloadTexture(TO_LOAD_TEX); + // gui->LoadGuiTexture(TO_LOAD_TEX, *g_rdp.texture_to_load.raw_tex_metadata.resource, + // ImVec4{ 1.0f, 1.0f, 1.0f, 1.0f }); + // } + + // ImGui::Image(gui->GetTextureByName(TO_LOAD_TEX), ImVec2{ 100.0f, 100.0f }); + draw_img(std::nullopt, TO_LOAD_TEX, tex.raw_tex_metadata); + } + ImGui::EndChild(); + } + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + { + auto showColor = [](const char* text, RGBA c) { + float cf[] = { c.r / 255.0f, c.g / 255.0f, c.b / 255.0f, c.a / 255.0f }; + ImGui::ColorEdit3(text, cf, ImGuiColorEditFlags_NoInputs); + }; + + showColor("Env Color", mInterpreter.lock()->mRdp->env_color); + showColor("Prim Color", mInterpreter.lock()->mRdp->prim_color); + showColor("Fog Color", mInterpreter.lock()->mRdp->fog_color); + showColor("Fill Color", mInterpreter.lock()->mRdp->fill_color); + showColor("Grayscale Color", mInterpreter.lock()->mRdp->grayscale_color); + } + + ImGui::EndGroup(); + } + ImGui::EndChild(); + + ImGui::BeginChild("##Disassembler", ImVec2(0.0f, 0.0f), true); + { + std::vector gfxPath; + DrawDisasNode(dlist, gfxPath); + } + ImGui::EndChild(); +} + +void GfxDebuggerWindow::DrawElement() { + auto dbg = Ship::Context::GetInstance()->GetGfxDebugger(); + // const ImVec2 pos = ImGui::GetWindowPos(); + // const ImVec2 size = ImGui::GetWindowSize(); + + if (!dbg->IsDebugging()) { + if (ImGui::Button(LUS_LOC("BUTTON_DEBUG"))) { + dbg->RequestDebugging(); + } + } else { + bool resumed = false; + if (ImGui::Button("WIDGET_RESUME_GAME")) { + dbg->ResumeGame(); + resumed = true; + } + + if (!resumed) { + DrawDisas(); + } + } +} + +} // namespace LUS diff --git a/libultraship/src/ship/window/gui/ConsoleWindow.cpp b/libultraship/src/ship/window/gui/ConsoleWindow.cpp new file mode 100644 index 000000000..7c11488a1 --- /dev/null +++ b/libultraship/src/ship/window/gui/ConsoleWindow.cpp @@ -0,0 +1,718 @@ +#include "ship/window/gui/ConsoleWindow.h" + +#include "ship/config/ConsoleVariable.h" +#include "ship/window/Window.h" +#include "ship/Context.h" +#include "ship/utils/StringHelper.h" +#include "ship/utils/Utils.h" +#include +#include "../../../../../soh/soh/Localization.h" + +namespace Ship { + +int32_t ConsoleWindow::HelpCommand(std::shared_ptr console, const std::vector& args, + std::string* output) { + if (output) { + *output += "Commands:"; + for (const auto& cmd : console->GetCommands()) { + *output += "\n - " + cmd.first + ": " + cmd.second.Description; + + if (!cmd.second.Arguments.empty()) { + *output += "\n - Arguments:"; + for (size_t i = 0; i < cmd.second.Arguments.size(); i += 1) { + const CommandArgument& argument = cmd.second.Arguments[i]; + + *output += "\n - Info=" + argument.Info; + + if (argument.Type == ArgumentType::NUMBER) { + *output += " Type=Number"; + } else if (argument.Type == ArgumentType::TEXT) { + *output += " Type=Text"; + } else { + *output += " Type=Unknown"; + } + + if (argument.Optional) { + *output += " [Optional]"; + } + } + } + } + + return 0; + } + + return 1; +} + +int32_t ConsoleWindow::ClearCommand(std::shared_ptr console, const std::vector& args, + std::string* output) { + auto window = + std::static_pointer_cast(Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console")); + if (!window) { + if (output) { + *output += "A console window is necessary for Clear"; + } + + return 1; + } + + window->ClearLogs(window->GetCurrentChannel()); + return 0; +} + +int32_t ConsoleWindow::UnbindCommand(std::shared_ptr console, const std::vector& args, + std::string* output) { + if (args.size() > 1) { + auto window = std::static_pointer_cast( + Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console")); + if (!window) { + if (output) { + *output += "A console window is necessary for Unbind"; + } + + return 1; + } + + for (int k = ImGuiKey_NamedKey_BEGIN; k < ImGuiKey_NamedKey_END; k++) { + std::string key(ImGui::GetKeyName((ImGuiKey)k)); + bool unbound = false; + + if (toLowerCase(args[1]) == toLowerCase(key)) { + if (window->mBindings.contains((ImGuiKey)k)) { + if (output) { + *output += "Unbound '" + args[1] + " from " + window->mBindings[(ImGuiKey)k]; + } + window->mBindings.erase((ImGuiKey)k); + unbound = true; + } + if (window->mBindingToggle.contains((ImGuiKey)k)) { + if (output) { + if (unbound) { + *output += "\n"; + } + *output += "Unbound toggle '" + args[1] + " from " + window->mBindingToggle[(ImGuiKey)k]; + } + window->mBindingToggle.erase((ImGuiKey)k); + unbound = true; + } + + if (!unbound) { + if (output) { + *output += "Nothing bound to '" + args[1]; + } + } + break; + } + } + } else { + if (output) { + *output += "Not enough arguments"; + } + return 1; + } + + return 0; +} + +int32_t ConsoleWindow::BindCommand(std::shared_ptr console, const std::vector& args, + std::string* output) { + if (args.size() > 2) { + auto window = std::static_pointer_cast( + Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console")); + if (!window) { + if (output) { + *output += "A console window is necessary for Bind"; + } + + return 1; + } + + for (int k = ImGuiKey_NamedKey_BEGIN; k < ImGuiKey_NamedKey_END; k++) { + std::string key(ImGui::GetKeyName((ImGuiKey)k)); + + if (toLowerCase(args[1]) == toLowerCase(key)) { + std::vector tmp; + const char* const delim = " "; + std::ostringstream imploded; + std::copy(args.begin() + 2, args.end(), std::ostream_iterator(imploded, delim)); + window->mBindings[(ImGuiKey)k] = imploded.str(); + if (output) { + *output += "Binding '" + args[1] + " to " + window->mBindings[(ImGuiKey)k]; + } + break; + } + } + } else { + if (output) { + *output += "Not enough arguments"; + } + return 1; + } + + return 0; +} + +int32_t ConsoleWindow::BindToggleCommand(std::shared_ptr console, const std::vector& args, + std::string* output) { + if (args.size() > 2) { + auto window = std::static_pointer_cast( + Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console")); + if (!window) { + if (output) { + *output += "A console window is necessary for BindToggle"; + } + + return 1; + } + + for (int k = ImGuiKey_NamedKey_BEGIN; k < ImGuiKey_NamedKey_END; k++) { + std::string key(ImGui::GetKeyName((ImGuiKey)k)); + + if (toLowerCase(args[1]) == toLowerCase(key)) { + window->mBindingToggle[(ImGuiKey)k] = args[2]; + window->SendInfoMessage("Binding toggle '%s' to %s", args[1].c_str(), + window->mBindingToggle[(ImGuiKey)k].c_str()); + break; + } + } + } else { + if (output) { + *output += "Missing arguments"; + } + return 1; + } + + return 0; +} + +#define VARTYPE_INTEGER 0 +#define VARTYPE_FLOAT 1 +#define VARTYPE_STRING 2 +#define VARTYPE_RGBA 3 + +int32_t ConsoleWindow::SetCommand(std::shared_ptr console, const std::vector& args, + std::string* output) { + if (args.size() < 3) { + if (output) { + *output += "Not enough arguments."; + } + + return 1; + } + + int vType = CheckVarType(args[2]); + + if (vType == VARTYPE_STRING) { + Ship::Context::GetInstance()->GetConsoleVariables()->SetString(args[1].c_str(), args[2].c_str()); + } else if (vType == VARTYPE_FLOAT) { + Ship::Context::GetInstance()->GetConsoleVariables()->SetFloat((char*)args[1].c_str(), std::stof(args[2])); + } else if (vType == VARTYPE_RGBA) { + uint32_t val = std::stoul(&args[2].c_str()[1], nullptr, 16); + Color_RGBA8 clr; + clr.r = val >> 24; + clr.g = val >> 16; + clr.b = val >> 8; + clr.a = val & 0xFF; + Ship::Context::GetInstance()->GetConsoleVariables()->SetColor((char*)args[1].c_str(), clr); + } else { + Ship::Context::GetInstance()->GetConsoleVariables()->SetInteger(args[1].c_str(), std::stoi(args[2])); + } + + Ship::Context::GetInstance()->GetConsoleVariables()->Save(); + + return 0; +} + +int32_t ConsoleWindow::GetCommand(std::shared_ptr console, const std::vector& args, + std::string* output) { + if (args.size() < 2) { + if (output) { + *output += "Not enough arguments."; + } + + return 1; + } + + auto cvar = Ship::Context::GetInstance()->GetConsoleVariables()->Get(args[1].c_str()); + + if (cvar != nullptr) { + if (cvar->Type == ConsoleVariableType::Integer) { + if (output) { + *output += StringHelper::Sprintf("[LUS] Variable %s is %i", args[1].c_str(), cvar->Integer); + } + } else if (cvar->Type == ConsoleVariableType::Float) { + if (output) { + *output += StringHelper::Sprintf("[LUS] Variable %s is %f", args[1].c_str(), cvar->Float); + } + } else if (cvar->Type == ConsoleVariableType::String) { + if (output) { + *output += StringHelper::Sprintf("[LUS] Variable %s is %s", args[1].c_str(), cvar->String); + } + } else if (cvar->Type == ConsoleVariableType::Color) { + if (output) { + *output += StringHelper::Sprintf("[LUS] Variable %s is %08X", args[1].c_str(), cvar->Color); + } + } else { + if (output) { + *output += StringHelper::Sprintf("[LUS] Loaded CVar with unsupported type: %s", cvar->Type); + } + return 1; + } + } else { + if (output) { + *output += StringHelper::Sprintf("[LUS] Could not find variable %s", args[1].c_str()); + } + } + + return 0; +} + +int32_t ConsoleWindow::CheckVarType(const std::string& input) { + int32_t result = VARTYPE_STRING; + + if (input[0] == '#') { + return VARTYPE_RGBA; + } + + for (size_t i = 0; i < input.size(); i++) { + if (!(std::isdigit(input[i]) || input[i] == '.')) { + break; + } else { + if (input[i] == '.') { + result = VARTYPE_FLOAT; + } else if (std::isdigit(input[i]) && result != VARTYPE_FLOAT) { + result = VARTYPE_INTEGER; + } + } + } + + return result; +} + +ConsoleWindow::~ConsoleWindow() { + SPDLOG_TRACE("destruct console window"); + delete[] mInputBuffer; + delete[] mFilterBuffer; +} + +void ConsoleWindow::InitElement() { + mInputBuffer = new char[gMaxBufferSize]; + strcpy(mInputBuffer, ""); + mFilterBuffer = new char[gMaxBufferSize]; + strcpy(mFilterBuffer, ""); + + Context::GetInstance()->GetConsole()->AddCommand( + "set", { SetCommand, + "Sets a console variable.", + { { "varName", ArgumentType::TEXT }, { "varValue", ArgumentType::TEXT } } }); + Context::GetInstance()->GetConsole()->AddCommand( + "get", { GetCommand, "Gets a console variable", { { "varName", ArgumentType::TEXT } } }); + Context::GetInstance()->GetConsole()->AddCommand("help", { HelpCommand, "Shows all the commands" }); + Context::GetInstance()->GetConsole()->AddCommand("clear", { ClearCommand, "Clear the console history" }); + Context::GetInstance()->GetConsole()->AddCommand( + "unbind", { UnbindCommand, "Unbinds a key", { { "key", ArgumentType::TEXT } } }); + Context::GetInstance()->GetConsole()->AddCommand( + "bind", + { BindCommand, "Binds key to commands", { { "key", ArgumentType::TEXT }, { "cmd", ArgumentType::TEXT } } }); + Context::GetInstance()->GetConsole()->AddCommand( + "bind-toggle", { BindToggleCommand, + "Bind key as a bool toggle", + { { "key", ArgumentType::TEXT }, { "cmd", ArgumentType::TEXT } } }); +} + +void ConsoleWindow::UpdateElement() { + for (auto [key, cmd] : mBindings) { + if (ImGui::IsKeyPressed(key)) { + Dispatch(cmd); + } + } + for (auto [key, var] : mBindingToggle) { + if (ImGui::IsKeyPressed(key)) { + Dispatch("set " + var + " " + + std::to_string(!static_cast( + Ship::Context::GetInstance()->GetConsoleVariables()->GetInteger(var.c_str(), 0)))); + } + } +} + +void ConsoleWindow::DrawElement() { + bool inputFocus = false; + const ImVec2 pos = ImGui::GetWindowPos(); + const ImVec2 size = ImGui::GetWindowSize(); + + // Renders autocomplete window + if (mOpenAutocomplete) { + auto console = Context::GetInstance()->GetConsole(); + + ImGui::SetNextWindowSize(ImVec2(350, std::min(static_cast(mAutoComplete.size()), 3) * 20.f), + ImGuiCond_Once); + ImGui::SetNextWindowPos(ImVec2(pos.x + 8, pos.y + size.y - 1)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(3, 3)); + ImGui::Begin("##WndAutocomplete", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove); + ImGui::BeginChild("AC_Child", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(.3f, .3f, .3f, 1.0f)); + if (ImGui::BeginTable("AC_History", 1)) { + for (const auto& cmd : mAutoComplete) { + std::string usage = console->BuildUsage(cmd); + std::string preview = cmd + " - " + console->GetCommand(cmd).Description; + std::string autoComplete = (usage == "None" ? cmd : usage); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + if (ImGui::Selectable(preview.c_str())) { + memset(mInputBuffer, 0, gMaxBufferSize); + memcpy(mInputBuffer, autoComplete.c_str(), sizeof(char) * autoComplete.size()); + mOpenAutocomplete = false; + inputFocus = true; + } + } + ImGui::EndTable(); + } + if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { + mOpenAutocomplete = false; + } + ImGui::PopStyleColor(); + ImGui::EndChild(); + ImGui::End(); + ImGui::PopStyleVar(); + } + + if (ImGui::BeginPopupContextWindow("Context Menu")) { + if (ImGui::MenuItem("MENU_COPY_TEXT")) { + ImGui::SetClipboardText(mLog[mCurrentChannel][mSelectedId].Text.c_str()); + mSelectedId = -1; + } + ImGui::EndPopup(); + } + if (mSelectedId != -1 && ImGui::IsMouseClicked(1)) { + ImGui::OpenPopup("##WndAutocomplete"); + } + + // Renders top bar filters + if (ImGui::Button(LUS_LOC("BUTTON_CLEAR"))) { + ClearLogs(mCurrentChannel); + } + + if (Ship::Context::GetInstance()->GetConsoleVariables()->GetInteger("gSinkEnabled", 0)) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(150); + if (ImGui::BeginCombo("##channel", mCurrentChannel.c_str())) { + for (const auto& channel : mLogChannels) { + const bool isSelected = (channel == std::string(mCurrentChannel)); + if (ImGui::Selectable(channel.c_str(), isSelected)) { + mCurrentChannel = channel; + } + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + } else { + mCurrentChannel = "Console"; + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(150); + + if (mCurrentChannel != "Console") { + if (ImGui::BeginCombo("##level", spdlog::level::to_string_view(mLevelFilter).data())) { + for (const auto& priorityFilter : mPriorityFilters) { + const bool isSelected = priorityFilter == mLevelFilter; + if (ImGui::Selectable(spdlog::level::to_string_view(priorityFilter).data(), isSelected)) { + mLevelFilter = priorityFilter; + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + } + ImGui::EndCombo(); + } + } else { + mLevelFilter = spdlog::level::trace; + } + ImGui::SameLine(); + ImGui::PushItemWidth(-1); + if (ImGui::InputTextWithHint("##input", "Filter", mFilterBuffer, gMaxBufferSize)) { + mFilter = std::string(mFilterBuffer); + } + ImGui::PopItemWidth(); + + // Renders console history + const float footerHeightToReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footerHeightToReserve), false, + ImGuiWindowFlags_HorizontalScrollbar); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(.3f, .3f, .3f, 1.0f)); + if (ImGui::BeginTable("History", 1)) { + bool focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows); + const std::vector channel = mLog[mCurrentChannel]; + + if (focused && ImGui::IsKeyPressed(ImGuiKey_DownArrow)) { + if (mSelectedId < (int32_t)channel.size() - 1) { + ++mSelectedId; + } + } + if (focused && ImGui::IsKeyPressed(ImGuiKey_UpArrow)) { + if (mSelectedId > 0) { + --mSelectedId; + } + } + + for (size_t i = 0; i < channel.size(); i++) { + ConsoleLine line = channel[i]; + if (!mFilter.empty() && line.Text.find(mFilter) == std::string::npos) { + continue; + } + if (mLevelFilter > line.Priority) { + continue; + } + std::string id = line.Text + "##" + std::to_string(i); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + const bool isSelected = + (mSelectedId == (int32_t)i) || + std::find(mSelectedEntries.begin(), mSelectedEntries.end(), i) != mSelectedEntries.end(); + ImGui::PushStyleColor(ImGuiCol_Text, mPriorityColours[line.Priority]); + if (ImGui::Selectable(id.c_str(), isSelected)) { + if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && !isSelected) { + mSelectedEntries.push_back(i); + + } else { + mSelectedEntries.clear(); + } + mSelectedId = isSelected ? -1 : i; + } + ImGui::PopStyleColor(); + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndTable(); + } + ImGui::PopStyleColor(); + if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) { + ImGui::SetScrollHereY(1.0f); + } + ImGui::EndChild(); + + if (mCurrentChannel == "Console") { + // Renders input textfield + constexpr ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackEdit | + ImGuiInputTextFlags_CallbackCompletion | + ImGuiInputTextFlags_CallbackHistory; + ImGui::PushItemWidth(-53.0f); + + float yBeforeInput = ImGui::GetCursorPosY(); + + if (ImGui::InputTextWithHint("##CMDInput", ">", mInputBuffer, gMaxBufferSize, flags, + &ConsoleWindow::CallbackStub, this)) { + inputFocus = true; + if (mInputBuffer[0] != '\0' && mInputBuffer[0] != ' ') { + Dispatch(std::string(mInputBuffer)); + } + memset(mInputBuffer, 0, gMaxBufferSize); + } + + if (mCmdHint != "None") { + if (ImGui::IsItemFocused()) { + // Place the tooltip above the console input field + ImGui::SetNextWindowPos(ImVec2(pos.x, pos.y + size.y - ((size.y - yBeforeInput) * 2))); + ImGui::SameLine(); + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(mCmdHint.c_str()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + } + + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 50); + + if (ImGui::Button("WIDGET_SUBMIT") && !inputFocus && mInputBuffer[0] != '\0' && mInputBuffer[0] != ' ') { + Dispatch(std::string(mInputBuffer)); + memset(mInputBuffer, 0, gMaxBufferSize); + } + + ImGui::SetItemDefaultFocus(); + if (inputFocus) { + ImGui::SetKeyboardFocusHere(-1); + } + ImGui::PopItemWidth(); + } +} + +void ConsoleWindow::Dispatch(const std::string& line) { + mCmdHint = "None"; + mHistoryIndex = -1; + mHistory.push_back(line); + SendInfoMessage("> " + line); + auto console = Context::GetInstance()->GetConsole(); + const std::vector cmdArgs = StringHelper::Split(line, " "); + if (console->HasCommand(cmdArgs[0])) { + const CommandEntry entry = console->GetCommand(cmdArgs[0]); + std::string output = ""; + int32_t commandResult = console->Run(line, &output); + + if (commandResult != 0) { + SendErrorMessage(StringHelper::Sprintf("[LUS] Command Failed with code %d", commandResult)); + if (!output.empty()) { + SendErrorMessage(output); + } + SendErrorMessage("[LUS] Usage: " + cmdArgs[0] + " " + console->BuildUsage(entry)); + } else { + if (!output.empty()) { + SendInfoMessage(output); + } else { + if (line != "clear") { + SendInfoMessage(std::string("[LUS] Command Success!")); + } + } + } + + return; + } + SendErrorMessage("[LUS] Command not found"); +} + +int ConsoleWindow::CallbackStub(ImGuiInputTextCallbackData* data) { + const auto instance = static_cast(data->UserData); + const bool emptyHistory = instance->mHistory.empty(); + auto console = Context::GetInstance()->GetConsole(); + std::string history; + + switch (data->EventKey) { + case ImGuiKey_Tab: + instance->mAutoComplete.clear(); + for (auto& [cmd, entry] : console->GetCommands()) { + if (cmd.find(std::string(data->Buf)) != std::string::npos) { + instance->mAutoComplete.push_back(cmd); + } + } + instance->mOpenAutocomplete = !instance->mAutoComplete.empty(); + instance->mCmdHint = "None"; + break; + case ImGuiKey_UpArrow: + if (emptyHistory) { + break; + } + if (instance->mHistoryIndex > 0) { + instance->mHistoryIndex -= 1; + } else if (instance->mHistoryIndex < 0) { + instance->mHistoryIndex = static_cast(instance->mHistory.size()) - 1; + } + data->DeleteChars(0, data->BufTextLen); + if (instance->mHistoryIndex >= 0) { + data->InsertChars(0, instance->mHistory[instance->mHistoryIndex].c_str()); + } + instance->mCmdHint = "None"; + break; + case ImGuiKey_DownArrow: + if (emptyHistory) { + break; + } + if (instance->mHistoryIndex >= 0 && + instance->mHistoryIndex < static_cast(instance->mHistory.size()) - 1) { + instance->mHistoryIndex += 1; + } else { + instance->mHistoryIndex = -1; + } + data->DeleteChars(0, data->BufTextLen); + if (instance->mHistoryIndex >= 0) { + data->InsertChars(0, instance->mHistory[instance->mHistoryIndex].c_str()); + } + instance->mCmdHint = "None"; + break; + case ImGuiKey_Escape: + instance->mHistoryIndex = -1; + data->DeleteChars(0, data->BufTextLen); + instance->mOpenAutocomplete = false; + instance->mCmdHint = "None"; + break; + default: + instance->mOpenAutocomplete = false; + for (auto& [cmd, entry] : console->GetCommands()) { + const std::vector cmdArgs = StringHelper::Split(std::string(data->Buf), " "); + if (data->BufTextLen > 2 && !cmdArgs.empty() && cmd.find(cmdArgs[0]) != std::string::npos) { + instance->mCmdHint = cmd + " " + console->BuildUsage(entry); + break; + } + instance->mCmdHint = "None"; + } + } + return 0; +} + +void ConsoleWindow::Append(const std::string& channel, spdlog::level::level_enum priority, const char* fmt, + va_list args) { + // Determine the size of the formatted string + va_list argsCopy; + va_copy(argsCopy, args); + int size = vsnprintf(nullptr, 0, fmt, argsCopy); + va_end(argsCopy); + + if (size < 0) { + SPDLOG_ERROR("Error during formatting."); + SendErrorMessage("There has been an error during formatting!"); + return; + } + + std::vector buf(size + 1); + vsnprintf(buf.data(), buf.size(), fmt, args); + + buf[buf.size() - 1] = 0; + // Do not copy the null terminator into the std::string + mLog[channel].push_back({ std::string(buf.begin(), buf.end() - 1), priority }); +} + +void ConsoleWindow::Append(const std::string& channel, spdlog::level::level_enum priority, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + Append(channel, priority, fmt, args); + va_end(args); +} + +void ConsoleWindow::SendInfoMessage(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + Append("Console", spdlog::level::info, fmt, args); + va_end(args); +} + +void ConsoleWindow::SendErrorMessage(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + Append("Console", spdlog::level::err, fmt, args); + va_end(args); +} + +void ConsoleWindow::SendInfoMessage(const std::string& str) { + Append("Console", spdlog::level::info, str.c_str()); +} + +void ConsoleWindow::SendErrorMessage(const std::string& str) { + Append("Console", spdlog::level::err, str.c_str()); +} + +void ConsoleWindow::ClearLogs(std::string channel) { + mLog[channel].clear(); + mSelectedEntries.clear(); + mSelectedId = -1; +} + +void ConsoleWindow::ClearLogs() { + for (auto [key, var] : mLog) { + var.clear(); + } + mSelectedEntries.clear(); + mSelectedId = -1; +} + +std::string ConsoleWindow::GetCurrentChannel() { + return mCurrentChannel; +} + +void ConsoleWindow::ClearBindings() { + mBindings.clear(); + mBindingToggle.clear(); +} +} // namespace Ship diff --git a/libultraship/src/ship/window/gui/InputEditorWindow.cpp b/libultraship/src/ship/window/gui/InputEditorWindow.cpp new file mode 100644 index 000000000..5d89808a5 --- /dev/null +++ b/libultraship/src/ship/window/gui/InputEditorWindow.cpp @@ -0,0 +1,1410 @@ +#include "ship/window/gui/InputEditorWindow.h" +#include "ship/Context.h" +#include "ship/window/gui/Gui.h" +#include "ship/utils/StringHelper.h" +#include "ship/config/ConsoleVariable.h" +#include "ship/controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToButtonMapping.h" +#include "ship/controller/controldeck/ControlDeck.h" +#include "libultraship/libultra/controller.h" +#include "../../../../../soh/soh/Localization.h" + +#define SCALE_IMGUI_SIZE(value) ((value / 13.0f) * ImGui::GetFontSize()) + +namespace Ship { + +InputEditorWindow::~InputEditorWindow() { + SPDLOG_TRACE("destruct input editor window"); +} + +void InputEditorWindow::InitElement() { + mGameInputBlockTimer = INT32_MAX; + mMappingInputBlockTimer = INT32_MAX; + mRumbleTimer = INT32_MAX; + mRumbleMappingToTest = nullptr; + mInputEditorPopupOpen = false; + + mButtonsBitmasks = { BTN_A, BTN_B, BTN_START, BTN_L, BTN_R, BTN_Z, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT }; + mDpadBitmasks = { BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; +} + +#define INPUT_EDITOR_WINDOW_GAME_INPUT_BLOCK_ID 95237929 +void InputEditorWindow::UpdateElement() { + if (mRumbleTimer != INT32_MAX) { + mRumbleTimer--; + if (mRumbleMappingToTest != nullptr) { + mRumbleMappingToTest->StartRumble(); + } + if (mRumbleTimer <= 0) { + if (mRumbleMappingToTest != nullptr) { + mRumbleMappingToTest->StopRumble(); + } + mRumbleTimer = INT32_MAX; + mRumbleMappingToTest = nullptr; + } + } + + if (mInputEditorPopupOpen && ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopupId)) { + Context::GetInstance()->GetControlDeck()->BlockGameInput(INPUT_EDITOR_WINDOW_GAME_INPUT_BLOCK_ID); + + // continue to block input for a third of a second after getting the mapping + mGameInputBlockTimer = ImGui::GetIO().Framerate / 3; + + if (mMappingInputBlockTimer != INT32_MAX) { + mMappingInputBlockTimer--; + if (mMappingInputBlockTimer <= 0) { + mMappingInputBlockTimer = INT32_MAX; + } + } + + Context::GetInstance()->GetWindow()->GetGui()->BlockGamepadNavigation(); + } else { + if (mGameInputBlockTimer != INT32_MAX) { + mGameInputBlockTimer--; + if (mGameInputBlockTimer <= 0) { + Context::GetInstance()->GetControlDeck()->UnblockGameInput(INPUT_EDITOR_WINDOW_GAME_INPUT_BLOCK_ID); + mGameInputBlockTimer = INT32_MAX; + } + } + + if (Context::GetInstance()->GetWindow()->GetGui()->GamepadNavigationEnabled()) { + mMappingInputBlockTimer = ImGui::GetIO().Framerate / 3; + } else { + mMappingInputBlockTimer = INT32_MAX; + } + + Context::GetInstance()->GetWindow()->GetGui()->UnblockGamepadNavigation(); + } +} + +void InputEditorWindow::DrawAnalogPreview(const char* label, ImVec2 stick, float deadzone, bool gyro) { + ImGui::BeginChild(label, ImVec2(gyro ? SCALE_IMGUI_SIZE(78) : SCALE_IMGUI_SIZE(96), SCALE_IMGUI_SIZE(85)), false); + ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + gyro ? SCALE_IMGUI_SIZE(10) : SCALE_IMGUI_SIZE(18), + ImGui::GetCursorPos().y + gyro ? SCALE_IMGUI_SIZE(10) : 0)); + ImDrawList* drawList = ImGui::GetWindowDrawList(); + + const ImVec2 cursorScreenPosition = ImGui::GetCursorScreenPos(); + + // Draw the border box + float borderSquareLeft = cursorScreenPosition.x + SCALE_IMGUI_SIZE(2.0f); + float borderSquareTop = cursorScreenPosition.y + SCALE_IMGUI_SIZE(2.0f); + float borderSquareSize = SCALE_IMGUI_SIZE(65.0f); + drawList->AddRect(ImVec2(borderSquareLeft, borderSquareTop), + ImVec2(borderSquareLeft + borderSquareSize, borderSquareTop + borderSquareSize), + ImColor(100, 100, 100, 255), 0.0f, 0, 1.5f); + + // Draw the gate background + float cardinalRadius = SCALE_IMGUI_SIZE(22.5f); + float diagonalRadius = SCALE_IMGUI_SIZE(22.5f * (69.0f / 85.0f)); + + ImVec2 joystickCenterpoint = ImVec2(cursorScreenPosition.x + cardinalRadius + SCALE_IMGUI_SIZE(12), + cursorScreenPosition.y + cardinalRadius + SCALE_IMGUI_SIZE(11)); + drawList->AddQuadFilled(joystickCenterpoint, + ImVec2(joystickCenterpoint.x - diagonalRadius, joystickCenterpoint.y + diagonalRadius), + ImVec2(joystickCenterpoint.x, joystickCenterpoint.y + cardinalRadius), + ImVec2(joystickCenterpoint.x + diagonalRadius, joystickCenterpoint.y + diagonalRadius), + ImColor(130, 130, 130, 255)); + drawList->AddQuadFilled(joystickCenterpoint, + ImVec2(joystickCenterpoint.x + diagonalRadius, joystickCenterpoint.y + diagonalRadius), + ImVec2(joystickCenterpoint.x + cardinalRadius, joystickCenterpoint.y), + ImVec2(joystickCenterpoint.x + diagonalRadius, joystickCenterpoint.y - diagonalRadius), + ImColor(130, 130, 130, 255)); + drawList->AddQuadFilled(joystickCenterpoint, + ImVec2(joystickCenterpoint.x + diagonalRadius, joystickCenterpoint.y - diagonalRadius), + ImVec2(joystickCenterpoint.x, joystickCenterpoint.y - cardinalRadius), + ImVec2(joystickCenterpoint.x - diagonalRadius, joystickCenterpoint.y - diagonalRadius), + ImColor(130, 130, 130, 255)); + drawList->AddQuadFilled(joystickCenterpoint, + ImVec2(joystickCenterpoint.x - diagonalRadius, joystickCenterpoint.y - diagonalRadius), + ImVec2(joystickCenterpoint.x - cardinalRadius, joystickCenterpoint.y), + ImVec2(joystickCenterpoint.x - diagonalRadius, joystickCenterpoint.y + diagonalRadius), + ImColor(130, 130, 130, 255)); + + // Draw the joystick position indicator + ImVec2 joystickIndicatorDistanceFromCenter = ImVec2(0, 0); + if ((stick.x * stick.x + stick.y * stick.y) > (deadzone * deadzone)) { + joystickIndicatorDistanceFromCenter = + ImVec2((stick.x * (cardinalRadius / 85.0f)), -(stick.y * (cardinalRadius / 85.0f))); + } + float indicatorRadius = SCALE_IMGUI_SIZE(5.0f); + drawList->AddCircleFilled(ImVec2(joystickCenterpoint.x + joystickIndicatorDistanceFromCenter.x, + joystickCenterpoint.y + joystickIndicatorDistanceFromCenter.y), + indicatorRadius, ImColor(34, 51, 76, 255), 7); + + if (!gyro) { + ImGui::SetCursorPos( + ImVec2(ImGui::GetCursorPos().x - SCALE_IMGUI_SIZE(8), ImGui::GetCursorPos().y + SCALE_IMGUI_SIZE(72))); + ImGui::Text("X:%3d, Y:%3d", static_cast(stick.x), static_cast(stick.y)); + } + ImGui::EndChild(); +} + +#define CHIP_COLOR_N64_GREY ImVec4(0.4f, 0.4f, 0.4f, 1.0f) +#define CHIP_COLOR_N64_BLUE ImVec4(0.176f, 0.176f, 0.5f, 1.0f) +#define CHIP_COLOR_N64_GREEN ImVec4(0.0f, 0.294f, 0.0f, 1.0f) +#define CHIP_COLOR_N64_YELLOW ImVec4(0.5f, 0.314f, 0.0f, 1.0f) +#define CHIP_COLOR_N64_RED ImVec4(0.392f, 0.0f, 0.0f, 1.0f) + +#define BUTTON_COLOR_KEYBOARD_BEIGE ImVec4(0.651f, 0.482f, 0.357f, 0.5f) +#define BUTTON_COLOR_KEYBOARD_BEIGE_HOVERED ImVec4(0.651f, 0.482f, 0.357f, 1.0f) + +#define BUTTON_COLOR_MOUSE_BEIGE ImVec4(0.5f, 0.5f, 0.5f, 0.5f) +#define BUTTON_COLOR_MOUSE_BEIGE_HOVERED ImVec4(0.5f, 0.5f, 0.5f, 1.0f) + +#define BUTTON_COLOR_GAMEPAD_BLUE ImVec4(0.0f, 0.255f, 0.976f, 0.5f) +#define BUTTON_COLOR_GAMEPAD_BLUE_HOVERED ImVec4(0.0f, 0.255f, 0.976f, 1.0f) + +#define BUTTON_COLOR_GAMEPAD_RED ImVec4(0.976f, 0.0f, 0.094f, 0.5f) +#define BUTTON_COLOR_GAMEPAD_RED_HOVERED ImVec4(0.976f, 0.0f, 0.094f, 1.0f) + +#define BUTTON_COLOR_GAMEPAD_ORANGE ImVec4(0.976f, 0.376f, 0.0f, 0.5f) +#define BUTTON_COLOR_GAMEPAD_ORANGE_HOVERED ImVec4(0.976f, 0.376f, 0.0f, 1.0f) + +#define BUTTON_COLOR_GAMEPAD_GREEN ImVec4(0.0f, 0.5f, 0.0f, 0.5f) +#define BUTTON_COLOR_GAMEPAD_GREEN_HOVERED ImVec4(0.0f, 0.5f, 0.0f, 1.0f) + +#define BUTTON_COLOR_GAMEPAD_PURPLE ImVec4(0.431f, 0.369f, 0.706f, 0.5f) +#define BUTTON_COLOR_GAMEPAD_PURPLE_HOVERED ImVec4(0.431f, 0.369f, 0.706f, 1.0f) + +void InputEditorWindow::GetButtonColorsForPhysicalDeviceType(PhysicalDeviceType lusIndex, ImVec4& buttonColor, + ImVec4& buttonHoveredColor) { + switch (lusIndex) { + case PhysicalDeviceType::Keyboard: + buttonColor = BUTTON_COLOR_KEYBOARD_BEIGE; + buttonHoveredColor = BUTTON_COLOR_KEYBOARD_BEIGE_HOVERED; + break; + case PhysicalDeviceType::Mouse: + buttonColor = BUTTON_COLOR_MOUSE_BEIGE; + buttonHoveredColor = BUTTON_COLOR_MOUSE_BEIGE_HOVERED; + break; + case PhysicalDeviceType::SDLGamepad: + buttonColor = BUTTON_COLOR_GAMEPAD_BLUE; + buttonHoveredColor = BUTTON_COLOR_GAMEPAD_BLUE_HOVERED; + break; + default: + buttonColor = BUTTON_COLOR_GAMEPAD_PURPLE; + buttonHoveredColor = BUTTON_COLOR_GAMEPAD_PURPLE_HOVERED; + } +} + +void InputEditorWindow::DrawInputChip(const char* buttonName, ImVec4 color = CHIP_COLOR_N64_GREY) { + ImGui::BeginDisabled(); + ImGui::PushStyleColor(ImGuiCol_Button, color); + ImGui::Button(buttonName, ImVec2(SCALE_IMGUI_SIZE(50.0f), 0)); + ImGui::PopStyleColor(); + ImGui::EndDisabled(); +} + +void InputEditorWindow::DrawButtonLineAddMappingButton(uint8_t port, CONTROLLERBUTTONS_T bitmask) { + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + auto popupId = StringHelper::Sprintf("addButtonMappingPopup##%d-%d", port, bitmask); + if (ImGui::Button(StringHelper::Sprintf("%s###addButtonMappingButton%d-%d", ICON_FA_PLUS, port, bitmask).c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), 0.0f))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + }; + ImGui::PopStyleVar(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text(LUS_LOC("TEXT_PRESS_ANY_BUTTONNMOVE_ANY")); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + // todo: figure out why optional params (using id = "" in the definition) wasn't working + if (mMappingInputBlockTimer == INT32_MAX && Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetButton(bitmask) + ->AddOrEditButtonMappingFromRawPress(bitmask, "")) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +void InputEditorWindow::DrawButtonLineEditMappingButton(uint8_t port, CONTROLLERBUTTONS_T bitmask, std::string id) { + auto mapping = + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetButton(bitmask)->GetButtonMappingById( + id); + if (mapping == nullptr) { + return; + } + + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); + std::string icon = ""; + switch (mapping->GetMappingType()) { + case MAPPING_TYPE_GAMEPAD: + icon = ICON_FA_GAMEPAD; + break; + case MAPPING_TYPE_KEYBOARD: + case MAPPING_TYPE_MOUSE: + icon = ICON_FA_KEYBOARD_O; + break; + case MAPPING_TYPE_UNKNOWN: + icon = ICON_FA_BUG; + break; + } + auto buttonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto buttonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + auto physicalInputDisplayName = + StringHelper::Sprintf("%s %s", icon.c_str(), mapping->GetPhysicalInputName().c_str()); + GetButtonColorsForPhysicalDeviceType(mapping->GetPhysicalDeviceType(), buttonColor, buttonHoveredColor); + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + auto popupId = StringHelper::Sprintf("editButtonMappingPopup##%s", id.c_str()); + if (ImGui::Button( + StringHelper::Sprintf("%s###editButtonMappingButton%s", physicalInputDisplayName.c_str(), id.c_str()) + .c_str(), + ImVec2(ImGui::CalcTextSize(physicalInputDisplayName.c_str()).x + SCALE_IMGUI_SIZE(12.0f), 0.0f))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + } + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) { + ImGui::SetTooltip("%s", mapping->GetPhysicalDeviceName().c_str()); + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text(LUS_LOC("TEXT_PRESS_ANY_BUTTONNMOVE_ANY")); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + if (mMappingInputBlockTimer == INT32_MAX && Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetButton(bitmask) + ->AddOrEditButtonMappingFromRawPress(bitmask, id)) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::PopStyleVar(); + ImGui::SameLine(0, 0); + + auto sdlAxisDirectionToButtonMapping = std::dynamic_pointer_cast(mapping); + if (sdlAxisDirectionToButtonMapping != nullptr) { + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); + auto buttonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto buttonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + GetButtonColorsForPhysicalDeviceType(mapping->GetPhysicalDeviceType(), buttonColor, buttonHoveredColor); + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + auto popupId = StringHelper::Sprintf("editAxisThresholdPopup##%s", id.c_str()); + if (ImGui::Button(StringHelper::Sprintf("%s###editAxisThresholdButton%s", ICON_FA_COG, id.c_str()).c_str(), + ImVec2(ImGui::CalcTextSize(ICON_FA_COG).x + SCALE_IMGUI_SIZE(10.0f), 0.0f))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + } + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) { + ImGui::SetTooltip("Edit axis threshold"); + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text("Axis Threshold\n\nThe extent to which the joystick\nmust be moved or the trigger\npressed to " + "initiate the assigned\nbutton action.\n\n"); + + auto globalSettings = Context::GetInstance()->GetControlDeck()->GetGlobalSDLDeviceSettings(); + + if (sdlAxisDirectionToButtonMapping->AxisIsStick()) { + ImGui::Text("TEXT_STICK_AXIS_THRESHOLD"); + + int32_t stickAxisThreshold = globalSettings->GetStickAxisThresholdPercentage(); + if (stickAxisThreshold == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##Stick Axis Threshold%s", id.c_str()).c_str())) { + globalSettings->SetStickAxisThresholdPercentage(stickAxisThreshold - 1); + globalSettings->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (stickAxisThreshold == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##Stick Axis Threshold%s", id.c_str()).c_str(), + &stickAxisThreshold, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp)) { + globalSettings->SetStickAxisThresholdPercentage(stickAxisThreshold); + globalSettings->SaveToConfig(); + } + ImGui::SameLine(0.0f, 0.0f); + if (stickAxisThreshold == 100) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##Stick Axis Threshold%s", id.c_str()).c_str())) { + globalSettings->SetStickAxisThresholdPercentage(stickAxisThreshold + 1); + globalSettings->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (stickAxisThreshold == 100) { + ImGui::EndDisabled(); + } + } + + if (sdlAxisDirectionToButtonMapping->AxisIsTrigger()) { + ImGui::Text("TEXT_TRIGGER_AXIS_THRESHOLD"); + + int32_t triggerAxisThreshold = globalSettings->GetTriggerAxisThresholdPercentage(); + if (triggerAxisThreshold == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##Trigger Axis Threshold%s", id.c_str()).c_str())) { + globalSettings->SetTriggerAxisThresholdPercentage(triggerAxisThreshold - 1); + globalSettings->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (triggerAxisThreshold == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##Trigger Axis Threshold%s", id.c_str()).c_str(), + &triggerAxisThreshold, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp)) { + globalSettings->SetTriggerAxisThresholdPercentage(triggerAxisThreshold); + globalSettings->SaveToConfig(); + } + ImGui::SameLine(0.0f, 0.0f); + if (triggerAxisThreshold == 100) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##Trigger Axis Threshold%s", id.c_str()).c_str())) { + globalSettings->SetTriggerAxisThresholdPercentage(triggerAxisThreshold + 1); + globalSettings->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (triggerAxisThreshold == 100) { + ImGui::EndDisabled(); + } + } + + if (ImGui::Button("WIDGET_CLOSE")) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + ImGui::PopStyleVar(); + ImGui::SameLine(0, 0); + } + + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + if (ImGui::Button(StringHelper::Sprintf("%s###removeButtonMappingButton%s", ICON_FA_TIMES, id.c_str()).c_str(), + ImVec2(ImGui::CalcTextSize(ICON_FA_TIMES).x + SCALE_IMGUI_SIZE(10.0f), 0.0f))) { + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetButton(bitmask)->ClearButtonMapping(id); + }; + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + + ImGui::SameLine(0, SCALE_IMGUI_SIZE(4.0f)); +} + +void InputEditorWindow::DrawButtonLine(const char* buttonName, uint8_t port, CONTROLLERBUTTONS_T bitmask, + ImVec4 color = CHIP_COLOR_N64_GREY) { + ImGui::NewLine(); + ImGui::SameLine(SCALE_IMGUI_SIZE(32.0f)); + DrawInputChip(buttonName, color); + ImGui::SameLine(SCALE_IMGUI_SIZE(86.0f)); + for (auto id : mBitmaskToMappingIds[port][bitmask]) { + DrawButtonLineEditMappingButton(port, bitmask, id); + } + DrawButtonLineAddMappingButton(port, bitmask); +} + +void InputEditorWindow::DrawStickDirectionLineAddMappingButton(uint8_t port, uint8_t stick, Direction direction) { + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + auto popupId = StringHelper::Sprintf("addStickDirectionMappingPopup##%d-%d-%d", port, stick, direction); + if (ImGui::Button( + StringHelper::Sprintf("%s###addStickDirectionMappingButton%d-%d-%d", ICON_FA_PLUS, port, stick, direction) + .c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), 0.0f))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + }; + ImGui::PopStyleVar(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text(LUS_LOC("TEXT_PRESS_ANY_BUTTONNMOVE_ANY")); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + if (stick == LEFT) { + if (mMappingInputBlockTimer == INT32_MAX && + Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetLeftStick() + ->AddOrEditAxisDirectionMappingFromRawPress(direction, "")) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + } else { + if (mMappingInputBlockTimer == INT32_MAX && + Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetRightStick() + ->AddOrEditAxisDirectionMappingFromRawPress(direction, "")) { + ImGui::CloseCurrentPopup(); + } + } + ImGui::EndPopup(); + } +} + +void InputEditorWindow::DrawStickDirectionLineEditMappingButton(uint8_t port, uint8_t stick, Direction direction, + std::string id) { + std::shared_ptr mapping = nullptr; + if (stick == LEFT) { + mapping = Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetLeftStick() + ->GetAxisDirectionMappingById(direction, id); + } else { + mapping = Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetRightStick() + ->GetAxisDirectionMappingById(direction, id); + } + + if (mapping == nullptr) { + return; + } + + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f)); + std::string icon = ""; + switch (mapping->GetMappingType()) { + case MAPPING_TYPE_GAMEPAD: + icon = ICON_FA_GAMEPAD; + break; + case MAPPING_TYPE_KEYBOARD: + case MAPPING_TYPE_MOUSE: + icon = ICON_FA_KEYBOARD_O; + break; + case MAPPING_TYPE_UNKNOWN: + icon = ICON_FA_BUG; + break; + } + auto buttonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto buttonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + auto physicalInputDisplayName = + StringHelper::Sprintf("%s %s", icon.c_str(), mapping->GetPhysicalInputName().c_str()); + GetButtonColorsForPhysicalDeviceType(mapping->GetPhysicalDeviceType(), buttonColor, buttonHoveredColor); + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + auto popupId = StringHelper::Sprintf("editStickDirectionMappingPopup##%s", id.c_str()); + if (ImGui::Button( + StringHelper::Sprintf("%s###editStickDirectionMappingButton%s", physicalInputDisplayName.c_str(), + id.c_str()) + .c_str(), + ImVec2(ImGui::CalcTextSize(physicalInputDisplayName.c_str()).x + SCALE_IMGUI_SIZE(12.0f), 0.0f))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + } + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) { + ImGui::SetTooltip("%s", mapping->GetPhysicalDeviceName().c_str()); + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text(LUS_LOC("TEXT_PRESS_ANY_BUTTONNMOVE_ANY")); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + + if (stick == LEFT) { + if (mMappingInputBlockTimer == INT32_MAX && + Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetLeftStick() + ->AddOrEditAxisDirectionMappingFromRawPress(direction, id)) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + } else { + if (mMappingInputBlockTimer == INT32_MAX && + Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetRightStick() + ->AddOrEditAxisDirectionMappingFromRawPress(direction, id)) { + ImGui::CloseCurrentPopup(); + } + } + ImGui::EndPopup(); + } + + ImGui::PopStyleVar(); + ImGui::SameLine(0, 0); + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + if (ImGui::Button( + StringHelper::Sprintf("%s###removeStickDirectionMappingButton%s", ICON_FA_TIMES, id.c_str()).c_str(), + ImVec2(ImGui::CalcTextSize(ICON_FA_TIMES).x + SCALE_IMGUI_SIZE(10.0f), 0.0f))) { + if (stick == LEFT) { + Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetLeftStick() + ->ClearAxisDirectionMapping(direction, id); + } else { + Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetRightStick() + ->ClearAxisDirectionMapping(direction, id); + } + }; + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + ImGui::SameLine(0, SCALE_IMGUI_SIZE(4.0f)); +} + +void InputEditorWindow::DrawStickDirectionLine(const char* axisDirectionName, uint8_t port, uint8_t stick, + Direction direction, ImVec4 color = CHIP_COLOR_N64_GREY) { + ImGui::NewLine(); + ImGui::SameLine(); + ImGui::BeginDisabled(); + ImGui::PushStyleColor(ImGuiCol_Button, color); + ImGui::Button(axisDirectionName, ImVec2(SCALE_IMGUI_SIZE(26.0f), 0)); + ImGui::PopStyleColor(); + ImGui::EndDisabled(); + ImGui::SameLine(0.0f, SCALE_IMGUI_SIZE(4.0f)); + for (auto id : mStickDirectionToMappingIds[port][stick][direction]) { + DrawStickDirectionLineEditMappingButton(port, stick, direction, id); + } + DrawStickDirectionLineAddMappingButton(port, stick, direction); +} + +void InputEditorWindow::DrawStickSection(uint8_t port, uint8_t stick, int32_t id, ImVec4 color = CHIP_COLOR_N64_GREY) { + static int8_t sX, sY; + std::shared_ptr controllerStick = nullptr; + if (stick == LEFT) { + controllerStick = Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetLeftStick(); + } else { + controllerStick = Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetRightStick(); + } + controllerStick->Process(sX, sY); + DrawAnalogPreview(StringHelper::Sprintf("##AnalogPreview%d", id).c_str(), ImVec2(sX, sY)); + + ImGui::SameLine(); + ImGui::BeginGroup(); + DrawStickDirectionLine(ICON_FA_ARROW_UP, port, stick, UP, color); + DrawStickDirectionLine(ICON_FA_ARROW_DOWN, port, stick, DOWN, color); + DrawStickDirectionLine(ICON_FA_ARROW_LEFT, port, stick, LEFT, color); + DrawStickDirectionLine(ICON_FA_ARROW_RIGHT, port, stick, RIGHT, color); + ImGui::EndGroup(); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + if (ImGui::TreeNode(StringHelper::Sprintf("Analog Stick Options##%d", id).c_str())) { + ImGui::Text("TEXT_SENSITIVITY"); + + int32_t sensitivityPercentage = controllerStick->GetSensitivityPercentage(); + if (sensitivityPercentage == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##Sensitivity%d", id).c_str())) { + controllerStick->SetSensitivity(sensitivityPercentage - 1); + } + ImGui::PopButtonRepeat(); + if (sensitivityPercentage == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##Sensitivity%d", id).c_str(), &sensitivityPercentage, 0, 200, + "%d%%", ImGuiSliderFlags_AlwaysClamp)) { + controllerStick->SetSensitivity(sensitivityPercentage); + } + ImGui::SameLine(0.0f, 0.0f); + if (sensitivityPercentage == 200) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##Sensitivity%d", id).c_str())) { + controllerStick->SetSensitivity(sensitivityPercentage + 1); + } + ImGui::PopButtonRepeat(); + if (sensitivityPercentage == 200) { + ImGui::EndDisabled(); + } + if (!controllerStick->SensitivityIsDefault()) { + ImGui::SameLine(); + if (ImGui::Button(StringHelper::Sprintf("Reset to Default###resetStickSensitivity%d", id).c_str())) { + controllerStick->ResetSensitivityToDefault(); + } + } + + ImGui::Text("TEXT_DEADZONE"); + + int32_t deadzonePercentage = controllerStick->GetDeadzonePercentage(); + if (deadzonePercentage == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##Deadzone%d", id).c_str())) { + controllerStick->SetDeadzone(deadzonePercentage - 1); + } + ImGui::PopButtonRepeat(); + if (deadzonePercentage == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##Deadzone%d", id).c_str(), &deadzonePercentage, 0, 100, "%d%%", + ImGuiSliderFlags_AlwaysClamp)) { + controllerStick->SetDeadzone(deadzonePercentage); + } + ImGui::SameLine(0.0f, 0.0f); + if (deadzonePercentage == 100) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##Deadzone%d", id).c_str())) { + controllerStick->SetDeadzone(deadzonePercentage + 1); + } + ImGui::PopButtonRepeat(); + if (deadzonePercentage == 100) { + ImGui::EndDisabled(); + } + if (!controllerStick->DeadzoneIsDefault()) { + ImGui::SameLine(); + if (ImGui::Button(StringHelper::Sprintf("Reset to Default###resetStickDeadzone%d", id).c_str())) { + controllerStick->ResetDeadzoneToDefault(); + } + } + + ImGui::Text("TEXT_NOTCH_SNAP_ANGLE"); + int32_t notchSnapAngle = controllerStick->GetNotchSnapAngle(); + if (notchSnapAngle == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##NotchProximityThreshold%d", id).c_str())) { + controllerStick->SetNotchSnapAngle(notchSnapAngle - 1); + } + ImGui::PopButtonRepeat(); + if (notchSnapAngle == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##NotchProximityThreshold%d", id).c_str(), ¬chSnapAngle, 0, 45, + "%d°", ImGuiSliderFlags_AlwaysClamp)) { + controllerStick->SetNotchSnapAngle(notchSnapAngle); + } + ImGui::SameLine(0.0f, 0.0f); + if (notchSnapAngle == 45) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##NotchProximityThreshold%d", id).c_str())) { + controllerStick->SetNotchSnapAngle(notchSnapAngle + 1); + } + ImGui::PopButtonRepeat(); + if (notchSnapAngle == 45) { + ImGui::EndDisabled(); + } + if (!controllerStick->NotchSnapAngleIsDefault()) { + ImGui::SameLine(); + if (ImGui::Button(StringHelper::Sprintf("Reset to Default###resetStickSnap%d", id).c_str())) { + controllerStick->ResetNotchSnapAngleToDefault(); + } + } + + ImGui::TreePop(); + } +} + +void InputEditorWindow::UpdateBitmaskToMappingIds(uint8_t port) { + // todo: do we need this now that ControllerButton exists? + + for (auto [bitmask, button] : + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetAllButtons()) { + for (auto [id, mapping] : button->GetAllButtonMappings()) { + // using a vector here instead of a set because i want newly added mappings + // to go to the end of the list instead of autosorting + if (std::find(mBitmaskToMappingIds[port][bitmask].begin(), mBitmaskToMappingIds[port][bitmask].end(), id) == + mBitmaskToMappingIds[port][bitmask].end()) { + mBitmaskToMappingIds[port][bitmask].push_back(id); + } + } + } +} + +void InputEditorWindow::UpdateStickDirectionToMappingIds(uint8_t port) { + // todo: do we need this? + for (auto stick : + { std::make_pair>( + LEFT, Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetLeftStick()), + std::make_pair>( + RIGHT, Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetRightStick()) }) { + for (auto direction : { LEFT, RIGHT, UP, DOWN }) { + for (auto [id, mapping] : stick.second->GetAllAxisDirectionMappingByDirection(direction)) { + // using a vector here instead of a set because i want newly added mappings + // to go to the end of the list instead of autosorting + if (std::find(mStickDirectionToMappingIds[port][stick.first][direction].begin(), + mStickDirectionToMappingIds[port][stick.first][direction].end(), + id) == mStickDirectionToMappingIds[port][stick.first][direction].end()) { + mStickDirectionToMappingIds[port][stick.first][direction].push_back(id); + } + } + } + } +} + +void InputEditorWindow::DrawRemoveRumbleMappingButton(uint8_t port, std::string id) { + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + if (ImGui::Button(StringHelper::Sprintf("%s###removeRumbleMapping%s", ICON_FA_TIMES, id.c_str()).c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), SCALE_IMGUI_SIZE(20.0f)))) { + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetRumble()->ClearRumbleMapping(id); + } + ImGui::PopStyleVar(); +} + +void InputEditorWindow::DrawAddRumbleMappingButton(uint8_t port) { + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + auto popupId = StringHelper::Sprintf("addRumbleMappingPopup##%d", port); + if (ImGui::Button(StringHelper::Sprintf("%s###addRumbleMapping%d", ICON_FA_PLUS, port).c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), SCALE_IMGUI_SIZE(20.0f)))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + } + ImGui::PopStyleVar(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text(LUS_LOC("TEXT_PRESS_ANY_BUTTONNOR_MOVE")); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + + if (mMappingInputBlockTimer == INT32_MAX && Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetRumble() + ->AddRumbleMappingFromRawPress()) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +bool InputEditorWindow::TestingRumble() { + return mRumbleTimer != INT32_MAX; +} + +void InputEditorWindow::DrawRumbleSection(uint8_t port) { + for (auto [id, mapping] : + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetRumble()->GetAllRumbleMappings()) { + ImGui::AlignTextToFramePadding(); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + auto buttonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto buttonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + GetButtonColorsForPhysicalDeviceType(mapping->GetPhysicalDeviceType(), buttonColor, buttonHoveredColor); + // begin hackaround https://github.com/ocornut/imgui/issues/282#issuecomment-123763192 + // spaces to have background color for text in a tree node + std::string spaces = ""; + for (size_t i = 0; i < mapping->GetPhysicalDeviceName().length(); i++) { + spaces += " "; + } + auto open = ImGui::TreeNode(StringHelper::Sprintf("%s###Rumble%s", spaces.c_str(), id.c_str()).c_str()); + ImGui::SameLine(); + ImGui::SetCursorPosX(SCALE_IMGUI_SIZE(30.0f)); + // end hackaround + + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::Button(mapping->GetPhysicalDeviceName().c_str()); + ImGui::PopStyleColor(); + ImGui::PopItemFlag(); + + DrawRemoveRumbleMappingButton(port, id); + ImGui::SameLine(); + if (ImGui::Button( + StringHelper::Sprintf("%s###rumbleTestButton%s", TestingRumble() ? "Stop" : "Test", id.c_str()) + .c_str())) { + if (mRumbleTimer != INT32_MAX) { + mRumbleTimer = INT32_MAX; + mRumbleMappingToTest->StopRumble(); + mRumbleMappingToTest = nullptr; + } else { + mRumbleTimer = ImGui::GetIO().Framerate; + mRumbleMappingToTest = mapping; + } + } + if (open) { + ImGui::Text("TEXT_SMALL_MOTOR_INTENSITY"); + + int32_t smallMotorIntensity = mapping->GetHighFrequencyIntensityPercentage(); + if (smallMotorIntensity == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##Small Motor Intensity%s", id.c_str()).c_str())) { + mapping->SetHighFrequencyIntensity(smallMotorIntensity - 1); + mapping->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (smallMotorIntensity == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##Small Motor Intensity%s", id.c_str()).c_str(), + &smallMotorIntensity, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp)) { + mapping->SetHighFrequencyIntensity(smallMotorIntensity); + mapping->SaveToConfig(); + } + ImGui::SameLine(0.0f, 0.0f); + if (smallMotorIntensity == 100) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##Small Motor Intensity%s", id.c_str()).c_str())) { + mapping->SetHighFrequencyIntensity(smallMotorIntensity + 1); + mapping->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (smallMotorIntensity == 100) { + ImGui::EndDisabled(); + } + if (!mapping->HighFrequencyIntensityIsDefault()) { + ImGui::SameLine(); + if (ImGui::Button(StringHelper::Sprintf("Reset to Default###resetHighFrequencyIntensity%s", id.c_str()) + .c_str())) { + mapping->ResetHighFrequencyIntensityToDefault(); + } + } + + ImGui::Text("TEXT_LARGE_MOTOR_INTENSITY"); + + int32_t largeMotorIntensity = mapping->GetLowFrequencyIntensityPercentage(); + if (largeMotorIntensity == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##Large Motor Intensity%s", id.c_str()).c_str())) { + mapping->SetLowFrequencyIntensity(largeMotorIntensity - 1); + mapping->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (largeMotorIntensity == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##Large Motor Intensity%s", id.c_str()).c_str(), + &largeMotorIntensity, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp)) { + mapping->SetLowFrequencyIntensity(largeMotorIntensity); + mapping->SaveToConfig(); + } + ImGui::SameLine(0.0f, 0.0f); + if (largeMotorIntensity == 100) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##Large Motor Intensity%s", id.c_str()).c_str())) { + mapping->SetLowFrequencyIntensity(largeMotorIntensity + 1); + mapping->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (largeMotorIntensity == 100) { + ImGui::EndDisabled(); + } + if (!mapping->LowFrequencyIntensityIsDefault()) { + ImGui::SameLine(); + if (ImGui::Button( + StringHelper::Sprintf("Reset to Default###resetLowFrequencyIntensity%s", id.c_str()).c_str())) { + mapping->ResetLowFrequencyIntensityToDefault(); + } + } + ImGui::Dummy(ImVec2(0, SCALE_IMGUI_SIZE(20))); + + ImGui::TreePop(); + } + } + + ImGui::AlignTextToFramePadding(); + ImGui::BulletText("Add rumble device"); + DrawAddRumbleMappingButton(port); +} + +void InputEditorWindow::DrawRemoveLEDMappingButton(uint8_t port, std::string id) { + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + if (ImGui::Button(StringHelper::Sprintf("%s###removeLEDMapping%s", ICON_FA_TIMES, id.c_str()).c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), SCALE_IMGUI_SIZE(20.0f)))) { + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetLED()->ClearLEDMapping(id); + } + ImGui::PopStyleVar(); +} + +void InputEditorWindow::DrawAddLEDMappingButton(uint8_t port) { + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + auto popupId = StringHelper::Sprintf("addLEDMappingPopup##%d", port); + if (ImGui::Button(StringHelper::Sprintf("%s###addLEDMapping%d", ICON_FA_PLUS, port).c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), SCALE_IMGUI_SIZE(20.0f)))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + } + ImGui::PopStyleVar(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text(LUS_LOC("TEXT_PRESS_ANY_BUTTONNOR_MOVE")); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + + if (mMappingInputBlockTimer == INT32_MAX && Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetLED() + ->AddLEDMappingFromRawPress()) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +void InputEditorWindow::DrawLEDSection(uint8_t port) { + for (auto [id, mapping] : + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetLED()->GetAllLEDMappings()) { + ImGui::AlignTextToFramePadding(); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + auto open = ImGui::TreeNode( + StringHelper::Sprintf("%s##LED%s", mapping->GetPhysicalDeviceName().c_str(), id.c_str()).c_str()); + DrawRemoveLEDMappingButton(port, id); + if (open) { + ImGui::AlignTextToFramePadding(); + ImGui::Text("TEXT_LED_COLOR"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(80.0f)); + int32_t colorSource = mapping->GetColorSource(); + if (ImGui::Combo(StringHelper::Sprintf("###ledColorSource%s", mapping->GetLEDMappingId().c_str()).c_str(), + &colorSource, "Off\0Set\0Game\0\0")) { + mapping->SetColorSource(colorSource); + }; + if (mapping->GetColorSource() == LED_COLOR_SOURCE_SET) { + ImGui::SameLine(); + ImVec4 color = { mapping->GetSavedColor().r / 255.0f, mapping->GetSavedColor().g / 255.0f, + mapping->GetSavedColor().b / 255.0f, 1.0f }; + if (ImGui::ColorEdit3( + StringHelper::Sprintf("###ledSavedColor%s", mapping->GetLEDMappingId().c_str()).c_str(), + (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) { + mapping->SetSavedColor( + Color_RGB8({ static_cast(color.x * 255.0), static_cast(color.y * 255.0), + static_cast(color.z * 255.0) })); + } + } + ImGui::TreePop(); + } + } + + ImGui::AlignTextToFramePadding(); + ImGui::BulletText("Add LED device"); + DrawAddLEDMappingButton(port); +} + +void InputEditorWindow::DrawRemoveGyroMappingButton(uint8_t port, std::string id) { + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + if (ImGui::Button(StringHelper::Sprintf("%s###removeGyroMapping%s", ICON_FA_TIMES, id.c_str()).c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), SCALE_IMGUI_SIZE(20.0f)))) { + Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetGyro()->ClearGyroMapping(); + } + ImGui::PopStyleVar(); +} + +void InputEditorWindow::DrawAddGyroMappingButton(uint8_t port) { + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(1.0f, 0.5f)); + auto popupId = StringHelper::Sprintf("addGyroMappingPopup##%d", port); + if (ImGui::Button(StringHelper::Sprintf("%s###addGyroMapping%d", ICON_FA_PLUS, port).c_str(), + ImVec2(SCALE_IMGUI_SIZE(20.0f), SCALE_IMGUI_SIZE(20.0f)))) { + ImGui::OpenPopup(popupId.c_str()); + OffsetMappingPopup(); + } + ImGui::PopStyleVar(); + + if (ImGui::BeginPopup(popupId.c_str())) { + mInputEditorPopupOpen = true; + ImGui::Text(LUS_LOC("TEXT_PRESS_ANY_BUTTONNOR_MOVE")); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + + if (mMappingInputBlockTimer == INT32_MAX && Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(port) + ->GetGyro() + ->SetGyroMappingFromRawPress()) { + mInputEditorPopupOpen = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +void InputEditorWindow::DrawGyroSection(uint8_t port) { + auto mapping = Context::GetInstance()->GetControlDeck()->GetControllerByPort(port)->GetGyro()->GetGyroMapping(); + if (mapping != nullptr) { + auto id = mapping->GetGyroMappingId(); + ImGui::AlignTextToFramePadding(); + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + ImGui::BulletText("%s", mapping->GetPhysicalDeviceName().c_str()); + DrawRemoveGyroMappingButton(port, id); + + static float sPitch, sYaw = 0.0f; + mapping->UpdatePad(sPitch, sYaw); + + ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x, ImGui::GetCursorPos().y - SCALE_IMGUI_SIZE(8))); + // to find a reasonable scaling factor gyro values + // I tried to find the maximum value reported by shaking + // a PS5 controller as hard as I could without worrying about breaking it + // the max I found for both pitch and yaw was ~21 + // the preview window expects values in an n64 analog stick range (-85 to 85) + // so I decided to multiply these by 85/21 + DrawAnalogPreview(StringHelper::Sprintf("###GyroPreview%s", id.c_str()).c_str(), + ImVec2(sYaw * (85.0f / 21.0f), sPitch * (85.0f / 21.0f)), 0.0f, true); + ImGui::SameLine(); + ImGui::SetCursorPos( + ImVec2(ImGui::GetCursorPos().x + SCALE_IMGUI_SIZE(8), ImGui::GetCursorPos().y + SCALE_IMGUI_SIZE(8))); + + ImGui::BeginGroup(); + ImGui::Text("TEXT_SENSITIVITY"); + + int32_t sensitivity = mapping->GetSensitivityPercent(); + if (sensitivity == 0) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("-##GyroSensitivity%s", id.c_str()).c_str())) { + mapping->SetSensitivity(sensitivity - 1); + mapping->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (sensitivity == 0) { + ImGui::EndDisabled(); + } + ImGui::SameLine(0.0f, 0.0f); + ImGui::SetNextItemWidth(SCALE_IMGUI_SIZE(160.0f)); + if (ImGui::SliderInt(StringHelper::Sprintf("##GyroSensitivity%s", id.c_str()).c_str(), &sensitivity, 0, 100, + "%d%%", ImGuiSliderFlags_AlwaysClamp)) { + mapping->SetSensitivity(sensitivity); + mapping->SaveToConfig(); + } + ImGui::SameLine(0.0f, 0.0f); + if (sensitivity == 100) { + ImGui::BeginDisabled(); + } + ImGui::PushButtonRepeat(true); + if (ImGui::Button(StringHelper::Sprintf("+##GyroSensitivity%s", id.c_str()).c_str())) { + mapping->SetSensitivity(sensitivity + 1); + mapping->SaveToConfig(); + } + ImGui::PopButtonRepeat(); + if (sensitivity == 100) { + ImGui::EndDisabled(); + } + + if (!mapping->SensitivityIsDefault()) { + ImGui::SameLine(); + if (ImGui::Button(StringHelper::Sprintf("Reset to Default###resetGyroSensitivity%s", id.c_str()).c_str())) { + mapping->ResetSensitivityToDefault(); + } + } + + ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x, ImGui::GetCursorPos().y + SCALE_IMGUI_SIZE(8))); + if (ImGui::Button("WIDGET_RECALIBRATE")) { + mapping->Recalibrate(); + mapping->SaveToConfig(); + } + ImGui::EndGroup(); + ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x, ImGui::GetCursorPos().y - SCALE_IMGUI_SIZE(8))); + } else { + ImGui::AlignTextToFramePadding(); + ImGui::BulletText("Add gyro device"); + DrawAddGyroMappingButton(port); + } +} + +void InputEditorWindow::DrawDeviceToggles(uint8_t portIndex) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + + auto keyboardButtonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto keyboardButtonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + GetButtonColorsForPhysicalDeviceType(PhysicalDeviceType::Keyboard, keyboardButtonColor, keyboardButtonHoveredColor); + ImGui::PushStyleColor(ImGuiCol_Button, keyboardButtonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, keyboardButtonHoveredColor); + ImGui::Button(StringHelper::Sprintf("%s Keyboard", ICON_FA_KEYBOARD_O).c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + auto mouseButtonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto mouseButtonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + GetButtonColorsForPhysicalDeviceType(PhysicalDeviceType::Mouse, mouseButtonColor, mouseButtonHoveredColor); + ImGui::PushStyleColor(ImGuiCol_Button, mouseButtonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, mouseButtonHoveredColor); + ImGui::Button(StringHelper::Sprintf("%s Mouse", ICON_FA_KEYBOARD_O).c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + + ImGui::PopItemFlag(); + + auto connectedDeviceManager = Ship::Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager(); + for (const auto& [instanceId, name] : connectedDeviceManager->GetConnectedSDLGamepadNames()) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + auto buttonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto buttonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + GetButtonColorsForPhysicalDeviceType(PhysicalDeviceType::SDLGamepad, buttonColor, buttonHoveredColor); + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + auto notIgnored = !connectedDeviceManager->PortIsIgnoringInstanceId(portIndex, instanceId); + ImGui::PopItemFlag(); + if (ImGui::Checkbox(StringHelper::Sprintf("###instanceId_%d", instanceId).c_str(), ¬Ignored)) { + if (notIgnored) { + connectedDeviceManager->UnignoreInstanceIdForPort(portIndex, instanceId); + } else { + connectedDeviceManager->IgnoreInstanceIdForPort(portIndex, instanceId); + } + }; + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::SameLine(); + ImGui::Button(StringHelper::Sprintf("%s %s (SDL)", ICON_FA_GAMEPAD, name.c_str()).c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopItemFlag(); + } +} + +void InputEditorWindow::DrawClearAllButton(uint8_t portIndex) { + if (ImGui::Button("Clear All", ImGui::CalcTextSize("Clear All") * 2)) { + ImGui::OpenPopup("Clear All##clearAllPopup"); + } + if (ImGui::BeginPopupModal("Clear All##clearAllPopup", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("This will clear all mappings for port %d.\n\nContinue?", portIndex + 1); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("WIDGET_CLEAR_ALL")) { + Context::GetInstance()->GetControlDeck()->GetControllerByPort(portIndex)->ClearAllMappings(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } +} + +void InputEditorWindow::DrawPortTab(uint8_t portIndex) { + if (ImGui::BeginTabItem(StringHelper::Sprintf("Port %d###port%d", portIndex + 1, portIndex).c_str())) { + DrawClearAllButton(portIndex); + DrawSetDefaultsButton(portIndex); + DrawDeviceToggles(portIndex); + + UpdateBitmaskToMappingIds(portIndex); + UpdateStickDirectionToMappingIds(portIndex); + + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + + if (ImGui::CollapsingHeader("Buttons", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { + DrawButtonLine("A", portIndex, BTN_A, CHIP_COLOR_N64_BLUE); + DrawButtonLine("B", portIndex, BTN_B, CHIP_COLOR_N64_GREEN); + DrawButtonLine("Start", portIndex, BTN_START, CHIP_COLOR_N64_RED); + DrawButtonLine("L", portIndex, BTN_L); + DrawButtonLine("R", portIndex, BTN_R); + DrawButtonLine("Z", portIndex, BTN_Z); + DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_UP).c_str(), portIndex, BTN_CUP, + CHIP_COLOR_N64_YELLOW); + DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_DOWN).c_str(), portIndex, BTN_CDOWN, + CHIP_COLOR_N64_YELLOW); + DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_LEFT).c_str(), portIndex, BTN_CLEFT, + CHIP_COLOR_N64_YELLOW); + DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_RIGHT).c_str(), portIndex, BTN_CRIGHT, + CHIP_COLOR_N64_YELLOW); + } + + if (ImGui::CollapsingHeader("D-Pad", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { + DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_UP).c_str(), portIndex, BTN_DUP); + DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_DOWN).c_str(), portIndex, BTN_DDOWN); + DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_LEFT).c_str(), portIndex, BTN_DLEFT); + DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_RIGHT).c_str(), portIndex, BTN_DRIGHT); + } + + if (ImGui::CollapsingHeader("Analog Stick", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { + DrawStickSection(portIndex, LEFT, 0); + } + + if (ImGui::CollapsingHeader("Additional (\"Right\") Stick")) { + DrawStickSection(portIndex, RIGHT, 1, CHIP_COLOR_N64_YELLOW); + } + + if (ImGui::CollapsingHeader("Rumble")) { + DrawRumbleSection(portIndex); + } + + if (ImGui::CollapsingHeader("Gyro")) { + DrawGyroSection(portIndex); + } + + if (ImGui::CollapsingHeader("LEDs")) { + DrawLEDSection(portIndex); + } + + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::EndTabItem(); + } +} + +void InputEditorWindow::DrawSetDefaultsButton(uint8_t portIndex) { + ImGui::SameLine(); + auto popupId = StringHelper::Sprintf("setDefaultsPopup##%d", portIndex); + if (ImGui::Button(StringHelper::Sprintf("Set Defaults##%d", portIndex).c_str(), + ImVec2(ImGui::CalcTextSize("Set Defaults") * 2))) { + ImGui::OpenPopup(popupId.c_str()); + } + + if (ImGui::BeginPopup(popupId.c_str())) { + bool shouldClose = false; + ImGui::PushStyleColor(ImGuiCol_Button, BUTTON_COLOR_KEYBOARD_BEIGE); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, BUTTON_COLOR_KEYBOARD_BEIGE_HOVERED); + if (ImGui::Button(StringHelper::Sprintf("%s Keyboard", ICON_FA_KEYBOARD_O).c_str())) { + ImGui::OpenPopup("Set Defaults for Keyboard"); + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + if (ImGui::BeginPopupModal("Set Defaults for Keyboard", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("This will clear all existing mappings for\nKeyboard on port %d.\n\nContinue?", portIndex + 1); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + shouldClose = true; + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("WIDGET_SET_DEFAULTS")) { + Context::GetInstance()->GetControlDeck()->GetControllerByPort(portIndex)->ClearAllMappingsForDeviceType( + PhysicalDeviceType::Keyboard); + Context::GetInstance()->GetControlDeck()->GetControllerByPort(portIndex)->AddDefaultMappings( + PhysicalDeviceType::Keyboard); + shouldClose = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::PushStyleColor(ImGuiCol_Button, BUTTON_COLOR_MOUSE_BEIGE); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, BUTTON_COLOR_MOUSE_BEIGE_HOVERED); + if (ImGui::Button(StringHelper::Sprintf("%s Mouse", ICON_FA_KEYBOARD_O).c_str())) { + ImGui::OpenPopup("Set Defaults for Mouse"); + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + if (ImGui::BeginPopupModal("Set Defaults for Mouse", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("This will clear all existing mappings for\nMouse on port %d.\n\nContinue?", portIndex + 1); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + shouldClose = true; + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("WIDGET_SET_DEFAULTS")) { + Context::GetInstance()->GetControlDeck()->GetControllerByPort(portIndex)->ClearAllMappingsForDeviceType( + PhysicalDeviceType::Mouse); + Context::GetInstance()->GetControlDeck()->GetControllerByPort(portIndex)->AddDefaultMappings( + PhysicalDeviceType::Mouse); + shouldClose = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + auto buttonColor = ImGui::GetStyleColorVec4(ImGuiCol_Button); + auto buttonHoveredColor = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered); + GetButtonColorsForPhysicalDeviceType(Ship::PhysicalDeviceType::SDLGamepad, buttonColor, buttonHoveredColor); + ImGui::PushStyleColor(ImGuiCol_Button, buttonColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonHoveredColor); + if (ImGui::Button(StringHelper::Sprintf("%s %s", ICON_FA_GAMEPAD, "Gamepad (SDL)").c_str())) { + ImGui::OpenPopup("Set Defaults for Gamepad (SDL)"); + } + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + if (ImGui::BeginPopupModal("Set Defaults for Gamepad (SDL)", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("This will clear all existing mappings for\nGamepad (SDL) on port %d.\n\nContinue?", + portIndex + 1); + if (ImGui::Button(LUS_LOC("BUTTON_CANCEL"))) { + shouldClose = true; + ImGui::CloseCurrentPopup(); + } + if (ImGui::Button("WIDGET_SET_DEFAULTS")) { + Ship::Context::GetInstance() + ->GetControlDeck() + ->GetControllerByPort(portIndex) + ->ClearAllMappingsForDeviceType(Ship::PhysicalDeviceType::SDLGamepad); + Ship::Context::GetInstance()->GetControlDeck()->GetControllerByPort(portIndex)->AddDefaultMappings( + Ship::PhysicalDeviceType::SDLGamepad); + shouldClose = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + if (ImGui::Button("WIDGET_CANCEL") || shouldClose) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + +void InputEditorWindow::DrawElement() { + ImGui::BeginTabBar("##ControllerConfigPortTabs"); + for (uint8_t i = 0; i < 4; i++) { + DrawPortTab(i); + } + ImGui::EndTabBar(); +} + +void InputEditorWindow::OffsetMappingPopup() { + const float HORIZONTAL_OFFSET = 10.0f; + ImVec2 pos = ImGui::GetMousePos(); + pos.x += HORIZONTAL_OFFSET; + ImGui::SetNextWindowPos(pos); +} +} // namespace Ship diff --git a/soh/soh/Localization.cpp b/soh/soh/Localization.cpp new file mode 100644 index 000000000..38cdf952e --- /dev/null +++ b/soh/soh/Localization.cpp @@ -0,0 +1,44 @@ +#include "Localization.h" +#include +#include +#include +#include + +namespace LUS { + Localization* Localization::mInstance = nullptr; + + Localization* Localization::GetInstance() { + if (mInstance == nullptr) { + mInstance = new Localization(); + } + return mInstance; + } + + void Localization::LoadLanguage(const std::string& langCode) { + std::string fileName = "languages/" + langCode + ".json"; + + SPDLOG_INFO("[Localization] Loading language: {}", langCode); + + std::ifstream f(fileName); + if (!f.is_open()) { + SPDLOG_ERROR("[Localization] Could not open language file: {}", fileName); + return; + } + + try { + nlohmann::json data = nlohmann::json::parse(f); + mTranslations = data.get>(); + mCurrentLang = langCode; + } catch (const std::exception& e) { + std::cerr << "Error parsing language JSON: " << e.what() << std::endl; + } + } + + const char* Localization::Get(const std::string& key) { + auto it = mTranslations.find(key); + if (it != mTranslations.end()) { + return it->second.c_str(); + } + return key.c_str(); + } +} diff --git a/soh/soh/Localization.h b/soh/soh/Localization.h new file mode 100644 index 000000000..0577e8475 --- /dev/null +++ b/soh/soh/Localization.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include + +namespace LUS { + class Localization { + public: + static Localization* GetInstance(); + + // Carga el idioma seleccionado desde un archivo JSON + void LoadLanguage(const std::string& langCode); + + // Obtiene el texto traducido. Si no existe la clave, devuelve la clave misma. + // Devuelve const char* para compatibilidad con funciones C-style. + const char* Get(const std::string& key); + + private: + static Localization* mInstance; + std::map mTranslations; + std::string mCurrentLang; + + Localization() = default; + }; +} + +// Macro global para facilitar el acceso en el código +#define LUS_LOC(key) LUS::Localization::GetInstance()->Get(key) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 2f40c4881..255bdcf41 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "soh/Localization.h" #include "ResourceManagerHelpers.h" #include @@ -43,7 +44,6 @@ #include #include "Enhancements/custom-message/CustomMessageManager.h" #include "util.h" -#include "Localization.h" #if not defined(__SWITCH__) && not defined(__WIIU__) #include "Extractor/Extract.h"