<?xml version="1.0" encoding="utf-8"?>
<!--
  ═══════════════════════════════════════════════════════════════════════════════
  VPVisualDamageRootByHealth — Referencia XML  [ESPAÑOL]
  ═══════════════════════════════════════════════════════════════════════════════

  QUÉ HACE
  ════════
  Esta parte de vehículo visualiza tres estados de salud intercambiando
  GameObjects en la jerarquía de mallas:

	Estado 0 — sano     (healthyRootName  activo, los otros ocultos)
	Estado 1 — dañado   (damagedRootName  activo, los otros ocultos)
	Estado 2 — crítico  (criticalRootName activo, los otros ocultos)

  Además puede:
  - Aplicar el color de pintura/tintado del vehículo a sub-nodos concretos.
  - Activar un TARTAMUDEO del motor en estado crítico: alterna encendido/apagado aleatoriamente.
  - Reducir la potencia del motor mientras está en crítico mediante un mapa estático compartido.
  - Reproducir una alarma en bucle mientras dure el estado crítico.
  - Reaccionar al evento FuelEmpty cortando el motor inmediatamente.

  DÓNDE PONER ESTO
  ═════════════════
  Inserta el bloque <properties> dentro de la definición de la parte en
  vehicleparts.xml o en el XML del vehículo donde declares la parte:

	<vehiclePart name="DamageVisual">
	  <class>VPVisualDamageRootByHealth,suspension</class>
	  <properties>
		...
	  </properties>
	</vehiclePart>

  CÓMO SE CALCULA EL ESTADO
  ══════════════════════════
  Cada refreshSeconds segundos el script lee la vida del vehículo (0–1):
	hp ≤ criticalAt01  →  estado 2 (crítico)
	hp ≤ damagedAt01   →  estado 1 (dañado)
	hp  > damagedAt01  →  estado 0 (sano)

  BÚSQUEDA DE ROOTS
  ═════════════════
  Los roots se buscan recursivamente bajo vehicle.GetMeshTransform() por nombre
  exacto (sensible a mayúsculas). El primer transform que coincide se usa.
  Los objetos pueden estar bajo cualquier nodo padre (M/, Physics/, GameObject/, etc.).

  ═══════════════════════════════════════════════════════════════════════════════
-->

<vp_visual_damage_reference>

  <!-- ══════════════════════════════════════════════════════════════════════
	   EJEMPLO MÍNIMO  (solo lo estrictamente necesario)
	   ══════════════════════════════════════════════════════════════════════ -->

  <vehiclePart name="DamageVisual_Minimo">
	<class>VPVisualDamageRootByHealth,suspension</class>
	<properties>

	  <!-- Nombres de los roots en tu prefab — sensibles a mayúsculas. OBLIGATORIO. -->
	  <property name="healthyRootName"  value="Heal"/>
	  <property name="damagedRootName"  value="damage"/>
	  <property name="criticalRootName" value="critical"/>

	  <!-- Umbrales de salud (0.0–1.0). El estado cambia cuando la vida cae por debajo. -->
	  <property name="damagedAt01"      value="0.66"/>
	  <property name="criticalAt01"     value="0.30"/>

	</properties>
  </vehiclePart>


  <!-- ══════════════════════════════════════════════════════════════════════
	   EJEMPLO COMPLETO  (todas las propiedades disponibles)
	   ══════════════════════════════════════════════════════════════════════ -->

  <vehiclePart name="DamageVisual_Completo">
	<class>VPVisualDamageRootByHealth,suspension</class>
	<properties>

	  <!-- ── NOMBRES DE LOS ROOTS ───────────────────────────────────────── -->

	  <!-- Nombre del root del estado sano en la jerarquía de mallas del prefab.
		   Visible cuando hp > damagedAt01. Sensible a mayúsculas. -->
	  <property name="healthyRootName"               value="Heal"/>

	  <!-- Nombre del root del estado dañado.
		   Visible cuando criticalAt01 < hp ≤ damagedAt01. -->
	  <property name="damagedRootName"               value="damage"/>

	  <!-- Nombre del root del estado crítico.
		   Visible cuando hp ≤ criticalAt01. -->
	  <property name="criticalRootName"              value="critical"/>

	  <!-- ── DESACTIVAR INTERCAMBIO DE ROOTS ───────────────────────────── -->

	  <!-- Cuando es true, el script NO activa/desactiva los tres roots.
		   Todos los roots permanecen en su estado Unity actual.
		   El tintado y los efectos críticos siguen funcionando con normalidad.
		   Útil cuando tu vehículo gestiona la visibilidad de las mallas desde
		   otro lugar (p. ej. animación o script propio) y solo quieres el tinte
		   y el comportamiento del motor crítico/alarma.
		   Valor por defecto: false -->
	  <property name="disableDamageRootSwap"         value="false"/>

	  <!-- ── UMBRALES Y REFRESCO ────────────────────────────────────────── -->

	  <!-- Fracción de vida (0.0–1.0) a la que el vehículo pasa a DAÑADO.
		   Valor por defecto: 0.66 -->
	  <property name="damagedAt01"                   value="0.66"/>

	  <!-- Fracción de vida (0.0–1.0) a la que el vehículo pasa a CRÍTICO.
		   Debe ser menor que damagedAt01.
		   Valor por defecto: 0.30 -->
	  <property name="criticalAt01"                  value="0.30"/>

	  <!-- Segundos entre comprobaciones de vida y actualizaciones de estado.
		   Menor = más reactivo pero algo más de CPU.
		   Valor por defecto: 0.25 -->
	  <property name="refreshSeconds"                value="0.25"/>

	  <!-- ── TINTADO ────────────────────────────────────────────────────── -->

	  <!-- Si se aplica el color de pintura del vehículo a los sub-nodos.
		   El color se lee del itemValue TintColor del vehículo (el mismo color
		   que el jugador elige en la UI de pintura).
		   Valor por defecto: true -->
	  <property name="applyTint"                     value="true"/>

	  <!-- Nombre de la propiedad de color del shader donde se escribe el tinte.
		   Cambiar si tu material usa un nombre de propiedad diferente.
		   Valor por defecto: _Color -->
	  <property name="shaderColorProperty"           value="_Color"/>

	  <!-- Lista CSV de nombres de nodos hijos (bajo el root activo) cuyos
		   renderers recibirán el tinte. Cada nombre se busca recursivamente.
		   Ejemplo: "body,Missiles,Hood"
		   Valor por defecto: body,Missiles -->
	  <property name="tintChildNames"                value="body,Missiles"/>

	  <!-- ── TARTAMUDEO DEL MOTOR EN CRÍTICO ────────────────────────────── -->

	  <!-- Si se alterna el motor encendido/apagado aleatoriamente en crítico.
		   Requiere una parte VPEngine en la lista de partes del vehículo,
		   encontrada por reflexión en tiempo de ejecución.
		   Si el vehículo no tiene VPEngine no tiene efecto.
		   Valor por defecto: true -->
	  <property name="criticalEngineStutterEnabled"  value="true"/>

	  <!-- Multiplicador de potencia del motor mientras está en estado crítico.
		   Se escribe en el diccionario estático compartido
		   VPVisualDamageRootByHealth.CriticalPowerMulMap, que VPEngine lee
		   para reducir la salida.
		   Rango: 0.05 (casi sin potencia) – 1.0 (potencia completa).
		   Valor por defecto: 0.55 -->
	  <property name="criticalPowerMul"              value="0.55"/>

	  <!-- Segundos mínimos que el motor permanece APAGADO en cada ciclo.
		   Valor por defecto: 0.10 -->
	  <property name="criticalOffMin"                value="0.10"/>

	  <!-- Segundos máximos que el motor permanece APAGADO en cada ciclo.
		   Valor por defecto: 0.35 -->
	  <property name="criticalOffMax"                value="0.35"/>

	  <!-- Segundos mínimos que el motor permanece ENCENDIDO en cada ciclo.
		   Valor por defecto: 0.25 -->
	  <property name="criticalOnMin"                 value="0.25"/>

	  <!-- Segundos máximos que el motor permanece ENCENDIDO en cada ciclo.
		   Valor por defecto: 0.90 -->
	  <property name="criticalOnMax"                 value="0.90"/>

	  <!-- ── SONIDO DE ALARMA EN CRÍTICO ────────────────────────────────── -->

	  <!-- Si se reproduce una alarma sonora mientras está en estado crítico.
		   Solo suena en la entidad LOCAL y solo si hay un conductor.
		   Valor por defecto: false -->
	  <property name="criticalBeepEnabled"           value="true"/>

	  <!-- Nombre del recurso de audio a reproducir como alarma crítica.
		   Si el nombre del audio termina en "_lp" se reproduce como bucle
		   continuo mediante Audio.Manager.Play (handle de bucle, iniciado una
		   vez y detenido al salir del estado crítico o al marcharse el conductor).
		   Si NO termina en "_lp" se inicia igualmente como bucle.
		   Ejemplos: "caralarm1_lp", "ui_denied"
		   NOTA: criticalBeepInterval se parsea del XML pero no se usa
		   internamente — la alarma siempre es un handle de audio en bucle,
		   no disparos a intervalos.
		   Valor por defecto: ui_denied -->
	  <property name="criticalBeepSound"             value="caralarm1_lp"/>

	  <!-- Se parsea pero actualmente el script no lo usa. Reservado para
		   uso futuro. El bucle de alarma se inicia al entrar en crítico y
		   se detiene al salir, independientemente de este valor.
		   Valor por defecto: 1.0 -->
	  <property name="criticalBeepInterval"          value="1.0"/>

	  <!-- ── DEPURACIÓN ─────────────────────────────────────────────────── -->

	  <!-- Cuando es true, escribe mensajes detallados en el log del juego
		   mediante Log.Out.
		   Formato: [VPVisualDamageRootByHealth id=N] mensaje
		   Activar durante el desarrollo, desactivar en la versión final.
		   Valor por defecto: false -->
	  <property name="debugLog"                      value="false"/>

	</properties>
  </vehiclePart>


  <!-- ══════════════════════════════════════════════════════════════════════
	   JERARQUÍA DEL PREFAB DE UNITY  (estructura recomendada)
	   ══════════════════════════════════════════════════════════════════════

	El script llama a vehicle.GetMeshTransform() para obtener el root de
	mallas y luego hace una búsqueda profunda por nombre en cada root.

	Mantén los colliders de física en una rama separada (p. ej. Physics/)
	para que nunca sean confundidos con roots visuales.

	VehicleRoot
	├── Physics/           ← colliders (ignorados por este script)
	└── M/                 ← root de mallas (vehicle.GetMeshTransform())
		├── Heal/          ← healthyRootName  (activo por defecto)
		│   ├── body/      ← tintado si está en tintChildNames
		│   └── Missiles/  ← tintado si está en tintChildNames
		├── damage/        ← damagedRootName  (oculto por defecto)
		│   ├── body/
		│   └── Missiles/
		└── critical/      ← criticalRootName (oculto por defecto)
			├── body/
			└── Missiles/

	Cuando disableDamageRootSwap=true los tres roots no se intercambian.
	Para el tintado, el script usa el root de mallas completo (M/) como
	root activo, por lo que tintChildNames debe ser accesible directamente
	bajo M/.
  -->


  <!-- ══════════════════════════════════════════════════════════════════════
	   TABLA DE REFERENCIA RÁPIDA
	   ══════════════════════════════════════════════════════════════════════

  ┌────────────────────────────────┬────────┬────────────┬────────────────────┐
  │ Propiedad                      │ Tipo   │ Por defecto│ Notas              │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ NOMBRES DE ROOTS               │        │            │                    │
  │ healthyRootName                │ string │ MH6Heal    │ Sensible a mayúsc. │
  │ damagedRootName                │ string │ MH6damage  │ Sensible a mayúsc. │
  │ criticalRootName               │ string │ MH6critical│ Sensible a mayúsc. │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ INTERCAMBIO DE ROOTS           │        │            │                    │
  │ disableDamageRootSwap          │ bool   │ false      │ Omite el toggle    │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ UMBRALES                       │        │            │                    │
  │ damagedAt01                    │ float  │ 0.66       │ 0.0–1.0            │
  │ criticalAt01                   │ float  │ 0.30       │ < damagedAt01      │
  │ refreshSeconds                 │ float  │ 0.25       │ segundos           │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ TINTADO                        │        │            │                    │
  │ applyTint                      │ bool   │ true       │                    │
  │ shaderColorProperty            │ string │ _Color     │                    │
  │ tintChildNames                 │ CSV    │ body,Miss..│ Búsqueda recursiva │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ TARTAMUDEO DEL MOTOR           │        │            │                    │
  │ criticalEngineStutterEnabled   │ bool   │ true       │ Necesita VPEngine  │
  │ criticalPowerMul               │ float  │ 0.55       │ 0.05–1.0           │
  │ criticalOffMin                 │ float  │ 0.10       │ seg. motor APAGADO │
  │ criticalOffMax                 │ float  │ 0.35       │ seg. motor APAGADO │
  │ criticalOnMin                  │ float  │ 0.25       │ seg. motor ENCEND. │
  │ criticalOnMax                  │ float  │ 0.90       │ seg. motor ENCEND. │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ SONIDO DE ALARMA               │        │            │                    │
  │ criticalBeepEnabled            │ bool   │ false      │                    │
  │ criticalBeepSound              │ string │ ui_denied  │ "_lp" = bucle      │
  │ criticalBeepInterval           │ float  │ 1.0        │ Parseado, no usado │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ DEPURACIÓN                     │        │            │                    │
  │ debugLog                       │ bool   │ false      │ Mensajes Log.Out   │
  └────────────────────────────────┴────────┴────────────┴────────────────────┘
  -->


  <!-- ══════════════════════════════════════════════════════════════════════
	   NOTAS Y CONSEJOS
	   ══════════════════════════════════════════════════════════════════════

  NOMBRES DE ROOTS
  ● Los nombres son sensibles a mayúsculas y se buscan recursivamente bajo
	el root de mallas. Si no se encuentra un root no hay cambio visual (sin
	errores en consola).
  ● Los roots pueden estar bajo cualquier nodo padre dentro de la jerarquía.

  disableDamageRootSwap
  ● Ponlo a true cuando quieras el tinte y los efectos críticos pero gestiones
	la visibilidad de las mallas tú mismo (p. ej. mediante animación o script).
  ● Cuando está desactivado el intercambio, el tinte se aplica a los hijos
	del root de mallas completo, no a los roots de estado individuales.

  TINTADO
  ● El color de tinte se lee del itemValue TintColor del vehículo —
	el mismo color que el jugador establece en la UI de pintura.
  ● Si tu material no usa _Color, ajusta shaderColorProperty al nombre
	correcto (p. ej. _BaseColor para URP).
  ● El tinte se refresca cada ~0.5 s aunque el estado no cambie, por lo que
	los cambios de color de pintura se recogen sin recargar el vehículo.

  TARTAMUDEO Y POTENCIA DEL MOTOR
  ● El tartamudeo requiere una parte VPEngine en la misma lista de partes del
	vehículo. La parte se encuentra en tiempo de ejecución por reflexión —
	no hace falta una referencia directa en el código.
  ● El multiplicador de potencia (criticalPowerMul) se guarda en el diccionario
	estático VPVisualDamageRootByHealth.CriticalPowerMulMap[entityId]. VPEngine
	lo lee cada fotograma para limitar la salida. Al salir del estado crítico
	la entrada se elimina y la potencia completa se restaura automáticamente.
  ● El tartamudeo se detiene inmediatamente si el conductor se va (sin nadie
	que lo experimente). Se reanuda en cuanto vuelve un conductor si el
	vehículo sigue en crítico.

  EVENTO FUEL EMPTY
  ● Este script escucha el evento VehiclePart.Event.FuelEmpty.
	Cuando se dispara, corta el motor (stopEngine) inmediatamente
	independientemente del estado de salud. Esto evita que el motor se reinicie
	durante el tartamudeo si el vehículo se quedó sin combustible al mismo
	tiempo que entró en crítico.

  SONIDO DE ALARMA
  ● criticalBeepEnabled solo reproduce en la entidad LOCAL y solo mientras
	hay un conductor. Los clientes remotos NO lo escuchan desde este script.
  ● Los recursos de audio que terminan en "_lp" se reproducen en bucle continuo
	mediante Audio.Manager.Play con un handle. El handle se detiene limpiamente
	al salir del crítico o cuando el conductor se va.
  ● criticalBeepInterval se parsea del XML pero el código NO lo usa.
	La alarma siempre se reproduce como bucle continuo — no hay repetición
	a intervalos fijos.

  DEPURACIÓN
  ● Activa debugLog=true para rastrear transiciones de estado, aplicaciones de
	tinte, descubrimiento de VPEngine y ciclo de vida del handle de audio en
	el log del juego.
  ● Las entradas del log llevan el prefijo [VPVisualDamageRootByHealth id=N].
  ● Desactívalo en producción — Log.Out escribe en cada tick de tartamudeo
	cuando está habilitado.
  -->

</vp_visual_damage_reference>
