diff --git a/lenguajes/Espanol.json b/lenguajes/Espanol.json index 00640b3f7..1f92aec17 100644 --- a/lenguajes/Espanol.json +++ b/lenguajes/Espanol.json @@ -1,204 +1,10 @@ { "language": "Español", "strings": { - "Settings": "Configuración", - "Enhancements": "Mejoras", - "Randomizer": "Randomizer", - "Network": "Red", - "Dev Tools": "Desarrollo", - "General": "General", - "General Settings": "Configuración General", - "Graphics": "Gráficos", - "Audio": "Audio", - "Controls": "Controles", - "Input Viewer": "Visor de Entrada", - "Notifications": "Notificaciones", - "Mod Menu": "Menú de Módos", - "About": "Acerca de", - "Enabled": "Activado", - "Disabled": "Desactivado", - "On": "Activado", - "Off": "Desactivado", - "Yes": "Sí", - "No": "No", - "Apply": "Aplicar", - "Cancel": "Cancelar", - "Resolution": "Resolución", - "FPS Limit": "Límite de FPS", - "VSync": "Sincronización Vertical", - "Master Volume": "Volumen Principal", - "Master Volume: %d %%": "Volumen Principal: %d %%", - "Main Music Volume: %d %%": "Volumen de Música Principal: %d %%", - "Sub Music Volume: %d %%": "Volumen de Música Secondary: %d %%", - "Fanfare Volume: %d %%": "Volumen de Fanfarria: %d %%", - "Sound Effects Volume: %d %%": "Volumen de Efectos: %d %%", - "Music Volume": "Volumen de Música", - "SFX Volume": "Volumen de Efectos", - "Anti-aliasing (MSAA)": "Antialiasing (MSAA)", - "Menu Settings": "Configuración del Menú", - "Menu Theme": "Tema del Menú", - "Menu Controller Navigation": "Navegación con Mando", - "Allow background inputs": "Permitir entradas en segundo plano", - "Menu Background Opacity": "Opacidad del Fondo del Menú", - "General Settings": "Configuración General", - "Cursor Always Visible": "Cursor Siempre Visible", - "Search In Sidebar": "Buscar en Barra Lateral", - "Search Input Autofocus": "Autofoco en Búsqueda", - "Reset Button Combination:": "Combinación de Botón de Reseteo:", - "Open App Files Folder": "Abrir Carpeta de Archivos", - "Boot": "Arranque", - "Boot Sequence": "Secuencia de Arranque", - "Languages": "Idiomas", - "Translate Title Screen": "Traducir Pantalla de Título", - "Language": "Idioma", - "UI Translation": "Traducción de Interfaz", - "Accessibility": "Accesibilidad", - "Text to Speech": "Texto a Voz", - "Disable Idle Camera Re-Centering": "Desactivar Recentrado de Cámara", - "Disable Screen Flash for Finishing Blow": "Desactivar Flash de Pantalla", - "Disable Jabu Wobble": "Desactivar Tambaleo de Jabu", - "EXPERIMENTAL": "EXPERIMENTAL", - "ImGui Menu Scaling": "Escala del Menú ImGui", - "Ship Of Harkinian": "Ship Of Harkinian", - "Graphics Options": "Opciones de Gráficos", - "Toggle Fullscreen": "Alternar Pantalla Completa", - "Internal Resolution": "Resolución Interna", - "Current FPS": "FPS Actuales", - "Match Refresh Rate": "Coincidir Tasa de Refresco", - "Renderer API (Needs reload)": "API de Renderizado (Requiere Recarga)", - "Enable Vsync": "Activar Vsync", - "Windowed Fullscreen": "Pantalla Completa en Ventana", - "Allow multi-windows": "Permitir Multi-ventanas", - "Texture Filter (Needs reload)": "Filtro de Textura (Requiere Recarga)", - "Advanced Graphics Options": "Opciones Avanzadas de Gráficos", - "Clear Devices": "Limpiar Dispositivos", - "Controller Bindings": "Asignaciones de Mando", - "Popout Bindings Window": "Ventana de Asignaciones", - "Input Viewer Settings": "Configuración del Visor de Entrada", - "Popout Input Viewer Settings": "Ventana de Configuración", - "Position": "Posición", - "Duration (seconds):": "Duración (segundos):", - "Background Opacity": "Opacidad del Fondo", - "Size:": "Tamaño:", - "Test Notification": "Probar Notificación", - "Mute Notification Sound": "Silenciar Sonido de Notificación", - "Popout Mod Menu Window": "Ventana de Menú de Módos", - "Saving": "Guardado", - "Autosave": "Guardado Automático", - "Notification on Autosave": "Notificación de Guardado Automático", - "Remember Save Location": "Recordar Ubicación de Guardado", - "Containers Match Contents": "Contenedores Corresponden al Contenido", - "Containers of Agony": "Contenedores de Agonía", - "Time of Day": "Hora del Día", - "Nighttime GS Always Spawn": "GS Nocturnos Siempre Aparecen", - "Pull Grave During the Day": "Tumbar Durante el Día", - "Dampe Appears All Night": "Dampe Aparece Toda la Noche", - "Exit Market at Night": "Salir del Mercado de Noche", - "Shops and Games Always Open": "Tiendas y Juegos Siempre Abiertos", - "Pause Menu": "Menú de Pausa", - "Allow the Cursor to be on Any Slot": "Permitir Cursor en Cualquier Ranura", - "Pause Warp": "Teletransporte de Pausa", - "Answer Navi Prompt with L Button": "Responder a Navi con Botón L", - "Don't Require Input for Credits Sequence": "No Requiere Input para Secuencia de Créditos", - "Include Held Inputs at the Start of Pause Buffer Input Window": "Incluir Inputs Sostenidos", - "Pause Buffer Input Window: %d frames": "Ventana de Input de Pausa: %d frames", - "Simulated Input Lag: %d frames": "Lag de Input Simulado: %d frames", - "Reworked Targeting": "Cambio de Objetivo Revisado", - "Target Switch Button Combination:": "Combinación de Botón de Cambio de Objetivo:", - "Item Count Messages": "Mensajes de Cantidad de Objetos", - "Gold Skulltula Tokens": "Tokens de Skulltula de Oro", - "Pieces of Heart": "Piezas de Corazón", - "Heart Containers": "Contenedores de Corazón", - "Misc": "Varios", - "Disable Crit Wiggle": "Desactivar Crujido Crítico", - "Better Owl": "Mejor Búho", - "Convenience": "Comodidad", - "Quit Fishing at Door": "Salir de Pescar en Puerta", - "Instant Putaway": "Guardar Instantáneo", - "Navi Timer Resets on Scene Change": "Temporizador de Navi Resetea", - "Link's Cow in Both Time Periods": "Vaca de Link en Ambos Períodos", - "Play Zelda's Lullaby to Open Sleeping Waterfall": "Canción de Zelda Abre Cascada", - "Skip Feeding Jabu-Jabu": "Saltar Alimentar Jabu-Jabu", - "Cutscenes": "Escenas", - "All##Skips": "Todas##Saltos", - "None##Skips": "Ninguna##Saltos", - "Skip Intro": "Saltar Intro", - "Great Fairies": "Hadas Grandes", - "Horse": "Caballo", - "Ganon": "Ganon", - "Dampé": "Dampé", - "Title Screen": "Pantalla de Título", - "File Select": "Selección de Archivo", - "Boss Rush": "Combate de Jefes", - "Skips": "Saltos", - "Rainbow Bridge": "Puente Arcoíris", - "Bridge Requirement": "Requisito del Puente", - "Randomizer": "Randomizer", - "Enhancements": "Mejoras", - "Cheats": "Trampas", - "Randomizer Settings": "Configuración del Randomizer", - "Keyshuffle": "Mezcla de Llaves", - "Maps & Compasses": "Mapas y Brújulas", - "Small Keys": "Llaves Pequeñas", - "Boss Keys": "Llaves de Jefe", - "Skulltulas": "Skulltulas", - "Tokens": "Tokens", - "Stones": "Piedras", - "Medallions": "Medallones", - "Dungeon Items": "Objetos de Mazmorra", - "Start with Consumables": "Empezar con Consumibles", - "Start with Max Rupees": "Empezar con Rupias Máximas", - "Start with Deku Equipment": "Equipamiento Deku Inicial", - "Open Deku Tree": "Abrir Árbol Deku", - "Open Door of Time": "Abrir Puerta del Tiempo", - "Open Kak Bridge": "Abrir Puente de Kakariko", - "Open Market Entrance": "Abrir Entrada del Mercado", - "Open Castle Gate": "Abrir Puerta del Castillo", - "Network": "Red", - "Connect to Server": "Conectar al Servidor", - "Disconnect": "Desconectar", - "Server Address": "Dirección del Servidor", - "Username": "Nombre de Usuario", - "Room ID": "ID de Sala", - "Game Mode": "Modo de Juego", - "Co-op": "Cooperativo", - "Adventure": "Aventura", - "Time Sync": "Sincronización de Tiempo", - "Lag Compensation": "Compensación de Lag", - "Dev Tools": "Herramientas de Desarrollo", - "General": "General", - "Game Interaction": "Interacción del Juego", - "Visual": "Visual", - "Audio": "Audio", - "Cheats": "Trampas", - "Cosmetics": "Cosméticos", - "Restrict Debug Mode": "Restringir Modo Debug", - "Free Camera": "Cámara Libre", - "Frame Advance": "Avance de Fotograma", - "Pause Game": "Pausar Juego", - "Log Object Ages": "Registrar Edades de Objetos", - "Visual Cheats": "Trampas Visuales", - "No UI": "Sin Interfaz", - "Cheat Cheats": "Trampas de Trampas", - "Infinite Gold": "Oro Infinito", - "Infinite Health": "Salud Infinita", - "Infinite Magic": "Magia Infinita", - "Infinite Nails": "Uñas Infinitas", - "Infinite Eggs": "Huevos Infinitos", - "Infinite Arrows": "Flechas Infinitas", - "Unbreakable Umbrella": "Paraguas Irrompible", - "Cosmetics": "Cosméticos", - "Tunic Color": "Color del Túnico", - "Skin Color": "Color de Piel", - "Mirror Shield Frame": "Marco del Escudo Espejo", - "Link's Age": "Edad de Link", - "Default": "Predeterminado", - "Adult": "Adulto", - "Child": "Niño", - "Small": "Pequeño", - "Normal": "Normal", - "Large": "Grande", - "X-Large": "Extra Grande", + "English": "Inglés", + "German": "Alemán", + "French": "Francés", + "Japanese": "Japonés", "Red": "Rojo", "Dark Red": "Rojo Oscuro", "Orange": "Naranja", @@ -216,15 +22,411 @@ "Three-Point": "Tres Puntos", "Linear": "Lineal", "None": "Ninguno", - "Top Left": "Arriba Izquierda", - "Top Right": "Arriba Derecha", - "Bottom Left": "Abajo Izquierda", - "Bottom Right": "Abajo Derecha", + "Trace": "Rastro", + "Debug": "Depuración", + "Info": "Información", + "Warn": "Advertencia", + "Error": "Error", + "Critical": "Crítico", + "Off": "Desactivado", + "Top Left": "Superior Izquierda", + "Top Right": "Superior Derecha", + "Bottom Left": "Inferior Izquierda", + "Bottom Right": "Inferior Derecha", "Hidden": "Oculto", - "Default": "Predeterminado", + "Normal": "Normal", + "Unbreakable": "Inquebrantable", + "Unbreakable + Always on Fire": "Inquebrantable + Siempre en Llamas", + "Navi": "Navi", + "NPCs": "NPCs", + "All": "Todos", + "Disabled": "Desactivado", + "Junk Items": "Objetos Basura", + "All Items": "Todos los Objetos", + "Both": "Ambos", + "Texture Only": "Solo Textura", + "Size Only": "Solo Tamaño", + "Ocarina of Time": "Ocarina del Tiempo", + "Any Ocarina": "Cualquier Ocarina", + "Always": "Siempre", + "Once": "Una vez", + "Never": "Nunca", + "Vanilla (1x)": "Original (1x)", + "Double (2x)": "Doble (2x)", + "Quadruple (4x)": "Cuádruple (4x)", + "Octuple (8x)": "Óctuple (8x)", + "Foolish (16x)": "Absurdo (16x)", + "Ridiculous (32x)": "Ridículo (32x)", + "Merciless (64x)": "Cruel (64x)", + "Pure Torture (128x)": "Pura Tortura (128x)", + "OHKO (256x)": "KO (256x)", + "No Damage": "Sin Daño", + "0.25 Hearts": "0.25 Corazones", + "0.5 Hearts": "0.5 Corazones", + "1 Heart": "1 Corazón", + "2 Hearts": "2 Corazones", + "4 Hearts": "4 Corazones", + "8 Hearts": "8 Corazones", + "Only in Rando": "Solo en Rando", + "Child Toggle": "Cambiar Niño", + "Both Ages": "Ambas Edades", + "Consistent Vanish": "Desvanecer Consistente", + "No Vanish": "Sin Desvanecer", + "Always": "Siempre", + "Random": "Aleatorio", + "Random (Seeded)": "Aleatorio (Semilla)", + "Dungeons": "Mazmorras", + "Dungeons (Vanilla)": "Mazmorras (Original)", + "Dungeons (MQ)": "Mazmorras (MQ)", + "Dungeons Random": "Mazmorras Aleatorio", + "Dungeons Random (Seeded)": "Mazmorras Aleatorio (Semilla)", + "Off": "Apagado", + "Vanilla": "Original", + "Maxed": "Maximizado", + "Default": "Por Defecto", "Authentic": "Auténtico", "File Select": "Selección de Archivo", - "Debug Warp Screen": "Pantalla de Teletransporte Debug", - "Warp Point": "Punto de Teletransporte" + "Vanilla Plus": "Original Plus", + "Enhanced": "Mejorado", + "Randomizer": "Randomizer", + "Small": "Pequeño", + "Large": "Grande", + "X-Large": "Extra Grande", + "NTSC 1.0": "NTSC 1.0", + "NTSC 1.1": "NTSC 1.1", + "NTSC 1.2": "NTSC 1.2", + "NTSC-U GC": "NTSC-U GC", + "NTSC-J GC": "NTSC-J GC", + "NTSC-J GC (Collector's Edition)": "NTSC-J GC (Edición Coleccionista)", + "NTSC-U MQ": "NTSC-U MQ", + "NTSC-J MQ": "NTSC-J MQ", + "PAL 1.0": "PAL 1.0", + "PAL 1.1": "PAL 1.1", + "PAL GC": "PAL GC", + "PAL MQ": "PAL MQ", + "PAL GC-D": "PAL GC-D", + "PAL MQ-D": "PAL MQ-D", + "IQUE CN": "IQUE CN", + "IQUE TW": "IQUE TW", + "UNKNOWN": "DESCONOCIDO", + "Changes the Theme of the Menu Widgets.": "Cambia el tema de los widgets del menú.", + "Allows controller navigation of the port menu": "Permite navegación con controlador del menú de puertos", + "CAUTION: This will disable game inputs while the menu is visible.": "ADVERTENCIA: Esto deshabilitará las entradas del juego mientras el menú sea visible.", + "D-pad to move between items, A to select, B to move up in scope.": "D-pad para mover entre elementos, A para seleccionar, B para subir.", + "Sets the opacity of the background of the port menu.": "Establece la opacidad del fondo del menú de puertos.", + "Makes the cursor always visible, even in full screen.": "Hace el cursor siempre visible, incluso en pantalla completa.", + "Displays the Search menu as a sidebar entry in Settings instead of in the header.": "Muestra el menú de búsqueda como entrada lateral en Configuración.", + "Search input box gets autofocus when visible.": "La caja de búsqueda obtiene enfoque automático cuando es visible.", + "Allows pressing the Tab key to toggle alternate assets": "Permite presionar Tab para alternar assets alternativos", + "Opens the folder that contains the save and mods folders, etc.": "Abre la carpeta que contiene guardados y mods.", + "Configure what happens when starting or resetting the game.": "Configura qué sucede al iniciar o reiniciar el juego.", + "Default: LUS logo -> N64 logo": "Por defecto: Logo LUS -> Logo N64", + "Authentic: N64 logo only": "Auténtico: Solo logo N64", + "File Select: Skip to file select menu": "Selección de Archivo: Saltar al menú de selección", + "Enables text to speech for in game dialog": "Habilita texto a voz para diálogos del juego", + "Disables the automatic re-centering of the camera when idle.": "Deshabilita el recentrado automático de la cámara.", + "EXPERIMENTAL": "EXPERIMENTAL", + "Changes the scaling of the ImGui menu elements.": "Cambia la escala de los elementos del menú ImGui.", + "Ship Of Harkinian": "Ship Of Harkinian", + "Branch": "Rama", + "Commit": "Commit", + "Master Volume: %d %%": "Volumen Principal: %d %%", + "Main Music Volume: %d %%": "Volumen de Música Principal: %d %%", + "Sub Music Volume: %d %%": "Volumen de Música Secundaria: %d %%", + "Fanfare Volume: %d %%": "Volumen de Fanfarria: %d %%", + "Sound Effects Volume: %d %%": "Volumen de Efectos de Sonido: %d %%", + "Audio API (Needs reload)": "API de Audio (Necesita recargar)", + "Uses Matrix Interpolation to create extra frames": "Usa Interpolación de Matrices para crear cuadros extra", + "Toggles Fullscreen On/Off.": "Alternar Pantalla Completa.", + "Multiplies your output resolution by the value inputted": "Multiplica tu resolución de salida por el valor introducido", + "Activates MSAA (multi-sample anti-aliasing) from 2x up to 8x": "Activa MSAA (antialiasing multiamuestra) de 2x hasta 8x", + "Original (%d)": "Original (%d)", + "Match interpolation value to the refresh rate of your display.": "Coincidir valor de interpolación con tasa de refresco.", + "Renderer API (Needs reload)": "API del Renderizador (Necesita recargar)", + "Removes tearing, but clamps your max FPS to your displays refresh rate.": "Elimina tearing, pero limita FPS máximos.", + "Enables Windowed Fullscreen Mode.": "Habilita Modo Ventana Pantalla Completa.", + "Allows multiple windows to be opened at once.": "Permite múltiples ventanas a la vez.", + "Sets the applied Texture Filtering.": "Establece el filtro de textura aplicado.", + "Advanced Graphics Options": "Opciones Avanzadas de Gráficos", + "Controller Bindings": "Botones del Controlador", + "Enables the separate Bindings Window.": "Habilita la ventana de botones separada.", + "Input Viewer": "Visor de Entrada", + "Toggles the Input Viewer.": "Alterna el visor de entrada.", + "Input Viewer Settings": "Configuración del Visor de Entrada", + "Which corner of the screen notifications appear in.": "En qué esquina aparecen las notificaciones.", + "Duration (seconds):": "Duración (segundos):", + "How long notifications are displayed for.": "Cuánto tiempo se muestran las notificaciones.", + "Background Opacity": "Opacidad del Fondo", + "How opaque the background of notifications is.": "Qué tan opaco es el fondo de notificaciones.", + "Size:": "Tamaño:", + "How large notifications are.": "Qué tan grandes son las notificaciones.", + "Test Notification": "Probar Notificación", + "This is a test.": "Esto es una prueba.", + "Displays a test notification.": "Muestra una notificación de prueba.", + "Quality of Life": "Calidad de Vida", + "Saving": "Guardado", + "Save the game automatically on a 3 minute interval": "Guardar automáticamente cada 3 minutos", + "When loading a save, places Link at the last entrance": "Al cargar, coloca a Link en la última entrada", + "Containers Match Contents": "Contenedores equal Contenido", + "Chests of Agony": "Cofres de Agonía", + "Time of Day": "Hora del Día", + "Nighttime GS Always Spawn": "GS Nocturno Siempre Aparece", + "Pull Grave During the Day": "Tumbar de Día", + "Dampe Appears All Night": "Dampe Aparece Toda la Noche", + "Exit Market at Night": "Salir del Mercado de Noche", + "Shops and Games Always Open": "Tiendas y Juegos Siempre Abiertos", + "Pause Menu": "Menú de Pausa", + "Allow the Cursor to be on Any Slot": "Permitir Cursor en Cualquier Ranura", + "Pause Warp": "Teletransporte de Pausa", + "Controls": "Controles", + "Answer Navi Prompt with L Button": "Responder a Navi con Botón L", + "Don't Require Input for Credits Sequence": "No Requerir Input para Secuencia de Créditos", + "Include Held Inputs at the Start of Pause Buffer Input Window": "Incluir Inputs Sostenidos al Inicio de Ventana de Pausa", + "Pause Buffer Input Window: %d frames": "Ventana de Input de Pausa: %d cuadros", + "Simulated Input Lag: %d frames": "Lag de Input Simulado: %d cuadros", + "Item Count Messages": "Mensajes de Cantidad de Objetos", + "Gold Skulltula Tokens": "Tokens de Gold Skulltula", + "Pieces of Heart": "Piezas de Corazón", + "Heart Containers": "Contenedores de Corazón", + "Misc": "Varios", + "Disable Crit Wiggle": "Desactivar Crítico", + "Better Owl": "Mejor Búho", + "Quit Fishing at Door": "Salir de Pescar en Puerta", + "Instant Putaway": "Guardar Instantáneo", + "Navi Timer Resets on Scene Change": "Temporizador de Navi Resetea al Cambiar Escena", + "Link's Cow in Both Time Periods": "Vaca de Link en Ambos Períodos", + "Play Zelda's Lullaby to Open Sleeping Waterfall": "Tocar Canción de Zelda para Abrir Cascada Dormida", + "Skips & Speed-ups": "Saltos y Aceleraciones", + "Cutscenes": "Cinematicas", + "All": "Todos", + "Skip Intro": "Saltar Introducción", + "Skip Entrance Cutscenes": "Saltar Cinemáticas de Entrada", + "Skip Story Cutscenes": "Saltar Cinemáticas de Historia", + "Skip Song Cutscenes": "Saltar Cinemáticas de Canciones", + "Skip Boss Introductions": "Saltar Introducciones de Jefes", + "Quick Boss Deaths": "Muertes Rápidas de Jefes", + "Skip One Point Cutscenes": "Saltar Cinemáticas de Un Punto", + "Skip Owl Interactions": "Saltar Interacciones del Búho", + "Skip Misc Interactions": "Saltar Interacciones Varias", + "Disable Title Card": "Desactivar Tarjeta de Título", + "Exclude Glitch-Aiding Cutscenes": "Excluir Cinemáticas de Glitch", + "Text": "Texto", + "Skip Pickup Messages": "Saltar Mensajes de Recolección", + "Skip Forced Dialog": "Saltar Diálogo Forzado", + "Skip Text": "Saltar Texto", + "Text Speed: %dx": "Velocidad de Texto: %dx", + "Slow Text Speed: %dx": "Velocidad de Texto Lento: %dx", + "Animations": "Animaciones", + "Faster Heavy Block Lift": "Levantar Bloque Pesado Más Rápido", + "Fast Chests": "Cofres Rápidos", + "Skip Water Take Breath Animation": "Saltar Animación de Respirar Bajo Agua", + "Vine/Ladder Climb Speed +%d": "Velocidad de Escalar Enredadera/Escalera +%d", + "Block Pushing Speed +%d": "Velocidad de Empujar Bloques +%d", + "Crawl Speed %dx": "Velocidad de Arrastre %dx", + "King Zra Speed: %.2fx": "Velocidad de Rey Zora: %.2fx", + "Skip Child Stealth": "Saltar Sigilo de Niño", + "Skip Tower Escape": "Saltar Escape de Torre", + "Skip Scarecrow's Song": "Saltar Canción del Espantapájaros", + "Faster Rupee Accumulator": "Acumulador de Rupias Más Rápido", + "No Skulltula Freeze": "Sin Congelamiento de Skulltula", + "Skip Save Confirmation": "Saltar Confirmación de Guardado", + "Link as Default File Name": "Link como Nombre de Archivo por Defecto", + "Biggoron Forge Time: %d days": "Tiempo de Forja de Biggoron: %d días", + "Mods": "Mods", + "Use Alternate Assets": "Usar Assets Alternativos", + "Disable Bomb Billboarding": "Desactivar Billboard de Bombas", + "Disable Grotto Fixed Rotation": "Desactivar Rotación Fija de Grotto", + "Ingame Text Spacing: %d": "Espaciado de Texto en Juego: %d", + "Models & Textures": "Modelos y Texturas", + "Disable LOD": "Desactivar LOD", + "Enemy Health Bars": "Barras de Salud de Enemigos", + "Enable 3D Dropped Items/Projectiles": "Habilitar Objetos 3D Lanzados", + "Animated Link in Pause Menu": "Link Animado en Menú de Pausa", + "Show Age-Dependent Equipment": "Mostrar Equipo Dependiente de Edad", + "Scale Adult Equipment as Child": "Escalar Equipo de Adulto como Niño", + "Show Gauntlets in First Person": "Mostrar Guanteletes en Primera Persona", + "Show Chains on Both Sides of Locked Doors": "Mostrar Cadenas en Ambos Lados de Puertas Cerradas", + "Color Temple of Time's Medallions": "Colorear Medallones del Templo del Tiempo", + "UI": "Interfaz", + "Minimal UI": "Interfaz Mínima", + "Disable Hot/Underwater Warning Text": "Desactivar Texto de Advertencia Calor/Submarino", + "Remember Minimap State Between Areas": "Recordar Estado del Minimapa Entre Áreas", + "Visual Stone of Agony": "Piedra Visual de Agonía", + "Disable HUD Heart Animations": "Desactivar Animaciones de Corazones HUD", + "Glitch Line-up Tick": "Ticks de Alineación de Glitch", + "Disable Black Bar Letterboxes": "Desactivar Carteleras Negras", + "Dynamic Wallet Icon": "Icono de Billetera Dinámico", + "Always Show Dungeon Entrances": "Siempre Mostrar Entradas de Mazmorras", + "More Info in File Select": "Más Info en Selección de Archivo", + "Better Ammo Rendering in Pause Menu": "Mejor Renderizado de Munición en Pausa", + "Enable Passage of Time on File Select": "Habilitar Paso del Tiempo en Selección", + "Misc.": "Varios", + "N64 Mode": "Modo N64", + "Remove Spin Attack Darkness": "Remover Oscuridad de Ataque Giratorio", + "Draw Distance": "Distancia de Dibujo", + "Increase Actor Draw Distance: %dx": "Aumentar Distancia de Dibujo de Actores: %dx", + "Kokiri Draw Distance": "Distancia de Dibujo Kokiri", + "Widescreen Actor Culling": "Culling de Actores Widescreen", + "Cull Glitch Useful Actors": "Culling de Actores Útiles para Glitch", + "Items": "Objetos", + "Equipment": "Equipo", + "Equip Items on Dpad": "Equipar Objetos en Dpad", + "Assignable Tunics and Boots": "Túnicas y Botas Asignables", + "Equipment Toggle": "Alternar Equipo", + "Allow Strength Equipment to be Toggled": "Permitir Alternar Equipo de Fuerza", + "Sword Toggle Options": "Opciones de Alternar Espada", + "Ask to Equip New Items": "Preguntar para Equipar Nuevos Objetos", + "Ocarina": "Ocarina", + "Prevent Dropped Ocarina Inputs": "Prevenir Inputs de Ocarina Suelta", + "Fast Ocarina Playback": "Reproducción Rápida de Ocarina", + "Time Travel with Song of Time": "Viajar en el Tiempo con Canción del Tiempo", + "Masks": "Máscaras", + "Bunny Hood Effect": "Efecto de Máscara de Conejo", + "Masks Equippable as Adult": "Máscaras Equipables como Adulto", + "Persistent Masks": "Máscaras Persistentes", + "Invisible Bunny Hood": "Máscara de Conejo Invisible", + "Mask Select in Inventory": "Selección de Máscara en Inventario", + "Explosives": "Explosivos", + "Deku Nuts Explode Bombs": "Nueces Deku Explotan Bombas", + "Remove Explosive Limit": "Remover Límite de Explosivos", + "Static Explosion Radius": "Radio de Explosión Estático", + "Prevent Bombchus Forcing First-Person": "Prevenir Bombchus Forzando Primera Persona", + "Better Bombchu Shopping": "Mejor Compra de Bombchus", + "Bow / Slingshot": "Arco/Honda", + "Equip Multiple Arrows at Once": "Equipar Múltiples Flechas a la Vez", + "Skip Magic Arrow Equip Animation": "Saltar Animación de Equipar Flecha Mágica", + "Blue Fire Arrows": "Flechas de Fuego Azul", + "Sunlight Arrows": "Flechas de Luz Solar", + "Bow as Child/Slingshot as Adult": "Arco como Niño/Honda como Adulto", + "Aiming Reticle for the Bow/Slingshot": "Retícula de Puntería para Arco/Honda", + "Hookshot": "Gancho", + "Targetable Hookshot Reticle": "Retícula de Gancho Dirigible", + "Boomerang": "Bumerang", + "Instant Boomerang Recall": "Retorno Instantáneo de Bumerang", + "Aim Boomerang in First-Person Mode": "Apuntar Bumerang en Modo Primera Persona", + "Aiming Reticle for Boomerang": "Retícula de Puntería para Bumerang", + "Magic Spells": "Hechizos", + "Better Farore's Wind": "Mejor Viento de Farore", + "Faster Farore's Wind": "Viento de Farore Más Rápido", + "Fixes": "Arreglos", + "Gameplay Fixes": "Arreglos de Jugabilidad", + "Fix the Gravedigging Tour Glitch": "Arreglar Glitch de Excursión de Excavación", + "Fix Raised Floor Switches": "Arreglar Interruptores de Piso Elevado", + "Popout Randomizer Settings Window": "Abrir Ventana de Configuración de Randomizer", + "Randomizer Settings": "Configuración de Randomizer", + "Enables the separate Randomizer Settings Window.": "Habilita la ventana separada de configuración de Randomizer.", + "Randomizer Enhancements": "Mejoras de Randomizer", + "Rando-Relevant Navi Hints": "Pistas de Navi Relevantes para Rando", + "Random Rupee Names": "Nombres Aleatorios de Rupias", + "Use Custom Key Models": "Usar Modelos de Llaves Personalizados", + "Map & Compass Colors Match Dungeon": "Colores de Mapa y Brújula igual a Mazmorra", + "Quest Item Fanfares": "Fanfarrias de Objetos de Misión", + "Mysterious Shuffled Items": "Objetos Mezclados Misteriosos", + "Simpler Boss Soul Models": "Modelos de Almas de Jefe Simples", + "Skip Get Item Animations": "Saltar Animaciones de Obtener Objeto", + "Item Scale: %.2f": "Escala de Objeto: %.2f", + "Popout Plandomizer Window": "Abrir Ventana de Plandomizer", + "Plandomizer Editor": "Editor de Plandomizer", + "Item Tracker": "Rastreador de Objetos", + "Toggle Item Tracker": "Alternar Rastreador de Objetos", + "Item Tracker Settings": "Configuración de Rastreador de Objetos", + "Popout Item Tracker Settings": "Abrir Configuración de Rastreador de Objetos", + "Entrance Tracker": "Rastreador de Entradas", + "Toggle Entrance Tracker": "Alternar Rastreador de Entradas", + "Entrance Tracker Settings": "Configuración de Rastreador de Entradas", + "Popout Entrance Tracker Settings": "Abrir Configuración de Rastreador de Entradas", + "Check Tracker": "Rastreador de Checks", + "Toggle Check Tracker": "Alternar Rastreador de Checks", + "Check Tracker Settings": "Configuración de Rastreador de Checks", + "Popout Check Tracker Settings": "Abrir Configuración de Rastreador de Checks", + "Popout Menu": "Abrir Menú", + "Debug Mode": "Modo Depuración", + "OoT Registry Editor": "Editor de Registro de OoT", + "Debug Save File Mode": "Modo de Archivo de Guardado de Depuración", + "OoT Skulltula Debug": "Depuración de Skulltula de OoT", + "Better Debug Warp Screen": "Mejor Pantalla de Teletransporte de Depuración", + "Debug Warp Screen Translation": "Traducción de Pantalla de Teletransporte", + "Resource logging": "Registro de Recursos", + "Frame Advance": "Avance de Cuadro", + "Advance 1": "Avanzar 1", + "Advance (Hold)": "Avanzar (Mantener)", + "Popout Stats Window": "Abrir Ventana de Estadísticas", + "Stats": "Estadísticas", + "Popout Console": "Abrir Consola", + "Console": "Consola", + "Popout Save Editor": "Abrir Editor de Guardado", + "Save Editor": "Editor de Guardado", + "Popout Hook Debugger": "Abrir Depurador de Hooks", + "Hook Debugger": "Depurador de Hooks", + "Popout Collision Viewer": "Abrir Visor de Colisiones", + "Collision Viewer": "Visor de Colisiones", + "Popout Actor Viewer": "Abrir Visor de Actores", + "Actor Viewer": "Visor de Actores", + "Popout Display List Viewer": "Abrir Visor de Lista de Display", + "Display List Viewer": "Visor de Lista de Display", + "Popout Value Viewer": "Abrir Visor de Valores", + "Value Viewer": "Visor de Valores", + "Popout Message Viewer": "Abrir Visor de Mensajes", + "Message Viewer": "Visor de Mensajes", + "Popout Gfx Debugger": "Abrir Depurador de Gráficos", + "Gfx Debugger": "Depurador de Gráficos", + "DirectX": "DirectX", + "OpenGL": "OpenGL", + "Metal": "Metal", + "Resolution Presets": "Preajustes de Resolución", + "Off": "Apagado", + "Custom": "Personalizado", + "Original (4:3)": "Original (4:3)", + "Widescreen (16:9)": "Pantalla Amplia (16:9)", + "Nintendo 3DS (5:3)": "Nintendo 3DS (5:3)", + "16:10 (8:5)": "16:10 (8:5)", + "Ultrawide (21:9)": "Ultrawide (21:9)", + "Pixel Count Presets": "Preajustes de Cantidad de Píxeles", + "Native N64 (240p)": "Nativo N64 (240p)", + "2x (480p)": "2x (480p)", + "3x (720p)": "3x (720p)", + "4x (960p)": "4x (960p)", + "5x (1200p)": "5x (1200p)", + "6x (1440p)": "6x (1440p)", + "Full HD (1080p)": "Full HD (1080p)", + "4K (2160p)": "4K (2160p)", + "Set fixed vertical resolution": "Establecer resolución vertical fija", + "Horiz. Pixel Count": "Cantidad de Píxeles Horiz.", + "Force aspect ratio": "Forzar proporción de aspecto", + "Click to resolve": "Clic para resolver", + "Vertical Pixel Count": "Cantidad de Píxeles Vertical", + "Integer Scaling Settings": "Configuración de Escala Entera", + "Pixel Perfect Mode": "Modo Pixel Perfecto", + "Integer scale factor: {}": "Factor de escala entera: {}", + "Window exceeded.": "Ventana excedida.", + "Automatically scale image to fit viewport": "Escalar automáticamente imagen para ajustar", + "Additional Settings": "Configuración Adicional", + "If the image is stretched and you don't know why, click this.": "Si la imagen está estirada y no sabes por qué, haz clic.", + "Click to reenable aspect correction.": "Clic para rehabilitar corrección de aspecto.", + "Show a horizontal resolution field": "Mostrar campo de resolución horizontal", + "Allow integer scale factor to go +1 above maximum screen bounds.": "Permitir factor de escala entera +1 arriba del máximo.", + "A scroll bar may become visible if screen bounds are exceeded.": "Barra de desplazamiento puede aparecer.", + "Click to reset a console variable that may be causing this.": "Clic para reiniciar variable.", + "Viewport dimensions: {} x {}": "Dimensiones de viewport: {} x {}", + "Internal resolution: {} x {}": "Resolución interna: {} x {}", + "Enable advanced settings.": "Habilitar configuración avanzada.", + "Significant frame rate (FPS) drops may be occuring.": "Caídas significativas de FPS pueden estar ocurriendo.", + "N64 Mode is overriding these settings.": "Modo N64 está sobrescribiendo estas configuraciones.", + "Click to disable N64 mode": "Clic para desactivar modo N64", + "Aspect Ratio": "Proporción de Aspecto", + "Disabling VSync not supported": "Desactivar VSync no soportado", + "Windowed Fullscreen not supported": "Pantalla Completa en Ventana no soportada", + "Multi-viewports not supported": "Multi-viewports no soportado", + "Available Only on DirectX": "Solo Disponible en DirectX", + "Not Available on DirectX": "No Disponible en DirectX", + "Match Refresh Rate is Enabled": "Coincidir Tasa de Refresco está Habilitado", + "Advanced Resolution Enabled": "Resolución Avanzada Habilitada", + "Vertical Resolution Toggle Enabled": "Alternar Resolución Vertical Habilitado", + "N64 Mode Enabled": "Modo N64 Habilitado", + "Save Not Loaded": "Guardado No Cargado", + "Debug Mode is Disabled": "Modo Depuración está Desactivado", + "Frame Advance is Disabled": "Avance de Cuadro está Desactivado", + "Advanced Resolution is Disabled": "Resolución Avanzada está Desactivada", + "Vertical Resolution Toggle is Off": "Alternar Resolución Vertical está Apagado" } } diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp new file mode 100644 index 000000000..aaa51507d --- /dev/null +++ b/soh/soh/SohGui/Menu.cpp @@ -0,0 +1,962 @@ +#include "Menu.h" +#include "LanguageManager.h" +#include "UIWidgets.hpp" +#include "soh/OTRGlobals.h" +#include +#include +#include "SohModals.h" +#include +#include +#include + +extern "C" { +#include "z64.h" +extern PlayState* gPlayState; +} +std::vector windowTypeSizes = { {} }; + +extern std::unordered_map warpPointSceneList; +extern void Warp(); + +namespace SohGui { +extern std::shared_ptr mModalWindow; +} +std::vector extraSearchWidgets = {}; + +namespace Ship { +std::string disabledTempTooltip; +const char* disabledTooltip; +bool disabledValue = false; + +bool navigateToWidget = false; +const char* navigateMainEntry = ""; +const char* navigateSidebar = ""; +std::string navigateWidgetName = ""; +bool highlightWidget = false; + +bool operator==(Color_RGB8 const& l, Color_RGB8 const& r) noexcept { + return l.r == r.r && l.g == r.g && l.b == r.b; +} + +bool operator==(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept { + return l.r == r.r && l.g == r.g && l.b == r.b && l.a == r.a; +} + +bool operator<(Color_RGB8 const& l, Color_RGB8 const& r) noexcept { + return (l.r < r.r && l.g <= r.g && l.b <= r.b) || (l.r <= r.r && l.g < r.g && l.b <= r.b) || + (l.r <= r.r && l.g <= r.g && l.b < r.b); +} + +bool operator<(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept { + return (l.r < r.r && l.g <= r.g && l.b <= r.b && l.a <= r.a) || + (l.r <= r.r && l.g < r.g && l.b <= r.b && l.a <= r.a) || + (l.r <= r.r && l.g <= r.g && l.b < r.b && l.a <= r.a) || + (l.r <= r.r && l.g <= r.g && l.b <= r.b && l.a < r.a); +} + +bool operator>(Color_RGB8 const& l, Color_RGB8 const& r) noexcept { + return (l.r > r.r && l.g >= r.g && l.b >= r.b) || (l.r >= r.r && l.g > r.g && l.b >= r.b) || + (l.r >= r.r && l.g >= r.g && l.b > r.b); +} + +bool operator>(Color_RGBA8 const& l, Color_RGBA8 const& r) noexcept { + return (l.r > r.r && l.g >= r.g && l.b >= r.b && l.a >= r.a) || + (l.r >= r.r && l.g > r.g && l.b >= r.b && l.a >= r.a) || + (l.r >= r.r && l.g >= r.g && l.b > r.b && l.a >= r.a) || + (l.r >= r.r && l.g >= r.g && l.b >= r.b && l.a > r.a); +} + +uint32_t GetVectorIndexOf(std::vector& vector, std::string value) { + return std::distance(vector.begin(), std::find(vector.begin(), vector.end(), value)); +} + +static bool raceDisableActive = false; + +void Menu::InsertSidebarSearch() { + menuEntries["Settings"].sidebars.emplace("Search", searchSidebarEntry); + uint32_t curIndex = 0; + if (!Ship_IsCStringEmpty(CVarGetString(menuEntries["Settings"].sidebarCvar, ""))) { + curIndex = GetVectorIndexOf(menuEntries["Settings"].sidebarOrder, + CVarGetString(menuEntries["Settings"].sidebarCvar, "")); + } + menuEntries["Settings"].sidebarOrder.insert(menuEntries["Settings"].sidebarOrder.begin() + searchSidebarIndex, + "Search"); + if (curIndex > searchSidebarIndex) { + CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str()); + } +} + +void Menu::RemoveSidebarSearch() { + uint32_t curIndex = GetVectorIndexOf(menuEntries["Settings"].sidebarOrder, + CVarGetString(menuEntries["Settings"].sidebarCvar, "General")); + menuEntries["Settings"].sidebars.erase("Search"); + std::erase_if(menuEntries["Settings"].sidebarOrder, [](std::string& name) { return name == "Search"; }); + if (curIndex > searchSidebarIndex) { + curIndex--; + } else if (curIndex >= menuEntries["Settings"].sidebarOrder.size()) { + curIndex = menuEntries["Settings"].sidebarOrder.size() - 1; + } + CVarSetString(menuEntries["Settings"].sidebarCvar, menuEntries["Settings"].sidebarOrder.at(curIndex).c_str()); +} + +void Menu::UpdateWindowBackendObjects() { + Ship::WindowBackend runningWindowBackend = Ship::Context::GetInstance()->GetWindow()->GetWindowBackend(); + int32_t configWindowBackendId = Ship::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1); + if (Ship::Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(configWindowBackendId)) { + configWindowBackend = static_cast(configWindowBackendId); + } else { + configWindowBackend = runningWindowBackend; + } + + availableWindowBackends = Ship::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends(); + for (auto& backend : *availableWindowBackends) { + availableWindowBackendsMap[backend] = windowBackendsMap.at(backend); + } +} + +bool Menu::IsMenuPopped() { + return popped; +} + +UIWidgets::Colors Menu::GetMenuThemeColor() { + return menuThemeIndex; +} + +Menu::Menu(const std::string& cVar, const std::string& name, uint8_t searchSidebarIndex_, + UIWidgets::Colors defaultThemeIndex_) + : GuiWindow(cVar, name), searchSidebarIndex(searchSidebarIndex_), defaultThemeIndex(defaultThemeIndex_) { +} + +void Menu::InitElement() { + popped = CVarGetInteger(CVAR_SETTING("Menu.Popout"), 0); + poppedSize.x = CVarGetInteger(CVAR_SETTING("Menu.PoppedWidth"), 1280); + poppedSize.y = CVarGetInteger(CVAR_SETTING("Menu.PoppedHeight"), 800); + poppedPos.x = CVarGetInteger(CVAR_SETTING("Menu.PoppedPos.x"), 0); + poppedPos.y = CVarGetInteger(CVAR_SETTING("Menu.PoppedPos.y"), 0); + menuThemeIndex = static_cast(CVarGetInteger(CVAR_SETTING("Menu.Theme"), defaultThemeIndex)); + + UpdateWindowBackendObjects(); +} + +void Menu::UpdateElement() { + menuThemeIndex = static_cast(CVarGetInteger(CVAR_SETTING("Menu.Theme"), defaultThemeIndex)); +} + +bool ModernMenuSidebarEntry(std::string label) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStyle& style = ImGui::GetStyle(); + ImVec2 pos = window->DC.CursorPos; + const ImGuiID sidebarId = window->GetID(std::string(label + "##Sidebar").c_str()); + ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + pos.y += style.FramePadding.y; + pos.x = window->WorkRect.GetCenter().x - labelSize.x / 2; + ImRect bb = { pos - style.FramePadding, pos + labelSize + style.FramePadding }; + ImGui::ItemSize(bb, style.FramePadding.y); + ImGui::ItemAdd(bb, sidebarId); + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, sidebarId, &hovered, &held); + if (pressed) { + ImGui::MarkItemEdited(sidebarId); + } + window->DrawList->AddRectFilled(pos - style.FramePadding, pos + labelSize + style.FramePadding, + ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive + : hovered ? ImGuiCol_ButtonHovered + : ImGuiCol_Button), + 3.0f); + UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + return pressed; +} + +bool ModernMenuHeaderEntry(std::string label) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStyle& style = ImGui::GetStyle(); + ImVec2 pos = window->DC.CursorPos; + const ImGuiID headerId = window->GetID(std::string(label + "##Header").c_str()); + ImVec2 labelSize = ImGui::CalcTextSize(label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + ImRect bb = { pos, pos + labelSize + style.FramePadding * 2 }; + ImGui::ItemSize(bb, style.FramePadding.y); + ImGui::ItemAdd(bb, headerId); + bool hovered, held; + bool pressed = ImGui::ButtonBehavior(bb, headerId, &hovered, &held); + window->DrawList->AddRectFilled(bb.Min, bb.Max, + ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive + : hovered ? ImGuiCol_ButtonHovered + : ImGuiCol_Button), + 3.0f); + pos += style.FramePadding; + UIWidgets::RenderText(pos, label.c_str(), ImGui::FindRenderedTextEnd(label.c_str()), true); + return pressed; +} + +uint32_t Menu::DrawSearchResults(std::string& menuSearchText) { + int searchCount = 0; + std::transform(menuSearchText.begin(), menuSearchText.end(), menuSearchText.begin(), ::tolower); + menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end()); + ImGui::SetNextWindowSizeConstraints({ ImGui::GetContentRegionAvail().x / 2, 0 }, + { ImGui::GetContentRegionAvail().x / 2, ImGui::GetContentRegionAvail().y }); + if (ImGui::BeginChild("Search Results Col 1", { ImGui::GetContentRegionAvail().x / 2, 0 }, + ImGuiChildFlags_AutoResizeY, ImGuiWindowFlags_NoTitleBar)) { + for (auto& menuLabel : menuOrder) { + auto& menuEntry = menuEntries.at(menuLabel); + for (auto& sidebarLabel : menuEntry.sidebarOrder) { + auto& sidebar = menuEntry.sidebars[sidebarLabel]; + for (size_t i = 0; i < sidebar.columnWidgets.size(); i++) { + auto& column = sidebar.columnWidgets.at(i); + for (auto& info : column) { + if (info.type == WIDGET_SEARCH || info.type == WIDGET_SEPARATOR || + info.type == WIDGET_SEPARATOR_TEXT || info.isHidden || info.hideInSearch) { + continue; + } + const char* tooltip = info.options->tooltip; + std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : ""); + std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); + widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); + if (widgetStr.find(menuSearchText) != std::string::npos) { + UIWidgets::ComponentAlignments backupAlignment; + UIWidgets::LabelPositions backupLabelPos; + if (info.type == WIDGET_COMBOBOX || info.type == WIDGET_CVAR_COMBOBOX) { + backupAlignment = + std::static_pointer_cast(info.options)->alignment; + backupLabelPos = + std::static_pointer_cast(info.options)->labelPosition; + std::static_pointer_cast(info.options)->alignment = + UIWidgets::ComponentAlignments::Left; + std::static_pointer_cast(info.options)->labelPosition = + UIWidgets::LabelPositions::Above; + } + MenuDrawItem(info, 400, menuThemeIndex); + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); + std::string origin = + fmt::format(" ({} -> {}, Col {})", menuEntry.label, sidebarLabel, i + 1); + ImGui::Text("%s", origin.c_str()); + ImGui::PopStyleColor(); + searchCount++; + if (info.type == WIDGET_COMBOBOX || info.type == WIDGET_CVAR_COMBOBOX) { + std::static_pointer_cast(info.options)->alignment = + backupAlignment; + std::static_pointer_cast(info.options)->labelPosition = + backupLabelPos; + } + } + } + } + } + } + for (auto& entry : extraSearchWidgets) { + if (entry.info.type == WIDGET_SEARCH || entry.info.type == WIDGET_SEPARATOR || + entry.info.type == WIDGET_SEPARATOR_TEXT || entry.info.isHidden || entry.info.hideInSearch) { + continue; + } + std::string widgetStr = + entry.info.name + entry.info.options->tooltip + entry.extraTerms + entry.sidebarName; + std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); + widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); + if (widgetStr.find(menuSearchText) != std::string::npos) { + MenuDrawItem(entry.info, 400, menuThemeIndex); + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(UIWidgets::Colors::Gray)); + std::string origin = fmt::format(" ({} -> {}, {})", entry.menuName, entry.sidebarName, entry.location); + ImGui::Text("%s", origin.c_str()); + ImGui::PopStyleColor(); + searchCount++; + } + } + ImGui::EndChild(); + } + return searchCount; +} + +void Menu::AddMenuEntry(std::string entryName, const char* entryCvar) { + std::string translatedName = SohGui::LanguageManager::Instance().GetString(entryName); + menuEntries.emplace(entryName, MainMenuEntry{ translatedName, entryCvar }); + menuOrder.push_back(entryName); +} + +void Menu::AddSearchWidget(SearchWidget widget) { + extraSearchWidgets.push_back(widget); +} + +std::unordered_map& Menu::GetDisabledMap() { + return disabledMap; +} + +void Menu::MenuDrawItem(WidgetInfo& widget, uint32_t width, UIWidgets::Colors menuThemeIndex) { + disabledTempTooltip = "This setting is disabled because: \n"; + disabledValue = false; + disabledTooltip = " "; + + if (widget.preFunc != nullptr) { + widget.ResetDisables(); + widget.preFunc(widget); + if (widget.isHidden) { + return; + } + if (!widget.activeDisables.empty()) { + widget.options->disabled = true; + for (auto option : widget.activeDisables) { + disabledTempTooltip += std::string("\n- ") + disabledMap.at(option).reason; + } + widget.options->disabledTooltip = disabledTempTooltip.c_str(); + } + } + if (widget.raceDisable && raceDisableActive) { + widget.options->disabled = true; + disabledTempTooltip += std::string("\n- Race Lockout Active"); + widget.options->disabledTooltip = disabledTempTooltip.c_str(); + } + + if (widget.sameLine) { + ImGui::SameLine(); + } + + try { + switch (widget.type) { + case WIDGET_CHECKBOX: { + bool* pointer = std::get(widget.valuePointer); + if (pointer == nullptr) { + SPDLOG_ERROR("Checkbox Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::Checkbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), pointer, + *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_CVAR_CHECKBOX: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarCheckbox(UIWidgets::WrappedText(widget.name.c_str(), width).c_str(), widget.cVar, + *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_AUDIO_BACKEND: { + auto currentAudioBackend = Ship::Context::GetInstance()->GetAudio()->GetCurrentAudioBackend(); + UIWidgets::ComboboxOptions options = {}; + options.color = menuThemeIndex; + options.tooltip = "Sets the audio API used by the game. Requires a relaunch to take effect."; + options.disabled = Ship::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1; + options.disabledTooltip = "Only one audio API is available on this platform."; + if (UIWidgets::Combobox("Audio API", ¤tAudioBackend, audioBackendsMap, options)) { + Ship::Context::GetInstance()->GetAudio()->SetCurrentAudioBackend(currentAudioBackend); + } + } break; + case WIDGET_VIDEO_BACKEND: { + UIWidgets::ComboboxOptions options = {}; + options.color = menuThemeIndex; + options.tooltip = "Sets the renderer API used by the game."; + options.disabled = availableWindowBackends->size() <= 1; + options.disabledTooltip = "Only one renderer API is available on this platform."; + if (UIWidgets::Combobox("Renderer API (Needs reload)", &configWindowBackend, availableWindowBackendsMap, + options)) { + Ship::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", + (int32_t)(configWindowBackend)); + Ship::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name", + windowBackendsMap.at(configWindowBackend)); + Ship::Context::GetInstance()->GetConfig()->Save(); + UpdateWindowBackendObjects(); + } + } break; + case WIDGET_SEPARATOR: { + ImGui::Separator(); + } break; + case WIDGET_SEPARATOR_TEXT: { + auto options = std::static_pointer_cast(widget.options); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(options->color)); + } + ImGui::SeparatorText(widget.name.c_str()); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PopStyleColor(); + } + } break; + case WIDGET_TEXT: { + auto options = std::static_pointer_cast(widget.options); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PushStyleColor(ImGuiCol_Text, UIWidgets::ColorValues.at(options->color)); + } + ImGui::AlignTextToFramePadding(); + ImGui::TextWrapped("%s", widget.name.c_str()); + if (options->color != UIWidgets::Colors::NoColor) { + ImGui::PopStyleColor(); + } + } break; + case WIDGET_COMBOBOX: { + int32_t* pointer = std::get(widget.valuePointer); + if (pointer == nullptr) { + SPDLOG_ERROR("Combobox Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::Combobox(widget.name.c_str(), pointer, options->comboMap, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_CVAR_COMBOBOX: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarCombobox(widget.name.c_str(), widget.cVar, options->comboMap, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_SLIDER_INT: { + int32_t* pointer = std::get(widget.valuePointer); + if (pointer == nullptr) { + SPDLOG_ERROR("int32 Slider Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::SliderInt(widget.name.c_str(), pointer, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_CVAR_SLIDER_INT: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarSliderInt(widget.name.c_str(), widget.cVar, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + }; + } break; + case WIDGET_SLIDER_FLOAT: { + float* pointer = std::get(widget.valuePointer); + + if (pointer == nullptr) { + SPDLOG_ERROR("float Slider Widget requires a value pointer, currently nullptr"); + assert(false); + return; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::SliderFloat(widget.name.c_str(), pointer, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_CVAR_SLIDER_FLOAT: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarSliderFloat(widget.name.c_str(), widget.cVar, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_CVAR_BTN_SELECTOR: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::CVarBtnSelector(widget.name.c_str(), widget.cVar, *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_BUTTON: { + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (UIWidgets::Button(widget.name.c_str(), *options)) { + if (widget.callback != nullptr) { + widget.callback(widget); + } + } + } break; + case WIDGET_CUSTOM: { + if (widget.customFunction != nullptr) { + widget.customFunction(widget); + } + } break; + case WIDGET_WINDOW_BUTTON: { + if (widget.windowName == nullptr || widget.windowName[0] == '\0') { + std::string msg = + fmt::format("Error drawing window contents for {}: windowName not defined", widget.name); + SPDLOG_ERROR(msg.c_str()); + break; + } + auto window = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow(widget.windowName); + if (!window) { + std::string msg = + fmt::format("Error drawing window contents: windowName {} does not exist", widget.windowName); + SPDLOG_ERROR(msg.c_str()); + break; + } + auto options = std::static_pointer_cast(widget.options); + options->color = menuThemeIndex; + if (options->showButton) { + UIWidgets::WindowButton(widget.name.c_str(), widget.cVar, window, *options); + } + if (!window->IsVisible() && options->embedWindow) { + window->DrawElement(); + } + } break; + case WIDGET_CVAR_COLOR_PICKER: { + auto options = std::static_pointer_cast(widget.options); + uint32_t modifiers = 0; + if (options->showLock) + modifiers |= UIWidgets::ColorPickerLockCheck; + if (options->showRandom) + modifiers |= UIWidgets::ColorPickerRandomButton; + if (options->showReset) + modifiers |= UIWidgets::ColorPickerResetButton; + if (options->showRainbow) + modifiers |= UIWidgets::ColorPickerRainbowCheck; + UIWidgets::CVarColorPicker(widget.name.c_str(), widget.cVar, options->defaultValue, options->useAlpha, + modifiers, options->color); + } break; + case WIDGET_SEARCH: { + UIWidgets::PushStyleButton(menuThemeIndex); + if (ImGui::Button("Clear")) { + menuSearch.Clear(); + } + ImGui::SameLine(); + if (CVarGetInteger(CVAR_SETTING("Menu.SearchAutofocus"), 0) && + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && !ImGui::IsAnyItemActive() && + !ImGui::IsMouseClicked(0)) { + ImGui::SetKeyboardFocusHere(0); + } + UIWidgets::PushStyleCombobox(menuThemeIndex); + ImGui::PushStyleColor(ImGuiCol_Border, UIWidgets::ColorValues.at(menuThemeIndex)); + menuSearch.Draw(); + ImGui::PopStyleColor(); + UIWidgets::PopStyleCombobox(); + UIWidgets::PopStyleButton(); + std::string menuSearchText(menuSearch.InputBuf); + + if (menuSearchText == "") { + ImGui::Text("Start typing to see results."); + return; + } + DrawSearchResults(menuSearchText); + } break; + default: + break; + } + if (widget.postFunc != nullptr) { + widget.postFunc(widget); + } + } catch (const std::bad_variant_access& e) { + SPDLOG_ERROR("Failed to draw menu item \"{}\" due to: {}", widget.name, e.what()); + assert(false); + } +} + +void Menu::Draw() { + if (!IsVisible()) { + return; + } + DrawElement(); + // Sync up the IsVisible flag if it was changed by ImGui + SyncVisibilityConsoleVariable(); +} + +static bool freshOpen = true; +void Menu::DrawElement() { + if (OTRGlobals::Instance->fontStandardLargest == nullptr) { + return; + } + for (auto& [reason, info] : disabledMap) { + info.active = info.evaluation(info); + } + const char* headerCvar = CVAR_SETTING("Menu.ActiveHeader"); + + if (navigateToWidget) { + if (menuEntries.contains(navigateMainEntry) && + menuEntries.at(navigateMainEntry).sidebars.contains(navigateSidebar)) { + menuSearch.Clear(); + CVarSetString(headerCvar, navigateMainEntry); + const char* sidebarCvar = menuEntries.at(navigateMainEntry).sidebarCvar; + CVarSetString(sidebarCvar, navigateSidebar); + highlightWidget = true; + } + navigateToWidget = false; + } + + raceDisableActive = CVarGetInteger(CVAR_SETTING("DisableChanges"), 0); + + windowHeight = ImGui::GetMainViewport()->WorkSize.y; + windowWidth = ImGui::GetMainViewport()->WorkSize.x; + auto windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; + bool popout = CVarGetInteger(CVAR_SETTING("Menu.Popout"), 0) && allowPopout; + if (popout) { + windowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking; + } + if (popout != popped) { + if (popout) { + windowHeight = poppedSize.y; + windowWidth = poppedSize.x; + ImGui::SetNextWindowSize({ static_cast(windowWidth), static_cast(windowHeight) }, + ImGuiCond_Always); + ImGui::SetNextWindowPos(poppedPos, ImGuiCond_Always); + } else if (popped) { + CVarSetFloat(CVAR_SETTING("Menu.PoppedWidth"), poppedSize.x); + CVarSetFloat(CVAR_SETTING("Menu.PoppedHeight"), poppedSize.y); + CVarSave(); + } + } + popped = popout; + auto windowCond = ImGuiCond_Always; + if (!popout) { + ImGui::SetNextWindowSize({ static_cast(windowWidth), static_cast(windowHeight) }, windowCond); + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), windowCond, { 0.5f, 0.5f }); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + } + + ImGui::PushStyleColor(ImGuiCol_WindowBg, + ImVec4(0, 0, 0, CVarGetFloat(CVAR_SETTING("Menu.BackgroundOpacity"), 0.85f))); + + if (!ImGui::Begin("Main Menu", NULL, windowFlags)) { + if (!popout) { + ImGui::PopStyleVar(); + } + freshOpen = true; + ImGui::PopStyleColor(); + ImGui::End(); + return; + } + + ImGui::PopStyleColor(); + + if (popped != popout) { + if (!popout) { + ImGui::PopStyleVar(); + } + ImGui::PopStyleColor(); + CVarSetInteger(CVAR_SETTING("Menu.Popout"), popped); + CVarSetFloat(CVAR_SETTING("Menu.PoppedWidth"), poppedSize.x); + CVarSetFloat(CVAR_SETTING("Menu.PoppedHeight"), poppedSize.y); + CVarSetFloat(CVAR_SETTING("Menu.PoppedPos.x"), poppedSize.x); + CVarSetFloat(CVAR_SETTING("Menu.PoppedPos.y"), poppedSize.y); + CVarSave(); + ImGui::End(); + return; + } + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStyle& style = ImGui::GetStyle(); + windowHeight = window->WorkRect.GetHeight(); + windowWidth = window->WorkRect.GetWidth(); + + ImGui::PushFont(OTRGlobals::Instance->fontStandardLargest); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 8.0f)); + std::string headerIndex = CVarGetString(headerCvar, "Settings"); + ImVec2 pos = window->DC.CursorPos; + float centerX = pos.x + windowWidth / 2 - (style.ItemSpacing.x * (menuEntries.size() + 1)); + std::vector headerSizes; + float headerWidth = 0.0f; + bool headerSearch = !CVarGetInteger(CVAR_SETTING("Menu.SidebarSearch"), 0); + if (headerSearch) { + headerWidth += 200.0f; + } + for (auto& label : menuOrder) { + ImVec2 size = ImGui::CalcTextSize(label.c_str()); + headerSizes.push_back(size); + headerWidth += size.x + style.FramePadding.x * 2 + style.ItemSpacing.x; + } + + // Full screen menu with widths below 1280, heights below 800. + // 5% of screen width/height padding on both sides above those resolutions. + // Menu width will never exceed a 16:9 aspect ratio. + ImVec2 menuSize = { windowWidth, windowHeight }; + if (windowWidth > 1280) { + menuSize.x = std::fminf(windowWidth * 0.9f, (windowHeight * 1.77f)); + } + if (windowHeight > 800) { + menuSize.y = windowHeight * 0.9f; + } + + pos += window->WorkRect.GetSize() / 2 - menuSize / 2; + ImGui::SetNextWindowPos(pos); + ImGui::BeginChild("Menu Block", menuSize, + ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar); + + std::unordered_map* sidebar; + float headerHeight = headerSizes.at(0).y + style.FramePadding.y * 2; + ImVec2 buttonSize = ImGui::CalcTextSize(ICON_FA_TIMES_CIRCLE) + style.FramePadding * 2; + bool scrollbar = false; + if (headerWidth > menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3) { + headerHeight += style.ScrollbarSize; + scrollbar = true; + } + ImGui::SetNextWindowSizeConstraints({ 0, headerHeight }, { headerWidth, headerHeight }); + ImVec2 headerSelSize = { menuSize.x - buttonSize.x * 3 - style.ItemSpacing.x * 3, headerHeight }; + if (scrollbar) { + headerSelSize.y += style.ScrollbarSize; + } + bool autoFocus = CVarGetInteger(CVAR_SETTING("Menu.SearchAutofocus"), 0); + ImGui::BeginChild("Header Selection", headerSelSize, + ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_HorizontalScrollbar); + uint8_t curIndex = 0; + for (auto& label : menuOrder) { + if (curIndex != 0) { + ImGui::SameLine(); + } + auto& entry = menuEntries.at(label); + std::string displayLabel = entry.label; + if (displayLabel != label) { + displayLabel = SohGui::LanguageManager::Instance().GetString(label); + } + std::string nextIndex = label; + UIWidgets::PushStyleButton(menuThemeIndex); + if (headerIndex != label) { + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + } + if (ModernMenuHeaderEntry(displayLabel.c_str())) { + if (headerSearch) { + menuSearch.Clear(); + } + CVarSetString(headerCvar, label.c_str()); + CVarSave(); + nextIndex = label; + } + if (headerIndex != label) { + ImGui::PopStyleColor(); + } + UIWidgets::PopStyleButton(); + if (headerIndex == label) { + sidebar = &entry.sidebars; + } + if (nextIndex != label) { + headerIndex = nextIndex; + } + curIndex++; + } + std::string menuSearchText = ""; + if (headerSearch) { + ImGui::SameLine(); + if (autoFocus && freshOpen) { + ImGui::SetKeyboardFocusHere(); + } + auto color = UIWidgets::ColorValues.at(menuThemeIndex); + color.w = 0.6f; + ImGui::PushStyleColor(ImGuiCol_FrameBg, color); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + menuSearch.Draw("##search", 200.0f); + menuSearchText = menuSearch.InputBuf; + menuSearchText.erase(std::remove(menuSearchText.begin(), menuSearchText.end(), ' '), menuSearchText.end()); + if (menuSearchText.length() < 1) { + ImGui::SameLine(headerWidth - 200.0f + style.ItemSpacing.x); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "Search..."); + } + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } + ImGui::EndChild(); + ImGui::SameLine(menuSize.x - (buttonSize.x * 3) - (style.ItemSpacing.x * 2)); + UIWidgets::ButtonOptions options3 = {}; + options3.color = UIWidgets::Colors::Red; + options3.size = UIWidgets::Sizes::Inline; + options3.tooltip = "Quit SoH"; + if (UIWidgets::Button(ICON_FA_POWER_OFF, options3)) { + SohGui::mModalWindow->RegisterPopup( + "Quit SoH", "Are you sure you want to quit SoH?", "Quit", "Cancel", + []() { + std::shared_ptr menu = + static_pointer_cast(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenu()); + if (!menu->IsMenuPopped()) { + menu->ToggleVisibility(); + } + Ship::Context::GetInstance()->GetWindow()->Close(); + }, + nullptr); + } + ImGui::PopStyleVar(); + ImGui::SameLine(); + UIWidgets::ButtonOptions options2 = {}; + options2.color = UIWidgets::Colors::Red; + options2.size = UIWidgets::Sizes::Inline; + options2.tooltip = "Reset" +#ifdef __APPLE__ + " (Command-R)" +#elif !defined(__SWITCH__) && !defined(__WIIU__) + " (Ctrl+R)" +#else + "" +#endif + ; + if (UIWidgets::Button(ICON_FA_UNDO, options2)) { + std::reinterpret_pointer_cast( + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console")) + ->Dispatch("reset"); + } + ImGui::SameLine(); + UIWidgets::ButtonOptions options = {}; + options.size = UIWidgets::Sizes::Inline; + options.tooltip = "Close Menu (Esc)"; + if (UIWidgets::Button(ICON_FA_TIMES_CIRCLE, options)) { + ToggleVisibility(); + + // Update gamepad navigation after close based on if other menus are still visible + auto mImGuiIo = &ImGui::GetIO(); + if (CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0) && + Ship::Context::GetInstance()->GetWindow()->GetGui()->GetMenuOrMenubarVisible()) { + mImGuiIo->ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + } else { + mImGuiIo->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; + } + } + + pos.y += headerHeight + style.ItemSpacing.y; + pos.x = centerX - menuSize.x / 2 + (style.ItemSpacing.x * (menuEntries.size() + 1)); + window->DrawList->AddRectFilled(pos, pos + ImVec2{ menuSize.x, 4 }, ImGui::GetColorU32({ 255, 255, 255, 255 }), + true, style.WindowRounding); + pos.y += style.ItemSpacing.y; + float sectionHeight = menuSize.y - headerHeight - 4 - style.ItemSpacing.y * 2; + float columnHeight = sectionHeight - style.ItemSpacing.y * 4; + ImGui::SetNextWindowPos(pos + style.ItemSpacing * 2); + + // Increase sidebar width on larger screens to accomodate people scaling their menus. + float sidebarWidth = 200 - style.ItemSpacing.x; + if (menuSize.x > 1600) { + sidebarWidth = menuSize.x * 0.15f; + } + + const char* sidebarCvar = menuEntries.at(headerIndex).sidebarCvar; + + std::string sectionIndex = CVarGetString(sidebarCvar, ""); + if (!sidebar->contains(sectionIndex)) { + sectionIndex = menuEntries.at(headerIndex).sidebarOrder.at(0); + } + float sectionCenterX = pos.x + (sidebarWidth / 2); + float topY = pos.y; + ImGui::SetNextWindowSizeConstraints({ sidebarWidth, 0 }, { sidebarWidth, columnHeight }); + ImGui::BeginChild((menuEntries.at(headerIndex).label + " Section").c_str(), { sidebarWidth, columnHeight * 3 }, + ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoTitleBar); + for (auto& sidebarLabel : menuEntries.at(headerIndex).sidebarOrder) { + std::string displayLabel = sidebarLabel; + auto& sidebarEntry = menuEntries.at(headerIndex).sidebars.at(sidebarLabel); + if (!sidebarEntry.translatedName.empty() && sidebarEntry.translatedName != sidebarLabel) { + displayLabel = sidebarEntry.translatedName; + } + std::string nextIndex = ""; + UIWidgets::PushStyleButton(menuThemeIndex); + if (sectionIndex != sidebarLabel) { + ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 }); + } + if (ModernMenuSidebarEntry(displayLabel.c_str())) { + if (headerSearch) { + menuSearch.Clear(); + } + CVarSetString(sidebarCvar, sidebarLabel.c_str()); + CVarSave(); + nextIndex = sidebarLabel; + } + if (sectionIndex != sidebarLabel) { + ImGui::PopStyleColor(); + } + UIWidgets::PopStyleButton(); + if (nextIndex != "") { + sectionIndex = nextIndex; + } + } + ImGui::EndChild(); + ImGui::PopFont(); + + pos = ImVec2{ sectionCenterX + (sidebarWidth / 2), topY } + style.ItemSpacing * 2; + window->DrawList->AddRectFilled(pos, pos + ImVec2{ 4, sectionHeight - style.FramePadding.y * 2 }, + ImGui::GetColorU32({ 255, 255, 255, 255 }), true, style.WindowRounding); + pos.x += 4 + style.ItemSpacing.x; + ImGui::SetNextWindowPos(pos + style.ItemSpacing); + float sectionWidth = menuSize.x - sidebarWidth - 4 - style.ItemSpacing.x * 4; + std::string sectionMenuId = sectionIndex + " Settings"; + size_t columns = sidebar->at(sectionIndex).columnCount; + size_t columnFuncs = sidebar->at(sectionIndex).columnWidgets.size(); + if (windowWidth < 800) { + columns = 1; + } + float columnWidth = (sectionWidth - style.ItemSpacing.x * columns) / columns; + bool useColumns = columns > 1; + if (!useColumns || (headerSearch && menuSearchText.length() > 0)) { + ImGui::SameLine(); + ImGui::SetNextWindowSizeConstraints({ sectionWidth, 0 }, { sectionWidth, columnHeight }); + ImGui::BeginChild(sectionMenuId.c_str(), { sectionWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY, + ImGuiWindowFlags_NoTitleBar); + } + if (headerSearch && menuSearchText.length() > 0) { + ImGui::AlignTextToFramePadding(); + ImGui::PushFont(OTRGlobals::Instance->fontMonoLargest); + ImGui::Text("Search Results"); + ImGui::PopFont(); + ImGui::SameLine(); + UIWidgets::ButtonOptions clearBtnOpts = {}; + clearBtnOpts.size = UIWidgets::Sizes::Inline; + if (UIWidgets::Button("Clear Search", clearBtnOpts)) { + menuSearch.Clear(); + } + ImGui::BeginChild("searchSeparator", ImVec2(ImGui::GetContentRegionAvail().x / 2, 20), + ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY); + UIWidgets::Separator(true, true, 0, 10); + ImGui::EndChild(); + uint32_t searchCount = DrawSearchResults(menuSearchText); + if (searchCount == 0) { + ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize("No results found").x) / 2); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f); + ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.4f), "No results found"); + } + } else { + std::string menuLabel = menuEntries.at(headerIndex).label; + if (MenuInit::GetUpdateFuncs().contains(menuLabel)) { + if (MenuInit::GetUpdateFuncs()[menuLabel].contains(sectionIndex)) { + for (auto& updateFunc : MenuInit::GetUpdateFuncs()[menuLabel][sectionIndex]) { + updateFunc(); + } + } + } + for (size_t i = 0; i < columnFuncs; i++) { + std::string sectionId = fmt::format("{} Column {}", sectionMenuId, i); + if (useColumns) { + ImGui::SetNextWindowSizeConstraints({ columnWidth, 0 }, { columnWidth, columnHeight }); + ImGui::BeginChild(sectionId.c_str(), { columnWidth, windowHeight * 4 }, ImGuiChildFlags_AutoResizeY, + ImGuiWindowFlags_NoTitleBar); + } + // for (auto& entryName : sidebar->at(sectionIndex).sidebarOrder) { + for (auto& entry : sidebar->at(sectionIndex).columnWidgets.at(i)) { + MenuDrawItem(entry, 90 / sidebar->at(sectionIndex).columnCount, menuThemeIndex); + } + //} + if (useColumns) { + ImGui::EndChild(); + } + if (i < columns - 1) { + ImGui::SameLine(); + } + } + } + if (!useColumns || menuSearchText.length() > 0) { + ImGui::EndChild(); + } + + if (!popout) { + ImGui::PopStyleVar(); + } + ImGui::EndChild(); + if (popout) { + poppedSize = ImGui::GetWindowSize(); + poppedPos = ImGui::GetWindowPos(); + } + if (freshOpen) { + freshOpen = false; + } + ImGui::End(); +} +} // namespace Ship diff --git a/soh/soh/SohGui/SohMenu.cpp b/soh/soh/SohGui/SohMenu.cpp index 0ca18314c..89779ee6c 100644 --- a/soh/soh/SohGui/SohMenu.cpp +++ b/soh/soh/SohGui/SohMenu.cpp @@ -19,7 +19,8 @@ using namespace UIWidgets; void SohMenu::AddSidebarEntry(std::string sectionName, std::string sidebarName, uint32_t columnCount) { assert(!sectionName.empty()); assert(!sidebarName.empty()); - menuEntries.at(sectionName).sidebars.emplace(sidebarName, SidebarEntry{ .columnCount = columnCount }); + std::string translatedSidebar = LanguageManager::Instance().GetString(sidebarName); + menuEntries.at(sectionName).sidebars.emplace(sidebarName, SidebarEntry{ .columnCount = columnCount, .translatedName = translatedSidebar }); menuEntries.at(sectionName).sidebarOrder.push_back(sidebarName); }