Files
GeoSusGame/Assets/TimingWheelShooter.cs
2026-04-26 14:35:54 +02:00

315 lines
8.3 KiB
C#

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Video;
public class TimingWheelShooter : MonoBehaviour, ITask
{
[Header("Task Info")]
[SerializeField] private string taskID = "RING_SHOOTER_01";
[SerializeField] private string taskName = "Break the Ring";
[SerializeField] private TaskType taskType = TaskType.Task;
[SerializeField] private Vector2 taskLocation = Vector2.zero;
public string TaskID => taskID;
public TaskType TaskType => taskType;
public string TaskName => taskName;
public (double, double) TaskLocation => (taskLocation.x, taskLocation.y);
public bool IsCompleted { get; private set; }
[Header("Wheel")]
public Transform rotatingWheel;
public float defaultAngle = 90f;
public float sweepAngle = 90f;
public float wheelSpeed = 120f;
[Header("Projectile")]
public ProjectileBehaviour potatoPrefab;
public Transform projectileSpawn;
public Transform aimPoint;
public GlassRingController ring;
public float baseLaunchSpeed = 12f;
[Header("Audio")]
public AudioSource audioSource;
public AudioClip startClip;
[Range(0f, 1f)] public float startVolume = 1f;
public AudioClip winClip;
[Range(0f, 1f)] public float winVolume = 1f;
[Header("Finish Video")]
public VideoPlayer videoPlayer;
public VideoClip finishVideo;
public AudioClip finishVideoAudio;
[Range(0f, 1f)] public float finishVideoAudioVolume = 1f;
public Camera targetCamera;
[Header("State")]
public bool autoRestartAfterShot = true;
public float restartDelay = 0.75f;
private bool isRunning = true;
private bool finishSequenceStarted = false;
private float currentAngle;
private float direction = 1f;
private Action<ITask> onCompletedCallback;
private void Awake()
{
if (audioSource == null)
audioSource = GetComponent<AudioSource>();
if (audioSource == null)
audioSource = gameObject.AddComponent<AudioSource>();
if (videoPlayer == null)
videoPlayer = GetComponent<VideoPlayer>();
if (videoPlayer == null)
videoPlayer = gameObject.AddComponent<VideoPlayer>();
if (targetCamera == null)
targetCamera = Camera.main;
// Video setup
videoPlayer.playOnAwake = false;
videoPlayer.waitForFirstFrame = true;
videoPlayer.skipOnDrop = true;
videoPlayer.isLooping = false;
videoPlayer.audioOutputMode = VideoAudioOutputMode.None;
videoPlayer.renderMode = VideoRenderMode.CameraNearPlane;
if (targetCamera != null)
videoPlayer.targetCamera = targetCamera;
}
private void Start()
{
currentAngle = defaultAngle;
SetWheelAngle(currentAngle);
PlayStartSound();
}
private void Update()
{
if (IsCompleted || finishSequenceStarted) return;
if (isRunning)
UpdateWheelMotion();
if (Input.GetMouseButtonDown(0))
StopAndShoot();
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
StopAndShoot();
}
private void UpdateWheelMotion()
{
float halfRange = sweepAngle * 0.5f;
float minAngle = defaultAngle - halfRange;
float maxAngle = defaultAngle + halfRange;
currentAngle += direction * wheelSpeed * Time.deltaTime;
if (currentAngle >= maxAngle)
{
currentAngle = maxAngle;
direction = -1f;
}
else if (currentAngle <= minAngle)
{
currentAngle = minAngle;
direction = 1f;
}
SetWheelAngle(currentAngle);
}
private void SetWheelAngle(float angle)
{
if (rotatingWheel == null) return;
Vector3 e = rotatingWheel.localEulerAngles;
e.y = angle; // your current setup uses Y
rotatingWheel.localEulerAngles = e;
}
private float EvaluateMultiplier(float angle)
{
float halfRange = sweepAngle * 0.5f;
float offset = Mathf.Abs(angle - defaultAngle);
float zone5 = halfRange / 2.5f;
float zone2 = halfRange / 1.5f;
if (offset <= zone5) return 5f;
if (offset <= zone2) return 2f;
return 1f;
}
public void StopAndShoot()
{
if (!isRunning || IsCompleted || finishSequenceStarted) return;
isRunning = false;
float multiplier = EvaluateMultiplier(currentAngle);
Shoot(multiplier);
Debug.Log($"Wheel stopped at {currentAngle:F1}°, multiplier = {multiplier}x");
if (autoRestartAfterShot)
Invoke(nameof(RestartWheel), restartDelay);
}
private void Shoot(float multiplier)
{
if (potatoPrefab == null || projectileSpawn == null || aimPoint == null)
return;
Vector3 dir = (aimPoint.position - projectileSpawn.position).normalized;
float speed = baseLaunchSpeed * multiplier;
ProjectileBehaviour p = Instantiate(
potatoPrefab,
projectileSpawn.position,
Quaternion.LookRotation(dir, Vector3.up)
);
p.baseDamage *= multiplier;
p.breakImpulse *= multiplier;
p.Launch(ring, dir * speed, 1f);
}
private void RestartWheel()
{
if (!IsCompleted && !finishSequenceStarted)
isRunning = true;
}
public void NotifyButtonHit(ProjectileBehaviour projectile)
{
if (IsCompleted || finishSequenceStarted) return;
Debug.Log("Diamond hit. Starting finish sequence.");
StartCoroutine(PlayFinishSequenceThenComplete());
}
private IEnumerator PlayFinishSequenceThenComplete()
{
finishSequenceStarted = true;
isRunning = false;
CancelInvoke();
if (ring != null)
ring.enabled = false;
if (finishVideo == null)
{
if (audioSource != null && winClip != null)
audioSource.PlayOneShot(winClip, winVolume);
Complete();
yield break;
}
videoPlayer.Stop();
videoPlayer.clip = finishVideo;
videoPlayer.renderMode = VideoRenderMode.CameraNearPlane;
if (targetCamera == null)
targetCamera = Camera.main;
if (targetCamera != null)
videoPlayer.targetCamera = targetCamera;
bool finished = false;
void OnVideoFinished(VideoPlayer vp) => finished = true;
videoPlayer.loopPointReached += OnVideoFinished;
videoPlayer.Prepare();
while (!videoPlayer.isPrepared)
yield return null;
videoPlayer.Play();
if (audioSource != null && finishVideoAudio != null)
{
audioSource.clip = finishVideoAudio;
audioSource.volume = finishVideoAudioVolume;
audioSource.Play();
}
else if (audioSource != null && winClip != null)
{
audioSource.PlayOneShot(winClip, winVolume);
}
while (!finished)
yield return null;
videoPlayer.loopPointReached -= OnVideoFinished;
videoPlayer.Stop();
Complete();
}
public void Initialize(Action<ITask> onCompleted)
{
onCompletedCallback = onCompleted;
IsCompleted = false;
finishSequenceStarted = false;
isRunning = true;
direction = 1f;
currentAngle = defaultAngle;
SetWheelAngle(currentAngle);
CancelInvoke();
if (ring != null)
ring.enabled = true;
if (audioSource != null)
audioSource.Stop();
if (videoPlayer != null)
videoPlayer.Stop();
PlayStartSound();
}
public void ExitTask(Action<ITask> onExit)
{
isRunning = false;
finishSequenceStarted = false;
CancelInvoke();
if (audioSource != null)
audioSource.Stop();
if (videoPlayer != null)
videoPlayer.Stop();
onExit?.Invoke(this);
}
public void Complete()
{
if (IsCompleted) return;
IsCompleted = true;
isRunning = false;
finishSequenceStarted = false;
CancelInvoke();
onCompletedCallback?.Invoke(this);
}
private void PlayStartSound()
{
if (audioSource != null && startClip != null)
audioSource.PlayOneShot(startClip, startVolume);
}
}