using System; using System.Collections.Generic; using System.Linq; using DefaultNamespace; using Mapbox.CheapRulerCs; using Mapbox.Unity.Map; using Mapbox.Utils; using UnityEngine; using Random = UnityEngine.Random; public class CollectableDuckManager : MonoBehaviour { private class SimpleDuckData { public Vector2d location; public DateTime spawnTime; public DateTime despawnTime; } public List collectableDucks; private readonly HashSet _collectedDucks = new HashSet(); private readonly Dictionary _spawnedDucks = new Dictionary(); private List existingSimpleDucks = new List(); private readonly Dictionary _spawnedSimpleDucks = new Dictionary(); public AbstractMap map; public Transform player; public Transform collectableDuckParent; public float spawnStandardDuckTimeout = 1f; public float spawnStandardDuckProbability = 0.1f; public float standardDuckTimeout = 15 * 60; public GameObject simpleDuckPrefab; private float timeSinceStandardDuckSpawn = 0f; public event EventHandler DuckClicked; /// /// Spawn distance in meters (i.e. how close should the player be to a duck before it spawns on the map /// public float spawnDistance = 50; public CollectableDuck duckSpawnPrefab; private static CollectableDuckManager _instance; public static CollectableDuckManager Instance { get { return _instance; } private set { _instance = value; } } public CollectableDuckManager() { Instance = this; } private void Awake() { CheckCollectableDuckIds(); _collectedDucks.Clear(); foreach (var duck in collectableDucks.Where(duck => PlayerPrefs.GetInt("duck." + duck.Id) > 0)) { _collectedDucks.Add(duck); } } private void CheckCollectableDuckIds() { var duckIds = new HashSet(); foreach (var duck in collectableDucks) { if (duckIds.Contains(duck.Id)) { Debug.LogError("Duplicate duck ID: '" + duck.Id + "' for duck '" + duck.name + "'"); } duckIds.Add(duck.Id); } } private void Update() { var ruler = new CheapRuler(map.CenterLatitudeLongitude.x, CheapRulerUnits.Meters); var playerPosition = map.WorldToGeoPosition(player.position).ToArray(); for (int i = 0; i < collectableDucks.Count; i++) { var duck = collectableDucks[i]; var duckCoord = duck.LatitudeLongitude; var dist = ruler.Distance(duckCoord.ToArray(), playerPosition); // Debug.Log("Distance to " + duck.name + ": " + dist); if (dist < spawnDistance && !_collectedDucks.Contains(duck)) { SpawnDuck(duck); } else { RemoveDuck(duck); } } timeSinceStandardDuckSpawn += Time.deltaTime; if (timeSinceStandardDuckSpawn > spawnStandardDuckTimeout) { timeSinceStandardDuckSpawn = 0; if (Random.value < spawnStandardDuckProbability) { // Time to spawn a duck somewhere close to the player double[] max = ruler.Destination(playerPosition, 100, 45); double[] min = ruler.Destination(playerPosition, 100, 225); var latDiff = max[0] - min[0]; var lonDiff = max[1] - min[1]; var duckCoord = new Vector2d(min[0] + (latDiff * Random.value), min[1] + (lonDiff * Random.value)); existingSimpleDucks.Add(new SimpleDuckData() { location = duckCoord, despawnTime = DateTime.Now.AddSeconds(standardDuckTimeout), spawnTime = DateTime.Now }); } } for (int i = 0; i < existingSimpleDucks.Count; i++) { var duck = existingSimpleDucks[i]; if (duck.despawnTime < DateTime.Now) { existingSimpleDucks.RemoveAt(i); i--; HideSimpleDuck(duck); } else { var duckCoord = duck.location; var dist = ruler.Distance(duckCoord.ToArray(), playerPosition); // Debug.Log("Distance to " + duck.name + ": " + dist); if (dist < spawnDistance) { SpawnSimpleDuck(duck); } else { HideSimpleDuck(duck); } } } } private void SpawnDuck(CollectableDuckData duck) { if (!_spawnedDucks.ContainsKey(duck)) { // Spawn the duck var duckSpawn = Instantiate(duckSpawnPrefab, map.GeoToWorldPosition(duck.LatitudeLongitude), Quaternion.identity, collectableDuckParent); duckSpawn.CollectableDuckData = duck; var duckModel = Instantiate(duck.ModelPrefab, duckSpawn.transform); duckModel.transform.localPosition = Vector3.zero; _spawnedDucks[duck] = duckSpawn; } else { if (!_spawnedDucks[duck].gameObject.activeSelf) { _spawnedDucks[duck].gameObject.SetActive(true); } } } private void SpawnSimpleDuck(SimpleDuckData duck) { if (!_spawnedSimpleDucks.ContainsKey(duck)) { // Spawn the duck var duckSpawn = Instantiate(duckSpawnPrefab, map.GeoToWorldPosition(duck.location), Quaternion.identity, collectableDuckParent); var duckModel = Instantiate(simpleDuckPrefab, duckSpawn.transform); duckModel.transform.localPosition = Vector3.zero; _spawnedSimpleDucks[duck] = duckSpawn; } else { if (!_spawnedSimpleDucks[duck].gameObject.activeSelf) { _spawnedSimpleDucks[duck].gameObject.SetActive(true); } } } private void RemoveDuck(CollectableDuckData duck) { if (_spawnedDucks.ContainsKey(duck)) { if (_spawnedDucks[duck].gameObject.activeSelf) { _spawnedDucks[duck].gameObject.SetActive(false); } } } private void HideSimpleDuck(SimpleDuckData duck) { if (_spawnedSimpleDucks.ContainsKey(duck)) { if (_spawnedSimpleDucks[duck].gameObject.activeSelf) { _spawnedSimpleDucks[duck].gameObject.SetActive(false); } } } public void OnDuckClicked(CollectableDuckData duck) { if (DuckClicked != null) { DuckClicked.Invoke(this, duck); } } public void OnDuckCollected(CollectableDuckData duck) { _collectedDucks.Add(duck); PlayerPrefs.SetInt("duck." + duck.Id, 1); RemoveDuck(duck); } }