using UnityEngine; using UnityEngine.UI; public class QuestLogUI : MonoBehaviour { public static QuestLogUI Instance { get; private set; } public GameObject root; public RectTransform content; public Font uiFont; public Color titleColor = new Color(1f, 0.74f, 0.25f); public Color textColor = new Color(0.92f, 0.9f, 0.82f); public Color mutedColor = new Color(0.7f, 0.7f, 0.7f); public KeyCode toggleKey = KeyCode.J; public bool IsOpen => root != null && root.activeSelf; private void Awake() { if (Instance != null && Instance != this) { Destroy(this); return; } Instance = this; } private void Start() { if (root != null) { root.SetActive(false); } Subscribe(); Refresh(); } private void OnEnable() { Subscribe(); } private void OnDisable() { if (QuestManager.Instance != null) { QuestManager.Instance.OnQuestsChanged -= Refresh; } } private void Subscribe() { if (QuestManager.Instance != null) { QuestManager.Instance.OnQuestsChanged -= Refresh; QuestManager.Instance.OnQuestsChanged += Refresh; } } private void Update() { if (Input.GetKeyDown(toggleKey)) { Toggle(); } } public void Toggle() { if (root == null) { return; } bool next = !root.activeSelf; root.SetActive(next); Cursor.lockState = next ? CursorLockMode.None : CursorLockMode.Locked; Cursor.visible = next; Refresh(); } public void Refresh() { if (content == null) { return; } ClearContent(); if (QuestManager.Instance == null) { CreateText("QuestManager не найден", mutedColor, 16, FontStyle.Normal); return; } CreateText("Активные", titleColor, 22, FontStyle.Bold); if (QuestManager.Instance.ActiveQuests.Count == 0) { CreateText("Нет активных квестов", mutedColor, 16, FontStyle.Normal); } else { foreach (QuestState state in QuestManager.Instance.ActiveQuests) { CreateQuestBlock(state); } } CreateText("Завершённые", titleColor, 22, FontStyle.Bold); if (QuestManager.Instance.CompletedQuests.Count == 0) { CreateText("Пока пусто", mutedColor, 16, FontStyle.Normal); } else { foreach (QuestState state in QuestManager.Instance.CompletedQuests) { CreateQuestBlock(state); } } if (QuestManager.Instance.FailedQuests.Count > 0) { CreateText("Проваленные", titleColor, 22, FontStyle.Bold); foreach (QuestState state in QuestManager.Instance.FailedQuests) { CreateQuestBlock(state); } } } private void CreateQuestBlock(QuestState state) { if (state == null || state.questData == null) { return; } QuestData quest = state.questData; CreateText(quest.questName, titleColor, 18, FontStyle.Bold); CreateText(quest.description, textColor, 15, FontStyle.Normal); CreateText($"Статус: {state.status}", mutedColor, 14, FontStyle.Normal); for (int i = 0; i < quest.goals.Count; i++) { QuestGoal goal = quest.goals[i]; CreateText($"{goal.GetDisplayName()} {state.GetProgress(i)}/{goal.requiredAmount}", textColor, 15, FontStyle.Normal); } float percent = QuestManager.Instance.GetQuestCompletionPercent(state); CreateText($"{BuildProgressBar(percent)} {Mathf.RoundToInt(percent * 100f)}%", titleColor, 15, FontStyle.Normal); CreateSpacer(8f); } private string BuildProgressBar(float percent) { const int length = 10; int filled = Mathf.RoundToInt(Mathf.Clamp01(percent) * length); return new string('█', filled) + new string('░', length - filled); } private void ClearContent() { for (int i = content.childCount - 1; i >= 0; i--) { Destroy(content.GetChild(i).gameObject); } } private void CreateSpacer(float height) { GameObject spacer = new GameObject("Quest Log Spacer", typeof(RectTransform)); spacer.transform.SetParent(content, false); spacer.GetComponent().sizeDelta = new Vector2(0f, height); } private void CreateText(string value, Color color, int size, FontStyle style) { GameObject line = new GameObject("Quest Log Text", typeof(RectTransform), typeof(Text)); line.transform.SetParent(content, false); Text text = line.GetComponent(); text.font = uiFont != null ? uiFont : Resources.GetBuiltinResource("Arial.ttf"); text.fontSize = size; text.fontStyle = style; text.color = color; text.text = value; text.horizontalOverflow = HorizontalWrapMode.Wrap; text.verticalOverflow = VerticalWrapMode.Overflow; RectTransform rect = line.GetComponent(); rect.sizeDelta = new Vector2(0f, Mathf.Max(size + 10f, EstimateHeight(value, size))); } private float EstimateHeight(string value, int size) { if (string.IsNullOrEmpty(value)) { return size + 8f; } int lineCount = Mathf.Max(1, Mathf.CeilToInt(value.Length / 42f)); return lineCount * (size + 8f); } }