QuestMapMarkerManager.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. /// <summary>
  5. /// Manages quest markers on the map - shows quest locations and handles completion detection
  6. /// </summary>
  7. public class QuestMapMarkerManager : MonoBehaviour
  8. {
  9. [Header("Marker Prefabs")]
  10. [Tooltip("Prefab for quest objective markers")]
  11. public GameObject questMarkerPrefab;
  12. [Tooltip("Prefab for quest completion areas")]
  13. public GameObject questAreaPrefab;
  14. [Header("Marker Settings")]
  15. [Tooltip("Scale factor for quest markers")]
  16. public float markerScale = 0.5f;
  17. [Tooltip("How often to check for quest completion (seconds)")]
  18. public float completionCheckInterval = 1f;
  19. [Header("Marker Colors")]
  20. public Color easyQuestColor = Color.green;
  21. public Color normalQuestColor = Color.yellow;
  22. public Color hardQuestColor = Color.red;
  23. public Color legendaryQuestColor = Color.magenta;
  24. public Color completedQuestColor = Color.gray;
  25. [Header("Debug")]
  26. public bool enableDebugLogs = false;
  27. private Dictionary<string, QuestMarker> activeQuestMarkers = new Dictionary<string, QuestMarker>();
  28. private Transform teamMarker;
  29. private float lastCompletionCheck = 0f;
  30. private void Start()
  31. {
  32. InitializeMarkerManager();
  33. // Subscribe to quest events
  34. if (QuestManager.Instance != null)
  35. {
  36. QuestManager.Instance.OnQuestAccepted += OnQuestAccepted;
  37. QuestManager.Instance.OnQuestCompleted += OnQuestCompleted;
  38. QuestManager.Instance.OnQuestFailed += OnQuestFailed;
  39. QuestManager.Instance.OnQuestAbandoned += OnQuestAbandoned;
  40. // Create markers for any existing active quests
  41. StartCoroutine(CreateMarkersForExistingQuests());
  42. }
  43. }
  44. private System.Collections.IEnumerator CreateMarkersForExistingQuests()
  45. {
  46. // Wait a frame to ensure all systems are initialized
  47. yield return null;
  48. RefreshAllMarkers();
  49. LogDebug("Created markers for existing active quests");
  50. }
  51. private void Update()
  52. {
  53. CheckQuestCompletion();
  54. }
  55. private void OnDestroy()
  56. {
  57. // Unsubscribe from quest events
  58. if (QuestManager.Instance != null)
  59. {
  60. QuestManager.Instance.OnQuestAccepted -= OnQuestAccepted;
  61. QuestManager.Instance.OnQuestCompleted -= OnQuestCompleted;
  62. QuestManager.Instance.OnQuestFailed -= OnQuestFailed;
  63. QuestManager.Instance.OnQuestAbandoned -= OnQuestAbandoned;
  64. }
  65. }
  66. private void InitializeMarkerManager()
  67. {
  68. // Find team marker
  69. var teamMarkerObject = GameObject.Find("TeamMarker");
  70. if (teamMarkerObject != null)
  71. {
  72. teamMarker = teamMarkerObject.transform;
  73. LogDebug("Found TeamMarker");
  74. }
  75. else
  76. {
  77. LogDebug("⚠️ TeamMarker not found");
  78. }
  79. // Create default marker prefabs if none assigned
  80. if (questMarkerPrefab == null)
  81. {
  82. questMarkerPrefab = CreateDefaultMarkerPrefab();
  83. }
  84. if (questAreaPrefab == null)
  85. {
  86. questAreaPrefab = CreateDefaultAreaPrefab();
  87. }
  88. LogDebug("QuestMapMarkerManager initialized");
  89. }
  90. private void CheckQuestCompletion()
  91. {
  92. if (Time.time < lastCompletionCheck + completionCheckInterval) return;
  93. if (teamMarker == null || QuestManager.Instance == null) return;
  94. lastCompletionCheck = Time.time;
  95. // Get current team position in map coordinates
  96. Vector2Int teamPosition = WorldToMapCoordinates(teamMarker.position);
  97. // Check if any quests can be completed at current position
  98. var completedQuests = QuestManager.Instance.TryCompleteQuestsAtPosition(teamPosition);
  99. if (completedQuests.Count > 0)
  100. {
  101. LogDebug($"Completed {completedQuests.Count} quests at position {teamPosition}");
  102. }
  103. }
  104. private Vector2Int WorldToMapCoordinates(Vector3 worldPosition)
  105. {
  106. // Convert world position to map tile coordinates
  107. // This should match the conversion used in other map systems
  108. float tileSize = 1f;
  109. var mapVisualizer = FindFirstObjectByType<MapVisualizer>();
  110. if (mapVisualizer != null && mapVisualizer.tileSize > 0)
  111. {
  112. tileSize = mapVisualizer.tileSize;
  113. }
  114. return new Vector2Int(
  115. Mathf.RoundToInt(worldPosition.x / tileSize),
  116. Mathf.RoundToInt(worldPosition.z / tileSize) // Use Z for 3D maps
  117. );
  118. }
  119. private Vector3 MapToWorldCoordinates(Vector2Int mapPosition)
  120. {
  121. // Convert map tile coordinates to world position
  122. float tileSize = 1f;
  123. var mapVisualizer = FindFirstObjectByType<MapVisualizer>();
  124. if (mapVisualizer != null && mapVisualizer.tileSize > 0)
  125. {
  126. tileSize = mapVisualizer.tileSize;
  127. }
  128. return new Vector3(
  129. mapPosition.x * tileSize,
  130. 0f,
  131. mapPosition.y * tileSize
  132. );
  133. }
  134. private void OnQuestAccepted(ActiveQuest quest)
  135. {
  136. CreateQuestMarker(quest);
  137. }
  138. private void OnQuestCompleted(ActiveQuest quest, List<QuestReward> rewards)
  139. {
  140. RemoveQuestMarker(quest.questId);
  141. }
  142. private void OnQuestFailed(ActiveQuest quest)
  143. {
  144. RemoveQuestMarker(quest.questId);
  145. }
  146. private void OnQuestAbandoned(ActiveQuest quest)
  147. {
  148. RemoveQuestMarker(quest.questId);
  149. }
  150. private void CreateQuestMarker(ActiveQuest quest)
  151. {
  152. if (quest?.questData == null) return;
  153. // Remove existing marker if it exists
  154. RemoveQuestMarker(quest.questId);
  155. // Convert quest position to world coordinates
  156. Vector3 worldPosition = MapToWorldCoordinates(quest.questData.targetMapPosition);
  157. // Create marker GameObject
  158. GameObject markerObject = Instantiate(questMarkerPrefab, worldPosition, Quaternion.identity);
  159. markerObject.name = $"QuestMarker_{quest.questData.questTitle}";
  160. markerObject.transform.localScale = Vector3.one * markerScale;
  161. // Create completion area
  162. GameObject areaObject = Instantiate(questAreaPrefab, worldPosition, Quaternion.identity);
  163. areaObject.name = $"QuestArea_{quest.questData.questTitle}";
  164. areaObject.transform.localScale = Vector3.one * quest.questData.completionRadius;
  165. // Set up the quest marker component
  166. var questMarker = markerObject.GetComponent<QuestMarker>();
  167. if (questMarker == null)
  168. {
  169. questMarker = markerObject.AddComponent<QuestMarker>();
  170. }
  171. questMarker.Initialize(quest, areaObject);
  172. // Set marker color based on difficulty
  173. SetMarkerColor(questMarker, quest.questData.difficulty);
  174. // Store reference
  175. activeQuestMarkers[quest.questId] = questMarker;
  176. LogDebug($"Created quest marker for '{quest.questData.questTitle}' at {quest.questData.targetMapPosition}");
  177. }
  178. private void RemoveQuestMarker(string questId)
  179. {
  180. if (activeQuestMarkers.TryGetValue(questId, out QuestMarker marker))
  181. {
  182. if (marker != null && marker.gameObject != null)
  183. {
  184. Destroy(marker.gameObject);
  185. }
  186. activeQuestMarkers.Remove(questId);
  187. LogDebug($"Removed quest marker for quest {questId}");
  188. }
  189. }
  190. private void SetMarkerColor(QuestMarker marker, QuestDifficulty difficulty)
  191. {
  192. Color color = difficulty switch
  193. {
  194. QuestDifficulty.Easy => easyQuestColor,
  195. QuestDifficulty.Normal => normalQuestColor,
  196. QuestDifficulty.Hard => hardQuestColor,
  197. QuestDifficulty.Legendary => legendaryQuestColor,
  198. _ => normalQuestColor
  199. };
  200. marker.SetColor(color);
  201. }
  202. private GameObject CreateDefaultMarkerPrefab()
  203. {
  204. // Create a simple cube marker
  205. GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Cube);
  206. marker.name = "DefaultQuestMarker";
  207. // Make it slightly elevated
  208. marker.transform.position = Vector3.up * 0.5f;
  209. // Add a distinctive material
  210. var renderer = marker.GetComponent<Renderer>();
  211. if (renderer != null)
  212. {
  213. renderer.material.color = Color.yellow;
  214. }
  215. // Remove collider to avoid interference
  216. var collider = marker.GetComponent<Collider>();
  217. if (collider != null)
  218. {
  219. DestroyImmediate(collider);
  220. }
  221. return marker;
  222. }
  223. private GameObject CreateDefaultAreaPrefab()
  224. {
  225. // Create a simple cylinder for the completion area
  226. GameObject area = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
  227. area.name = "DefaultQuestArea";
  228. // Make it flat and transparent
  229. area.transform.localScale = new Vector3(1f, 0.1f, 1f);
  230. var renderer = area.GetComponent<Renderer>();
  231. if (renderer != null)
  232. {
  233. // Make it semi-transparent
  234. var material = new Material(Shader.Find("Legacy Shaders/Transparent/Diffuse"));
  235. material.color = new Color(1f, 1f, 0f, 0.3f); // Semi-transparent yellow
  236. renderer.material = material;
  237. }
  238. // Remove collider
  239. var collider = area.GetComponent<Collider>();
  240. if (collider != null)
  241. {
  242. DestroyImmediate(collider);
  243. }
  244. return area;
  245. }
  246. /// <summary>
  247. /// Get all active quest markers
  248. /// </summary>
  249. public List<QuestMarker> GetActiveMarkers()
  250. {
  251. return activeQuestMarkers.Values.Where(m => m != null).ToList();
  252. }
  253. /// <summary>
  254. /// Get quest marker by quest ID
  255. /// </summary>
  256. public QuestMarker GetMarkerByQuestId(string questId)
  257. {
  258. activeQuestMarkers.TryGetValue(questId, out QuestMarker marker);
  259. return marker;
  260. }
  261. /// <summary>
  262. /// Refresh all quest markers (useful when loading a saved game)
  263. /// </summary>
  264. public void RefreshAllMarkers()
  265. {
  266. // Clear existing markers
  267. foreach (var marker in activeQuestMarkers.Values)
  268. {
  269. if (marker != null && marker.gameObject != null)
  270. {
  271. Destroy(marker.gameObject);
  272. }
  273. }
  274. activeQuestMarkers.Clear();
  275. // Recreate markers for all active quests
  276. if (QuestManager.Instance != null)
  277. {
  278. var activeQuests = QuestManager.Instance.GetActiveQuests();
  279. foreach (var quest in activeQuests)
  280. {
  281. CreateQuestMarker(quest);
  282. }
  283. }
  284. LogDebug("Refreshed all quest markers");
  285. }
  286. private void LogDebug(string message)
  287. {
  288. if (enableDebugLogs)
  289. {
  290. Debug.Log($"[QuestMapMarkerManager] {message}");
  291. }
  292. }
  293. /// <summary>
  294. /// Focus the camera on a specific quest's marker
  295. /// </summary>
  296. public void FocusOnQuest(ActiveQuest quest)
  297. {
  298. if (quest == null || !activeQuestMarkers.ContainsKey(quest.questId))
  299. {
  300. LogDebug($"❌ Cannot focus on quest {quest?.questData?.questTitle} - marker not found");
  301. return;
  302. }
  303. var marker = activeQuestMarkers[quest.questId];
  304. if (marker != null)
  305. {
  306. var targetPosition = marker.transform.position;
  307. // Try to find a camera controller or similar component
  308. var cameraController = FindFirstObjectByType<CameraController>();
  309. if (cameraController != null)
  310. {
  311. // Assuming the camera controller has a method to focus on position
  312. // cameraController.FocusOnPosition(targetPosition);
  313. LogDebug($"✅ Focusing camera on quest: {quest.questData.questTitle}");
  314. }
  315. else
  316. {
  317. // Fallback: just move the main camera
  318. var mainCamera = Camera.main;
  319. if (mainCamera != null)
  320. {
  321. var cameraPos = mainCamera.transform.position;
  322. mainCamera.transform.position = new Vector3(targetPosition.x, targetPosition.y, cameraPos.z);
  323. LogDebug($"✅ Moved camera to quest location: {quest.questData.questTitle}");
  324. }
  325. }
  326. }
  327. }
  328. /// <summary>
  329. /// Get the world position of a quest marker
  330. /// </summary>
  331. public Vector3? GetQuestMarkerPosition(ActiveQuest quest)
  332. {
  333. if (quest == null || !activeQuestMarkers.ContainsKey(quest.questId))
  334. return null;
  335. var marker = activeQuestMarkers[quest.questId];
  336. return marker?.transform.position;
  337. }
  338. #region Context Menu Debug Methods
  339. [ContextMenu("Refresh All Markers")]
  340. private void DebugRefreshAllMarkers()
  341. {
  342. RefreshAllMarkers();
  343. }
  344. [ContextMenu("List Active Markers")]
  345. private void DebugListActiveMarkers()
  346. {
  347. LogDebug($"Active Quest Markers ({activeQuestMarkers.Count}):");
  348. foreach (var kvp in activeQuestMarkers)
  349. {
  350. var marker = kvp.Value;
  351. if (marker != null)
  352. {
  353. LogDebug($" - {marker.ActiveQuest.questData.questTitle} at {marker.transform.position}");
  354. }
  355. }
  356. }
  357. [ContextMenu("Force Create Test Marker")]
  358. private void DebugCreateTestMarker()
  359. {
  360. if (QuestManager.Instance == null)
  361. {
  362. LogDebug("❌ No QuestManager found!");
  363. return;
  364. }
  365. var activeQuests = QuestManager.Instance.GetActiveQuests();
  366. if (activeQuests.Count == 0)
  367. {
  368. LogDebug("❌ No active quests to create markers for!");
  369. return;
  370. }
  371. foreach (var quest in activeQuests)
  372. {
  373. CreateQuestMarker(quest);
  374. LogDebug($"✅ Created test marker for: {quest.questData.questTitle}");
  375. }
  376. }
  377. [ContextMenu("Check Quest System Status")]
  378. private void DebugQuestSystemStatus()
  379. {
  380. LogDebug("=== QUEST SYSTEM STATUS ===");
  381. LogDebug($"QuestManager exists: {QuestManager.Instance != null}");
  382. if (QuestManager.Instance != null)
  383. {
  384. var activeQuests = QuestManager.Instance.GetActiveQuests();
  385. LogDebug($"Active quests: {activeQuests.Count}");
  386. foreach (var quest in activeQuests)
  387. {
  388. LogDebug($" - {quest.questData.questTitle} → {quest.questData.targetMapPosition}");
  389. }
  390. }
  391. LogDebug($"Active markers: {activeQuestMarkers.Count}");
  392. LogDebug($"Team marker found: {teamMarker != null}");
  393. LogDebug($"Marker prefabs: Marker={questMarkerPrefab != null}, Area={questAreaPrefab != null}");
  394. }
  395. #endregion
  396. }