425 lines
13 KiB
C#

using UnityEngine;
using System.Collections;
public class Cannon : MonoBehaviour
{
[Header("Parts")]
public Transform basePart; // Ïîäñòàâêà (âðàùàåòñÿ ïî Y)
public Transform barrel; // Äóëî (âðàùàåòñÿ ïî X + îòäà÷à)
[Header("Projectile")]
public GameObject projectilePrefab;
public float projectileSpeed = 20f;
[Header("Reloading")]
public float reloadTime = 2f;
private bool isReloading = false;
[Header("Rotation")]
public float rotationSpeed = 60f; // ãðàäóñîâ â ñåêóíäó
private float currentLaunchAngle = 10f;
public float coreSpeed = 1f;
public ParticleSystem[] shotParticles; // ÷àñòèöû èç äóëà
public ParticleSystem smokeParticles;
[Header("Command")]
public PirateCommand assignedCommand;
public Transform barrelMuzzle;
public Transform[] reloadPoints;
public Transform reloadTarget;
public bool IsReady => !isReloading;
private Quaternion baseInitialRotation;
private Quaternion barrelInitialRotation;
private Vector3 barrelInitialLocalPosition;
private Vector3 barrelInitialLocalScale; // NEW
private Coroutine _reloadPoseCo; // NEW
private int playerId = 1;
private void Awake()
{
baseInitialRotation = basePart.rotation;
barrelInitialRotation = barrel.localRotation;
barrelInitialLocalPosition = barrel.localPosition;
barrelInitialLocalScale = barrel.localScale; // NEW
var player = transform.root.GetComponent<Player>();
if (player != null)
playerId = player.PlayerId;
}
public void ShootAtTransform(Transform target, System.Action onImpact)
{
if (isReloading) return;
StartCoroutine(ShootRoutine(target, onImpact));
}
private IEnumerator ShootRoutine(Transform target, System.Action onImpact)
{
// 1. Ïîâîðîò
yield return StartCoroutine(RotateBaseToTarget(target.position));
yield return StartCoroutine(AimBarrelAtTarget(target.position));
// 2. Àíèìàöèÿ îòäà÷è + âûñòðåë
yield return StartCoroutine(BarrelShootWithProjectile(target.position, onImpact));
// 4. Âîçâðàò â èñõîäíîå ïîëîæåíèå
yield return StartCoroutine(ReturnToDefaultPose());
// 3. Ïåðåçàðÿäêà
yield return StartCoroutine(Reload());
}
public void OnReloadComplete()
{
isReloading = false; // ïóøêó ìîæíî ñòðåëÿòü ñíîâà
}
private IEnumerator BarrelShootWithProjectile(Vector3 targetPos, System.Action onImpact)
{
SoundManager.Instance.PlayShoot(playerId);
Vector3 startPos = barrel.localPosition;
Vector3 backPos = startPos - new Vector3(0, 0, 0.2f);
Vector3 stretchPos = startPos + new Vector3(0, 0, 0.1f);
Vector3 startScale = barrel.localScale;
Vector3 squeezeScale = new Vector3(startScale.x * 1.8f, startScale.y * 1.8f, startScale.z * 0.5f);
Vector3 stretchScale = new Vector3(startScale.x * 0.9f, startScale.y * 0.9f, startScale.z * 1.1f);
float t = 0f;
while (t < 1f)
{
barrel.localPosition = Vector3.Lerp(startPos, backPos, t);
barrel.localScale = Vector3.Lerp(startScale, squeezeScale, t);
t += Time.deltaTime * 10f;
yield return null;
}
if (shotParticles != null && shotParticles.Length > 0)
{
foreach (var ps in shotParticles)
{
if (ps == null) continue;
ps.transform.position = barrelMuzzle.position;
ps.transform.rotation = barrelMuzzle.rotation;
ps.Play();
}
}
if (EffectManager.Instance != null)
{
Vector3 spawnPos = basePart.position
+ basePart.right * -1.5f
+ Vector3.up * 4f;
EffectManager.Instance.PlayShotText(spawnPos);
}
GameObject projectile = Instantiate(projectilePrefab, barrelMuzzle.position, barrelMuzzle.rotation);
StartCoroutine(MoveProjectileArc(projectile.transform, targetPos, currentLaunchAngle, coreSpeed, onImpact));
t = 0f;
while (t < 1f)
{
barrel.localPosition = Vector3.Lerp(backPos, stretchPos, t);
barrel.localScale = Vector3.Lerp(squeezeScale, stretchScale, t);
t += Time.deltaTime * 10f;
yield return null;
}
t = 0f;
while (t < 1f)
{
barrel.localPosition = Vector3.Lerp(stretchPos, startPos, t);
barrel.localScale = Vector3.Lerp(stretchScale, startScale, t);
t += Time.deltaTime * 10f;
yield return null;
}
}
private IEnumerator MoveProjectileArc(
Transform proj,
Vector3 target,
float launchAngleDegrees,
float timeScale,
System.Action onImpact)
{
float g = Mathf.Abs(Physics.gravity.y);
Vector3 start = proj.position;
Vector3 planarStart = new Vector3(start.x, 0, start.z);
Vector3 planarTarget = new Vector3(target.x, 0, target.z);
float distance = Vector3.Distance(planarStart, planarTarget);
float heightDifference = target.y - start.y;
float launchAngle = launchAngleDegrees * Mathf.Deg2Rad;
float denom = distance * Mathf.Tan(launchAngle) - heightDifference;
if (denom <= 0.001f)
{
proj.position = target;
onImpact?.Invoke();
Destroy(proj.gameObject);
yield break;
}
float v2 = Mathf.Pow((1f / Mathf.Cos(launchAngle)) * Mathf.Sqrt(
(0.5f * g * distance * distance) / denom), 2f);
Vector3 direction = (planarTarget - planarStart).normalized;
float v = Mathf.Sqrt(v2);
float flightDuration = distance / (v * Mathf.Cos(launchAngle));
float elapsed = 0f;
float hitThreshold = 0.2f;
while (elapsed < flightDuration)
{
float t = elapsed;
Vector3 moveXZ = direction * v * Mathf.Cos(launchAngle) * t;
float moveY = v * Mathf.Sin(launchAngle) * t - 0.5f * g * t * t;
proj.position = start + moveXZ + Vector3.up * moveY;
if (Vector3.Distance(proj.position, target) <= hitThreshold)
{
proj.position = target;
onImpact?.Invoke();
Destroy(proj.gameObject);
yield break;
}
elapsed += Time.deltaTime * timeScale;
yield return null;
}
proj.position = target;
onImpact?.Invoke();
Destroy(proj.gameObject);
}
private IEnumerator ReturnToDefaultPose()
{
float t = 0f;
float duration = 1f;
Quaternion baseStartRot = basePart.rotation;
Quaternion barrelStartRot = barrel.localRotation;
Vector3 barrelStartPos = barrel.localPosition;
Vector3 barrelStartScale = barrel.localScale; // NEW: çàïîìèíàåì òåêóùèé scale
while (t < 1f)
{
basePart.rotation = Quaternion.Slerp(baseStartRot, baseInitialRotation, t);
barrel.localRotation = Quaternion.Slerp(barrelStartRot, barrelInitialRotation, t);
barrel.localPosition = Vector3.Lerp(barrelStartPos, barrelInitialLocalPosition, t);
barrel.localScale = Vector3.Lerp(barrelStartScale, barrelInitialLocalScale, t); // NEW: âîçâðàùàåì scale
t += Time.deltaTime / duration;
yield return null;
}
basePart.rotation = baseInitialRotation;
barrel.localRotation = barrelInitialRotation;
barrel.localPosition = barrelInitialLocalPosition;
barrel.localScale = barrelInitialLocalScale; // NEW: ôèíàëüíàÿ óñòàíîâêà scale
}
private IEnumerator RotateBaseToTarget(Vector3 targetPos)
{
while (true)
{
Vector3 flatTarget = new Vector3(targetPos.x, basePart.position.y, targetPos.z);
Vector3 dir = (flatTarget - basePart.position).normalized;
if (dir.sqrMagnitude < 0.0001f)
yield break;
Quaternion targetRot = Quaternion.LookRotation(dir);
basePart.rotation = Quaternion.RotateTowards(
basePart.rotation,
targetRot,
rotationSpeed * Time.deltaTime
);
if (Quaternion.Angle(basePart.rotation, targetRot) < 0.5f)
break;
yield return null;
}
}
private IEnumerator AimBarrelAtTarget(Vector3 targetPos)
{
Vector3 dir = targetPos - barrel.position;
float distance = new Vector2(dir.x, dir.z).magnitude;
float height = dir.y;
float g = Mathf.Abs(Physics.gravity.y);
float v = projectileSpeed;
float v2 = v * v;
float underSqrt = v2 * v2 - g * (g * distance * distance + 2 * height * v2);
float angle;
if (underSqrt >= 0)
{
float sqrtPart = Mathf.Sqrt(underSqrt);
float lowAngle = Mathf.Atan((v2 - sqrtPart) / (g * distance));
float highAngle = Mathf.Atan((v2 + sqrtPart) / (g * distance));
angle = lowAngle * Mathf.Rad2Deg;
}
else
{
angle = 45f;
}
angle = Mathf.Clamp(angle, 10f, 70f);
currentLaunchAngle = angle;
Quaternion targetRot = Quaternion.Euler(-angle, 0, 0);
while (true)
{
barrel.localRotation = Quaternion.RotateTowards(
barrel.localRotation,
targetRot,
rotationSpeed * Time.deltaTime
);
if (Quaternion.Angle(barrel.localRotation, targetRot) < 0.5f)
break;
yield return null;
}
}
private IEnumerator Reload()
{
isReloading = true;
_reloadPoseCo = StartCoroutine(BarrelReloadPose()); // NEW
if (assignedCommand != null)
{
assignedCommand.AssignPirateToCannon(this);
while (isReloading)
yield return null;
}
else
{
yield return new WaitForSeconds(reloadTime);
isReloading = false;
}
}
private IEnumerator BarrelReloadPose()
{
Vector3 startScale = barrel.localScale;
Vector3 reloadScale = new Vector3(startScale.x * 1.2f, startScale.y * 1.2f, startScale.z * 0.6f);
Quaternion startRot = barrel.localRotation;
Quaternion reloadRot = startRot * Quaternion.Euler(-45f, 0, 0);
float t = 0f;
float duration = 0.3f;
if (smokeParticles != null)
{
smokeParticles.transform.rotation = Quaternion.identity;
var main = smokeParticles.main;
main.simulationSpeed = 0.5f;
smokeParticles.Play();
}
while (t < 1f)
{
barrel.localScale = Vector3.Lerp(startScale, reloadScale, t);
barrel.localRotation = Quaternion.Slerp(startRot, reloadRot, t);
t += Time.deltaTime / duration;
yield return null;
}
float pulseSpeed = 10f;
float pulseAmount = 0.05f;
while (isReloading)
{
float pulse = 1f + Mathf.Sin(Time.time * pulseSpeed) * pulseAmount;
barrel.localScale = reloadScale * pulse;
barrel.localRotation = reloadRot;
yield return null;
}
if (smokeParticles != null)
smokeParticles.Stop();
t = 0f;
while (t < 1f)
{
barrel.localScale = Vector3.Lerp(reloadScale, startScale, t);
barrel.localRotation = Quaternion.Slerp(reloadRot, startRot, t);
t += Time.deltaTime / duration;
yield return null;
}
barrel.localScale = startScale;
barrel.localRotation = startRot;
_reloadPoseCo = null; // NEW
}
public Transform GetNearestReloadPoint(Vector3 fromPos)
{
Transform best = null;
float bestDist = Mathf.Infinity;
foreach (var p in reloadPoints)
{
float d = Vector3.Distance(fromPos, p.position);
if (d < bestDist)
{
bestDist = d;
best = p;
}
}
return best;
}
/// <summary>
/// Ôîðñ-ïåðåâîä ïóøêè â ñîñòîÿíèå "ãîòîâà". Ñíèìàåò çàâèñàíèå ïåðåçàðÿäêè,
/// îñòàíàâëèâàåò âèçóàëüíóþ ïîçó è âîçâðàùàåò ñòâîë/÷àñòèöû ê äåôîëòó.
/// </summary>
/// <param name="instant">true — ìîìåíòàëüíî âåðíóòü ïîçó; false — äàòü êîðóòèíå çàâåðøèòüñÿ ïëàâíî</param>
public void ForceReloadReset(bool instant = true)
{
// ñíÿòü çàâèñàíèå îæèäàíèÿ çàãðóç÷èêà
isReloading = false;
if (instant)
{
// îñòàíîâèòü ïîçó ïåðåçàðÿäêè, åñëè êðóòèòñÿ
if (_reloadPoseCo != null)
{
StopCoroutine(_reloadPoseCo);
_reloadPoseCo = null;
}
if (smokeParticles != null)
smokeParticles.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
// âåðíóòü ãåîìåòðèþ/ïîçó â èñõîäíîå ñîñòîÿíèå
basePart.rotation = baseInitialRotation;
barrel.localRotation = barrelInitialRotation;
barrel.localPosition = barrelInitialLocalPosition;
barrel.localScale = barrelInitialLocalScale;
}
// åñëè instant == false, BarrelReloadPose çàìåòèò isReloading=false è ïëàâíî îòêàòèò ïîçó
}
}