lucasvsj
Volver al Blog
PersonalPlayground

PersonalPlayground

Publicado el 3 de julio de 20265 min de lectura20 vistas

IDEAS.md — Banco de features para Mech Brawlers

Para el modelo que implemente esto: cada idea es autocontenida e incluye diseño (qué se siente jugar), números iniciales (balance de partida, ajustables) y guía de implementación (archivos y enfoque concretos de ESTE repo). Implementa UNA idea por vez, valida con python3 -m gdtoolkit.parser (no hay Godot CLI en esta máquina) y actualiza el README. Respeta las convenciones existentes: constantes de balance en la cabecera del script, comentarios en español sin tildes, catálogo compartido vía preload (sin class_name), y las cosas que se eligen en el lobby viajan por SessionData.current_match_loadout.

Mapa del código (referencia rápida):

SistemaArchivo
Mecha (movimiento, energia, armas, IA de bots)entities/player/MechaPlayer.gd
Piloto (eyección, escudo, canalización de rebuild)entities/player/Pilot.gd
Chatarraentities/interactables/Scrap.gd
Catálogo de personalización (piezas/chasis/armas)entities/player/MechaParts.gd
Flujo de partida, spawns, countdown, leaderboardscenes/TestLevel.gd (compartido por los 3 mapas)
Estado de sesión (jugadores, loadout, wins, settings)autoloads/SessionData.gd
Audio (SFX generados con tools/gen_audio.py)autoloads/AudioManager.gd
Lobby / personalización / votación de mapascenes/menus/PreMatchMenu.gd, MechaCustomizationMenu.gd, MapSelectionMenu.gd
HUD por jugadorsystems/ui/PlayerHUD.gd
Cámara compartidasystems/camera/DynamicCamera.gd

1. Gameplay — Combate

1.1 Habilidad por chasis (identidad activa, no solo stats)

Diseño. Hoy el chasis solo cambia velocidad/HP/escala. Darle a cada uno una habilidad activa (botón dedicado, p. ej. LB / E) crea identidad de "clase":

  • LIGERO — Blink Dash: teletransporte corto (6 m) en la dirección de movimiento, 2 cargas, recarga 5 s por carga. Deja una estela fantasma.
  • MEDIO — Pulso EMP: onda expansiva (radio 6 m) que no daña pero empuja proyectiles enemigos (invierte direction de los CannonBall en el área) y frena 40% por 1.5 s a los Mechas alcanzados. Cooldown 8 s.
  • PESADO — Ground Slam: salto corto + caída que genera onda de choque (radio 5 m, 20 de daño, knockback fuerte, sacude la cámara). Cooldown 10 s.

Implementación. En MechaParts.CHASSIS añadir "ability" (string) y mostrarla en la card del menú de chasis (MechaCustomizationMenu._build_chassis_row). En MechaPlayer.gd: constantes ABILITY_*, un ability_cooldown, input nuevo (teclado E, joypad JOY_BUTTON_LEFT_SHOULDER — hoy libre), y un match por chasis. El EMP puede iterar get_tree().get_nodes_in_group("projectiles") filtrando por distancia. Los bots la disparan con probabilidad por tick cuando aplica (dash al recibir daño, slam con 2+ enemigos cerca).

1.2 Melee con identidad por brazos

Diseño. Los brazos (ESTÁNDAR/PÚGIL/GARRA) hoy son cosméticos. Diferenciarlos:

  • ESTÁNDAR: el melee actual (50 daño, knockback).
  • PÚGIL: +daño (65), −alcance, y el knockback aturde 0.4 s (el objetivo no puede disparar).
  • GARRA: −daño (35) pero roba 10 de energía al objetivo y golpea en arco más amplio (rotar melee_area ±30° durante el golpe).

Implementación. Igual que las armas: constantes MELEE_* en MechaPlayer.gd, match _variant("arms") dentro de melee_attack(). El stun es un timer stunned_until que bloquea el bloque de disparo en _physics_process. Actualizar descripciones en MechaParts.OPTIONS["arms"] y el comentario de qué piezas afectan gameplay.

1.3 Cuarta arma: LANZAMINAS

Diseño. Dispara una mina que se pega al piso donde cae (arco corto, ~8 m). Arma hasta 3 minas activas; la más vieja explota al colocar la cuarta. Explota por proximidad (radio 2 m, 45 de daño) o a los 15 s. Las minas brillan del color del dueño (los rivales pueden verlas y dispararles para detonarlas). Coste 25 de energía. Convierte al jugador en controlador de zona — counter natural del OBÚS que ancla al que carga.

Implementación. Añadir entrada a MechaParts.OPTIONS["weapon"] y variante 3 en _build_weapon_details (caja + tubo corto inclinado). Nueva escena entities/weapons/Mine.tscn (Area3D, como CannonBall.tscn pero estática con timer). En MechaPlayer.gd: rama 3 en _handle_weapon_input y en el match de bots (los bots la sueltan al estar a 4–8 m del objetivo, o al huir). Llevar las minas vivas en un array del shooter para el límite de 3.

1.4 Hazards de arena activos

Diseño. Cada mapa gana un peligro periódico propio, telegrafiado 2 s antes:

  • GRID HOLO: columnas de la grilla que se "des-renderizan" (agujeros de caída falsa: hacen 15 de daño y te re-materializan en el centro).
  • ARENA PRIME: la prensa/imán de la chatarrería barre una franja (daño 30 + knockback fuerte). Las franjas hazard naranjas ya existentes marcan el borde.
  • CRÁTER: los géiseres de las esquinas erupcionan (área 3 m, 25 de daño + lanzamiento vertical), y las grietas del shader de lava podrían iluminarse más fuerte durante el aviso.

Implementación. Script nuevo systems/arena/ArenaHazard.gd (Node3D con @export var interval/warn_time/damage/area) instanciado en cada .tscn de nivel sobre las posiciones deseadas. El aviso es un MeshInstance3D con material emisivo pulsante; el golpe, un intersect_shape en capa 2 (mismo patrón que fire_beam en MechaPlayer.gd). TestLevel.gd no necesita cambios si el hazard es autónomo.

1.5 Overheat con contrajuego: venteo manual

Diseño. Estando sobrecalentado, mantener el botón de escudo 1 s ejecuta un "venteo": el Mecha queda inmóvil, expulsa vapor y recupera energía al doble de velocidad. Convierte el overheat de espera pasiva en decisión de riesgo.

Implementación. En MechaPlayer.gd, dentro del bloque de overheat: si shield_pressed y is_overheated, acumular vent_timer; al pasar 1 s, current_energy += 40.0 * delta y velocity a cero. Partículas: reutilizar el patrón de materiales emisivos procedurales. Bots: ventean si no hay enemigo a menos de 8 m.


2. Gameplay — Piloto y reconstrucción

2.1 El Piloto puede secuestrar Mechas enemigos vacíos

Diseño. Si un Mecha rival está destruido y su Piloto anda lejos, el Piloto propio puede canalizar 4 s sobre el cadáver para robarlo: renace con 40% de HP, pintado mitad y mitad (el sistema apply_foreign_paint ya tiñe piezas). El dueño original ve un aviso y su Piloto gana +15% de velocidad ("rabia") hasta recuperar chatarra suficiente para reconstruir otro.

Implementación. En Pilot.gd, al detectar cercanía a un Mecha con is_dead == true que no es original_mecha, ofrecer el prompt de canalización (reutilizar la plataforma holográfica del rebuild con otro color). Al completar, transferir input_device_id/is_bot al Mecha robado, llamar rebuild() con target_hp = max_hp * 0.4 y re-vincular original_mecha de ambos pilotos. Cuidado con el hilo de IA: explode()/rebuild() ya manejan start/stop.

2.2 Scrap-denial: quemar chatarra ajena

Diseño. Disparar a un scrap ajeno 2 veces lo destruye (hoy la chatarra es intocable). Le quita economía de rebuild al rival a cambio de munición y tiempo de puntería. El scrap propio no se puede quemar (evita griefing accidental del propio equipo en modos por equipos).

Implementación. Scrap.gd: añadir hp := 2 y take_damage(); al morir, efecto de chispas + sfx "drop". CannonBall._on_body_entered ya llama take_damage sobre cualquier body que lo tenga — solo hay que asegurarse de que el Scrap esté en una capa que las balas colisionen (mask 3) y filtrar shooter == owner_mecha.

2.3 Mochila del Piloto: llevar 1 scrap encima

Diseño. El Piloto puede cargar físicamente UN scrap (se le monta en la espalda, visible) y soltarlo donde quiera. Permite "mudar" la economía a una esquina segura antes de canalizar, a costa de −20% de velocidad mientras carga.

Implementación. Pilot.gd: al recoger con un input dedicado (mantener la tecla de interacción), reparentar el mesh del scrap a un Marker3D en la espalda y marcar el scrap como carried (sin hitbox). Soltar lo re-instancia a los pies. La lógica de conteo de scraps del rebuild no cambia: el scrap cargado cuenta como "en el suelo" solo al soltarlo.


3. Rejugabilidad y progresión de sesión

3.1 Torneo local (primera a N victorias) con marcador persistente

Diseño. En el lobby, elegir "PARTIDA RÁPIDA" o "TORNEO A 3/5/7". En torneo, tras cada ronda el PostMatch muestra el marcador acumulado (ya existe SessionData.player_wins por color) y rota automáticamente al siguiente mapa votado. Al llegar al objetivo: pantalla de campeón con el Mecha ganador en grande sobre el pedestal del showcase, lluvia de confeti de su color y stats de la serie (kills, scraps, rebuilds).

Implementación. SessionData: tournament_target := 0 (0 = rápida) y round_stats. PreMatchMenu: selector arriba junto a COMENZAR. PostMatchMenu.gd: si hay torneo y nadie llegó al target, botón "SIGUIENTE RONDA" que salta directo a MapSelectionMenu conservando el loadout (ya viaja en SessionData.current_match_loadout). La pantalla de campeón puede reutilizar MechaShowcase3D.tscn con la customización del ganador.

3.2 Mutadores de partida (votados u ocultos)

Diseño. Al votar mapa, una tercera columna sortea 1 mutador (o "NINGUNO"):

  • Energía infinita (sin overheat, caos de gatlings),
  • Un solo HP (todo mata; el rebuild revive con 1 HP también),
  • Gravedad lunar (gravity *= 0.4, saltos flotados),
  • Chatarra doble (10 scraps por muerte),
  • Velocidad x1.5 para todos,
  • Sin escudos.

Implementación. SessionData.active_mutator: String. Un diccionario MUTATORS en un nuevo systems/match/Mutators.gd (RefCounted, mismo patrón que MechaParts.gd) con apply(mecha) por mutador; TestLevel._ready() lo aplica a cada Mecha tras el spawn y ajusta lo global (gravedad se lee de ProjectSettings en MechaPlayer, así que multiplicar mecha.gravity directamente). MapSelectionMenu sortea y lo anuncia junto al mapa elegido.

3.3 Estadísticas y títulos de fin de partida

Diseño. Al final de cada ronda, además del leaderboard: premios estilo "party game" que dan personalidad aunque pierdas — Verdugo (más kills), Fénix (más rebuilds), Chatarrero (más scraps), Pacifista (menos daño hecho, gana igual), Kamikaze (más autodestrucciones). Cada premio muestra la card del Mecha con su decal.

Implementación. Contadores en MechaPlayer/Pilot (kills requieren atribuir el shooter en take_damage — añadir parámetro opcional attacker y propagarlo desde CannonBall, fire_beam y melee_attack). TestLevel agrega los totales a SessionData.round_stats; PostMatchMenu los pinta.

3.4 Desbloqueables por sesión (decals y colores especiales)

Diseño. Colores metálicos (oro, cromo) y 3 decals extra que se desbloquean por logros de la sesión: ganar con full-charge del obús, ganar sin morir, ganar 3 seguidas. Se anuncian en el PostMatch ("¡Desbloqueaste ORO!") y quedan disponibles en la paleta del lobby hasta cerrar el juego (o persistir en user://unlocks.cfg si se quiere entre sesiones).

Implementación. SessionData.unlocked_colors/decals + chequeo de condiciones en show_leaderboard(). PreMatchMenu.palette y MechaParts.OPTIONS["decal"] se extienden en runtime leyendo esos arrays (OPTIONS es const: usar un getter static func decal_options() que concatene).


4. Nuevos modos de juego

Patrón general: SessionData.game_mode: String, seleccionado en el lobby (fila de botones bajo el título). TestLevel.gd ya centraliza spawn, countdown y condición de fin — extraer la condición de victoria a un método _check_win_condition() y hacer match SessionData.game_mode ahí dentro es el 80% del trabajo de cada modo.

4.1 REY DE LA CHATARRA (King of the Hill)

Diseño. Una zona circular (anillo holográfico, 6 m) aparece en el mapa. Estar dentro sin rivales suma puntos (1/s); gana el primero en llegar a 60. La zona se muda a otra posición cada 25 s. Estar en zona da un brillo del color del ocupante. Fuerza el conflicto constante y castiga esconderse — y el OBÚS de área ancha se vuelve herramienta de desalojo.

Implementación. systems/match/HillZone.gd (Area3D cilíndrica + torus emisivo). Posiciones candidatas: reutilizar los SpawnPoints del nivel. Marcador: labels de color en un CanvasLayer (el patrón de _run_countdown). Fin de partida: en vez de "último vivo", scores[color] >= 60. Los muertos reviven solos a los 5 s (en este modo el Piloto/rebuild puede desactivarse para mantener el ritmo: flag respawn_mode en TestLevel).

4.2 FIEBRE DEL SCRAP (recolección competitiva)

Diseño. Llueven scraps neutrales (grises) del cielo cada pocos segundos. Cada Mecha tiene un contenedor en su esquina; hay que recoger scrap y depositarlo en el contenedor propio (entrar al área lo descarga). Primer equipo/jugador con 15 gana. Puedes matar para hacer soltar lo que el rival lleva encima (hasta 3 slots por Mecha). Combina combate con courier-play.

Implementación. Reutiliza casi todo Scrap.gd (con owner_mecha = null y color gris). MechaPlayer gana carried_scraps := 0 (el HUD ya muestra energía; añadir contador). Contenedores: Area3D por jugador con su color en posiciones de spawn. Al morir, soltar los scraps cargados (igual que suelta el Piloto). Condición de fin en _check_win_condition().

4.3 JUGGERNAUT (1 vs todos rotativo)

Diseño. Un jugador es el Juggernaut: chasis PESADO forzado con x2 HP, gatling infinita y +15% velocidad. Los demás cooperan para derribarlo; quien le da el golpe final se convierte en el nuevo Juggernaut y hereda el marcador. Gana quien acumule 90 s total como Juggernaut. Perfecto para 3–6 jugadores de niveles dispares (los novatos cooperan, el bueno tanquea).

Implementación. TestLevel: variable juggernaut: MechaPlayer, aplicar buffs con overrides sobre move_speed/max_hp tras _apply_chassis_stats() y un aura visual (anillo hp_ring escalado + luz). En take_damage ya se puede saber el atacante si se implementó 3.3. Timer por jugador en un dict. Los no-Juggernaut no se dañan entre sí (chequear en take_damage con un flag de modo).

4.4 DUELO DE PILOTOS (modo fiesta, ronda corta)

Diseño. Nadie tiene Mecha: todos son Pilotos con su pistolita, HP mínimo, y hay UN solo Mecha vacío en el centro. Quien lo alcanza y canaliza primero aplasta al resto... pero el Mecha aparece con 30% de HP y los Pilotos pueden concentrar fuego para destronarlo. Última persona viva gana la ronda; primera a 3 rondas gana. Rondas de <90 s, cero downtime.

Implementación. TestLevel con flag de modo: spawnear Pilotos directamente (ya existe todo el flujo de Pilot.tscn y su combate) y un MechaPlayer con is_dead = true en el centro esperando canalización (el flujo de rebuild de Pilot.gd ya lo contempla — solo hay que permitir canalizar un Mecha que no es original_mecha, mismo cambio que la idea 2.1). Sin countdown de scraps: la canalización en este modo no exige chatarra (flag).

4.5 HORDA (cooperativo vs oleadas)

Diseño. 1–4 jugadores contra oleadas de bots que crecen (oleada N: N+1 bots, cada 3 oleadas entra un "élite" PESADO con obús). Entre oleadas hay 15 s para recoger scrap y reconstruir caídos. Se termina al caer todos: el score es la oleada alcanzada, con récord de sesión en SessionData. Da un modo PvE que enseña las armas sin presión de PvP.

Implementación. El spawner reutiliza el loop de spawn de TestLevel._ready con MechaParts.random_custom() para variedad visual. Los bots enemigos entran en un "equipo": no se atacan entre sí — el filtro está en find_aim_target() y el loop de IA (shared_targets hoy incluye a todos; filtrar por get_meta("team")). El fin de oleada revive Pilotos caídos automáticamente.

4.6 EQUIPOS 2v2 / 3v3

Diseño. En el lobby, arrastrar cards a dos columnas (o botón "EQUIPO A/B" en cada card). Fuego amigo apagado, colores forzados a la gama del equipo (rojos vs azules — la paleta ya tiene variantes), y el anillo de HP muestra el símbolo del equipo. Condición de victoria: último EQUIPO en pie; los scraps de un aliado pueden usarse para reconstruirlo (rebuild asistido: dos pilotos canalizando dividen el tiempo a la mitad).

Implementación. slot["team"] en PreMatchMenu.player_slots/bot_slots y en el loadout. mecha.set_meta("team", n) al spawnear. Filtros: en take_damage (ignorar daño de mismo team salvo knockback reducido), en find_aim_target() y en el thread de IA. El rebuild asistido toca Pilot.gd (contar pilotos aliados canalizando el mismo Mecha).


5. Bots más interesantes

5.1 Personalidades de IA

Diseño. Cada bot sortea una personalidad que sesga sus timers ya existentes: Agresivo (rangos de fuego más cortos, más melee), Francotirador (mantiene 12–20 m, prefiere obús cargado), Carroñero (prioriza scraps y huye con poca vida), Guardaespaldas (en equipos, orbita a un aliado). Mostrar la personalidad bajo el nombre del bot en su card del lobby.

Implementación. MechaPlayer: bot_profile: Dictionary con multiplicadores (fire_min/max, preferred_dist, flee_hp_ratio) sorteado en _ready() y usado por _bot_ai_thread_loop en lugar de los literales. La huida: si current_hp < flee_hp_ratio * max_hp, invertir want_dir y buscar el scrap más cercano (pasar posiciones de scraps por shared_targets con un segundo array shared_scraps).

5.2 Dificultad seleccionable por bot

Diseño. Botón en la card del bot (FÁCIL/NORMAL/DIFÍCIL). Fácil: +50% en timers de fuego, ±8° de error de puntería, no ventea ni usa habilidades. Difícil: −30% timers, esquiva lateral al detectar un obús cargando cerca (leer is_charging de los rivales en el snapshot del hilo), usa el melee al tener rival aturdido.

Implementación. bot_slots[i]["difficulty"] en PreMatchMenu → loadout → mecha.bot_difficulty. El error de puntería se aplica en el bloque de aim del bot en _physics_process (rotar target_pos unos grados aleatorios). El "esquivar obús" requiere compartir is_charging de los targets en shared_targets (convertirlo en array de dicts {pos, charging} — tocar también el consumidor).


6. UX / UI / Onboarding

6.1 Pantalla de controles + práctica en el countdown

Diseño. El countdown de 3 s es poco para leer nada. Añadir en el lobby un botón "¿CÓMO SE JUEGA?" que abre un overlay con el mapeo visual de mando y teclado (dibujado con Controls, sin imágenes externas) y las 5 reglas de oro (mueres → eres Piloto → junta 3-5 scraps → canaliza → vuelves). Además, un modo práctica: entrar solo con 0 bots deja un dummy estático que muestra números de daño flotantes.

Implementación. Overlay: escena nueva scenes/menus/HowToPlay.tscn siguiendo el patrón CanvasLayer + cierre con B/ESC de MechaCustomizationMenu.gd. Números de daño: en take_damage, instanciar un Label3D (el patrón exacto ya existe en TestLevel._add_name_tag) con el número, tween hacia arriba y queue_free. El dummy: MechaPlayer con controls_enabled = false e HP infinito.

6.2 Killfeed y anuncios de partida

Diseño. Esquina superior: "ROJO destruyó a AZUL (OBÚS FULL)", "VERDE se autodestruyó", "AMARILLO reconstruyó su Mecha". Anuncios grandes para hitos: "¡DOBLE BAJA!" si un haz mata a 2, "¡ÚLTIMO MECHA!" cuando quedan 2 jugadores.

Implementación. Autoload ligero o nodo en TestLevel: Killfeed.gd con announce(text, color) que apila labels con fade-out. Requiere atribución de atacante (idea 3.3). El "doble baja" se detecta en fire_beam contando víctimas del mismo disparo.

6.3 Indicadores off-screen

Diseño. Flechas en el borde de pantalla apuntando a: tu Piloto (si tu Mecha murió y la cámara está lejos), scraps propios, y el Juggernaut/zona en esos modos. Color = color del dueño.

Implementación. systems/ui/OffscreenIndicators.gd (CanvasLayer): por target, camera.unproject_position(); si queda fuera del rect visible, clampear al borde con margen y rotar la flecha hacia el punto. La cámara es única (DynamicCamera), lo que lo hace barato.

6.4 Vibración de mando (juice barato)

Diseño. Rumble corto al: recibir daño (fuerte), disparar obús full (medio), overheat (doble pulso), countdown GO (suave). Solo al device del jugador.

Implementación. Input.start_joy_vibration(device_id, weak, strong, duration) en los puntos correspondientes de MechaPlayer.gd (guard con is_bot y input_device_id >= 0).


7. Audio / Visual

7.1 SFX propios para las armas nuevas

Diseño. La gatling y el obús reutilizan "shoot" con pitch. Sonidos dedicados: gatling (tick metálico corto), beam_charge (rampa ascendente en loop mientras carga, pitch sube con charge), beam_fire (zap grave + cola).

Implementación. tools/gen_audio.py ya tiene osciladores/envelopes (osc, noise, env_adsr, write_wav) y cada SFX es una función sfx_*. Añadir las 3 funciones, regenerar (python3 tools/gen_audio.py), registrar en AudioManager.SFX_STREAMS y reemplazar las llamadas en MechaPlayer.gd. Para la rampa de carga: un AudioStreamPlayer dedicado en el Mecha cuyo pitch_scale = 1.0 + charge se actualiza en _update_charge.

7.2 Música dinámica por intensidad

Diseño. battle_theme sube una capa (más percusión) cuando quedan ≤2 Mechas o alguien carga un obús full. Bajada suave al resolverse.

Implementación. Generar una segunda pista alineada (battle_theme_intense) con gen_audio.py, reproducir ambas en paralelo sincronizadas y crossfadear volúmenes en AudioManager (set_intensity(0..1) llamado desde TestLevel).

7.3 Restos de combate persistentes

Diseño. Las explosiones dejan marcas: decal oscuro simple (quad con material quemado) y 2–3 piezas de escombro físico (RigidBody pequeño que se duerme). El haz del obús deja una línea chamuscada 10 s. La arena cuenta la historia de la ronda.

Implementación. En explode() y fire_beam: instanciar quads con material oscuro semitransparente sobre el piso (raycast hacia abajo para altura), con límite global (pool de 30, FIFO) para no acumular draw calls en el renderer mobile.


8. Técnico / Infraestructura

8.1 Replay de la jugada final (kill cam)

Diseño. Al terminar la ronda, repetición de los últimos 5 s desde una cámara cercana al ganador antes del leaderboard (skippeable).

Implementación. Grabador barato: cada 0.1 s, guardar {pos, rot, aim, anim_state} de cada Mecha en un ring buffer (50 frames). Para reproducir: congelar gameplay (get_tree().paused, los actores del replay en PROCESS_MODE_ALWAYS), mover "cáscaras" visuales interpolando el buffer. No simular física: solo transforms.

8.2 Pantalla dividida opcional (cámara por jugador)

Diseño. Hoy hay una cámara compartida; con 2 jugadores en mapas grandes se sufre. Opción en OPCIONES: "Cámara: COMPARTIDA / DIVIDIDA (2P)". Dividida: dos SubViewports lado a lado, cada uno con su DynamicCamera siguiendo a un jugador.

Implementación. TestLevel: si el setting está activo y hay exactamente 2 humanos, mover el World3D a un contenedor de 2 SubViewportContainer (world_3d compartido entre viewports, cámara propia por viewport). El HUD de cada jugador va al viewport correspondiente. Es el cambio más invasivo de esta lista — hacerlo detrás de un flag y con fallback limpio.

8.3 Guardado de loadouts favoritos

Diseño. En la pantalla de personalización, botón "GUARDAR COMO FAVORITO" (3 slots). En el lobby, mantener X sobre la card carga el favorito 1. Persiste en user://loadouts.cfg.

Implementación. SessionData: save_loadout(slot, custom) / load_loadout(slot) con ConfigFile (mismo patrón que save_settings). UI: fila de 3 botones en MechaCustomizationMenu bajo el chasis.

8.4 Suite de smoke-tests sin editor

Diseño. Script que valida lo validable sin Godot CLI: parseo gdtoolkit de todos los .gd, consistencia de .tscn (refs/ids/parents — ya existe el patrón en los scripts de validación usados en este repo), y chequeos de contrato (que MechaParts.OPTIONS tenga las mismas keys que usa _build_*_details, que todo play_sfx("x") exista en SFX_STREAMS).

Implementación. tools/check.py ejecutable con python3 tools/check.py; los chequeos de contrato son greps estructurados sobre los fuentes. Documentar en README como paso previo a probar en el editor.


Prioridades sugeridas

NivelIdeasRacional
Alto impacto / bajo esfuerzo1.5 venteo, 3.3 stats+títulos, 6.2 killfeed, 6.4 rumble, 7.1 SFX armas, 2.2 quemar scrapJuice y legibilidad inmediatos con poco riesgo
Alto impacto / esfuerzo medio3.1 torneo, 4.1 King of the Hill, 3.2 mutadores, 5.1/5.2 bots, 1.1 habilidades de chasisRejugabilidad estructural
Apuestas grandes4.5 horda, 4.6 equipos, 4.3 juggernaut, 8.1 killcam, 8.2 split-screenModos completos; hacer tras estabilizar lo anterior

Orden recomendado si se implementa en serie: 3.3 (atribución de daño — es dependencia de killfeed, juggernaut y títulos) → 6.2 → 3.1 → 4.1 → 3.2 → 5.1 → 1.1 → resto según feedback de juego real.