283 lines
9.6 KiB
C#
283 lines
9.6 KiB
C#
using UnityEngine;
|
||
|
||
public enum PartState { Installed, InHand, Dropped }
|
||
|
||
public class CarPart : MonoBehaviour, IInteractable, ISecondaryInteractable
|
||
{
|
||
[Header("Данные")]
|
||
[SerializeField] private CarPartData partData;
|
||
[SerializeField] private float mass = 12f;
|
||
|
||
[Header("Позиционирование в руках")]
|
||
[SerializeField] private Vector3 inHandPositionOffset = Vector3.zero;
|
||
[SerializeField] private Vector3 inHandRotationOffset = Vector3.zero;
|
||
|
||
private CarPartAnimator partAnimator;
|
||
private PartState currentState = PartState.Installed;
|
||
private Transform originalParent;
|
||
private Vector3 originalLocalPos;
|
||
private Quaternion originalLocalRot;
|
||
private Vector3 originalLocalScale;
|
||
private WheelBolt[] wheelBolts;
|
||
|
||
private WheelCollider linkedWheelCollider;
|
||
private Transform currentHoldPoint;
|
||
private Vector3 targetInHandLocalPos;
|
||
private Quaternion targetInHandLocalRot;
|
||
|
||
private Rigidbody rb;
|
||
private Collider partCollider;
|
||
private GameObject currentMountZone;
|
||
|
||
private float unboltDuration => (partData != null && partData.boltDuration > 0f) ? partData.boltDuration : 1.2f;
|
||
private float unboltProgress = 0f;
|
||
|
||
public float UnboltProgress01 => Mathf.Clamp01(unboltProgress / unboltDuration);
|
||
public void ResetUnbolt() => unboltProgress = 0f;
|
||
public CarPartData Data => partData;
|
||
public PartState State => currentState;
|
||
public bool HasAnimator => partAnimator != null;
|
||
public int TotalBoltsCount => wheelBolts != null ? wheelBolts.Length : 0;
|
||
|
||
public int TightenedBoltsCount
|
||
{
|
||
get
|
||
{
|
||
if (wheelBolts == null || wheelBolts.Length == 0) return 0;
|
||
int count = 0;
|
||
foreach (var b in wheelBolts)
|
||
if (b != null && b.IsTightened) count++;
|
||
return count;
|
||
}
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
partCollider = GetComponent<Collider>();
|
||
partAnimator = GetComponent<CarPartAnimator>();
|
||
wheelBolts = GetComponentsInChildren<WheelBolt>(true);
|
||
|
||
originalParent = transform.parent;
|
||
originalLocalPos = transform.localPosition;
|
||
originalLocalRot = transform.localRotation;
|
||
originalLocalScale = transform.localScale;
|
||
|
||
VehicleInteraction car = GetComponentInParent<VehicleInteraction>();
|
||
if (car != null)
|
||
{
|
||
foreach (WheelCollider wc in car.GetComponentsInChildren<WheelCollider>(true))
|
||
{
|
||
if (wc.name == gameObject.name)
|
||
{
|
||
linkedWheelCollider = wc;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ВАЖНО: Если это колесо, на машине его MeshCollider ДОЛЖЕН быть триггером!
|
||
// Иначе он трется об асфальт и крылья машины, мешая WheelCollider'у ехать!
|
||
if (linkedWheelCollider != null && partCollider != null)
|
||
{
|
||
if (partCollider is MeshCollider mc) mc.convex = true;
|
||
partCollider.isTrigger = true;
|
||
}
|
||
}
|
||
|
||
private void LateUpdate()
|
||
{
|
||
if (currentState == PartState.InHand && currentHoldPoint != null)
|
||
{
|
||
transform.localPosition = targetInHandLocalPos;
|
||
transform.localRotation = targetInHandLocalRot;
|
||
}
|
||
}
|
||
|
||
public string GetPrompt()
|
||
{
|
||
if (partData == null) return "Деталь (E)";
|
||
|
||
if (currentState == PartState.Installed)
|
||
{
|
||
if (wheelBolts != null && wheelBolts.Length > 0)
|
||
{
|
||
if (!CarJack.IsWheelLifted(transform))
|
||
return "Колесо под нагрузкой! Поднимите авто домкратом";
|
||
|
||
if (TightenedBoltsCount > 0)
|
||
return $"Сначала открутите болты ({TightenedBoltsCount}/{wheelBolts.Length})";
|
||
|
||
return $"[E] Снять {partData.partName}";
|
||
}
|
||
|
||
if (unboltProgress > 0f)
|
||
return $"Откручивание болтов ({partData.partName})... {Mathf.RoundToInt(UnboltProgress01 * 100f)}%";
|
||
|
||
if (partAnimator != null)
|
||
{
|
||
string action = partAnimator.IsOpen ? "Закрыть" : "Открыть";
|
||
return $"[E] {action} {partData.partName} | [Удерж. R] Снять";
|
||
}
|
||
|
||
return $"[Удерж. E] Снять {partData.partName}";
|
||
}
|
||
|
||
if (currentState == PartState.Dropped)
|
||
return $"[E] Взять {partData.partName}";
|
||
|
||
return string.Empty;
|
||
}
|
||
|
||
public void Interact(GameObject interactor)
|
||
{
|
||
if (currentState == PartState.Installed)
|
||
{
|
||
if (partAnimator != null)
|
||
partAnimator.TogglePart();
|
||
else if (wheelBolts != null && wheelBolts.Length > 0 && TightenedBoltsCount == 0 && CarJack.IsWheelLifted(transform))
|
||
TakeToHands(interactor);
|
||
}
|
||
else if (currentState == PartState.Dropped)
|
||
{
|
||
TakeToHands(interactor);
|
||
}
|
||
}
|
||
|
||
public void SecondaryInteract(GameObject interactor) { }
|
||
|
||
public void ProcessUnbolting(GameObject interactor, float deltaTime)
|
||
{
|
||
if (currentState != PartState.Installed) return;
|
||
|
||
if (wheelBolts != null && wheelBolts.Length > 0)
|
||
{
|
||
if (!CarJack.IsWheelLifted(transform) || TightenedBoltsCount > 0) return;
|
||
TakeToHands(interactor);
|
||
return;
|
||
}
|
||
|
||
PlayerToolManager toolMgr = interactor.GetComponentInChildren<PlayerToolManager>();
|
||
if (toolMgr != null && toolMgr.currentTool != ToolType.Hands) return;
|
||
|
||
unboltProgress += deltaTime;
|
||
if (unboltProgress >= unboltDuration)
|
||
{
|
||
unboltProgress = 0f;
|
||
TakeToHands(interactor);
|
||
}
|
||
}
|
||
|
||
private void TakeToHands(GameObject interactor)
|
||
{
|
||
PlayerHands hands = interactor.GetComponentInChildren<PlayerHands>() ?? interactor.GetComponentInParent<PlayerHands>();
|
||
if (hands == null || hands.IsHolding) return;
|
||
|
||
if (currentState == PartState.Installed)
|
||
{
|
||
if (partAnimator != null) partAnimator.ResetToClosed();
|
||
CreateMountZone();
|
||
}
|
||
|
||
hands.PickUp(this);
|
||
}
|
||
|
||
public void OnPickedUp(Transform holdPoint)
|
||
{
|
||
currentState = PartState.InHand;
|
||
currentHoldPoint = holdPoint;
|
||
unboltProgress = 0f;
|
||
|
||
if (rb != null) Destroy(rb);
|
||
if (partCollider != null) partCollider.enabled = false;
|
||
|
||
if (linkedWheelCollider != null)
|
||
linkedWheelCollider.enabled = false;
|
||
|
||
transform.SetParent(holdPoint);
|
||
transform.localScale = originalLocalScale;
|
||
transform.localRotation = Quaternion.Euler(inHandRotationOffset);
|
||
transform.localPosition = Vector3.zero;
|
||
|
||
Renderer meshRenderer = GetComponentInChildren<Renderer>();
|
||
if (meshRenderer != null)
|
||
{
|
||
Vector3 visualCenterWorld = meshRenderer.bounds.center;
|
||
Vector3 offsetToCenter = holdPoint.position - visualCenterWorld;
|
||
transform.position += offsetToCenter;
|
||
transform.localPosition += inHandPositionOffset;
|
||
}
|
||
|
||
targetInHandLocalPos = transform.localPosition;
|
||
targetInHandLocalRot = transform.localRotation;
|
||
}
|
||
|
||
public void OnDropped(Vector3 throwForce)
|
||
{
|
||
currentState = PartState.Dropped;
|
||
unboltProgress = 0f;
|
||
|
||
if (currentHoldPoint != null)
|
||
transform.position = currentHoldPoint.position + currentHoldPoint.forward * 0.45f;
|
||
currentHoldPoint = null;
|
||
|
||
transform.SetParent(null);
|
||
transform.localScale = originalLocalScale;
|
||
|
||
if (partCollider != null)
|
||
{
|
||
if (partCollider is MeshCollider meshCol) meshCol.convex = true;
|
||
partCollider.isTrigger = false; // На земле деталь твердая
|
||
partCollider.enabled = true;
|
||
}
|
||
|
||
rb = gameObject.AddComponent<Rigidbody>();
|
||
rb.mass = mass;
|
||
rb.interpolation = RigidbodyInterpolation.Interpolate;
|
||
|
||
if (throwForce != Vector3.zero)
|
||
rb.AddForce(throwForce, ForceMode.Impulse);
|
||
}
|
||
|
||
public void Install()
|
||
{
|
||
currentState = PartState.Installed;
|
||
currentHoldPoint = null;
|
||
unboltProgress = 0f;
|
||
|
||
if (rb != null) Destroy(rb);
|
||
|
||
transform.SetParent(originalParent);
|
||
transform.localPosition = originalLocalPos;
|
||
transform.localRotation = originalLocalRot;
|
||
transform.localScale = originalLocalScale;
|
||
|
||
if (partCollider != null)
|
||
{
|
||
partCollider.enabled = true;
|
||
// Установленное колесо делаем триггером, чтобы оно не мешало физике езды!
|
||
if (linkedWheelCollider != null)
|
||
partCollider.isTrigger = true;
|
||
}
|
||
|
||
if (linkedWheelCollider != null)
|
||
{
|
||
linkedWheelCollider.enabled = true;
|
||
linkedWheelCollider.motorTorque = 0f;
|
||
linkedWheelCollider.brakeTorque = 0f;
|
||
}
|
||
}
|
||
|
||
private void CreateMountZone()
|
||
{
|
||
if (currentMountZone != null) return;
|
||
|
||
currentMountZone = new GameObject($"MountZone_{gameObject.name}");
|
||
currentMountZone.transform.SetParent(originalParent);
|
||
currentMountZone.transform.localPosition = originalLocalPos;
|
||
currentMountZone.transform.localRotation = originalLocalRot;
|
||
currentMountZone.transform.localScale = originalLocalScale;
|
||
|
||
CarPartMountZone zone = currentMountZone.AddComponent<CarPartMountZone>();
|
||
zone.Initialize(this);
|
||
}
|
||
} |