<?xml version="1.0" encoding="utf-8"?>
<!--
  ═══════════════════════════════════════════════════════════════════════════════
  VPVisualDamageRootByHealth — XML Reference  [ENGLISH]
  ═══════════════════════════════════════════════════════════════════════════════

  WHAT IT DOES
  ════════════
  This vehicle part visualizes three health states by swapping GameObjects
  in the mesh hierarchy:

	State 0 — healthy   (healthyRootName  is active, others hidden)
	State 1 — damaged   (damagedRootName  is active, others hidden)
	State 2 — critical  (criticalRootName is active, others hidden)

  Beyond the root swap it can also:
  - Apply the vehicle's tint color (shader property) to specific sub-nodes.
  - Trigger a CRITICAL engine stutter: alternates engine on/off randomly.
  - Reduce engine power while critical via a shared static map.
  - Play a looping alarm sound while in critical state.
  - React to the FuelEmpty event by cutting the engine immediately.

  WHERE TO PUT THIS
  ═════════════════
  Insert the <properties> block inside your vehiclePart definition in
  vehicleparts.xml or the vehicle XML where you declare the part:

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

  HOW STATE IS COMPUTED
  ══════════════════════
  Every refreshSeconds seconds, the script reads the vehicle's health (0–1):
	hp ≤ criticalAt01  →  state 2 (critical)
	hp ≤ damagedAt01   →  state 1 (damaged)
	hp  > damagedAt01  →  state 0 (healthy)

  ROOT SEARCH
  ═══════════
  Roots are searched recursively under vehicle.GetMeshTransform() by exact
  name (case-sensitive). The first matching transform wins. Objects can live
  under any parent node (M/, Physics/, GameObject/, etc.).

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

<vp_visual_damage_reference>

  <!-- ══════════════════════════════════════════════════════════════════════
	   MINIMAL EXAMPLE  (only what is strictly required)
	   ══════════════════════════════════════════════════════════════════════ -->

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

	  <!-- Root names in your prefab — case-sensitive, exact match. REQUIRED. -->
	  <property name="healthyRootName"  value="Heal"/>
	  <property name="damagedRootName"  value="damage"/>
	  <property name="criticalRootName" value="critical"/>

	  <!-- Health thresholds (0.0–1.0). State switches when hp falls below. -->
	  <property name="damagedAt01"      value="0.66"/>
	  <property name="criticalAt01"     value="0.30"/>

	</properties>
  </vehiclePart>


  <!-- ══════════════════════════════════════════════════════════════════════
	   COMPLETE EXAMPLE  (all available properties)
	   ══════════════════════════════════════════════════════════════════════ -->

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

	  <!-- ── ROOT NAMES ─────────────────────────────────────────────────── -->

	  <!-- Name of the healthy state root in the prefab mesh hierarchy.
		   Shown when hp > damagedAt01. Case-sensitive. -->
	  <property name="healthyRootName"               value="Heal"/>

	  <!-- Name of the damaged state root.
		   Shown when criticalAt01 < hp ≤ damagedAt01. -->
	  <property name="damagedRootName"               value="damage"/>

	  <!-- Name of the critical state root.
		   Shown when hp ≤ criticalAt01. -->
	  <property name="criticalRootName"              value="critical"/>

	  <!-- ── DISABLE ROOT SWAP ──────────────────────────────────────────── -->

	  <!-- When true, the script will NOT activate/deactivate the three roots.
		   All roots remain in their current Unity active state.
		   Tinting and critical effects still work normally.
		   Use this when your vehicle handles mesh swapping elsewhere and you
		   only want the tint + critical engine/alarm behavior.
		   Default: false -->
	  <property name="disableDamageRootSwap"         value="false"/>

	  <!-- ── THRESHOLDS & REFRESH ───────────────────────────────────────── -->

	  <!-- Health fraction (0.0–1.0) at which the vehicle switches to DAMAGED.
		   Default: 0.66 -->
	  <property name="damagedAt01"                   value="0.66"/>

	  <!-- Health fraction (0.0–1.0) at which the vehicle switches to CRITICAL.
		   Must be lower than damagedAt01.
		   Default: 0.30 -->
	  <property name="criticalAt01"                  value="0.30"/>

	  <!-- Seconds between health checks and state updates.
		   Lower = more responsive but slightly more CPU.
		   Default: 0.25 -->
	  <property name="refreshSeconds"                value="0.25"/>

	  <!-- ── TINT ───────────────────────────────────────────────────────── -->

	  <!-- Whether to apply the vehicle's paint/tint color to sub-nodes.
		   The tint color is read from the vehicle's itemValue TintColor property
		   (the same color the player picks in the UI).
		   Default: true -->
	  <property name="applyTint"                     value="true"/>

	  <!-- Name of the shader color property to write the tint to.
		   Change if your material uses a different property name.
		   Default: _Color -->
	  <property name="shaderColorProperty"           value="_Color"/>

	  <!-- Comma-separated list of child node names (under the active root)
		   whose renderers will receive the tint.
		   Each name is searched recursively under the active root.
		   Example: "body,Missiles,Hood"
		   Default: body,Missiles -->
	  <property name="tintChildNames"                value="body,Missiles"/>

	  <!-- ── CRITICAL ENGINE STUTTER ────────────────────────────────────── -->

	  <!-- Whether to alternate the engine on/off randomly while critical.
		   Uses VPEngine (found by reflection in the vehicle parts list).
		   If the vehicle has no VPEngine part, this has no effect.
		   Default: true -->
	  <property name="criticalEngineStutterEnabled"  value="true"/>

	  <!-- Engine power multiplier applied while in critical state.
		   Written to the shared VPVisualDamageRootByHealth.CriticalPowerMulMap
		   dictionary, which VPEngine reads to reduce output.
		   Range: 0.05 (almost no power) – 1.0 (full power).
		   Default: 0.55 -->
	  <property name="criticalPowerMul"              value="0.55"/>

	  <!-- Minimum seconds the engine stays OFF during a stutter cycle.
		   Default: 0.10 -->
	  <property name="criticalOffMin"                value="0.10"/>

	  <!-- Maximum seconds the engine stays OFF during a stutter cycle.
		   Default: 0.35 -->
	  <property name="criticalOffMax"                value="0.35"/>

	  <!-- Minimum seconds the engine stays ON during a stutter cycle.
		   Default: 0.25 -->
	  <property name="criticalOnMin"                 value="0.25"/>

	  <!-- Maximum seconds the engine stays ON during a stutter cycle.
		   Default: 0.90 -->
	  <property name="criticalOnMax"                 value="0.90"/>

	  <!-- ── CRITICAL ALARM SOUND ───────────────────────────────────────── -->

	  <!-- Whether to play an alarm sound while in critical state.
		   The sound only plays for the LOCAL entity and only if it has a driver.
		   Default: false -->
	  <property name="criticalBeepEnabled"           value="true"/>

	  <!-- Name of the audio resource to play as the critical alarm.
		   If the audio name ends with "_lp" it is played as a seamless loop
		   via Audio.Manager.Play (handle-based loop, started once and stopped
		   when leaving critical state or the driver leaves).
		   If it does NOT end with "_lp" it is still started as a loop.
		   Examples: "caralarm1_lp", "ui_denied"
		   NOTE: criticalBeepInterval is parsed from XML but is not used
		   internally — the alarm is always a looping audio handle, not
		   interval-based one-shots.
		   Default: ui_denied -->
	  <property name="criticalBeepSound"             value="caralarm1_lp"/>

	  <!-- Parsed but currently unused by the script. Reserved for future use.
		   The alarm loop is started once when entering critical state and
		   stopped on exit — regardless of this value.
		   Default: 1.0 -->
	  <property name="criticalBeepInterval"          value="1.0"/>

	  <!-- ── DEBUG ──────────────────────────────────────────────────────── -->

	  <!-- When true, detailed messages are written to the game log via Log.Out.
		   Format: [VPVisualDamageRootByHealth id=N] message
		   Turn on during development, turn off in release.
		   Default: false -->
	  <property name="debugLog"                      value="false"/>

	</properties>
  </vehiclePart>


  <!-- ══════════════════════════════════════════════════════════════════════
	   UNITY PREFAB HIERARCHY  (recommended structure)
	   ══════════════════════════════════════════════════════════════════════

	The script calls vehicle.GetMeshTransform() to get the mesh root, then
	does a deep recursive name search for each root.

	Keep physics colliders in a separate branch (e.g. Physics/) so they are
	never mistakenly picked up as mesh roots.

	VehicleRoot
	├── Physics/           ← colliders (ignored by this script)
	└── M/                 ← mesh root (vehicle.GetMeshTransform())
		├── Heal/          ← healthyRootName  (active by default)
		│   ├── body/      ← tinted if in tintChildNames
		│   └── Missiles/  ← tinted if in tintChildNames
		├── damage/        ← damagedRootName  (hidden by default)
		│   ├── body/
		│   └── Missiles/
		└── critical/      ← criticalRootName (hidden by default)
			├── body/
			└── Missiles/

	When disableDamageRootSwap=true the three roots are not toggled.
	Instead, for tinting, the script uses the mesh root itself (M/) as the
	active root, so tintChildNames must be reachable directly under M/.
  -->


  <!-- ══════════════════════════════════════════════════════════════════════
	   QUICK REFERENCE TABLE
	   ══════════════════════════════════════════════════════════════════════

  ┌────────────────────────────────┬────────┬────────────┬────────────────────┐
  │ Property                       │ Type   │ Default    │ Notes              │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ ROOT NAMES                     │        │            │                    │
  │ healthyRootName                │ string │ MH6Heal    │ Case-sensitive     │
  │ damagedRootName                │ string │ MH6damage  │ Case-sensitive     │
  │ criticalRootName               │ string │ MH6critical│ Case-sensitive     │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ ROOT SWAP                      │        │            │                    │
  │ disableDamageRootSwap          │ bool   │ false      │ Skip mesh toggle   │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ THRESHOLDS                     │        │            │                    │
  │ damagedAt01                    │ float  │ 0.66       │ 0.0–1.0            │
  │ criticalAt01                   │ float  │ 0.30       │ < damagedAt01      │
  │ refreshSeconds                 │ float  │ 0.25       │ seconds            │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ TINT                           │        │            │                    │
  │ applyTint                      │ bool   │ true       │                    │
  │ shaderColorProperty            │ string │ _Color     │                    │
  │ tintChildNames                 │ CSV    │ body,Miss..│ Recursive search   │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ ENGINE STUTTER                 │        │            │                    │
  │ criticalEngineStutterEnabled   │ bool   │ true       │ Needs VPEngine     │
  │ criticalPowerMul               │ float  │ 0.55       │ 0.05–1.0           │
  │ criticalOffMin                 │ float  │ 0.10       │ seconds engine OFF │
  │ criticalOffMax                 │ float  │ 0.35       │ seconds engine OFF │
  │ criticalOnMin                  │ float  │ 0.25       │ seconds engine ON  │
  │ criticalOnMax                  │ float  │ 0.90       │ seconds engine ON  │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ ALARM SOUND                    │        │            │                    │
  │ criticalBeepEnabled            │ bool   │ false      │                    │
  │ criticalBeepSound              │ string │ ui_denied  │ "_lp" = loop       │
  │ criticalBeepInterval           │ float  │ 1.0        │ Parsed, unused     │
  ├────────────────────────────────┼────────┼────────────┼────────────────────┤
  │ DEBUG                          │        │            │                    │
  │ debugLog                       │ bool   │ false      │ Log.Out messages   │
  └────────────────────────────────┴────────┴────────────┴────────────────────┘
  -->


  <!-- ══════════════════════════════════════════════════════════════════════
	   NOTES & TIPS
	   ══════════════════════════════════════════════════════════════════════

  ROOT NAMES
  ● Root names are case-sensitive and searched recursively under the mesh root.
	If a root is not found, no visual change happens (no crash).
  ● You can place roots under any parent node inside the mesh hierarchy.

  disableDamageRootSwap
  ● Set to true when you want the tint and critical effects but you are
	managing mesh visibility yourself (e.g. via animation or another script).
  ● When disabled, tint is applied to children of the mesh root itself,
	not the individual state roots.

  TINT
  ● The tint color is read from the vehicle's itemValue TintColor property —
	the same color the player sets via the vehicle paint UI.
  ● If your material does not use _Color, set shaderColorProperty to the
	correct shader property name (e.g. _BaseColor for URP).
  ● Tint is refreshed every ~0.5 s even when the state does not change,
	so paint color updates are picked up without reloading.

  ENGINE STUTTER & POWER
  ● The stutter requires a VPEngine part in the same vehicle parts list.
	The engine part is found at runtime by reflection — no hard reference needed.
  ● The power multiplier (criticalPowerMul) is stored in the static dictionary
	VPVisualDamageRootByHealth.CriticalPowerMulMap[entityId]. VPEngine reads
	this each frame to cap output. When the vehicle exits critical state the
	entry is removed and full power is restored automatically.
  ● The stutter stops immediately if the driver leaves (no one to experience it).
	It resumes as soon as a driver returns while still in critical state.

  FUEL EMPTY EVENT
  ● This script listens for the VehiclePart.Event.FuelEmpty event.
	When fired, it immediately cuts the engine (stopEngine) regardless of the
	current health state. This prevents the engine from restarting during stutter
	if the vehicle ran out of fuel at the same time it entered critical state.

  ALARM SOUND
  ● criticalBeepEnabled only plays on the LOCAL entity and only while a driver
	is present. Remote clients do NOT hear it from this script.
  ● Audio resources ending in "_lp" are looped seamlessly via Audio.Manager.Play
	with a handle. The handle is stopped cleanly when exiting critical state or
	when the driver leaves.
  ● criticalBeepInterval is parsed from XML but is NOT used by the code.
	The alarm always plays as a continuous loop — there is no interval repetition.

  DEBUG
  ● Enable debugLog=true to trace state transitions, tint applications,
	VPEngine discovery, and beep handle lifecycle in the game log.
  ● Log entries are prefixed [VPVisualDamageRootByHealth id=N].
  ● Disable in production — Log.Out writes every stutter tick when enabled.
  -->

</vp_visual_damage_reference>
