diff --git a/CMakeLists.txt b/CMakeLists.txt index d6956861..e3e31ae5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,9 @@ set(APP_SOURCES source/core/rendering/text.cpp source/core/rendering/texture.cpp + # Core - Locale + source/core/locale/locale.cpp + # Core - Resources source/core/resources/resource_list.cpp source/core/resources/resource_cache.cpp diff --git a/config/assets.yaml b/config/assets.yaml index fe00de98..b48470a8 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -56,6 +56,13 @@ assets: - type: PALETTE path: ${PREFIX}/data/palette/steam-lords.pal + # LOCALE + locale: + - type: DATA + path: ${PREFIX}/data/locale/en.yaml + - type: DATA + path: ${PREFIX}/data/locale/ca.yaml + # INPUT input: - type: DATA diff --git a/data/locale/ca.yaml b/data/locale/ca.yaml new file mode 100644 index 00000000..eb3fe301 --- /dev/null +++ b/data/locale/ca.yaml @@ -0,0 +1,139 @@ +# JailDoctor's Dilemma - Catalan Locale +# lang: ca +# Nota: s'utilitzen nomes caracters ASCII per compatibilitat amb la font del joc + +title: + marquee: "EH JAILEROS!! ES EL 2022 I ENCARA HO PETEM COM EL 1998!!! HEU SENTIT? ELS JAILGAMES HAN TORNAT!! SIII HAN TORNAT!! MES DE 10 TITOLS A LA CUINA DEL JAILDOC!! AIXO ES MOLT, PERO QUIN SERA EL PRIMER? TAMBE HI HA UN NOU APARELL QUE US FARA VOLAR EL CAP AMB JAILGAMES A TOT ARREU: P.A.C.O. PERO ESPERA! QUE ES AQUELLA BELLESA QUE VEIG ALLA? OOOH AQUELLA PETITA MINIASCII ES PUR AMOR!! VULL LLEPAR CADA BYTE! OH MERDA! I NO OBLIDEU PORTAR AQUELLS VELLS I GRASSOS JAILGAMES DE MS-DOS A GITHUB PER MANTENIR-LOS VIUS!! QUIN SERA EL PROPER LLANCAMENT DEL JAILDOC? QUIN PROJECTE COBRARA VIDA?? OH NANOS NO HO SABEM PERO AQUI PODEU TROBAR LA RESPOSTA, NOMES HEU DE COMPLETAR EL DILEMA DEL JAILDOCTOR ... PODEU?" + menu: + play: "1. JUGAR" + keyboard: "2. REDEFINIR TECLAT" + joystick: "3. REDEFINIR JOYSTICK" + projects: "4. PROJECTES" + keys: + prompt0: "PREM TECLA PER ESQUERRA" + prompt1: "PREM TECLA PER DRETA" + prompt2: "PREM TECLA PER SALTAR" + defined: "TECLES DEFINIDES" + label0: "ESQUERRA: " + label1: "DRETA: " + label2: "SALT: " + invalid: "TECLA INVALIDA! PROVA UNA ALTRA" + already_used: "TECLA JA USADA! PROVA UNA ALTRA" + buttons: + prompt0: "PREM BOTO PER ESQUERRA" + prompt1: "PREM BOTO PER DRETA" + prompt2: "PREM BOTO PER SALTAR" + defined: "BOTONS DEFINITS" + already_used: "BOTO JA USAT! PROVA UN ALTRE" + projects: "PROJECTES" + +game_over: + title: "G A M E O V E R" + items: "OBJECTES: " + rooms: "SALES: " + worst_nightmare: "EL TEU PITJOR MALSON ES" + +ending: + t0: "FINALMENT HO VA ACONSEGUIR" + t1: "ARRIBAR A LA PRESO" + t2: "AMB TOTS ELS SEUS PROJECTES" + t3: "A PUNT PER SER ALLIBERATS" + t4: "TOTS ELS JAILEROS HI EREN" + t5: "ESPERANT QUE ELS JAILGAMES" + t6: "FOSSIN ALLIBERATS" + t7: "HI HAVIA FINS I TOT BARRULLS I" + t8: "PRINCIPIANTS ENTRE LA MULTITUD" + t9: "LA BRY PLORAVA..." + t10: "PERO DE SOBTE ALGUNA COSA" + t11: "VA ATREURE LA SEVA ATENCIO" + t12: "UN MUNT DE FERALLA!" + t13: "PLE DE TRASTOS QUE NO FUNCIONEN!!" + t14: "I ALESHORES," + t15: "QUARANTA NOUS PROJECTES" + t16: "VAN NEIXER..." + +ending2: + starring: "PROTAGONISTES" + jaildoctor: "JAILDOCTOR" + thank_you: "GRACIES" + for_playing: "PER JUGAR!" + +credits: + instructions: "INSTRUCCIONS:" + l0: "AJUDA AL JAILDOC A RECUPERAR" + l1: "ELS SEUS PROJECTES I ANAR A" + l2: "LA PRESO PER ACABAR-LOS" + keys: "TECLES:" + keys_move: "CURSORS PER MOURE I SALTAR" + f8: "F8 ACTIVAR/DESACTIVAR MUSICA" + f11: "F11 PAUSAR EL JOC" + f1f2: "F1-F2 MIDA DE LA FINESTRA" + f3: "F3 PANTALLA COMPLETA" + f9: "F9 VORA DE LA PANTALLA" + author: "UN JOC DE JAILDESIGNER" + date: "FET A L'ESTIU/TARDOR DEL 2022" + love: "M'ENCANTEN ELS JAILGAMES! " + +achievements: + header: "ASSOLIMENT DESBLOQUEJAT!" + c1: "COSES BRILLANTS" + d1: "Obteniu el 25% dels objectes" + c2: "A MITAT DE CAMI" + d2: "Obteniu el 50% dels objectes" + c3: "QUASI HI SOM" + d3: "Obteniu el 75% dels objectes" + c4: "EL COL LECCIONISTA" + d4: "Obteniu el 100% dels objectes" + c5: "PASSEJANT PER AQUI" + d5: "Visiteu 20 sales" + c6: "M'HE PERDUT" + d6: "Visiteu 40 sales" + c7: "M'AGRADA EXPLORAR" + d7: "Visiteu totes les sales" + c8: "ACABA EL JOC" + d8: "Completa el joc" + c9: "EM VA XUCLAR UN FORAT" + d9: "Completa el joc sense entrar a la preso" + c10: "ELS MEUS PROJECTES" + d10: "Completa el joc amb tots els objectes" + c11: "M'AGRADEN ELS MEUS AMICS DE COLORS" + d11: "Completa el joc sense morir" + c12: "PROJECTES MALS FETS DE PRESSA" + d12: "Completa el joc en menys de 30 minuts" + +ui: + press_again_menu: "PREM DE NOU PER TORNAR AL MENU" + press_again_exit: "PREM DE NOU PER SORTIR" + border_enabled: "BORDE ACTIVAT" + border_disabled: "BORDE DESACTIVAT" + fullscreen_enabled: "PANTALLA COMPLETA ACTIVADA" + fullscreen_disabled: "PANTALLA COMPLETA DESACTIVADA" + window_zoom: "ZOOM FINESTRA x" + postfx_enabled: "POSTFX ACTIVAT" + postfx_disabled: "POSTFX DESACTIVAT" + postfx: "POSTFX" + palette: "PALETA" + integer_scale_enabled: "ESCALA ENTERA ACTIVADA" + integer_scale_disabled: "ESCALA ENTERA DESACTIVADA" + vsync_enabled: "V-SYNC ACTIVAT" + vsync_disabled: "V-SYNC DESACTIVAT" + +scoreboard: + items: "Objectes col.lec. " + time: " Temps " + rooms: "Sales" + +game: + music_enabled: "MUSICA ACTIVADA" + music_disabled: "MUSICA DESACTIVADA" + paused: "JOC EN PAUSA" + running: "JOC EN MARXA" + enabled: " ACTIVAT" + disabled: " DESACTIVAT" + cheat_infinite_lives: "VIDES INFINITES" + cheat_invincible: "INVENCIBLE" + cheat_jail_open: "PRESO OBERTA" + debug_enabled: "DEBUG ACTIVAT" + debug_disabled: "DEBUG DESACTIVAT" + achievement_unlocked: "ASSOLIMENT DESBLOQUEJAT!" + c11: "M'AGRADEN ELS MEUS AMICS DE COLORS" diff --git a/data/locale/en.yaml b/data/locale/en.yaml new file mode 100644 index 00000000..d9035b21 --- /dev/null +++ b/data/locale/en.yaml @@ -0,0 +1,138 @@ +# JailDoctor's Dilemma - English Locale +# lang: en + +title: + marquee: "HEY JAILERS!! IT'S 2022 AND WE'RE STILL ROCKING LIKE IT'S 1998!!! HAVE YOU HEARD IT? JAILGAMES ARE BACK!! YEEESSS BACK!! MORE THAN 10 TITLES ON JAILDOC'S KITCHEN!! THATS A LOOOOOOT OF JAILGAMES, BUT WHICH ONE WILL STRIKE FIRST? THERE IS ALSO A NEW DEVICE TO COME THAT WILL BLOW YOUR MIND WITH JAILGAMES ON THE GO: P.A.C.O. BUT WAIT! WHAT'S THAT BEAUTY I'M SEEING RIGHT OVER THERE?? OOOH THAT TINY MINIASCII IS PURE LOVE!! I WANT TO LICK EVERY BYTE OF IT!! OH SHIT! AND DON'T FORGET TO BRING BACK THOSE OLD AND FAT MS-DOS JAILGAMES TO GITHUB TO KEEP THEM ALIVE!! WHAT WILL BE THE NEXT JAILDOC RELEASE? WHAT WILL BE THE NEXT PROJECT TO COME ALIVE?? OH BABY WE DON'T KNOW BUT HERE YOU CAN FIND THE ANSWER, YOU JUST HAVE TO COMPLETE JAILDOCTOR'S DILEMMA ... COULD YOU?" + menu: + play: "1. PLAY" + keyboard: "2. REDEFINE KEYBOARD" + joystick: "3. REDEFINE JOYSTICK" + projects: "4. PROJECTS" + keys: + prompt0: "PRESS KEY FOR LEFT" + prompt1: "PRESS KEY FOR RIGHT" + prompt2: "PRESS KEY FOR JUMP" + defined: "KEYS DEFINED" + label0: "LEFT: " + label1: "RIGHT: " + label2: "JUMP: " + invalid: "INVALID KEY! TRY ANOTHER" + already_used: "KEY ALREADY USED! TRY ANOTHER" + buttons: + prompt0: "PRESS BUTTON FOR LEFT" + prompt1: "PRESS BUTTON FOR RIGHT" + prompt2: "PRESS BUTTON FOR JUMP" + defined: "BUTTONS DEFINED" + already_used: "BUTTON ALREADY USED! TRY ANOTHER" + projects: "PROJECTS" + +game_over: + title: "G A M E O V E R" + items: "ITEMS: " + rooms: "ROOMS: " + worst_nightmare: "YOUR WORST NIGHTMARE IS" + +ending: + t0: "HE FINALLY MANAGED" + t1: "TO GET TO THE JAIL" + t2: "WITH ALL HIS PROJECTS" + t3: "READY TO BE FREED" + t4: "ALL THE JAILERS WERE THERE" + t5: "WAITING FOR THE JAILGAMES" + t6: "TO BE RELEASED" + t7: "THERE WERE EVEN BARRULLS AND" + t8: "BEGINNERS AMONG THE CROWD" + t9: "BRY WAS CRYING..." + t10: "BUT SUDDENLY SOMETHING" + t11: "CAUGHT HIS ATTENTION" + t12: "A PILE OF JUNK!" + t13: "FULL OF NON WORKING TRASH!!" + t14: "AND THEN," + t15: "FOURTY NEW PROJECTS" + t16: "WERE BORN..." + +ending2: + starring: "STARRING" + jaildoctor: "JAILDOCTOR" + thank_you: "THANK YOU" + for_playing: "FOR PLAYING!" + +credits: + instructions: "INSTRUCTIONS:" + l0: "HELP JAILDOC TO GET BACK ALL" + l1: "HIS PROJECTS AND GO TO THE" + l2: "JAIL TO FINISH THEM" + keys: "KEYS:" + keys_move: "CURSORS TO MOVE AND JUMP" + f8: "F8 TOGGLE THE MUSIC" + f11: "F11 PAUSE THE GAME" + f1f2: "F1-F2 WINDOWS SIZE" + f3: "F3 TOGGLE FULLSCREEN" + f9: "F9 TOOGLE BORDER SCREEN" + author: "A GAME BY JAILDESIGNER" + date: "MADE ON SUMMER/FALL 2022" + love: "I LOVE JAILGAMES! " + +achievements: + header: "ACHIEVEMENT UNLOCKED!" + c1: "SHINY THINGS" + d1: "Get 25% of the items" + c2: "HALF THE WORK" + d2: "Get 50% of the items" + c3: "GETTING THERE" + d3: "Get 75% of the items" + c4: "THE COLLECTOR" + d4: "Get 100% of the items" + c5: "WANDERING AROUND" + d5: "Visit 20 rooms" + c6: "I GOT LOST" + d6: "Visit 40 rooms" + c7: "I LIKE TO EXPLORE" + d7: "Visit all rooms" + c8: "FINISH THE GAME" + d8: "Complete the game" + c9: "I WAS SUCKED BY A HOLE" + d9: "Complete the game without entering the jail" + c10: "MY LITTLE PROJECTS" + d10: "Complete the game with all items" + c11: "I LIKE MY MULTICOLOURED FRIENDS" + d11: "Complete the game without dying" + c12: "SHIT PROJECTS DONE FAST" + d12: "Complete the game in under 30 minutes" + +ui: + press_again_menu: "PRESS AGAIN TO RETURN TO MENU" + press_again_exit: "PRESS AGAIN TO EXIT" + border_enabled: "BORDER ENABLED" + border_disabled: "BORDER DISABLED" + fullscreen_enabled: "FULLSCREEN ENABLED" + fullscreen_disabled: "FULLSCREEN DISABLED" + window_zoom: "WINDOW ZOOM x" + postfx_enabled: "POSTFX ENABLED" + postfx_disabled: "POSTFX DISABLED" + postfx: "POSTFX" + palette: "PALETTE" + integer_scale_enabled: "INTEGER SCALE ENABLED" + integer_scale_disabled: "INTEGER SCALE DISABLED" + vsync_enabled: "V-SYNC ENABLED" + vsync_disabled: "V-SYNC DISABLED" + +scoreboard: + items: "Items collected " + time: " Time " + rooms: "Rooms" + +game: + music_enabled: "MUSIC ENABLED" + music_disabled: "MUSIC DISABLED" + paused: "GAME PAUSED" + running: "GAME RUNNING" + enabled: " ENABLED" + disabled: " DISABLED" + cheat_infinite_lives: "INFINITE LIVES" + cheat_invincible: "INVINCIBLE" + cheat_jail_open: "JAIL IS OPEN" + debug_enabled: "DEBUG ENABLED" + debug_disabled: "DEBUG DISABLED" + achievement_unlocked: "ACHIEVEMENT UNLOCKED!" + c11: "I LIKE MY MULTICOLOURED FRIENDS" diff --git a/data/room/01.yaml b/data/room/01.yaml index 2a4fd331..753498a2 100644 --- a/data/room/01.yaml +++ b/data/room/01.yaml @@ -1,6 +1,7 @@ # THE JAIL room: - name: "THE JAIL" + name_en: "THE JAIL" + name_ca: "LA PRESO" bgColor: bright_blue border: blue tileSetFile: standard.gif diff --git a/data/room/02.yaml b/data/room/02.yaml index 2a2a2838..62f7b9b3 100644 --- a/data/room/02.yaml +++ b/data/room/02.yaml @@ -1,6 +1,7 @@ # ROAD TO THE JAIL room: - name: "ROAD TO THE JAIL" + name_en: "ROAD TO THE JAIL" + name_ca: "CAMI A LA PRESO" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/03.yaml b/data/room/03.yaml index 89fe68eb..dfbd896b 100644 --- a/data/room/03.yaml +++ b/data/room/03.yaml @@ -1,6 +1,7 @@ # VOID MAIN room: - name: "VOID MAIN" + name_en: "VOID MAIN" + name_ca: "VOID MAIN" bgColor: black border: magenta tileSetFile: standard.gif diff --git a/data/room/04.yaml b/data/room/04.yaml index 4a8588cb..ecccd1b8 100644 --- a/data/room/04.yaml +++ b/data/room/04.yaml @@ -1,6 +1,7 @@ # JUMP THROUGH room: - name: "JUMP THROUGH" + name_en: "JUMP THROUGH" + name_ca: "SALTA A TRAVES" bgColor: black border: cyan tileSetFile: standard.gif diff --git a/data/room/05.yaml b/data/room/05.yaml index 76ebe5fd..6e3159b1 100644 --- a/data/room/05.yaml +++ b/data/room/05.yaml @@ -1,6 +1,7 @@ # BIG JUMP room: - name: "BIG JUMP" + name_en: "BIG JUMP" + name_ca: "GRAN SALT" bgColor: black border: red tileSetFile: standard.gif diff --git a/data/room/06.yaml b/data/room/06.yaml index 5fb3cc97..1dd52c7b 100644 --- a/data/room/06.yaml +++ b/data/room/06.yaml @@ -1,6 +1,7 @@ # WELCOME TO MY ABBEY room: - name: "WELCOME TO MY ABBEY" + name_en: "WELCOME TO MY ABBEY" + name_ca: "BENVINGUT A LA MEVA ABADIA" bgColor: blue border: yellow tileSetFile: standard.gif diff --git a/data/room/07.yaml b/data/room/07.yaml index 133c9fab..ebe500f8 100644 --- a/data/room/07.yaml +++ b/data/room/07.yaml @@ -1,6 +1,7 @@ # SIGMASUA > TELEGRAM room: - name: "SIGMASUA > TELEGRAM" + name_en: "SIGMASUA > TELEGRAM" + name_ca: "SIGMASUA > TELEGRAM" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/08.yaml b/data/room/08.yaml index ffc03b22..1a181fcd 100644 --- a/data/room/08.yaml +++ b/data/room/08.yaml @@ -1,6 +1,7 @@ # THE GARDEN room: - name: "THE GARDEN" + name_en: "THE GARDEN" + name_ca: "EL JARDI" bgColor: black border: cyan tileSetFile: standard.gif diff --git a/data/room/09.yaml b/data/room/09.yaml index 775fc9c0..a10558d0 100644 --- a/data/room/09.yaml +++ b/data/room/09.yaml @@ -1,6 +1,7 @@ # THE BIG TREE room: - name: "THE BIG TREE" + name_en: "THE BIG TREE" + name_ca: "EL ARBRE GRAN" bgColor: black border: bright_blue tileSetFile: standard.gif diff --git a/data/room/10.yaml b/data/room/10.yaml index a3427523..b3002c0e 100644 --- a/data/room/10.yaml +++ b/data/room/10.yaml @@ -1,6 +1,7 @@ # TREE TOP room: - name: "TREE TOP" + name_en: "TREE TOP" + name_ca: "CIMA DE L'ARBRE" bgColor: bright_black border: blue tileSetFile: standard.gif diff --git a/data/room/11.yaml b/data/room/11.yaml index e2ba7802..cb41b3c2 100644 --- a/data/room/11.yaml +++ b/data/room/11.yaml @@ -1,6 +1,7 @@ # LAZY ROOM room: - name: "LAZY ROOM" + name_en: "LAZY ROOM" + name_ca: "SALA MANDROSA" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/12.yaml b/data/room/12.yaml index 4f0d3cc3..1884077d 100644 --- a/data/room/12.yaml +++ b/data/room/12.yaml @@ -1,6 +1,7 @@ # THE PASSAGE room: - name: "THE PASSAGE" + name_en: "THE PASSAGE" + name_ca: "EL PASSATGE" bgColor: black border: green tileSetFile: standard.gif diff --git a/data/room/13.yaml b/data/room/13.yaml index b18e415f..d75f5ddf 100644 --- a/data/room/13.yaml +++ b/data/room/13.yaml @@ -1,6 +1,7 @@ # TUNO KILLER room: - name: "TUNO KILLER" + name_en: "TUNO KILLER" + name_ca: "ASSASSI DE TUNERS" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/14.yaml b/data/room/14.yaml index 44f87dd7..eda80293 100644 --- a/data/room/14.yaml +++ b/data/room/14.yaml @@ -1,6 +1,7 @@ # KILLING SPREE room: - name: "KILLING SPREE" + name_en: "KILLING SPREE" + name_ca: "MATANCA INDISCRIMINADA" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/15.yaml b/data/room/15.yaml index cfb97615..9185e374 100644 --- a/data/room/15.yaml +++ b/data/room/15.yaml @@ -1,6 +1,7 @@ # NOW THIS IS THE BATCAVE! room: - name: "NOW THIS IS THE BATCAVE!" + name_en: "NOW THIS IS THE BATCAVE!" + name_ca: "AQUESTA SI QUE ES LA BATCOVA!" bgColor: black border: black tileSetFile: standard.gif diff --git a/data/room/16.yaml b/data/room/16.yaml index 8e00a6ab..89663aaa 100644 --- a/data/room/16.yaml +++ b/data/room/16.yaml @@ -1,6 +1,7 @@ # THE FRIDGE room: - name: "THE FRIDGE" + name_en: "THE FRIDGE" + name_ca: "LA NEVERA" bgColor: blue border: blue tileSetFile: standard.gif diff --git a/data/room/17.yaml b/data/room/17.yaml index cc27cb8c..2a29a4c1 100644 --- a/data/room/17.yaml +++ b/data/room/17.yaml @@ -1,6 +1,7 @@ # I DID NOT COPY THIS ONE room: - name: "I DID NOT COPY THIS ONE" + name_en: "I DID NOT COPY THIS ONE" + name_ca: "AQUEST NO EL VAIG COPIAR" bgColor: black border: magenta tileSetFile: standard.gif diff --git a/data/room/18.yaml b/data/room/18.yaml index c042b639..87725c58 100644 --- a/data/room/18.yaml +++ b/data/room/18.yaml @@ -1,6 +1,7 @@ # MAKE MONEY room: - name: "MAKE MONEY" + name_en: "MAKE MONEY" + name_ca: "FES DINERS" bgColor: black border: yellow tileSetFile: standard.gif diff --git a/data/room/19.yaml b/data/room/19.yaml index 895e6716..4dfcbbd9 100644 --- a/data/room/19.yaml +++ b/data/room/19.yaml @@ -1,6 +1,7 @@ # THIS CAN'T BE THE BATCAVE room: - name: "THIS CAN'T BE THE BATCAVE" + name_en: "THIS CAN'T BE THE BATCAVE" + name_ca: "AQUESTA NO POT SER LA BATCOVA" bgColor: black border: cyan tileSetFile: standard.gif diff --git a/data/room/20.yaml b/data/room/20.yaml index 5810cedd..842b8ae8 100644 --- a/data/room/20.yaml +++ b/data/room/20.yaml @@ -1,6 +1,7 @@ # ENTRANCE TO THE VALLEY room: - name: "ENTRANCE TO THE VALLEY" + name_en: "ENTRANCE TO THE VALLEY" + name_ca: "ENTRADA A LA VALL" bgColor: black border: red tileSetFile: standard.gif diff --git a/data/room/21.yaml b/data/room/21.yaml index ca8c8fd7..10072a7a 100644 --- a/data/room/21.yaml +++ b/data/room/21.yaml @@ -1,6 +1,7 @@ # ENTER PAKU SIMBEL room: - name: "ENTER PAKU SIMBEL" + name_en: "ENTER PAKU SIMBEL" + name_ca: "ENTRA A PAKU SIMBEL" bgColor: bright_black border: yellow tileSetFile: standard.gif diff --git a/data/room/22.yaml b/data/room/22.yaml index 8a6bddc7..f2f279d3 100644 --- a/data/room/22.yaml +++ b/data/room/22.yaml @@ -1,6 +1,7 @@ # AEE REMAKE, PLEASE room: - name: "AEE REMAKE, PLEASE" + name_en: "AEE REMAKE, PLEASE" + name_ca: "AEE REMAKE, PLAU" bgColor: bright_black border: yellow tileSetFile: standard.gif diff --git a/data/room/23.yaml b/data/room/23.yaml index 25ebd75a..81bfb554 100644 --- a/data/room/23.yaml +++ b/data/room/23.yaml @@ -1,6 +1,7 @@ # INNER CHAMBER room: - name: "INNER CHAMBER" + name_en: "INNER CHAMBER" + name_ca: "CAMBRA INTERIOR" bgColor: black border: bright_yellow tileSetFile: standard.gif diff --git a/data/room/24.yaml b/data/room/24.yaml index e3a7c5bd..96a48a5a 100644 --- a/data/room/24.yaml +++ b/data/room/24.yaml @@ -1,6 +1,7 @@ # PLAY IT AGAIN, SAM room: - name: "PLAY IT AGAIN, SAM" + name_en: "PLAY IT AGAIN, SAM" + name_ca: "TORNA A TOCAR, SAM" bgColor: black border: bright_yellow tileSetFile: standard.gif diff --git a/data/room/25.yaml b/data/room/25.yaml index db0a561e..eb60ff8b 100644 --- a/data/room/25.yaml +++ b/data/room/25.yaml @@ -1,6 +1,7 @@ # THE CHAPPEL room: - name: "THE CHAPPEL" + name_en: "THE CHAPPEL" + name_ca: "LA CAPELLA" bgColor: blue border: yellow tileSetFile: standard.gif diff --git a/data/room/26.yaml b/data/room/26.yaml index 72ed9a9f..ac1aca70 100644 --- a/data/room/26.yaml +++ b/data/room/26.yaml @@ -1,6 +1,7 @@ # JINGLE BELLS room: - name: "JINGLE BELLS" + name_en: "JINGLE BELLS" + name_ca: "JINGLE BELLS" bgColor: blue border: yellow tileSetFile: standard.gif diff --git a/data/room/27.yaml b/data/room/27.yaml index 467189e6..b0d7d55c 100644 --- a/data/room/27.yaml +++ b/data/room/27.yaml @@ -1,6 +1,7 @@ # THE BACKYARD room: - name: "THE BACKYARD" + name_en: "THE BACKYARD" + name_ca: "EL PATI" bgColor: blue border: cyan tileSetFile: standard.gif diff --git a/data/room/28.yaml b/data/room/28.yaml index fb22ca4e..779b0cc8 100644 --- a/data/room/28.yaml +++ b/data/room/28.yaml @@ -1,6 +1,7 @@ # YOU SHALL NOT PASS room: - name: "YOU SHALL NOT PASS" + name_en: "YOU SHALL NOT PASS" + name_ca: "NO PASSARAS" bgColor: bright_black border: black tileSetFile: standard.gif diff --git a/data/room/29.yaml b/data/room/29.yaml index 39fc6312..cebe349d 100644 --- a/data/room/29.yaml +++ b/data/room/29.yaml @@ -1,6 +1,7 @@ # QUO VOIDIS room: - name: "QUO VOIDIS" + name_en: "QUO VOIDIS" + name_ca: "QUO VOIDIS" bgColor: blue border: bright_black tileSetFile: standard.gif diff --git a/data/room/30.yaml b/data/room/30.yaml index 01e703e4..658d16d4 100644 --- a/data/room/30.yaml +++ b/data/room/30.yaml @@ -1,6 +1,7 @@ # QVOID IS A JAILGAME! room: - name: "QVOID IS A JAILGAME!" + name_en: "QVOID IS A JAILGAME!" + name_ca: "QVOID ES UN JAILGAME!" bgColor: blue border: bright_black tileSetFile: standard.gif diff --git a/data/room/31.yaml b/data/room/31.yaml index f3e1a45f..235ae386 100644 --- a/data/room/31.yaml +++ b/data/room/31.yaml @@ -1,6 +1,7 @@ # 256 COLORS room: - name: "256 COLORS" + name_en: "256 COLORS" + name_ca: "256 COLORS" bgColor: black border: bright_magenta tileSetFile: standard.gif diff --git a/data/room/32.yaml b/data/room/32.yaml index e8ce9524..8b2f547d 100644 --- a/data/room/32.yaml +++ b/data/room/32.yaml @@ -1,6 +1,7 @@ # ...? room: - name: "...?" + name_en: "...?" + name_ca: "...?" bgColor: black border: cyan tileSetFile: standard.gif diff --git a/data/room/33.yaml b/data/room/33.yaml index 7f75ad65..8d04c228 100644 --- a/data/room/33.yaml +++ b/data/room/33.yaml @@ -1,6 +1,7 @@ # } WE ALL LOVE JAILGAMES } room: - name: "} WE ALL LOVE JAILGAMES }" + name_en: "} WE ALL LOVE JAILGAMES }" + name_ca: "} TOTS ESTIMEM JAILGAMES }" bgColor: black border: bright_black tileSetFile: standard.gif diff --git a/data/room/34.yaml b/data/room/34.yaml index 77d0f4aa..4922e884 100644 --- a/data/room/34.yaml +++ b/data/room/34.yaml @@ -1,6 +1,7 @@ # ULA HOP! room: - name: "ULA HOP!" + name_en: "ULA HOP!" + name_ca: "ULA HOP!" bgColor: black border: cyan tileSetFile: standard.gif diff --git a/data/room/35.yaml b/data/room/35.yaml index a83f69a7..201eeda7 100644 --- a/data/room/35.yaml +++ b/data/room/35.yaml @@ -1,6 +1,7 @@ # SILICON BOOBS room: - name: "SILICON BOOBS" + name_en: "SILICON BOOBS" + name_ca: "PITS DE SILICI" bgColor: black border: bright_green tileSetFile: standard.gif diff --git a/data/room/36.yaml b/data/room/36.yaml index 174e9aac..3310663c 100644 --- a/data/room/36.yaml +++ b/data/room/36.yaml @@ -1,6 +1,7 @@ # BE CAREFUL WITH THE FUSE room: - name: "BE CAREFUL WITH THE FUSE" + name_en: "BE CAREFUL WITH THE FUSE" + name_ca: "VIGILA AMB EL FUSIBLE" bgColor: black border: bright_cyan tileSetFile: standard.gif diff --git a/data/room/37.yaml b/data/room/37.yaml index 449e06dd..37fbd243 100644 --- a/data/room/37.yaml +++ b/data/room/37.yaml @@ -1,6 +1,7 @@ # CHIP'N CHIP room: - name: "CHIP'N CHIP" + name_en: "CHIP'N CHIP" + name_ca: "CHIP'N CHIP" bgColor: black border: bright_green tileSetFile: standard.gif diff --git a/data/room/38.yaml b/data/room/38.yaml index 39771de6..0723a810 100644 --- a/data/room/38.yaml +++ b/data/room/38.yaml @@ -1,6 +1,7 @@ # THE FINAL CROSSOVER room: - name: "THE FINAL CROSSOVER" + name_en: "THE FINAL CROSSOVER" + name_ca: "EL CROSSOVER FINAL" bgColor: bright_black border: yellow tileSetFile: standard.gif diff --git a/data/room/39.yaml b/data/room/39.yaml index e5bb95d5..2246fa9c 100644 --- a/data/room/39.yaml +++ b/data/room/39.yaml @@ -1,6 +1,7 @@ # YOU'LL BELIEVE AROUNDER CAN FLY room: - name: "YOU'LL BELIEVE AROUNDER CAN FLY" + name_en: "YOU'LL BELIEVE AROUNDER CAN FLY" + name_ca: "CREUREU QUE AROUNDER POT VOLAR" bgColor: black border: cyan tileSetFile: standard.gif diff --git a/data/room/40.yaml b/data/room/40.yaml index b18aab70..0049bd1d 100644 --- a/data/room/40.yaml +++ b/data/room/40.yaml @@ -1,6 +1,7 @@ # PREVENT THE CRISIS room: - name: "PREVENT THE CRISIS" + name_en: "PREVENT THE CRISIS" + name_ca: "PREVEU LA CRISI" bgColor: black border: bright_magenta tileSetFile: standard.gif diff --git a/data/room/41.yaml b/data/room/41.yaml index e853150c..116ba634 100644 --- a/data/room/41.yaml +++ b/data/room/41.yaml @@ -1,6 +1,7 @@ # AROUND WITH ME room: - name: "AROUND WITH ME" + name_en: "AROUND WITH ME" + name_ca: "VOLTA AMB MI" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/42.yaml b/data/room/42.yaml index 2bf72809..d9b3bd02 100644 --- a/data/room/42.yaml +++ b/data/room/42.yaml @@ -1,6 +1,7 @@ # P.A.C.O. ON THE GO room: - name: "P.A.C.O. ON THE GO" + name_en: "P.A.C.O. ON THE GO" + name_ca: "P.A.C.O. EN MARXA" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/43.yaml b/data/room/43.yaml index 85f23638..e6df19af 100644 --- a/data/room/43.yaml +++ b/data/room/43.yaml @@ -1,6 +1,7 @@ # THE TUBE room: - name: "THE TUBE" + name_en: "THE TUBE" + name_ca: "EL TUB" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/44.yaml b/data/room/44.yaml index 63f3e43a..1e6a942c 100644 --- a/data/room/44.yaml +++ b/data/room/44.yaml @@ -1,6 +1,7 @@ # SANDWITCH AND COUNTER room: - name: "SANDWITCH AND COUNTER" + name_en: "SANDWITCH AND COUNTER" + name_ca: "SANDVITX I COMPTADOR" bgColor: black border: cyan tileSetFile: standard.gif diff --git a/data/room/45.yaml b/data/room/45.yaml index 105adeaf..bef8063a 100644 --- a/data/room/45.yaml +++ b/data/room/45.yaml @@ -1,6 +1,7 @@ # FEEL THE PRESSURE room: - name: "FEEL THE PRESSURE" + name_en: "FEEL THE PRESSURE" + name_ca: "NOTA LA PRESSIO" bgColor: bright_black border: bright_yellow tileSetFile: standard.gif diff --git a/data/room/46.yaml b/data/room/46.yaml index 6eb5cd43..94c822ef 100644 --- a/data/room/46.yaml +++ b/data/room/46.yaml @@ -1,6 +1,7 @@ # FEEL THE HEAT room: - name: "FEEL THE HEAT" + name_en: "FEEL THE HEAT" + name_ca: "NOTA LA CALOR" bgColor: bright_black border: bright_yellow tileSetFile: standard.gif diff --git a/data/room/47.yaml b/data/room/47.yaml index 11e0d035..70f8251f 100644 --- a/data/room/47.yaml +++ b/data/room/47.yaml @@ -1,6 +1,7 @@ # THE BATTLE NEVER ENDS room: - name: "THE BATTLE NEVER ENDS" + name_en: "THE BATTLE NEVER ENDS" + name_ca: "LA BATALLA MAI ACABA" bgColor: black border: white tileSetFile: standard.gif diff --git a/data/room/48.yaml b/data/room/48.yaml index 99a6c9b2..b5999df5 100644 --- a/data/room/48.yaml +++ b/data/room/48.yaml @@ -1,6 +1,7 @@ # WELCOME TO THE JAILBATTLE room: - name: "WELCOME TO THE JAILBATTLE" + name_en: "WELCOME TO THE JAILBATTLE" + name_ca: "BENVINGUTS A LA JAILTAULA" bgColor: green border: bright_green tileSetFile: standard.gif diff --git a/data/room/49.yaml b/data/room/49.yaml index c8fc2be8..299593a2 100644 --- a/data/room/49.yaml +++ b/data/room/49.yaml @@ -1,6 +1,7 @@ # WE NEED A ROBOT room: - name: "WE NEED A ROBOT" + name_en: "WE NEED A ROBOT" + name_ca: "NECESSITEM UN ROBOT" bgColor: black border: red tileSetFile: standard.gif diff --git a/data/room/50.yaml b/data/room/50.yaml index 125870b3..ae953b33 100644 --- a/data/room/50.yaml +++ b/data/room/50.yaml @@ -1,6 +1,7 @@ # STORED JAILGAMES room: - name: "STORED JAILGAMES" + name_en: "STORED JAILGAMES" + name_ca: "JAILGAMES EMMAGATZEMATS" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/51.yaml b/data/room/51.yaml index c4b4ea00..bf82df9f 100644 --- a/data/room/51.yaml +++ b/data/room/51.yaml @@ -1,6 +1,7 @@ # MINI ASCII room: - name: "MINI ASCII" + name_en: "MINI ASCII" + name_ca: "MINI ASCII" bgColor: black border: black tileSetFile: standard.gif diff --git a/data/room/52.yaml b/data/room/52.yaml index f321e436..931a60d6 100644 --- a/data/room/52.yaml +++ b/data/room/52.yaml @@ -1,6 +1,7 @@ # BREAKOUT.LUA room: - name: "BREAKOUT.LUA" + name_en: "BREAKOUT.LUA" + name_ca: "BREAKOUT.LUA" bgColor: black border: black tileSetFile: standard.gif diff --git a/data/room/53.yaml b/data/room/53.yaml index a34d7f21..47487600 100644 --- a/data/room/53.yaml +++ b/data/room/53.yaml @@ -1,6 +1,7 @@ # P.A.C.O. WORKSHOP room: - name: "P.A.C.O. WORKSHOP" + name_en: "P.A.C.O. WORKSHOP" + name_ca: "TALLER DE P.A.C.O." bgColor: black border: yellow tileSetFile: standard.gif diff --git a/data/room/54.yaml b/data/room/54.yaml index c40a2ac5..53b681a2 100644 --- a/data/room/54.yaml +++ b/data/room/54.yaml @@ -1,6 +1,7 @@ # THE BASEMENT room: - name: "THE BASEMENT" + name_en: "THE BASEMENT" + name_ca: "EL SOTERRANI" bgColor: black border: blue tileSetFile: standard.gif diff --git a/data/room/55.yaml b/data/room/55.yaml index 2a74b470..a7812a2a 100644 --- a/data/room/55.yaml +++ b/data/room/55.yaml @@ -1,6 +1,7 @@ # THAT'S A GUITAR room: - name: "THAT'S A GUITAR" + name_en: "THAT'S A GUITAR" + name_ca: "AIXO ES UNA GUITARRA" bgColor: black border: black tileSetFile: standard.gif diff --git a/data/room/56.yaml b/data/room/56.yaml index 79eb1c46..61aa494e 100644 --- a/data/room/56.yaml +++ b/data/room/56.yaml @@ -1,6 +1,7 @@ # HEAVY DEMONS ON LEGGINS room: - name: "HEAVY DEMONS ON LEGGINS" + name_en: "HEAVY DEMONS ON LEGGINS" + name_ca: "DIMONIS PESATS AMB MALLES" bgColor: black border: black tileSetFile: standard.gif diff --git a/data/room/57.yaml b/data/room/57.yaml index afac50db..7e05fa8a 100644 --- a/data/room/57.yaml +++ b/data/room/57.yaml @@ -1,6 +1,7 @@ # JAILGAMES GO TO HELL room: - name: "JAILGAMES GO TO HELL" + name_en: "JAILGAMES GO TO HELL" + name_ca: "JAILGAMES ANEU A L'INFERN" bgColor: red border: bright_red tileSetFile: standard.gif diff --git a/data/room/58.yaml b/data/room/58.yaml index 910a8f77..fc8d52c0 100644 --- a/data/room/58.yaml +++ b/data/room/58.yaml @@ -1,6 +1,7 @@ # CHIRPING room: - name: "CHIRPING" + name_en: "CHIRPING" + name_ca: "PIULANT" bgColor: black border: magenta tileSetFile: standard.gif diff --git a/data/room/59.yaml b/data/room/59.yaml index 7da43033..de148db6 100644 --- a/data/room/59.yaml +++ b/data/room/59.yaml @@ -1,6 +1,7 @@ # STATIC room: - name: "STATIC" + name_en: "STATIC" + name_ca: "ESTATICA" bgColor: black border: bright_magenta tileSetFile: standard.gif diff --git a/data/room/60.yaml b/data/room/60.yaml index f0b2c853..1a52e55b 100644 --- a/data/room/60.yaml +++ b/data/room/60.yaml @@ -1,6 +1,7 @@ # MAGNETIC FIELDS room: - name: "MAGNETIC FIELDS" + name_en: "MAGNETIC FIELDS" + name_ca: "CAMPS MAGNETICS" bgColor: black border: bright_red tileSetFile: standard.gif diff --git a/source/core/input/global_inputs.cpp b/source/core/input/global_inputs.cpp index ee9d7d1a..fc64940a 100644 --- a/source/core/input/global_inputs.cpp +++ b/source/core/input/global_inputs.cpp @@ -6,6 +6,7 @@ #include // Para vector #include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "game/options.hpp" // Para Options, options, OptionsVideo, Section #include "game/scene_manager.hpp" // Para SceneManager @@ -27,7 +28,7 @@ namespace GlobalInputs { if (stringInVector(Notifier::get()->getCodes(), CODE)) { SceneManager::current = SceneManager::Scene::TITLE; } else { - Notifier::get()->show({CODE}, Notifier::Style::DEFAULT, -1, true, CODE); + Notifier::get()->show({Locale::get()->get("ui.press_again_menu")}, Notifier::Style::DEFAULT, -1, true, CODE); } return; } @@ -47,7 +48,7 @@ namespace GlobalInputs { if (stringInVector(Notifier::get()->getCodes(), CODE)) { SceneManager::current = SceneManager::Scene::QUIT; } else { - Notifier::get()->show({CODE}, Notifier::Style::DEFAULT, -1, true, CODE); + Notifier::get()->show({Locale::get()->get("ui.press_again_exit")}, Notifier::Style::DEFAULT, -1, true, CODE); } } @@ -71,58 +72,58 @@ namespace GlobalInputs { void handleToggleBorder() { Screen::get()->toggleBorder(); - Notifier::get()->show({"BORDER " + std::string(Options::video.border.enabled ? "ENABLED" : "DISABLED")}); + Notifier::get()->show({Locale::get()->get(Options::video.border.enabled ? "ui.border_enabled" : "ui.border_disabled")}); } void handleToggleVideoMode() { Screen::get()->toggleVideoMode(); - Notifier::get()->show({"FULLSCREEN " + std::string(static_cast(Options::video.fullscreen) == 0 ? "DISABLED" : "ENABLED")}); + Notifier::get()->show({Locale::get()->get(static_cast(Options::video.fullscreen) == 0 ? "ui.fullscreen_disabled" : "ui.fullscreen_enabled")}); } void handleDecWindowZoom() { if (Screen::get()->decWindowZoom()) { - Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(Options::window.zoom)}); + Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); } } void handleIncWindowZoom() { if (Screen::get()->incWindowZoom()) { - Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(Options::window.zoom)}); + Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)}); } } void handleTogglePostFX() { Screen::get()->togglePostFX(); - Notifier::get()->show({"POSTFX " + std::string(Options::video.postfx ? "ENABLED" : "DISABLED")}); + Notifier::get()->show({Locale::get()->get(Options::video.postfx ? "ui.postfx_enabled" : "ui.postfx_disabled")}); } void handleNextPostFXPreset() { if (!Options::postfx_presets.empty()) { Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast(Options::postfx_presets.size()); Screen::get()->reloadPostFX(); - Notifier::get()->show({"POSTFX " + Options::postfx_presets[static_cast(Options::current_postfx_preset)].name}); + Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + Options::postfx_presets[static_cast(Options::current_postfx_preset)].name}); } } void handleNextPalette() { Screen::get()->nextPalette(); - Notifier::get()->show({"PALETTE " + Options::video.palette}); + Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); } void handlePreviousPalette() { Screen::get()->previousPalette(); - Notifier::get()->show({"PALETTE " + Options::video.palette}); + Notifier::get()->show({Locale::get()->get("ui.palette") + " " + Options::video.palette}); } void handleToggleIntegerScale() { Screen::get()->toggleIntegerScale(); Screen::get()->setVideoMode(Options::video.fullscreen); - Notifier::get()->show({"INTEGER SCALE " + std::string(Options::video.integer_scale ? "ENABLED" : "DISABLED")}); + Notifier::get()->show({Locale::get()->get(Options::video.integer_scale ? "ui.integer_scale_enabled" : "ui.integer_scale_disabled")}); } void handleToggleVSync() { Screen::get()->toggleVSync(); - Notifier::get()->show({"V-SYNC " + std::string(Options::video.vertical_sync ? "ENABLED" : "DISABLED")}); + Notifier::get()->show({Locale::get()->get(Options::video.vertical_sync ? "ui.vsync_enabled" : "ui.vsync_disabled")}); } #ifdef _DEBUG diff --git a/source/core/locale/locale.cpp b/source/core/locale/locale.cpp new file mode 100644 index 00000000..951e899c --- /dev/null +++ b/source/core/locale/locale.cpp @@ -0,0 +1,90 @@ +#include "core/locale/locale.hpp" + +#include +#include +#include + +#include "external/fkyaml_node.hpp" // Para fkyaml::node +#include "game/options.hpp" // Para Options::console + +// [SINGLETON] +Locale* Locale::locale_ = nullptr; + +// [SINGLETON] Crea el objeto con esta función estática +void Locale::init(const std::string& file_path) { + Locale::locale_ = new Locale(); + Locale::locale_->loadFromFile(file_path); +} + +// [SINGLETON] Destruye el objeto con esta función estática +void Locale::destroy() { + delete Locale::locale_; + Locale::locale_ = nullptr; +} + +// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él +auto Locale::get() -> Locale* { + return Locale::locale_; +} + +// Devuelve la traducción de la clave o la clave como fallback +auto Locale::get(const std::string& key) const -> std::string { + auto it = strings_.find(key); + if (it != strings_.end()) { + return it->second; + } + + if (Options::console) { + std::cerr << "Locale: clave no encontrada: " << key << '\n'; + } + return key; +} + +// Aplana un nodo YAML de forma recursiva: {a: {b: "val"}} -> {"a.b" -> "val"} +void Locale::flatten(const void* node_ptr, const std::string& prefix) { + const auto& node = *static_cast(node_ptr); + + for (auto itr = node.begin(); itr != node.end(); ++itr) { + const std::string KEY = prefix.empty() + ? itr.key().get_value() + : prefix + "." + itr.key().get_value(); + + const auto& value = itr.value(); + if (value.is_mapping()) { + flatten(&value, KEY); + } else if (value.is_string()) { + strings_[KEY] = value.get_value(); + } + } +} + +// Carga las traducciones desde el fichero YAML indicado +void Locale::loadFromFile(const std::string& file_path) { + if (file_path.empty()) { + if (Options::console) { + std::cerr << "Locale: ruta de fichero vacía, sin traducciones cargadas\n"; + } + return; + } + + std::ifstream file(file_path); + if (!file.is_open()) { + if (Options::console) { + std::cerr << "Locale: no se puede abrir " << file_path << '\n'; + } + return; + } + + try { + auto yaml = fkyaml::node::deserialize(file); + flatten(&yaml, ""); + + if (Options::console) { + std::cout << "Locale: " << strings_.size() << " traducciones cargadas desde " << file_path << '\n'; + } + } catch (const fkyaml::exception& e) { + if (Options::console) { + std::cerr << "Locale: error al parsear YAML: " << e.what() << '\n'; + } + } +} diff --git a/source/core/locale/locale.hpp b/source/core/locale/locale.hpp new file mode 100644 index 00000000..cd04fb73 --- /dev/null +++ b/source/core/locale/locale.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +// Clase Locale: gestiona las traducciones del juego (singleton) +// Las traducciones se cargan desde un fichero YAML en el inicio. +// No se permite cambio de idioma en caliente. +class Locale { + public: + static void init(const std::string& file_path); // Crea e inicializa el singleton + static void destroy(); // Destruye el singleton + static auto get() -> Locale*; // Devuelve el singleton + + // Devuelve la traducción de la clave dada. + // Si la clave no existe, devuelve la propia clave como fallback. + [[nodiscard]] auto get(const std::string& key) const -> std::string; + + private: + Locale() = default; + void loadFromFile(const std::string& file_path); + void flatten(const void* node_ptr, const std::string& prefix); // Aplana nodos YAML anidados + + static Locale* locale_; + std::unordered_map strings_; +}; diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 7dfac50c..093a4b8e 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -13,6 +13,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/input.hpp" // Para Input, InputAction +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_helper.hpp" // Para ResourceHelper @@ -162,6 +163,14 @@ Director::Director(std::vector const& args) { std::cout << "\n"; // Fin de inicialización de sistemas + // Inicializa el sistema de localización (antes de Cheevos que usa textos traducidos) +#ifdef RELEASE_BUILD + std::string locale_path = executable_path_ + PREFIX + "/data/locale/" + Options::language + ".yaml"; + Locale::init(locale_path); +#else + Locale::init(Resource::List::get()->get(Options::language + ".yaml")); +#endif + // Special handling for cheevos.bin - also needs filesystem path #ifdef RELEASE_BUILD std::string cheevos_path = system_folder_ + "/cheevos.bin"; @@ -177,6 +186,7 @@ Director::~Director() { // Destruye los singletones Cheevos::destroy(); + Locale::destroy(); #ifdef _DEBUG Debug::destroy(); #endif diff --git a/source/game/defaults.hpp b/source/game/defaults.hpp index 579d8577..f0672a0d 100644 --- a/source/game/defaults.hpp +++ b/source/game/defaults.hpp @@ -81,6 +81,10 @@ namespace Defaults::Kiosk { constexpr bool INFINITE_LIVES = false; // Vidas infinitas en modo kiosko desactivadas por defecto } // namespace Defaults::Kiosk +namespace Defaults::Localization { + constexpr const char* LANGUAGE = "en"; // Idioma por defecto (en = inglés, ca = catalán) +} // namespace Defaults::Localization + namespace Defaults::Game::Room { #ifdef _DEBUG constexpr const char* INITIAL = "51.yaml"; // Habitación de inicio en debug diff --git a/source/game/gameplay/cheevos.cpp b/source/game/gameplay/cheevos.cpp index 773c0631..faa9656e 100644 --- a/source/game/gameplay/cheevos.cpp +++ b/source/game/gameplay/cheevos.cpp @@ -8,8 +8,9 @@ #include // Para cout, cerr #include -#include "game/options.hpp" // Para Options, options -#include "game/ui/notifier.hpp" // Para Notifier +#include "core/locale/locale.hpp" // Para Locale +#include "game/options.hpp" // Para Options, options +#include "game/ui/notifier.hpp" // Para Notifier // [SINGLETON] Cheevos* Cheevos::cheevos = nullptr; @@ -44,18 +45,19 @@ Cheevos::~Cheevos() { // Inicializa los logros void Cheevos::init() { cheevos_list_.clear(); - cheevos_list_.emplace_back(Achievement{.id = 1, .caption = "SHINY THINGS", .description = "Get 25% of the items", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 2, .caption = "HALF THE WORK", .description = "Get 50% of the items", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 3, .caption = "GETTING THERE", .description = "Get 75% of the items", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 4, .caption = "THE COLLECTOR", .description = "Get 100% of the items", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 5, .caption = "WANDERING AROUND", .description = "Visit 20 rooms", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 6, .caption = "I GOT LOST", .description = "Visit 40 rooms", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 7, .caption = "I LIKE TO EXPLORE", .description = "Visit all rooms", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 8, .caption = "FINISH THE GAME", .description = "Complete the game", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 9, .caption = "I WAS SUCKED BY A HOLE", .description = "Complete the game without entering the jail", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 10, .caption = "MY LITTLE PROJECTS", .description = "Complete the game with all items", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 11, .caption = "I LIKE MY MULTICOLOURED FRIENDS", .description = "Complete the game without dying", .icon = 2}); - cheevos_list_.emplace_back(Achievement{.id = 12, .caption = "SHIT PROJECTS DONE FAST", .description = "Complete the game in under 30 minutes", .icon = 2}); + auto* loc = Locale::get(); + cheevos_list_.emplace_back(Achievement{.id = 1, .caption = loc->get("achievements.c1"), .description = loc->get("achievements.d1"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 2, .caption = loc->get("achievements.c2"), .description = loc->get("achievements.d2"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 3, .caption = loc->get("achievements.c3"), .description = loc->get("achievements.d3"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 4, .caption = loc->get("achievements.c4"), .description = loc->get("achievements.d4"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 5, .caption = loc->get("achievements.c5"), .description = loc->get("achievements.d5"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 6, .caption = loc->get("achievements.c6"), .description = loc->get("achievements.d6"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 7, .caption = loc->get("achievements.c7"), .description = loc->get("achievements.d7"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 8, .caption = loc->get("achievements.c8"), .description = loc->get("achievements.d8"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 9, .caption = loc->get("achievements.c9"), .description = loc->get("achievements.d9"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 10, .caption = loc->get("achievements.c10"), .description = loc->get("achievements.d10"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 11, .caption = loc->get("achievements.c11"), .description = loc->get("achievements.d11"), .icon = 2}); + cheevos_list_.emplace_back(Achievement{.id = 12, .caption = loc->get("achievements.c12"), .description = loc->get("achievements.d12"), .icon = 2}); } // Busca un logro por id y devuelve el indice @@ -82,7 +84,7 @@ void Cheevos::unlock(int id) { cheevos_list_.at(INDEX).completed = true; // Mostrar notificación en la pantalla - Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", cheevos_list_.at(INDEX).caption}, Notifier::Style::CHEEVO, -1, false); + Notifier::get()->show({Locale::get()->get("achievements.header"), cheevos_list_.at(INDEX).caption}, Notifier::Style::CHEEVO, -1, false); // Guardar el estado de los logros saveToFile(); diff --git a/source/game/gameplay/room_loader.cpp b/source/game/gameplay/room_loader.cpp index 9126656a..50265d84 100644 --- a/source/game/gameplay/room_loader.cpp +++ b/source/game/gameplay/room_loader.cpp @@ -5,6 +5,7 @@ #include "core/resources/resource_helper.hpp" // Para Resource::Helper #include "external/fkyaml_node.hpp" // Para fkyaml::node +#include "game/options.hpp" // Para Options::language #include "utils/defines.hpp" // Para Tile::SIZE #include "utils/utils.hpp" // Para stringToColor @@ -63,7 +64,10 @@ void RoomLoader::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, con room.number = file_name.substr(0, file_name.find_last_of('.')); // Basic properties - if (room_node.contains("name")) { + const std::string LANG_KEY = "name_" + Options::language; + if (room_node.contains(LANG_KEY)) { + room.name = room_node[LANG_KEY].get_value(); + } else if (room_node.contains("name")) { room.name = room_node["name"].get_value(); } if (room_node.contains("bgColor")) { diff --git a/source/game/gameplay/scoreboard.cpp b/source/game/gameplay/scoreboard.cpp index 6b10e0c9..c42ab741 100644 --- a/source/game/gameplay/scoreboard.cpp +++ b/source/game/gameplay/scoreboard.cpp @@ -4,6 +4,7 @@ #include +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite @@ -156,13 +157,13 @@ void Scoreboard::fillTexture() { auto text = Resource::Cache::get()->getText("smb2"); const std::string TIME_TEXT = std::to_string((clock_.minutes % 100) / 10) + std::to_string(clock_.minutes % 10) + clock_.separator + std::to_string((clock_.seconds % 60) / 10) + std::to_string(clock_.seconds % 10); const std::string ITEMS_TEXT = std::to_string(data_->items / 100) + std::to_string((data_->items % 100) / 10) + std::to_string(data_->items % 10); - text->writeColored(Tile::SIZE, LINE1, "Items collected ", data_->color); + text->writeColored(Tile::SIZE, LINE1, Locale::get()->get("scoreboard.items"), data_->color); text->writeColored(17 * Tile::SIZE, LINE1, ITEMS_TEXT, items_color_); - text->writeColored(20 * Tile::SIZE, LINE1, " Time ", data_->color); + text->writeColored(20 * Tile::SIZE, LINE1, Locale::get()->get("scoreboard.time"), data_->color); text->writeColored(26 * Tile::SIZE, LINE1, TIME_TEXT, stringToColor("white")); const std::string ROOMS_TEXT = std::to_string(data_->rooms / 100) + std::to_string((data_->rooms % 100) / 10) + std::to_string(data_->rooms % 10); - text->writeColored(22 * Tile::SIZE, LINE2, "Rooms", stringToColor("white")); + text->writeColored(22 * Tile::SIZE, LINE2, Locale::get()->get("scoreboard.rooms"), stringToColor("white")); text->writeColored(28 * Tile::SIZE, LINE2, ROOMS_TEXT, stringToColor("white")); // Deja el renderizador como estaba diff --git a/source/game/options.cpp b/source/game/options.cpp index de28af19..1401faff 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -487,6 +487,21 @@ namespace Options { } } + // Carga configuración de idioma desde YAML + void loadLocalizationFromYaml(const fkyaml::node& yaml) { + if (yaml.contains("localization")) { + const auto& loc = yaml["localization"]; + + if (loc.contains("language")) { + try { + language = loc["language"].get_value(); + } catch (...) { + language = Defaults::Localization::LANGUAGE; + } + } + } + } + // Crea e inicializa las opciones del programa void init() { #ifdef _DEBUG @@ -550,6 +565,7 @@ namespace Options { loadKeyboardControlsFromYaml(yaml); loadGamepadControlsFromYaml(yaml); loadKioskConfigFromYaml(yaml); + loadLocalizationFromYaml(yaml); if (console) { std::cout << "Config file loaded successfully\n\n"; @@ -640,6 +656,12 @@ namespace Options { file << " text: \"" << kiosk.text << "\"\n"; file << " infinite_lives: " << (kiosk.infinite_lives ? "true" : "false") << "\n"; + // LOCALIZATION + file << "\n"; + file << "# LOCALIZATION (en = English, ca = Catalan)\n"; + file << "localization:\n"; + file << " language: \"" << language << "\"\n"; + file.close(); if (console) { diff --git a/source/game/options.hpp b/source/game/options.hpp index c0a9c6be..87815ffd 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -139,6 +139,9 @@ namespace Options { inline GamepadControls gamepad_controls{}; // Botones del gamepad usados para jugar inline Kiosk kiosk{}; // Opciones del modo kiosko + // Idioma del juego (establecido al inicio, sin cambio en caliente) + inline std::string language{Defaults::Localization::LANGUAGE}; + // Ruta completa del fichero de configuración (establecida mediante setConfigFile) inline std::string config_file_path{}; diff --git a/source/game/scenes/credits.cpp b/source/game/scenes/credits.cpp index 3812b7fb..b4b24f55 100644 --- a/source/game/scenes/credits.cpp +++ b/source/game/scenes/credits.cpp @@ -5,6 +5,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/pixel_reveal.hpp" // Para PixelReveal #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface @@ -52,35 +53,35 @@ void Credits::handleInput() { // Inicializa los textos void Credits::iniTexts() { - std::string keys = "CURSORS"; + auto* loc = Locale::get(); texts_.clear(); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"INSTRUCTIONS:", static_cast(PaletteColor::YELLOW)}); + texts_.push_back({loc->get("credits.instructions"), static_cast(PaletteColor::YELLOW)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"HELP JAILDOC TO GET BACK ALL", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"HIS PROJECTS AND GO TO THE", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"JAIL TO FINISH THEM", static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.l0"), static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.l1"), static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.l2"), static_cast(PaletteColor::WHITE)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"KEYS:", static_cast(PaletteColor::YELLOW)}); + texts_.push_back({loc->get("credits.keys"), static_cast(PaletteColor::YELLOW)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); - texts_.push_back({keys + " TO MOVE AND JUMP", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"F8 TOGGLE THE MUSIC", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"F11 PAUSE THE GAME", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"F1-F2 WINDOWS SIZE", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"F3 TOGGLE FULLSCREEN", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"F9 TOOGLE BORDER SCREEN", static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.keys_move"), static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.f8"), static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.f11"), static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.f1f2"), static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.f3"), static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.f9"), static_cast(PaletteColor::WHITE)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"A GAME BY JAILDESIGNER", static_cast(PaletteColor::YELLOW)}); - texts_.push_back({"MADE ON SUMMER/FALL 2022", static_cast(PaletteColor::YELLOW)}); + texts_.push_back({loc->get("credits.author"), static_cast(PaletteColor::YELLOW)}); + texts_.push_back({loc->get("credits.date"), static_cast(PaletteColor::YELLOW)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); - texts_.push_back({"I LOVE JAILGAMES! ", static_cast(PaletteColor::WHITE)}); + texts_.push_back({loc->get("credits.love"), static_cast(PaletteColor::WHITE)}); texts_.push_back({"", static_cast(PaletteColor::WHITE)}); } diff --git a/source/game/scenes/ending.cpp b/source/game/scenes/ending.cpp index 40b36121..9b64beee 100644 --- a/source/game/scenes/ending.cpp +++ b/source/game/scenes/ending.cpp @@ -5,6 +5,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/pixel_reveal.hpp" // Para PixelReveal #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface @@ -170,37 +171,38 @@ void Ending::updateState(float delta_time) { // Inicializa los textos void Ending::iniTexts() { - // Vector con los textos + // Vector con los textos (traducidos según el idioma activo) std::vector texts; + auto* loc = Locale::get(); // Escena #0 - texts.push_back({"HE FINALLY MANAGED", 32}); - texts.push_back({"TO GET TO THE JAIL", 42}); - texts.push_back({"WITH ALL HIS PROJECTS", 142}); - texts.push_back({"READY TO BE FREED", 152}); + texts.push_back({loc->get("ending.t0"), 32}); + texts.push_back({loc->get("ending.t1"), 42}); + texts.push_back({loc->get("ending.t2"), 142}); + texts.push_back({loc->get("ending.t3"), 152}); // Escena #1 - texts.push_back({"ALL THE JAILERS WERE THERE", 1}); - texts.push_back({"WAITING FOR THE JAILGAMES", 11}); - texts.push_back({"TO BE RELEASED", 21}); + texts.push_back({loc->get("ending.t4"), 1}); + texts.push_back({loc->get("ending.t5"), 11}); + texts.push_back({loc->get("ending.t6"), 21}); - texts.push_back({"THERE WERE EVEN BARRULLS AND", 161}); - texts.push_back({"BEGINNERS AMONG THE CROWD", 171}); + texts.push_back({loc->get("ending.t7"), 161}); + texts.push_back({loc->get("ending.t8"), 171}); - texts.push_back({"BRY WAS CRYING...", 181}); + texts.push_back({loc->get("ending.t9"), 181}); // Escena #2 - texts.push_back({"BUT SUDDENLY SOMETHING", 19}); - texts.push_back({"CAUGHT HIS ATTENTION", 29}); + texts.push_back({loc->get("ending.t10"), 19}); + texts.push_back({loc->get("ending.t11"), 29}); // Escena #3 - texts.push_back({"A PILE OF JUNK!", 36}); - texts.push_back({"FULL OF NON WORKING TRASH!!", 46}); + texts.push_back({loc->get("ending.t12"), 36}); + texts.push_back({loc->get("ending.t13"), 46}); // Escena #4 - texts.push_back({"AND THEN,", 36}); - texts.push_back({"FOURTY NEW PROJECTS", 46}); - texts.push_back({"WERE BORN...", 158}); + texts.push_back({loc->get("ending.t14"), 36}); + texts.push_back({loc->get("ending.t15"), 46}); + texts.push_back({loc->get("ending.t16"), 158}); // Crea los sprites sprite_texts_.clear(); diff --git a/source/game/scenes/ending2.cpp b/source/game/scenes/ending2.cpp index 4e4ddd77..0a48d0ec 100644 --- a/source/game/scenes/ending2.cpp +++ b/source/game/scenes/ending2.cpp @@ -7,6 +7,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface_dissolve_sprite.hpp" // Para SurfaceDissolveSprite @@ -403,7 +404,7 @@ void Ending2::createSpriteTexts() { std::string txt = sprite_list_[i]; std::ranges::replace(txt, '_', ' '); // Reemplaza '_' por ' ' if (txt == "player") { - txt = "JAILDOCTOR"; // Reemplaza "player" por "JAILDOCTOR" + txt = Locale::get()->get("ending2.jaildoctor"); // Reemplaza "player" por nombre localizado } // Calcula las dimensiones del texto @@ -438,7 +439,7 @@ void Ending2::createSpriteTexts() { void Ending2::createTexts() { // Crea los primeros textos std::vector list; - list.emplace_back("STARRING"); + list.emplace_back(Locale::get()->get("ending2.starring")); auto text = Resource::Cache::get()->getText("smb2"); @@ -469,8 +470,8 @@ void Ending2::createTexts() { // El primer texto va a continuación del ultimo spriteText const int START = sprite_texts_.back()->getPosY() + (text->getCharacterSize() * 15); list.clear(); - list.emplace_back("THANK YOU"); - list.emplace_back("FOR PLAYING!"); + list.emplace_back(Locale::get()->get("ending2.thank_you")); + list.emplace_back(Locale::get()->get("ending2.for_playing")); // Crea los sprites de texto a partir de la lista for (size_t i = 0; i < list.size(); ++i) { diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 25d78b2b..3e0a4b36 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -9,6 +9,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/text.hpp" // Para Text, Text::CENTER_FLAG, Text::COLOR_FLAG @@ -83,7 +84,7 @@ void Game::handleInput() { if (Input::get()->checkAction(InputAction::TOGGLE_MUSIC, Input::DO_NOT_ALLOW_REPEAT)) { scoreboard_data_->music = !scoreboard_data_->music; scoreboard_data_->music ? Audio::get()->resumeMusic() : Audio::get()->pauseMusic(); - Notifier::get()->show({"MUSIC " + std::string(scoreboard_data_->music ? "ENABLED" : "DISABLED")}); + Notifier::get()->show({scoreboard_data_->music ? Locale::get()->get("game.music_enabled") : Locale::get()->get("game.music_disabled")}); } // Durante fade/postfade, solo procesar inputs globales @@ -95,7 +96,7 @@ void Game::handleInput() { // Input de pausa solo en estado PLAYING if (Input::get()->checkAction(InputAction::PAUSE, Input::DO_NOT_ALLOW_REPEAT)) { togglePause(); - Notifier::get()->show({std::string(paused_ ? "GAME PAUSED" : "GAME RUNNING")}); + Notifier::get()->show({paused_ ? Locale::get()->get("game.paused") : Locale::get()->get("game.running")}); } GlobalInputs::handle(); @@ -370,7 +371,7 @@ void Game::renderPostFadeEnding() { static void toggleCheat(Options::Cheat::State& cheat, const std::string& label) { cheat = (cheat == Options::Cheat::State::ENABLED) ? Options::Cheat::State::DISABLED : Options::Cheat::State::ENABLED; const bool ENABLED = (cheat == Options::Cheat::State::ENABLED); - Notifier::get()->show({label + (ENABLED ? " ENABLED" : " DISABLED")}, Notifier::Style::DEFAULT, -1, true); + Notifier::get()->show({label + (ENABLED ? Locale::get()->get("game.enabled") : Locale::get()->get("game.disabled"))}, Notifier::Style::DEFAULT, -1, true); } // Pasa la información de debug @@ -415,7 +416,7 @@ void Game::handleDebugEvents(const SDL_Event& event) { switch (event.key.key) { case SDLK_F12: Debug::get()->toggleEnabled(); - Notifier::get()->show({"DEBUG " + std::string(Debug::get()->isEnabled() ? "ENABLED" : "DISABLED")}); + Notifier::get()->show({Debug::get()->isEnabled() ? Locale::get()->get("game.debug_enabled") : Locale::get()->get("game.debug_disabled")}); room_->redrawMap(); // Redibuja el tilemap para mostrar/ocultar líneas de colisión Options::cheats.invincible = static_cast(Debug::get()->isEnabled()); player_->setColor(); @@ -444,21 +445,21 @@ void Game::handleDebugEvents(const SDL_Event& event) { break; case SDLK_1: - toggleCheat(Options::cheats.infinite_lives, "INFINITE LIVES"); + toggleCheat(Options::cheats.infinite_lives, Locale::get()->get("game.cheat_infinite_lives")); player_->setColor(); break; case SDLK_2: - toggleCheat(Options::cheats.invincible, "INVINCIBLE"); + toggleCheat(Options::cheats.invincible, Locale::get()->get("game.cheat_invincible")); player_->setColor(); break; case SDLK_3: - toggleCheat(Options::cheats.jail_is_open, "JAIL IS OPEN"); + toggleCheat(Options::cheats.jail_is_open, Locale::get()->get("game.cheat_jail_open")); break; case SDLK_7: - Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", "I LIKE MY MULTICOLOURED FRIENDS"}, Notifier::Style::CHEEVO, -1, false, "F7"); + Notifier::get()->show({Locale::get()->get("game.achievement_unlocked"), Locale::get()->get("game.c11")}, Notifier::Style::CHEEVO, -1, false, "F7"); break; case SDLK_0: diff --git a/source/game/scenes/game_over.cpp b/source/game/scenes/game_over.cpp index 7fcc499d..b329b65d 100644 --- a/source/game/scenes/game_over.cpp +++ b/source/game/scenes/game_over.cpp @@ -8,6 +8,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite #include "core/rendering/text.hpp" // Para Text::CENTER_FLAG, Text::COLOR_FLAG, Text @@ -68,7 +69,8 @@ void GameOver::render() { auto text = Resource::Cache::get()->getText("smb2"); // Escribe el texto de GAME OVER - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y, "G A M E O V E R", 1, color_); + auto* loc = Locale::get(); + text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y, loc->get("game_over.title"), 1, color_); // Dibuja los sprites (ya posicionados en el constructor, solo ajustamos Y) player_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET); @@ -78,11 +80,11 @@ void GameOver::render() { // Escribe el texto con las habitaciones y los items const std::string ITEMS_TEXT = std::to_string(Options::stats.items / 100) + std::to_string((Options::stats.items % 100) / 10) + std::to_string(Options::stats.items % 10); const std::string ROOMS_TEXT = std::to_string(Options::stats.rooms / 100) + std::to_string((Options::stats.rooms % 100) / 10) + std::to_string(Options::stats.rooms % 10); - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ITEMS_Y_OFFSET, "ITEMS: " + ITEMS_TEXT, 1, color_); - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ROOMS_Y_OFFSET, "ROOMS: " + ROOMS_TEXT, 1, color_); + text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ITEMS_Y_OFFSET, loc->get("game_over.items") + ITEMS_TEXT, 1, color_); + text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ROOMS_Y_OFFSET, loc->get("game_over.rooms") + ROOMS_TEXT, 1, color_); // Escribe el texto con "Tu peor pesadilla" - text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + NIGHTMARE_TITLE_Y_OFFSET, "YOUR WORST NIGHTMARE IS", 1, color_); + text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + NIGHTMARE_TITLE_Y_OFFSET, loc->get("game_over.worst_nightmare"), 1, color_); text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + NIGHTMARE_TEXT_Y_OFFSET, Options::stats.worst_nightmare, 1, color_); // Vuelca el contenido del renderizador en pantalla diff --git a/source/game/scenes/title.cpp b/source/game/scenes/title.cpp index 7eb79b09..9d5c251b 100644 --- a/source/game/scenes/title.cpp +++ b/source/game/scenes/title.cpp @@ -7,6 +7,7 @@ #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT, REP... +#include "core/locale/locale.hpp" // Para Locale #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/surface_sprite.hpp" // Para SSprite @@ -57,7 +58,7 @@ Title::~Title() { // Inicializa la marquesina void Title::initMarquee() { letters_.clear(); - long_text_ = "HEY JAILERS!! IT'S 2022 AND WE'RE STILL ROCKING LIKE IT'S 1998!!! HAVE YOU HEARD IT? JAILGAMES ARE BACK!! YEEESSS BACK!! MORE THAN 10 TITLES ON JAILDOC'S KITCHEN!! THATS A LOOOOOOT OF JAILGAMES, BUT WHICH ONE WILL STRIKE FIRST? THERE IS ALSO A NEW DEVICE TO COME THAT WILL BLOW YOUR MIND WITH JAILGAMES ON THE GO: P.A.C.O. BUT WAIT! WHAT'S THAT BEAUTY I'M SEEING RIGHT OVER THERE?? OOOH THAT TINY MINIASCII IS PURE LOVE!! I WANT TO LICK EVERY BYTE OF IT!! OH SHIT! AND DON'T FORGET TO BRING BACK THOSE OLD AND FAT MS-DOS JAILGAMES TO GITHUB TO KEEP THEM ALIVE!! WHAT WILL BE THE NEXT JAILDOC RELEASE? WHAT WILL BE THE NEXT PROJECT TO COME ALIVE?? OH BABY WE DON'T KNOW BUT HERE YOU CAN FIND THE ANSWER, YOU JUST HAVE TO COMPLETE JAILDOCTOR'S DILEMMA ... COULD YOU?"; + long_text_ = Locale::get()->get("title.marquee"); // Pre-calcular anchos de caracteres para eficiencia for (size_t i = 0; i < long_text_.length(); ++i) { @@ -460,7 +461,7 @@ void Title::createCheevosTexture() { cheevos_surface_->clear(CHEEVOS_BG_COLOR); // Escribe la lista de logros en la textura - const std::string CHEEVOS_OWNER = "PROJECTS"; + const std::string CHEEVOS_OWNER = Locale::get()->get("title.projects"); const std::string CHEEVOS_LIST_CAPTION = CHEEVOS_OWNER + " (" + std::to_string(Cheevos::get()->getTotalUnlockedAchievements()) + " / " + std::to_string(Cheevos::get()->size()) + ")"; int pos = 2; TEXT->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, cheevos_surface_->getWidth() / 2, pos, CHEEVOS_LIST_CAPTION, 1, stringToColor("bright_green")); @@ -528,10 +529,11 @@ void Title::renderMainMenu() { const int TOTAL_HEIGHT = 3 * SPACING; // 3 espacios entre 4 items const int START_Y = MENU_CENTER_Y - (TOTAL_HEIGHT / 2); - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, "1. PLAY", 1, COLOR); - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + SPACING, "2. REDEFINE KEYBOARD", 1, COLOR); - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (2 * SPACING), "3. REDEFINE JOYSTICK", 1, COLOR); - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (3 * SPACING), "4. PROJECTS", 1, COLOR); + auto* loc = Locale::get(); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.menu.play"), 1, COLOR); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + SPACING, loc->get("title.menu.keyboard"), 1, COLOR); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (2 * SPACING), loc->get("title.menu.joystick"), 1, COLOR); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (3 * SPACING), loc->get("title.menu.projects"), 1, COLOR); } // Dibuja el menu de logros @@ -582,13 +584,13 @@ void Title::handleKeyboardRemap(const SDL_Event& event) { // Valida la tecla if (!isKeyValid(scancode)) { - remap_error_message_ = "INVALID KEY! TRY ANOTHER"; + remap_error_message_ = Locale::get()->get("title.keys.invalid"); return; } // Verifica duplicados if (isKeyDuplicate(scancode, remap_step_)) { - remap_error_message_ = "KEY ALREADY USED! TRY ANOTHER"; + remap_error_message_ = Locale::get()->get("title.keys.already_used"); return; } @@ -679,29 +681,28 @@ void Title::renderKeyboardRemap() { const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE); // Centrado aproximado // Mensaje principal: "PRESS KEY FOR [ACTION]" o "KEYS DEFINED" si completado + auto* loc = Locale::get(); if (remap_step_ >= 3) { - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, "KEYS DEFINED", 1, COLOR); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.keys.defined"), 1, COLOR); } else { - const std::string ACTION = getActionName(remap_step_); - const std::string MESSAGE = "PRESS KEY FOR " + ACTION; - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, MESSAGE, 1, COLOR); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.keys.prompt" + std::to_string(remap_step_)), 1, COLOR); } // Mostrar teclas ya capturadas (con espaciado de 2 líneas desde el mensaje principal) const int KEYS_START_Y = START_Y + (2 * LINE_SPACING); if (remap_step_ > 0) { const std::string LEFT_KEY = SDL_GetScancodeName(temp_keys_[0]); - const std::string LEFT_MSG = "LEFT: " + LEFT_KEY; + const std::string LEFT_MSG = loc->get("title.keys.label0") + LEFT_KEY; menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y, LEFT_MSG, 1, COLOR); } if (remap_step_ > 1) { const std::string RIGHT_KEY = SDL_GetScancodeName(temp_keys_[1]); - const std::string RIGHT_MSG = "RIGHT: " + RIGHT_KEY; + const std::string RIGHT_MSG = loc->get("title.keys.label1") + RIGHT_KEY; menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + LINE_SPACING, RIGHT_MSG, 1, COLOR); } if (remap_step_ >= 3) { const std::string JUMP_KEY = SDL_GetScancodeName(temp_keys_[2]); - const std::string JUMP_MSG = "JUMP: " + JUMP_KEY; + const std::string JUMP_MSG = loc->get("title.keys.label2") + JUMP_KEY; menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + (2 * LINE_SPACING), JUMP_MSG, 1, COLOR); } @@ -728,29 +729,28 @@ void Title::renderJoystickRemap() { const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE); // Centrado aproximado // Mensaje principal: "PRESS BUTTON FOR [ACTION]" o "BUTTONS DEFINED" si completado + auto* loc = Locale::get(); if (remap_step_ >= 3) { - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, "BUTTONS DEFINED", 1, COLOR); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.buttons.defined"), 1, COLOR); } else { - const std::string ACTION = getActionName(remap_step_); - const std::string MESSAGE = "PRESS BUTTON FOR " + ACTION; - menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, MESSAGE, 1, COLOR); + menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.buttons.prompt" + std::to_string(remap_step_)), 1, COLOR); } // Mostrar botones ya capturados (con espaciado de 2 líneas desde el mensaje principal) const int BUTTONS_START_Y = START_Y + (2 * LINE_SPACING); if (remap_step_ > 0) { const std::string LEFT_BTN = getButtonName(temp_buttons_[0]); - const std::string LEFT_MSG = "LEFT: " + LEFT_BTN; + const std::string LEFT_MSG = loc->get("title.keys.label0") + LEFT_BTN; menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y, LEFT_MSG, 1, COLOR); } if (remap_step_ > 1) { const std::string RIGHT_BTN = getButtonName(temp_buttons_[1]); - const std::string RIGHT_MSG = "RIGHT: " + RIGHT_BTN; + const std::string RIGHT_MSG = loc->get("title.keys.label1") + RIGHT_BTN; menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + LINE_SPACING, RIGHT_MSG, 1, COLOR); } if (remap_step_ >= 3) { const std::string JUMP_BTN = getButtonName(temp_buttons_[2]); - const std::string JUMP_MSG = "JUMP: " + JUMP_BTN; + const std::string JUMP_MSG = loc->get("title.keys.label2") + JUMP_BTN; menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + (2 * LINE_SPACING), JUMP_MSG, 1, COLOR); } @@ -805,7 +805,7 @@ void Title::handleJoystickRemap(const SDL_Event& event) { // Verifica duplicados if (isButtonDuplicate(captured_button, remap_step_)) { - remap_error_message_ = "BUTTON ALREADY USED! TRY ANOTHER"; + remap_error_message_ = Locale::get()->get("title.buttons.already_used"); return; }