Project_WL/Assets/FarmingIsland/Scripts/Fishing/FishingSpot.cs

328 lines
13 KiB
C#
Raw Permalink Normal View History

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
using DG.Tweening;
namespace CryingSnow.FarmingIsland
{
[RequireComponent(typeof(AudioSource))]
public class FishingSpot : MonoBehaviour, IProp
{
[Tooltip("Icon representing the fishing rod in the user interface.")]
[SerializeField] private Sprite fishingRodIcon;
[Tooltip("The virtual camera used to view the fishing spot.")]
[SerializeField] private CinemachineVirtualCamera virtualCamera;
[Tooltip("Prefab for the fishing bobber that appears on the water.")]
[SerializeField] private GameObject bobberPrefab;
[Tooltip("Minimum time required to catch a fish.")]
[SerializeField] private float minCatchTime = 3f;
[Tooltip("Maximum time required to catch a fish.")]
[SerializeField] private float maxCatchTime = 7f;
[Tooltip("Minimum time interval to spawn a fish.")]
[SerializeField] private float minSpawnTime = 60f;
[Tooltip("Maximum time interval to spawn a fish.")]
[SerializeField] private float maxSpawnTime = 180f;
[Tooltip("Radius within which fish will swim around their target position.")]
[SerializeField] private float swimRadius = 2.0f;
[Tooltip("Speed at which the fish swims.")]
[SerializeField] private float swimSpeed = 1.5f;
[Tooltip("Array of fish prefabs that can be used in the fishing mini game.")]
[SerializeField] private FishController[] fishPrefabs;
[Header("Sound Effects")]
[Tooltip("Sound effect played when the fishing rod is used.")]
[SerializeField] private AudioClip fishingRodSound;
[Tooltip("Sound effect played when a fish is hooked.")]
[SerializeField] private AudioClip fishHookedSound;
[Tooltip("Sound effect played when a fish escapes.")]
[SerializeField] private AudioClip fishEscapedSound;
[Tooltip("Sound effect played when reeling in the fishing line.")]
[SerializeField] private AudioClip reelingInSound;
[Tooltip("Sound effect played when letting out the fishing line.")]
[SerializeField] private AudioClip reelingOutSound;
[Tooltip("Sound effect played when a fish is successfully caught.")]
[SerializeField] private AudioClip fishCaughtSound;
[Header("Advanced Fishing Settings")]
[Tooltip("Base rate at which progress increases during the fishing minigame.")]
[SerializeField] private float baseProgressIncreaseRate = 0.25f;
[Tooltip("Base rate at which progress decreases during the fishing minigame.")]
[SerializeField] private float baseProgressDecreaseRate = 0.15f;
[Tooltip("Minimum amount by which tension increases when reeling in a fish.")]
[SerializeField] private float minTensionIncrease = 0.01f;
[Tooltip("Maximum amount by which tension increases when reeling in a fish.")]
[SerializeField] private float maxTensionIncrease = 0.7f;
[Tooltip("Base rate at which tension decreases when not reeling in a fish.")]
[SerializeField] private float baseTensionDecreaseRate = 0.3f;
public Location Location => Location.Fish;
private AudioSource audioSource;
private List<FishController> fishList = new List<FishController>();
private bool isPlayerFishing;
void Awake()
{
audioSource = GetComponent<AudioSource>();
}
public void Animate(bool instant, System.Action onFinished)
{
for (int i = 0; i < fishPrefabs.Length; i++)
{
float spawnDelay = instant ? 0f : (i + 1) * 0.1f;
StartCoroutine(SpawnFish(i, spawnDelay));
}
}
void OnTriggerEnter(Collider other)
{
if (fishList.Count > 0 && other.CompareTag("Player"))
{
var player = other.GetComponent<PlayerController>();
UIManager.Instance.ToggleActivityButton(fishingRodIcon, () => StartCoroutine(Fishing(player)));
}
}
void OnTriggerExit(Collider other)
{
if (other.CompareTag("Player"))
{
UIManager.Instance.ToggleActivityButton(null, null);
}
}
IEnumerator Fishing(PlayerController player)
{
isPlayerFishing = true;
// Disable UI activity button and the virtual joystick during fishing
UIManager.Instance.ToggleActivityButton(null, null);
UIManager.Instance.SetActiveJoystick(false);
UIManager.Instance.ToggleHUD(false);
// Activate the virtual camera to focus on the fishing spot
virtualCamera.gameObject.SetActive(true);
// Temporarily disable player control to focus on fishing
player.IsControllable = false;
// Rotate the player to face the fishing spot
Vector3 direction = (transform.position.Flatten() - player.transform.position.Flatten()).normalized;
Quaternion look = Quaternion.LookRotation(direction);
yield return player.transform.DORotateQuaternion(look, 0.3f)
.WaitForCompletion();
// Cast the fishing line and show the fishing rod
player.SetAnimatorInt("FishingIndex", 1);
player.FishingRod.SetActive(true);
AudioManager.Instance.PlaySFX(fishingRodSound);
// Spawn the fishing bobber at the specified position and animate it
yield return new WaitForSeconds(0.5f);
var bobberSpawnPosition = player.transform.TransformPoint(new Vector3(0.5f, 2.5f, -0.5f));
var bobber = Instantiate(bobberPrefab, bobberSpawnPosition, Quaternion.identity).transform;
float bobberOscillationHeight = transform.position.y + 0.1f;
bobber.DOJump(transform.position, 5f, 1, 1f)
.SetEase(Ease.OutQuint)
.OnComplete(() => bobber.DOMoveY(bobberOscillationHeight, 1f).SetLoops(-1, LoopType.Yoyo));
// Wait for a random amount of time before catching a fish
float catchTime = Random.Range(minCatchTime, maxCatchTime);
yield return new WaitForSeconds(catchTime);
// Stop the bobber animation and destroy it
DOTween.Kill(bobber);
Destroy(bobber.gameObject);
// Select a random fish and set its swim depth to simulate hooking
var hookedFish = fishList[Random.Range(0, fishList.Count)];
hookedFish.SwimDepth -= 1f;
// Play the effects for hooking a fish
AudioManager.Instance.PlaySFX(fishHookedSound);
Vibration.Vibrate();
// Start reeling sound effect using reeling out clip initially
audioSource.loop = true;
audioSource.clip = reelingOutSound;
audioSource.Play();
// Make all fish panic and swim deeper except the hooked one
fishList.ForEach(fish =>
{
fish.IsPanic = true;
if (fish != hookedFish) fish.SwimDepth -= 3f;
});
// Update UI for fishing progress and line tension
player.SetAnimatorInt("FishingIndex", 2);
UIManager.Instance.ProgressBar.Toggle("Catch the fish!");
UIManager.Instance.LineTension.Toggle(true);
float progress = 0.3f;
float tension = 0.5f;
bool isCaught = true;
// Continue updating progress and tension while fishing
while (progress < 1f && isCaught)
{
bool isReeling = Input.GetMouseButton(0);
float tensionIncreaseRate = Random.Range(minTensionIncrease, maxTensionIncrease);
// Increase or decrease progress and tension based on reeling status
if (isReeling)
{
progress += Time.deltaTime * baseProgressIncreaseRate;
tension += Time.deltaTime * tensionIncreaseRate;
}
else
{
progress -= Time.deltaTime * baseProgressDecreaseRate;
tension -= Time.deltaTime * baseTensionDecreaseRate;
}
// Clamp values to ensure they stay within the valid range
progress = Mathf.Clamp01(progress);
tension = Mathf.Clamp01(tension);
// Update UI with the current progress and tension values
UIManager.Instance.ProgressBar.UpdateDisplay(progress);
UIManager.Instance.LineTension.UpdateDisplay(tension);
// Check if the fish has escaped or progress is zero
if (progress <= 0f || tension >= 1f)
{
isCaught = false;
AudioManager.Instance.PlaySFX(fishEscapedSound);
}
// Update reeling sound effect based on reeling status
var reelingSound = isReeling ? reelingInSound : reelingOutSound;
if (audioSource.clip != reelingSound)
{
audioSource.clip = reelingSound;
audioSource.Play();
}
yield return null;
}
// Restore swim depth for all fish after the fishing attempt
hookedFish.SwimDepth += 1f;
fishList.ForEach(fish =>
{
fish.IsPanic = false;
if (fish != hookedFish) fish.SwimDepth += 3f;
});
// Hide progress and tension UI elements
UIManager.Instance.ProgressBar.Toggle("");
UIManager.Instance.LineTension.Toggle(false);
// Stop reeling sound and cleanup
audioSource.Stop();
audioSource.clip = null;
audioSource.loop = false;
player.SetAnimatorInt("FishingIndex", 3);
if (isCaught)
{
// Catch the fish and play the corresponding sound effect
hookedFish.Catch();
AudioManager.Instance.PlaySFX(fishCaughtSound);
// Move the caught fish near the player and add it to the inventory
Vector3 catchPoint = player.transform.TransformPoint(Vector3.right);
yield return hookedFish.transform.DOJump(catchPoint, 5f, 1, 1f)
.SetEase(Ease.InOutFlash)
.WaitForCompletion();
yield return new WaitForSeconds(3f);
player.Inventory.AddItem(hookedFish.FishItem.ItemId);
// Spawn a new fish after a random delay and remove the caught fish from the list
float spawnDelay = Random.Range(minSpawnTime, maxSpawnTime);
StartCoroutine(SpawnFish(hookedFish.FishIndex, spawnDelay));
fishList.Remove(hookedFish);
Destroy(hookedFish.gameObject);
}
// Deactivate the virtual camera and restore UI and player control
virtualCamera.gameObject.SetActive(false);
yield return new WaitForSeconds(1f);
player.FishingRod.SetActive(false);
player.IsControllable = true;
UIManager.Instance.SetActiveJoystick(true);
UIManager.Instance.ToggleHUD(true);
isPlayerFishing = false;
}
IEnumerator SpawnFish(int prefabIndex, float spawnDelay)
{
yield return new WaitForSeconds(spawnDelay);
// Ensure that the player is not currently fishing before spawning the fish
yield return new WaitWhile(() => isPlayerFishing);
// Calculate a random position within the defined swim radius
var randomPos = Random.insideUnitSphere.Flatten() * swimRadius;
var spawnPos = transform.position + randomPos;
// Instantiate the fish prefab at the calculated position
var fish = Instantiate(fishPrefabs[prefabIndex], spawnPos, Quaternion.identity);
fish.transform.SetParent(transform);
fishList.Add(fish);
// Initialize the fish with its specific settings
fish.Initialize(prefabIndex, transform.position, swimRadius, swimSpeed);
// If there was a delay before spawning (which occurs when a fishing spot is activated for the first time after a bridge purchase),
// animate the fish's scale from zero to its normal size.
if (spawnDelay > 0f)
{
fish.transform.localScale = Vector3.zero;
fish.transform.DOScale(Vector3.one, 1f)
.SetEase(Ease.OutBounce);
}
}
#if UNITY_EDITOR
void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, swimRadius);
}
#endif
}
}