ui.screens

ui/screens.py

Interfaz visual del juego.

Incluye:

  • Mapa interactivo de cámaras.
  • Visualización de habitaciones y linterna.
  • Pantallas de Game Over, historia, introducción e instrucciones.
  • Selección de dificultad.
  1"""
  2ui/screens.py
  3
  4Interfaz visual del juego.
  5
  6Incluye:
  7- Mapa interactivo de cámaras.
  8- Visualización de habitaciones y linterna.
  9- Pantallas de Game Over, historia, introducción e instrucciones.
 10- Selección de dificultad.
 11"""
 12
 13
 14import os
 15import time
 16import msvcrt
 17import warnings
 18from threading import Thread
 19
 20from colorama import Fore, init, Style
 21
 22from utils.utils import limpiar_pantalla, reproducir_sonido, cargar_plantilla_archivo
 23from core.energy import barra_energia
 24import core.config as estado
 25from core.animatronics import animatronics
 26from core.movement import mover_animatronico, detener_todos_los_canales
 27
 28init(autoreset=True)
 29
 30# Ignorar warnings de Pygame
 31os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
 32warnings.filterwarnings("ignore", category=UserWarning)
 33
 34import pygame
 35pygame.init()
 36
 37
 38# -------------------------- MAPA Y CÁMARAS -------------------------- #
 39def mostrar_mapa(camara_seleccionada):
 40    """
 41    Muestra en pantalla el mapa de cámaras interactivo, resaltando la cámara seleccionada.
 42    
 43    Args:
 44        camara_seleccionada (int): Número de cámara actualmente seleccionada.
 45    """
 46    limpiar_pantalla()
 47    def cam_texto(numero):
 48        texto = f"CAM 0{numero}"
 49        return Fore.RED + texto + Style.RESET_ALL if camara_seleccionada == numero else texto
 50
 51    print("\r" + barra_energia(), end="", flush=True)
 52    print(Fore.LIGHTYELLOW_EX + f"\nHora: {estado.horas[estado.hora_actual]}\n")    
 53    print(Fore.LIGHTWHITE_EX + f"""
 54                      ╔══════════╗
 55               ╔══════╣  {cam_texto(2)}  ╠════════════╗
 56               ║      ╚══════════╝            ║
 57             ╔═╩══════╗                 ╔═════╩══╗
 58      ╔══════╣        ║  ╔════════════╗ ║        ║
 59   ╔══╩═════╗║ {cam_texto(6)} ╠══╣   {cam_texto(1)}   ╠═╣ {cam_texto(3)} ╠══╗
 60   ║        ║║        ║  ╚════════╦═══╝ ║        ║  ║
 61{cam_texto(7)} ║╚══╦═════╝           ║     ╚═╦═══╦══╝  ║
 62   ║        ║   ║     ╔════════╗  ║       ║   ║     ║
 63   ╚══╦═════╝   ╚═════╣ {cam_texto(5)} ╠══╩═══════╝   ║     ║
 64      ║               ╚════════╝              ╠═════╝
 65      ║           ╔═════════════════╗         ║  
 66      ╚═══════════╣	 {cam_texto(4)}     ╠═════════╝   
 67                  ╚════╦══════╦═════╝        
 68                    ╔══╝      ╚══╗
 69                    ║   ╔════╗   ║        
 70                    ╚═══╣ CG ╠═══╝
 71                        ╚════╝
 72    """)
 73    print("Presiona 'Q' para salir")
 74
 75
 76def mapa_interactivo():
 77    """
 78    Muestra el mapa interactivo de cámaras y permite navegación.
 79    Controla refresco de pantalla, batería y sincronización con stop_event.
 80    """
 81    camaras_disponibles = [1, 2, 3, 4, 5, 6, 7]
 82    indice = 0
 83    camara_seleccionada = camaras_disponibles[indice]
 84    ultimo_refresco = time.time()
 85
 86    mostrar_mapa(camara_seleccionada)
 87
 88    while not estado.stop_event.is_set():
 89        # Refresco automático
 90        if time.time() - ultimo_refresco >= estado.config["tiempo_avanzar_hora"]:
 91            mostrar_mapa(camara_seleccionada)
 92            ultimo_refresco = time.time()
 93
 94        # Entrada de usuario
 95        if msvcrt.kbhit():
 96            if estado.stop_event.is_set():
 97                break 
 98            tecla = msvcrt.getch()
 99            
100            if tecla == b'\xe0':
101                flecha = msvcrt.getch()
102                if flecha in [b'H', b'M']:  # Arriba/derecha
103                    indice = (indice + 1) % len(camaras_disponibles)
104                elif flecha in [b'P', b'K']:  # Abajo/izquierda
105                    indice = (indice - 1) % len(camaras_disponibles)
106                camara_seleccionada = camaras_disponibles[indice]
107                mostrar_mapa(camara_seleccionada)
108
109            elif tecla == b'\r':  # Enter
110                if estado.energia_actual <= 0:
111                    print(Fore.RED + "\n¡Sin batería! No podés usar cámaras.")
112                    estado.energia_actual = 0
113                    time.sleep(1.5)
114                else:
115                    mostrar_habitacion(camara_seleccionada)
116                    mostrar_mapa(camara_seleccionada)
117
118            elif tecla in [b'q', b'Q']:
119                estado.stop_event.set()
120                print("Saliendo del juego...")
121                time.sleep(2)
122                detener_todos_los_canales()
123                limpiar_pantalla()
124                break
125
126
127def mostrar_habitacion(numero: int):
128    """
129    Muestra la cámara seleccionada y permite interacción con linterna.
130
131    Args:
132        numero (int): Número de la cámara.
133    """
134    if estado.energia_actual <= 0:
135        print(Fore.RED + "¡Sin batería! No podés usar cámaras.")
136        time.sleep(2)
137        return
138
139    estado.energia_actual -= estado.config["energia_uso_camara"]
140    estado.energia_actual = max(0, estado.energia_actual)
141
142    reproducir_sonido(estado.config["sonido_camara"], canal=estado.canal_interface)
143    limpiar_pantalla()
144    print(Fore.CYAN + f"[ Cámara {numero:02d} - {estado.habitaciones[numero]} ]\n")
145
146    anim_presentes = [n for n, a in animatronics.items() if a.posicion == numero]
147    plantilla = cargar_plantilla_archivo(numero, anim_presentes)
148    for linea in plantilla:
149        print(Fore.WHITE + linea.rstrip("\n"))
150
151    spawn_owners = [n for n, a in animatronics.items() if a.spawn == numero]
152    anim_own = any(owner in anim_presentes for owner in spawn_owners)
153    todos_son_duenios = all(anim in spawn_owners for anim in anim_presentes)
154
155    if not todos_son_duenios and anim_own:
156        print(Fore.YELLOW + "\n¡Animatrónico detectado! Presiona 'F' para usar la linterna.")
157        if input("\n> ").strip().upper() == 'F':
158            usar_linterna(anim_presentes, spawn_owners[0])
159    elif todos_son_duenios and anim_presentes:
160        print(Fore.RED + "\n¡Linterna Desactivada!")
161        input("\nPresiona Enter para volver.")
162    elif not anim_own and anim_presentes:
163        print(Fore.YELLOW + "\n¡Animatrónico detectado! Presiona 'F' para usar la linterna.")
164        if input("\n> ").strip().upper() == 'F':
165            usar_linterna(anim_presentes, None)
166    else:
167        if estado.energia_actual <= 0:
168            print(Fore.RED + "\nSin batería. No podés usar cámaras.")
169            time.sleep(2)
170        else:
171            print(Fore.CYAN + "\nPresiona Enter para volver.")
172            input()
173
174
175def usar_linterna(anim_presentes: list, spawn_owner=None):
176    """
177    Simula el uso de la linterna para retroceder animatrónicos no dueños del spawn.
178
179    Args:
180        anim_presentes (list): Animatrónicos presentes.
181        spawn_owner (str, optional): Animatrónico dueño del spawn.
182    """
183    if estado.energia_actual <= 0:
184        print(Fore.RED + "¡Sin batería! La linterna no funciona.")
185        time.sleep(1)
186        return
187
188    estado.energia_actual -= estado.config["energia_uso_linterna"]
189    estado.energia_actual = max(0, estado.energia_actual)
190
191    print(Fore.GREEN + "¡Usaste la linterna!")
192    for nombre in anim_presentes:
193        if nombre != spawn_owner:
194            animatronics[nombre].regresar_spawn()
195            print(Fore.GREEN + f"{animatronics[nombre].cara} {nombre} regresó a su posición inicial.")
196    time.sleep(1)
197
198
199# -------------------------- GAME OVER -------------------------- #
200def pantalla_game_over(nombre: str):
201    """
202    Muestra la pantalla de Game Over, reproduciendo sonidos y ASCII art.
203
204    Args:
205        nombre (str): Animatrónico responsable del Game Over.
206    """
207    anim = animatronics[nombre]
208    detener_todos_los_canales()
209
210    reproducir_sonido(estado.config["sonido_powerdown"])
211    while pygame.mixer.music.get_busy():
212        time.sleep(0.1)
213
214    reproducir_sonido(anim.cancion_muerte)
215    while pygame.mixer.music.get_busy():
216        time.sleep(0.1)
217
218    reproducir_sonido(estado.config["sonido_jumpscare"])
219    time.sleep(1.5)
220
221    estado.stop_event.set()
222    estado.juego_activo = False
223    limpiar_pantalla()
224    ancho = 80
225
226    mensaje = [
227        Fore.RED + Style.BRIGHT + f{nombre.upper()} ENTRÓ A LA OFICINA!".center(ancho),
228        "",
229        Fore.MAGENTA + Style.BRIGHT + "  ██████╗   █████╗  ███╗   ███╗ ███████╗     ██████╗  ██╗   ██╗ ███████╗ ██████╗ ",
230        Fore.MAGENTA + Style.BRIGHT + " ██╔════╝  ██╔══██╗ ████╗ ████║ ██╔════╝    ██╔═══██╗ ██║   ██║ ██╔════╝ ██╔══██╗",
231        Fore.MAGENTA + Style.BRIGHT + " ██║  ███╗ ███████║ ██╔████╔██║ █████╗      ██║   ██║ ██║   ██║ █████╗   ██████╔╝",
232        Fore.MAGENTA + Style.BRIGHT + " ██║   ██║ ██╔══██║ ██║╚██╔╝██║ ██╔══╝      ██║   ██║ ███╗ ███║ ██╔══╝   ██╔══██╗",
233        Fore.MAGENTA + Style.BRIGHT + " ╚██████╔╝ ██║  ██║ ██║ ╚═╝ ██║ ███████╗    ╚██████╔╝ ╚═████╔═╝ ███████╗ ██║  ██║",
234        Fore.MAGENTA + Style.BRIGHT + "  ╚═════╝  ╚═╝  ╚═╝ ╚═╝     ╚═╝ ╚══════╝     ╚═════╝    ╚═══╝   ╚══════╝ ╚═╝  ╚═╝"
235    ]
236
237    for linea in mensaje:
238        print(linea)
239        time.sleep(0.3)
240
241    time.sleep(2)
242    print(Fore.YELLOW + "Volviendo al menú principal...".center(ancho))
243    time.sleep(1)
244    from ui.menu import menu_principal
245    menu_principal()
246
247
248# ------------------------------ INTRO ------------------------------ #
249def intro():
250    """Reproduce la introducción de audio y luego inicia el movimiento de los animatrónicos."""
251    reproducir_sonido(estado.config["intro"])
252    while pygame.mixer.music.get_busy():
253        time.sleep(0.1)
254
255    for nombre in animatronics:
256        Thread(target=mover_animatronico, args=(nombre,), daemon=True).start()
257
258
259# -------------------------- INSTRUCCIONES -------------------------- #
260def mostrar_instrucciones():
261    """Muestra las instrucciones del juego en consola."""
262    limpiar_pantalla()
263    print("\n════════════════════════════════════════")
264    print(Style.BRIGHT + Fore.MAGENTA + "📜 INSTRUCCIONES 📜".center(38))
265    print("════════════════════════════════════════\n")
266
267    print(Style.BRIGHT + Fore.WHITE + "- Usa las flechas " + Fore.CYAN + "↑ ↓ ← →" + Fore.WHITE + " para moverte por el mapa.\n")
268    print(Fore.WHITE + "- Tu objetivo es " + Fore.RED + "sobrevivir la noche" + Fore.WHITE + " vigilando las cámaras.\n")
269    print(Fore.WHITE + "- Observa los movimientos de los animatrónicos.\n")
270    print(Fore.WHITE + "- Usa la " + Fore.LIGHTYELLOW_EX + "linterna" + Fore.WHITE + " para espantarlos.\n")
271    print(Fore.WHITE + "- Si un animatrónico entra en la oficina... " + Fore.RED + Style.BRIGHT + "GAME OVER!\n")
272    input(Fore.GREEN + "\nPresiona Enter para volver al menú.")
273
274
275# -------------------------- DIFICULTAD -------------------------- #
276def seleccionar_dificultad():
277    """
278    Muestra un menú interactivo para seleccionar la dificultad del juego.
279
280    Permite elegir entre NORMAL, DIFÍCIL, PESADILLA o VOLVER al menú principal.
281    Cada dificultad modifica:
282        - Velocidad de avance de las horas.
283        - Consumo de energía por linterna.
284        - Consumo de energía por uso de cámara.
285
286    Controles:
287        - Flechas arriba/abajo: navegan entre las opciones.
288        - Enter: confirma la selección.
289
290    Variables globales:
291        estado.config (dict): Diccionario con los parámetros de configuración.
292    """
293    opciones = ["NORMAL", "DIFÍCIL", "PESADILLA", "VOLVER"]
294    colores = [Fore.GREEN, Fore.YELLOW, Fore.MAGENTA, Fore.WHITE]
295    ancho_pantalla = 60
296    seleccion = 0
297
298    while True:
299        limpiar_pantalla()
300
301        # Título del menú
302        print(Style.BRIGHT + Fore.WHITE + """
303                    
304        ███╗   ███╗  ██████╗  ██████╗   ██████╗  ███████╗
305        ████╗ ████║ ██╔═══██╗ ██╔══██╗ ██╔═══██╗ ██╔════╝
306        ██╔████╔██║ ██║   ██║ ██║  ██║ ██║   ██║ ███████╗
307        ██║╚██╔╝██║ ██║   ██║ ██║  ██║ ██║   ██║ ╚════██║
308        ██║ ╚═╝ ██║ ╚██████╔╝ ██████╔╝ ╚██████╔╝ ███████║
309        ╚═╝     ╚═╝  ╚═════╝  ╚═════╝   ╚═════╝  ╚══════╝
310        """)
311
312        # Mostrar opciones con la selección actual
313        for i, opcion in enumerate(opciones):
314            flecha = ">> " if i == seleccion else "   "
315            linea = flecha + opcion
316            print("\n" + colores[i] + linea.center(ancho_pantalla))
317
318        # Lectura de teclas
319        tecla = msvcrt.getch()
320
321        if tecla == b'\xe0':  # Flechas
322            flecha = msvcrt.getch()
323            if flecha == b'H':  # Arriba
324                seleccion = (seleccion - 1) % len(opciones)
325            elif flecha == b'P':  # Abajo
326                seleccion = (seleccion + 1) % len(opciones)
327        elif tecla == b'\r':  # Enter
328            if seleccion == 0:  # NORMAL
329                estado.config.update({
330                    "tiempo_avanzar_hora": 60,
331                    "energia_uso_linterna": 5,
332                    "energia_uso_camara": 2,
333                    "dificultad": "NORMAL"
334                })
335            elif seleccion == 1:  # DIFÍCIL
336                estado.config.update({
337                    "tiempo_avanzar_hora": 120,
338                    "energia_uso_linterna": 3,
339                    "energia_uso_camara": 1.5,
340                    "dificultad": "DIFICIL"
341                })
342            elif seleccion == 2:  # PESADILLA
343                estado.config.update({
344                    "tiempo_avanzar_hora": 225,
345                    "energia_uso_linterna": 2,
346                    "energia_uso_camara": 1,
347                    "dificultad": "PESADILLA"
348                })
349            elif seleccion == 3:  # VOLVER
350                from ui.menu import menu_principal
351                menu_principal()
352                return
353            time.sleep(1)
354            return
def mostrar_mapa(camara_seleccionada):
40def mostrar_mapa(camara_seleccionada):
41    """
42    Muestra en pantalla el mapa de cámaras interactivo, resaltando la cámara seleccionada.
43    
44    Args:
45        camara_seleccionada (int): Número de cámara actualmente seleccionada.
46    """
47    limpiar_pantalla()
48    def cam_texto(numero):
49        texto = f"CAM 0{numero}"
50        return Fore.RED + texto + Style.RESET_ALL if camara_seleccionada == numero else texto
51
52    print("\r" + barra_energia(), end="", flush=True)
53    print(Fore.LIGHTYELLOW_EX + f"\nHora: {estado.horas[estado.hora_actual]}\n")    
54    print(Fore.LIGHTWHITE_EX + f"""
55                      ╔══════════╗
56               ╔══════╣  {cam_texto(2)}  ╠════════════╗
57               ║      ╚══════════╝            ║
58             ╔═╩══════╗                 ╔═════╩══╗
59      ╔══════╣        ║  ╔════════════╗ ║        ║
60   ╔══╩═════╗║ {cam_texto(6)} ╠══╣   {cam_texto(1)}   ╠═╣ {cam_texto(3)} ╠══╗
61   ║        ║║        ║  ╚════════╦═══╝ ║        ║  ║
62{cam_texto(7)} ║╚══╦═════╝           ║     ╚═╦═══╦══╝  ║
63   ║        ║   ║     ╔════════╗  ║       ║   ║     ║
64   ╚══╦═════╝   ╚═════╣ {cam_texto(5)} ╠══╩═══════╝   ║     ║
65      ║               ╚════════╝              ╠═════╝
66      ║           ╔═════════════════╗         ║  
67      ╚═══════════╣	 {cam_texto(4)}     ╠═════════╝   
68                  ╚════╦══════╦═════╝        
69                    ╔══╝      ╚══╗
70                    ║   ╔════╗   ║        
71                    ╚═══╣ CG ╠═══╝
72                        ╚════╝
73    """)
74    print("Presiona 'Q' para salir")

Muestra en pantalla el mapa de cámaras interactivo, resaltando la cámara seleccionada.

Args: camara_seleccionada (int): Número de cámara actualmente seleccionada.

def mapa_interactivo():
 77def mapa_interactivo():
 78    """
 79    Muestra el mapa interactivo de cámaras y permite navegación.
 80    Controla refresco de pantalla, batería y sincronización con stop_event.
 81    """
 82    camaras_disponibles = [1, 2, 3, 4, 5, 6, 7]
 83    indice = 0
 84    camara_seleccionada = camaras_disponibles[indice]
 85    ultimo_refresco = time.time()
 86
 87    mostrar_mapa(camara_seleccionada)
 88
 89    while not estado.stop_event.is_set():
 90        # Refresco automático
 91        if time.time() - ultimo_refresco >= estado.config["tiempo_avanzar_hora"]:
 92            mostrar_mapa(camara_seleccionada)
 93            ultimo_refresco = time.time()
 94
 95        # Entrada de usuario
 96        if msvcrt.kbhit():
 97            if estado.stop_event.is_set():
 98                break 
 99            tecla = msvcrt.getch()
100            
101            if tecla == b'\xe0':
102                flecha = msvcrt.getch()
103                if flecha in [b'H', b'M']:  # Arriba/derecha
104                    indice = (indice + 1) % len(camaras_disponibles)
105                elif flecha in [b'P', b'K']:  # Abajo/izquierda
106                    indice = (indice - 1) % len(camaras_disponibles)
107                camara_seleccionada = camaras_disponibles[indice]
108                mostrar_mapa(camara_seleccionada)
109
110            elif tecla == b'\r':  # Enter
111                if estado.energia_actual <= 0:
112                    print(Fore.RED + "\n¡Sin batería! No podés usar cámaras.")
113                    estado.energia_actual = 0
114                    time.sleep(1.5)
115                else:
116                    mostrar_habitacion(camara_seleccionada)
117                    mostrar_mapa(camara_seleccionada)
118
119            elif tecla in [b'q', b'Q']:
120                estado.stop_event.set()
121                print("Saliendo del juego...")
122                time.sleep(2)
123                detener_todos_los_canales()
124                limpiar_pantalla()
125                break

Muestra el mapa interactivo de cámaras y permite navegación. Controla refresco de pantalla, batería y sincronización con stop_event.

def mostrar_habitacion(numero: int):
128def mostrar_habitacion(numero: int):
129    """
130    Muestra la cámara seleccionada y permite interacción con linterna.
131
132    Args:
133        numero (int): Número de la cámara.
134    """
135    if estado.energia_actual <= 0:
136        print(Fore.RED + "¡Sin batería! No podés usar cámaras.")
137        time.sleep(2)
138        return
139
140    estado.energia_actual -= estado.config["energia_uso_camara"]
141    estado.energia_actual = max(0, estado.energia_actual)
142
143    reproducir_sonido(estado.config["sonido_camara"], canal=estado.canal_interface)
144    limpiar_pantalla()
145    print(Fore.CYAN + f"[ Cámara {numero:02d} - {estado.habitaciones[numero]} ]\n")
146
147    anim_presentes = [n for n, a in animatronics.items() if a.posicion == numero]
148    plantilla = cargar_plantilla_archivo(numero, anim_presentes)
149    for linea in plantilla:
150        print(Fore.WHITE + linea.rstrip("\n"))
151
152    spawn_owners = [n for n, a in animatronics.items() if a.spawn == numero]
153    anim_own = any(owner in anim_presentes for owner in spawn_owners)
154    todos_son_duenios = all(anim in spawn_owners for anim in anim_presentes)
155
156    if not todos_son_duenios and anim_own:
157        print(Fore.YELLOW + "\n¡Animatrónico detectado! Presiona 'F' para usar la linterna.")
158        if input("\n> ").strip().upper() == 'F':
159            usar_linterna(anim_presentes, spawn_owners[0])
160    elif todos_son_duenios and anim_presentes:
161        print(Fore.RED + "\n¡Linterna Desactivada!")
162        input("\nPresiona Enter para volver.")
163    elif not anim_own and anim_presentes:
164        print(Fore.YELLOW + "\n¡Animatrónico detectado! Presiona 'F' para usar la linterna.")
165        if input("\n> ").strip().upper() == 'F':
166            usar_linterna(anim_presentes, None)
167    else:
168        if estado.energia_actual <= 0:
169            print(Fore.RED + "\nSin batería. No podés usar cámaras.")
170            time.sleep(2)
171        else:
172            print(Fore.CYAN + "\nPresiona Enter para volver.")
173            input()

Muestra la cámara seleccionada y permite interacción con linterna.

Args: numero (int): Número de la cámara.

def usar_linterna(anim_presentes: list, spawn_owner=None):
176def usar_linterna(anim_presentes: list, spawn_owner=None):
177    """
178    Simula el uso de la linterna para retroceder animatrónicos no dueños del spawn.
179
180    Args:
181        anim_presentes (list): Animatrónicos presentes.
182        spawn_owner (str, optional): Animatrónico dueño del spawn.
183    """
184    if estado.energia_actual <= 0:
185        print(Fore.RED + "¡Sin batería! La linterna no funciona.")
186        time.sleep(1)
187        return
188
189    estado.energia_actual -= estado.config["energia_uso_linterna"]
190    estado.energia_actual = max(0, estado.energia_actual)
191
192    print(Fore.GREEN + "¡Usaste la linterna!")
193    for nombre in anim_presentes:
194        if nombre != spawn_owner:
195            animatronics[nombre].regresar_spawn()
196            print(Fore.GREEN + f"{animatronics[nombre].cara} {nombre} regresó a su posición inicial.")
197    time.sleep(1)

Simula el uso de la linterna para retroceder animatrónicos no dueños del spawn.

Args: anim_presentes (list): Animatrónicos presentes. spawn_owner (str, optional): Animatrónico dueño del spawn.

def pantalla_game_over(nombre: str):
201def pantalla_game_over(nombre: str):
202    """
203    Muestra la pantalla de Game Over, reproduciendo sonidos y ASCII art.
204
205    Args:
206        nombre (str): Animatrónico responsable del Game Over.
207    """
208    anim = animatronics[nombre]
209    detener_todos_los_canales()
210
211    reproducir_sonido(estado.config["sonido_powerdown"])
212    while pygame.mixer.music.get_busy():
213        time.sleep(0.1)
214
215    reproducir_sonido(anim.cancion_muerte)
216    while pygame.mixer.music.get_busy():
217        time.sleep(0.1)
218
219    reproducir_sonido(estado.config["sonido_jumpscare"])
220    time.sleep(1.5)
221
222    estado.stop_event.set()
223    estado.juego_activo = False
224    limpiar_pantalla()
225    ancho = 80
226
227    mensaje = [
228        Fore.RED + Style.BRIGHT + f{nombre.upper()} ENTRÓ A LA OFICINA!".center(ancho),
229        "",
230        Fore.MAGENTA + Style.BRIGHT + "  ██████╗   █████╗  ███╗   ███╗ ███████╗     ██████╗  ██╗   ██╗ ███████╗ ██████╗ ",
231        Fore.MAGENTA + Style.BRIGHT + " ██╔════╝  ██╔══██╗ ████╗ ████║ ██╔════╝    ██╔═══██╗ ██║   ██║ ██╔════╝ ██╔══██╗",
232        Fore.MAGENTA + Style.BRIGHT + " ██║  ███╗ ███████║ ██╔████╔██║ █████╗      ██║   ██║ ██║   ██║ █████╗   ██████╔╝",
233        Fore.MAGENTA + Style.BRIGHT + " ██║   ██║ ██╔══██║ ██║╚██╔╝██║ ██╔══╝      ██║   ██║ ███╗ ███║ ██╔══╝   ██╔══██╗",
234        Fore.MAGENTA + Style.BRIGHT + " ╚██████╔╝ ██║  ██║ ██║ ╚═╝ ██║ ███████╗    ╚██████╔╝ ╚═████╔═╝ ███████╗ ██║  ██║",
235        Fore.MAGENTA + Style.BRIGHT + "  ╚═════╝  ╚═╝  ╚═╝ ╚═╝     ╚═╝ ╚══════╝     ╚═════╝    ╚═══╝   ╚══════╝ ╚═╝  ╚═╝"
236    ]
237
238    for linea in mensaje:
239        print(linea)
240        time.sleep(0.3)
241
242    time.sleep(2)
243    print(Fore.YELLOW + "Volviendo al menú principal...".center(ancho))
244    time.sleep(1)
245    from ui.menu import menu_principal
246    menu_principal()

Muestra la pantalla de Game Over, reproduciendo sonidos y ASCII art.

Args: nombre (str): Animatrónico responsable del Game Over.

def intro():
250def intro():
251    """Reproduce la introducción de audio y luego inicia el movimiento de los animatrónicos."""
252    reproducir_sonido(estado.config["intro"])
253    while pygame.mixer.music.get_busy():
254        time.sleep(0.1)
255
256    for nombre in animatronics:
257        Thread(target=mover_animatronico, args=(nombre,), daemon=True).start()

Reproduce la introducción de audio y luego inicia el movimiento de los animatrónicos.

def mostrar_instrucciones():
261def mostrar_instrucciones():
262    """Muestra las instrucciones del juego en consola."""
263    limpiar_pantalla()
264    print("\n════════════════════════════════════════")
265    print(Style.BRIGHT + Fore.MAGENTA + "📜 INSTRUCCIONES 📜".center(38))
266    print("════════════════════════════════════════\n")
267
268    print(Style.BRIGHT + Fore.WHITE + "- Usa las flechas " + Fore.CYAN + "↑ ↓ ← →" + Fore.WHITE + " para moverte por el mapa.\n")
269    print(Fore.WHITE + "- Tu objetivo es " + Fore.RED + "sobrevivir la noche" + Fore.WHITE + " vigilando las cámaras.\n")
270    print(Fore.WHITE + "- Observa los movimientos de los animatrónicos.\n")
271    print(Fore.WHITE + "- Usa la " + Fore.LIGHTYELLOW_EX + "linterna" + Fore.WHITE + " para espantarlos.\n")
272    print(Fore.WHITE + "- Si un animatrónico entra en la oficina... " + Fore.RED + Style.BRIGHT + "GAME OVER!\n")
273    input(Fore.GREEN + "\nPresiona Enter para volver al menú.")

Muestra las instrucciones del juego en consola.

def seleccionar_dificultad():
277def seleccionar_dificultad():
278    """
279    Muestra un menú interactivo para seleccionar la dificultad del juego.
280
281    Permite elegir entre NORMAL, DIFÍCIL, PESADILLA o VOLVER al menú principal.
282    Cada dificultad modifica:
283        - Velocidad de avance de las horas.
284        - Consumo de energía por linterna.
285        - Consumo de energía por uso de cámara.
286
287    Controles:
288        - Flechas arriba/abajo: navegan entre las opciones.
289        - Enter: confirma la selección.
290
291    Variables globales:
292        estado.config (dict): Diccionario con los parámetros de configuración.
293    """
294    opciones = ["NORMAL", "DIFÍCIL", "PESADILLA", "VOLVER"]
295    colores = [Fore.GREEN, Fore.YELLOW, Fore.MAGENTA, Fore.WHITE]
296    ancho_pantalla = 60
297    seleccion = 0
298
299    while True:
300        limpiar_pantalla()
301
302        # Título del menú
303        print(Style.BRIGHT + Fore.WHITE + """
304                    
305        ███╗   ███╗  ██████╗  ██████╗   ██████╗  ███████╗
306        ████╗ ████║ ██╔═══██╗ ██╔══██╗ ██╔═══██╗ ██╔════╝
307        ██╔████╔██║ ██║   ██║ ██║  ██║ ██║   ██║ ███████╗
308        ██║╚██╔╝██║ ██║   ██║ ██║  ██║ ██║   ██║ ╚════██║
309        ██║ ╚═╝ ██║ ╚██████╔╝ ██████╔╝ ╚██████╔╝ ███████║
310        ╚═╝     ╚═╝  ╚═════╝  ╚═════╝   ╚═════╝  ╚══════╝
311        """)
312
313        # Mostrar opciones con la selección actual
314        for i, opcion in enumerate(opciones):
315            flecha = ">> " if i == seleccion else "   "
316            linea = flecha + opcion
317            print("\n" + colores[i] + linea.center(ancho_pantalla))
318
319        # Lectura de teclas
320        tecla = msvcrt.getch()
321
322        if tecla == b'\xe0':  # Flechas
323            flecha = msvcrt.getch()
324            if flecha == b'H':  # Arriba
325                seleccion = (seleccion - 1) % len(opciones)
326            elif flecha == b'P':  # Abajo
327                seleccion = (seleccion + 1) % len(opciones)
328        elif tecla == b'\r':  # Enter
329            if seleccion == 0:  # NORMAL
330                estado.config.update({
331                    "tiempo_avanzar_hora": 60,
332                    "energia_uso_linterna": 5,
333                    "energia_uso_camara": 2,
334                    "dificultad": "NORMAL"
335                })
336            elif seleccion == 1:  # DIFÍCIL
337                estado.config.update({
338                    "tiempo_avanzar_hora": 120,
339                    "energia_uso_linterna": 3,
340                    "energia_uso_camara": 1.5,
341                    "dificultad": "DIFICIL"
342                })
343            elif seleccion == 2:  # PESADILLA
344                estado.config.update({
345                    "tiempo_avanzar_hora": 225,
346                    "energia_uso_linterna": 2,
347                    "energia_uso_camara": 1,
348                    "dificultad": "PESADILLA"
349                })
350            elif seleccion == 3:  # VOLVER
351                from ui.menu import menu_principal
352                menu_principal()
353                return
354            time.sleep(1)
355            return

Muestra un menú interactivo para seleccionar la dificultad del juego.

Permite elegir entre NORMAL, DIFÍCIL, PESADILLA o VOLVER al menú principal. Cada dificultad modifica: - Velocidad de avance de las horas. - Consumo de energía por linterna. - Consumo de energía por uso de cámara.

Controles: - Flechas arriba/abajo: navegan entre las opciones. - Enter: confirma la selección.

Variables globales: estado.config (dict): Diccionario con los parámetros de configuración.