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 { get => taskID; set => taskID = value; } public TaskType TaskType { get => taskType; set => taskType = value; } public string TaskName { get => taskName; set => taskName = value; } public (double, double) TaskLocation { get => (taskLocation.x, taskLocation.y); set => taskLocation = new Vector2((float)value.Item1, (float)value.Item2); } 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 onCompletedCallback; private void Awake() { if (audioSource == null) audioSource = GetComponent(); if (audioSource == null) audioSource = gameObject.AddComponent(); if (videoPlayer == null) videoPlayer = GetComponent(); if (videoPlayer == null) videoPlayer = gameObject.AddComponent(); 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 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 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); } }