Perekyp/Assets/Scripts/Car/Visuals/CarPartVisuals.cs

189 lines
5.9 KiB
C#

using System.Collections.Generic;
using UnityEngine;
public enum RepairStage
{
Healthy, Rusted, Cleaned, Puttied, Sanded, Repaired
}
[DisallowMultipleComponent]
public class CarPartVisuals : MonoBehaviour
{
private static readonly int BaseColorProp = Shader.PropertyToID("_BaseColor");
private static readonly int RustAmountProp = Shader.PropertyToID("_RustAmount");
private static readonly int RustInitialProp = Shader.PropertyToID("_RustInitial");
private static readonly int PuttyProp = Shader.PropertyToID("_PuttyProgress");
private static readonly int SandProp = Shader.PropertyToID("_SandProgress");
private static readonly int PaintProp = Shader.PropertyToID("_PaintProgress");
private const float DoneThreshold = 0.98f;
private const float RustCleanThreshold = 0.02f;
[Header("Состояние детали")]
[Range(0f, 1f)] [SerializeField] private float rustInitial;
[Range(0f, 1f)] [SerializeField] private float rustCurrent;
[Range(0f, 1f)] [SerializeField] private float putty;
[Range(0f, 1f)] [SerializeField] private float sanding;
[Range(0f, 1f)] [SerializeField] private float paint;
[SerializeField] private Color paintColor = Color.white;
private struct Slot
{
public Renderer renderer;
public int materialIndex;
}
private readonly List<Slot> slots = new List<Slot>();
private MaterialPropertyBlock block;
private bool initialized;
private bool colorInitialized;
public float CurrentRust => rustCurrent;
public float InitialRust => rustInitial;
public Color CurrentColor => paintColor;
public RepairStage Stage
{
get
{
if (rustInitial <= 0.001f) return RepairStage.Healthy;
if (rustCurrent > RustCleanThreshold) return RepairStage.Rusted;
if (putty < DoneThreshold) return RepairStage.Cleaned;
if (sanding < DoneThreshold) return RepairStage.Puttied;
if (paint < DoneThreshold) return RepairStage.Sanded;
return RepairStage.Repaired;
}
}
public float StageProgress
{
get
{
switch (Stage)
{
case RepairStage.Rusted: return 1f - rustCurrent / Mathf.Max(rustInitial, 0.001f);
case RepairStage.Cleaned: return putty;
case RepairStage.Puttied: return sanding;
case RepairStage.Sanded: return paint;
default: return 1f;
}
}
}
private void EnsureInit()
{
if (initialized) return;
initialized = true;
block = new MaterialPropertyBlock();
CarPart owner = GetComponent<CarPart>();
foreach (Renderer r in GetComponentsInChildren<Renderer>(true))
{
if (owner != null && r.GetComponentInParent<CarPart>(true) != owner) continue;
Material[] mats = r.sharedMaterials;
for (int i = 0; i < mats.Length; i++)
{
if (mats[i] != null && mats[i].HasProperty(RustAmountProp))
{
// Автоматически запоминаем родной цвет машины из материала (например, синий!)
if (!colorInitialized && mats[i].HasProperty(BaseColorProp))
{
paintColor = mats[i].GetColor(BaseColorProp);
colorInitialized = true;
}
slots.Add(new Slot { renderer = r, materialIndex = i });
}
}
}
}
private void OnValidate()
{
if (Application.isPlaying && initialized) Apply();
}
public void SetDamage(float amount)
{
EnsureInit();
amount = Mathf.Clamp01(amount);
if (amount <= 0.001f) amount = 0f;
rustInitial = amount;
rustCurrent = amount;
putty = 0f;
sanding = 0f;
paint = 0f;
Apply();
}
public void SetPaintColor(Color newColor)
{
EnsureInit();
paintColor = newColor;
colorInitialized = true;
Apply();
}
public bool CleanRust(float amount01)
{
if (Stage != RepairStage.Rusted) return false;
rustCurrent -= rustInitial * amount01;
if (rustCurrent <= RustCleanThreshold) rustCurrent = 0f;
Apply();
return true;
}
public bool ApplyPutty(float amount01)
{
if (Stage != RepairStage.Cleaned) return false;
putty = Mathf.Min(1f, putty + amount01);
if (putty >= DoneThreshold) putty = 1f;
Apply();
return true;
}
public bool Sand(float amount01)
{
if (Stage != RepairStage.Puttied) return false;
sanding = Mathf.Min(1f, sanding + amount01);
if (sanding >= DoneThreshold) sanding = 1f;
Apply();
return true;
}
public bool PaintRepair(float amount01)
{
if (Stage != RepairStage.Sanded) return false;
paint = Mathf.Min(1f, paint + amount01);
if (paint >= DoneThreshold) paint = 1f;
Apply();
return true;
}
public bool Recolor(Color newColor)
{
RepairStage s = Stage;
if (s != RepairStage.Healthy && s != RepairStage.Repaired) return false;
SetPaintColor(newColor);
return true;
}
private void Apply()
{
EnsureInit();
foreach (Slot s in slots)
{
if (s.renderer == null) continue;
s.renderer.GetPropertyBlock(block, s.materialIndex);
block.SetColor(BaseColorProp, paintColor);
block.SetFloat(RustInitialProp, rustInitial);
block.SetFloat(RustAmountProp, rustCurrent);
block.SetFloat(PuttyProp, putty);
block.SetFloat(SandProp, sanding);
block.SetFloat(PaintProp, paint);
s.renderer.SetPropertyBlock(block, s.materialIndex);
}
}
}