181 lines
7.1 KiB
C#
181 lines
7.1 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
public struct CarDefectsSummary
|
||
{
|
||
public int rustedPartsCount;
|
||
public int missingBodyPartsCount;
|
||
public int flatTiresCount;
|
||
public int missingWheelsCount;
|
||
public List<string> missingPartNames;
|
||
public float totalPricePenalty;
|
||
}
|
||
|
||
public class CarConditionGenerator : MonoBehaviour
|
||
{
|
||
[Header("Настройки ржавчины")]
|
||
[Range(0f, 1f)] [SerializeField] private float rustProbability = 0.65f;
|
||
[SerializeField] private float minRustAmount = 0.25f;
|
||
[SerializeField] private float maxRustAmount = 0.80f;
|
||
|
||
[Header("Шанс отсутствия навесных деталей (бамперы, фары, крылья)")]
|
||
[Range(0f, 0.5f)] [SerializeField] private float missingBodyPartChance = 0.15f;
|
||
[SerializeField] private int maxMissingBodyParts = 3;
|
||
|
||
[Header("Проблемы с колесами")]
|
||
[Range(0f, 1f)] [SerializeField] private float flatTireChance = 0.25f;
|
||
[Range(0f, 1f)] [SerializeField] private float missingWheelChance = 0.12f;
|
||
|
||
[Header("Цвет кузова")]
|
||
[SerializeField] private bool overrideMaterialColor = false;
|
||
[SerializeField] private Color baseCarColor = Color.blue;
|
||
|
||
private bool configuredExternally = false;
|
||
|
||
private void Start()
|
||
{
|
||
// Если машина уже настроена через CarMarketManager (по Seed) — не перезаписываем её!
|
||
if (!configuredExternally)
|
||
{
|
||
GenerateCondition();
|
||
}
|
||
}
|
||
|
||
[ContextMenu("Сгенерировать случайное состояние")]
|
||
public void GenerateCondition()
|
||
{
|
||
int randomSeed = Random.Range(1, 999999);
|
||
Color col = overrideMaterialColor ? baseCarColor : GetCurrentBodyColor();
|
||
ApplyConditionBySeed(randomSeed, col, false, out _);
|
||
}
|
||
|
||
public Color GetCurrentBodyColor()
|
||
{
|
||
int rustProp = Shader.PropertyToID("_RustAmount");
|
||
int colorProp = Shader.PropertyToID("_BaseColor");
|
||
foreach (Renderer r in GetComponentsInChildren<Renderer>(true))
|
||
{
|
||
foreach (Material m in r.sharedMaterials)
|
||
{
|
||
if (m != null && m.HasProperty(rustProp) && m.HasProperty(colorProp))
|
||
return m.GetColor(colorProp);
|
||
}
|
||
}
|
||
return baseCarColor;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Генерирует 100% одинаковое состояние машины для одного и того же seed (и в фотобоксе, и на карте).
|
||
/// </summary>
|
||
public void ApplyConditionBySeed(int seed, Color carColor, bool isPhotoStudio, out CarDefectsSummary summary)
|
||
{
|
||
configuredExternally = true;
|
||
Random.State oldState = Random.state;
|
||
Random.InitState(seed);
|
||
|
||
summary = new CarDefectsSummary
|
||
{
|
||
missingPartNames = new List<string>()
|
||
};
|
||
|
||
CarPart[] allParts = GetComponentsInChildren<CarPart>(true);
|
||
|
||
// 1. Обрабатываем кузовные и навесные детали (кроме колес)
|
||
foreach (CarPart part in allParts)
|
||
{
|
||
// Пропускаем колеса (у них свой цикл ниже)
|
||
if (part.GetComponent<CarWheelCondition>() != null || part.TotalBoltsCount > 0)
|
||
continue;
|
||
|
||
CarPartVisuals visuals = part.GetComponent<CarPartVisuals>();
|
||
if (visuals == null) visuals = part.gameObject.AddComponent<CarPartVisuals>();
|
||
|
||
visuals.SetPaintColor(carColor);
|
||
|
||
float partPrice = part.Data != null ? part.Data.basePrice : 150f;
|
||
string partTitle = part.Data != null ? part.Data.partName : part.name;
|
||
|
||
// Проверяем, отсутствует ли эта навесная деталь (бампер, фара, крыло, дверь, зеркало)
|
||
if (summary.missingBodyPartsCount < maxMissingBodyParts && Random.value < missingBodyPartChance)
|
||
{
|
||
summary.missingBodyPartsCount++;
|
||
summary.missingPartNames.Add(partTitle.ToLower());
|
||
summary.totalPricePenalty += partPrice * 1.4f;
|
||
part.MakeMissingAtStart(null, false);
|
||
continue;
|
||
}
|
||
|
||
// Пластик не ржавеет
|
||
if (part.Data != null && !part.Data.canRust)
|
||
{
|
||
visuals.SetDamage(0f);
|
||
continue;
|
||
}
|
||
|
||
if (Random.value < rustProbability)
|
||
{
|
||
float dmg = Random.Range(minRustAmount, maxRustAmount);
|
||
visuals.SetDamage(dmg);
|
||
summary.rustedPartsCount++;
|
||
summary.totalPricePenalty += partPrice * dmg;
|
||
}
|
||
else
|
||
{
|
||
visuals.SetDamage(0f);
|
||
}
|
||
}
|
||
|
||
// 2. Обрабатываем основной несъемный кузов (body)
|
||
int rustProp = Shader.PropertyToID("_RustAmount");
|
||
foreach (Renderer r in GetComponentsInChildren<Renderer>(true))
|
||
{
|
||
if (r.GetComponentInParent<CarPart>(true) != null) continue;
|
||
|
||
foreach (Material m in r.sharedMaterials)
|
||
{
|
||
if (m != null && m.HasProperty(rustProp))
|
||
{
|
||
CarPartVisuals bodyVis = r.GetComponent<CarPartVisuals>();
|
||
if (bodyVis == null) bodyVis = r.gameObject.AddComponent<CarPartVisuals>();
|
||
bodyVis.SetPaintColor(carColor);
|
||
|
||
float bodyDmg = Random.value < rustProbability ? Random.Range(minRustAmount, maxRustAmount) : 0f;
|
||
bodyVis.SetDamage(bodyDmg);
|
||
if (bodyDmg > 0.05f)
|
||
{
|
||
summary.rustedPartsCount++;
|
||
summary.totalPricePenalty += 250f * bodyDmg;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3. Обрабатываем колеса (Нормальное / Спущенное / Отсутствует)
|
||
CarWheelCondition[] wheels = GetComponentsInChildren<CarWheelCondition>(true);
|
||
foreach (CarWheelCondition wheel in wheels)
|
||
{
|
||
float roll = Random.value;
|
||
// В одном объявлении убираем максимум 1 колесо, чтобы машина не висела вся на кирпичах
|
||
if (roll < missingWheelChance && summary.missingWheelsCount < 1)
|
||
{
|
||
summary.missingWheelsCount++;
|
||
summary.totalPricePenalty += 220f;
|
||
CarPart wp = wheel.GetComponent<CarPart>();
|
||
if (wp != null) wp.MakeMissingAtStart(null, !isPhotoStudio);
|
||
}
|
||
else if (roll < missingWheelChance + flatTireChance)
|
||
{
|
||
summary.flatTiresCount++;
|
||
summary.totalPricePenalty += 100f;
|
||
wheel.ApplyState(CarWheelCondition.TireState.Flat);
|
||
}
|
||
else
|
||
{
|
||
wheel.ApplyState(CarWheelCondition.TireState.Normal);
|
||
}
|
||
}
|
||
|
||
Random.state = oldState;
|
||
}
|
||
} |