first commit

This commit is contained in:
2026-06-04 23:22:13 +03:00
commit d329f501be
310 changed files with 36395 additions and 0 deletions
+171
View File
@@ -0,0 +1,171 @@
using System;
using OTGIntegrated.Abilities;
using OTGIntegrated.Core;
using OTGIntegrated.Quests;
using OTGIntegrated.Stats;
using OTGIntegrated.Weapons;
using UnityEngine;
using UnityEngine.UI;
namespace OTGIntegrated.UI
{
[Serializable]
public sealed class AbilityHudSlot
{
public Text keyText;
public Text nameText;
public Text manaText;
public Text cooldownText;
public Image cooldownOverlay;
public Image iconImage;
}
public sealed class HUDController : MonoBehaviour
{
[Header("Status")]
public Image hpFill;
public Image manaFill;
public Image expFill;
public Text hpText;
public Text manaText;
public Text expText;
public Text levelText;
[Header("Quest")]
public Text questTitleText;
public Text questBodyText;
[Header("Abilities")]
public AbilityHudSlot[] abilitySlots = new AbilityHudSlot[3];
[Header("Weapon")]
public Text weaponNameText;
public Text weaponDamageText;
public Text weaponHintText;
private PlayerStats stats;
private LevelSystem levelSystem;
private QuestManager questManager;
private AbilityManager abilityManager;
private WeaponManager weaponManager;
public void Initialize(PlayerStats stats, LevelSystem levelSystem, QuestManager questManager, AbilityManager abilityManager, WeaponManager weaponManager)
{
this.stats = stats;
this.levelSystem = levelSystem;
this.questManager = questManager;
this.abilityManager = abilityManager;
this.weaponManager = weaponManager;
if (stats != null) stats.OnStatsChanged += RefreshAll;
if (levelSystem != null)
{
levelSystem.OnExperienceChanged += RefreshAll;
levelSystem.OnLevelChanged += RefreshAll;
}
if (questManager != null) questManager.OnQuestsChanged += RefreshAll;
if (abilityManager != null) abilityManager.OnAbilitiesChanged += RefreshAbilities;
GameEvents.OnWeaponChanged += _ => RefreshWeapon();
GameEvents.OnStatsChanged += RefreshAll;
RefreshAll();
}
private void OnDestroy()
{
if (stats != null) stats.OnStatsChanged -= RefreshAll;
if (levelSystem != null)
{
levelSystem.OnExperienceChanged -= RefreshAll;
levelSystem.OnLevelChanged -= RefreshAll;
}
if (questManager != null) questManager.OnQuestsChanged -= RefreshAll;
if (abilityManager != null) abilityManager.OnAbilitiesChanged -= RefreshAbilities;
GameEvents.OnStatsChanged -= RefreshAll;
}
private void Update()
{
RefreshAbilities();
}
public void RefreshAll()
{
RefreshStatus();
RefreshQuest();
RefreshAbilities();
RefreshWeapon();
}
private void RefreshStatus()
{
if (stats != null)
{
if (hpFill != null) hpFill.fillAmount = stats.GetNormalizedHealth();
if (manaFill != null) manaFill.fillAmount = stats.GetNormalizedMana();
if (hpText != null) hpText.text = $"HP {Mathf.RoundToInt(stats.CurrentHealth)} / {Mathf.RoundToInt(stats.MaxHealth)}";
if (manaText != null) manaText.text = $"Mana {Mathf.RoundToInt(stats.CurrentMana)} / {Mathf.RoundToInt(stats.MaxMana)}";
}
if (levelSystem != null)
{
if (expFill != null) expFill.fillAmount = levelSystem.GetNormalizedExp();
if (expText != null) expText.text = $"EXP {levelSystem.currentExp} / {levelSystem.expToNextLevel}";
if (levelText != null) levelText.text = $"Level {levelSystem.level}";
}
}
private void RefreshQuest()
{
if (questTitleText != null)
{
questTitleText.text = "Активная цель";
}
if (questBodyText != null)
{
questBodyText.text = questManager != null ? questManager.GetPrimaryTrackerText() : "Нет активных квестов";
}
}
private void RefreshAbilities()
{
if (abilityManager == null || abilitySlots == null)
{
return;
}
for (int i = 0; i < abilitySlots.Length; i++)
{
AbilityHudSlot slot = abilitySlots[i];
if (slot == null)
{
continue;
}
AbilityData ability = i < abilityManager.LearnedAbilities.Count ? abilityManager.LearnedAbilities[i] : null;
float remaining = abilityManager.GetCooldownRemaining(i);
if (slot.keyText != null) slot.keyText.text = (i + 1).ToString();
if (slot.nameText != null) slot.nameText.text = ability != null ? ability.displayName : "-";
if (slot.manaText != null) slot.manaText.text = ability != null ? Mathf.RoundToInt(ability.manaCost).ToString() : string.Empty;
if (slot.cooldownOverlay != null) slot.cooldownOverlay.fillAmount = abilityManager.GetCooldownNormalized(i);
if (slot.cooldownText != null) slot.cooldownText.text = remaining > 0.05f ? remaining.ToString("0.0") : string.Empty;
if (slot.iconImage != null)
{
slot.iconImage.sprite = ability != null ? ability.icon : null;
slot.iconImage.color = ability != null && ability.icon != null ? Color.white : new Color(0.2f, 0.22f, 0.26f, 1f);
}
}
}
private void RefreshWeapon()
{
WeaponData weapon = weaponManager != null ? weaponManager.CurrentWeapon : null;
if (weaponNameText != null) weaponNameText.text = weapon != null ? $"Weapon: {weapon.displayName}" : "Weapon: -";
if (weaponDamageText != null) weaponDamageText.text = weaponManager != null ? $"Damage: {weaponManager.CurrentFinalDamage:0}" : "Damage: -";
if (weaponHintText != null) weaponHintText.text = "LMB — Attack";
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d32dbffc8851c0e5a61fa47ffffdb2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+40
View File
@@ -0,0 +1,40 @@
using UnityEngine;
using UnityEngine.UI;
namespace OTGIntegrated.UI
{
public sealed class InteractionPromptUI : MonoBehaviour
{
public GameObject root;
public Text promptText;
private void Awake()
{
if (root == null)
{
root = gameObject;
}
}
public void Show(string prompt)
{
if (root != null)
{
root.SetActive(true);
}
if (promptText != null)
{
promptText.text = prompt;
}
}
public void Hide()
{
if (root != null)
{
root.SetActive(false);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff9c804aac0e7ee4d89c5f24ba48aef1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+90
View File
@@ -0,0 +1,90 @@
using System.Collections;
using System.Collections.Generic;
using OTGIntegrated.Core;
using UnityEngine;
using UnityEngine.UI;
namespace OTGIntegrated.UI
{
public sealed class NotificationUI : MonoBehaviour
{
public RectTransform stackRoot;
public Font uiFont;
public float visibleSeconds = 2.4f;
public int maxMessages = 5;
private readonly Queue<GameObject> activeMessages = new Queue<GameObject>();
private void OnEnable()
{
GameEvents.OnNotificationRequested += Show;
}
private void OnDisable()
{
GameEvents.OnNotificationRequested -= Show;
}
public void Show(string message)
{
if (string.IsNullOrWhiteSpace(message) || stackRoot == null)
{
return;
}
GameObject root = new GameObject("Notification", typeof(RectTransform), typeof(CanvasGroup), typeof(Image));
root.transform.SetParent(stackRoot, false);
root.GetComponent<RectTransform>().sizeDelta = new Vector2(520f, 42f);
root.GetComponent<Image>().color = new Color(0.05f, 0.06f, 0.08f, 0.86f);
Text text = CreateText(root.transform, "Text", message, 16, TextAnchor.MiddleCenter);
text.rectTransform.anchorMin = Vector2.zero;
text.rectTransform.anchorMax = Vector2.one;
text.rectTransform.offsetMin = new Vector2(12f, 0f);
text.rectTransform.offsetMax = new Vector2(-12f, 0f);
activeMessages.Enqueue(root);
while (activeMessages.Count > maxMessages)
{
GameObject old = activeMessages.Dequeue();
if (old != null)
{
Destroy(old);
}
}
StartCoroutine(FadeAndDestroy(root));
}
private IEnumerator FadeAndDestroy(GameObject root)
{
CanvasGroup group = root.GetComponent<CanvasGroup>();
float age = 0f;
while (age < visibleSeconds)
{
age += Time.deltaTime;
if (group != null)
{
group.alpha = age < visibleSeconds - 0.45f ? 1f : Mathf.Clamp01((visibleSeconds - age) / 0.45f);
}
yield return null;
}
if (root != null)
{
Destroy(root);
}
}
private Text CreateText(Transform parent, string name, string value, int fontSize, TextAnchor anchor)
{
GameObject textObject = new GameObject(name, typeof(RectTransform), typeof(Text));
textObject.transform.SetParent(parent, false);
Text text = textObject.GetComponent<Text>();
UITheme.ApplyText(text, uiFont, fontSize, anchor);
text.text = value;
return text;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 891ff4dfb4fbfdc8cb2be806a1daa0b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+40
View File
@@ -0,0 +1,40 @@
using UnityEngine;
using UnityEngine.UI;
namespace OTGIntegrated.UI
{
public sealed class TooltipUI : MonoBehaviour
{
public GameObject root;
public Text contentText;
private void Awake()
{
if (root == null)
{
root = gameObject;
}
}
public void Show(string content)
{
if (root != null)
{
root.SetActive(true);
}
if (contentText != null)
{
contentText.text = content;
}
}
public void Hide()
{
if (root != null)
{
root.SetActive(false);
}
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2da5b49a75d8f18dab9d71dcfbfdb03b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+242
View File
@@ -0,0 +1,242 @@
using OTGIntegrated.Core;
using OTGIntegrated.Crafting;
using OTGIntegrated.Dialogue;
using OTGIntegrated.Inventory;
using OTGIntegrated.Player;
using OTGIntegrated.Quests;
using OTGIntegrated.Save;
using OTGIntegrated.Talents;
using UnityEngine;
using UnityEngine.UI;
namespace OTGIntegrated.UI
{
public enum UIWindowType
{
None,
Inventory,
Crafting,
QuestLog,
TalentTree,
Dialogue,
Pause
}
public sealed class UIManager : MonoBehaviour
{
public GameObject inventoryPanel;
public GameObject craftingPanel;
public GameObject questLogPanel;
public GameObject talentTreePanel;
public GameObject dialoguePanel;
public GameObject pausePanel;
public InventoryUI inventoryUI;
public CraftingUI craftingUI;
public QuestLogUI questLogUI;
public TalentTreeUI talentTreeUI;
public HUDController hudController;
public Button resumeButton;
public Button saveButton;
public Button loadButton;
public Button resetSaveButton;
private PlayerController playerController;
private UIWindowType currentWindow = UIWindowType.None;
public UIWindowType CurrentWindow => currentWindow;
public bool IsBlockingGameplay => currentWindow != UIWindowType.None;
public void Initialize(PlayerController controller)
{
playerController = controller;
GameManager gameManager = GameManager.Instance;
if (gameManager != null)
{
inventoryUI?.Initialize(gameManager.inventory);
craftingUI?.Initialize(gameManager.craftingSystem, gameManager.inventory);
questLogUI?.Initialize(gameManager.questManager);
talentTreeUI?.Initialize(gameManager.talentManager, gameManager.playerStats);
hudController?.Initialize(gameManager.playerStats, gameManager.levelSystem, gameManager.questManager, gameManager.abilityManager, gameManager.weaponManager);
}
if (resumeButton != null) resumeButton.onClick.AddListener(CloseCurrentWindow);
if (saveButton != null) saveButton.onClick.AddListener(() => FindObjectOfType<SaveSystem>()?.Save());
if (loadButton != null) loadButton.onClick.AddListener(() => FindObjectOfType<SaveSystem>()?.Load());
if (resetSaveButton != null) resetSaveButton.onClick.AddListener(() => FindObjectOfType<SaveSystem>()?.ResetSave());
HideAllPanels();
ApplyGameplayFocus();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.F5))
{
FindObjectOfType<SaveSystem>()?.Save();
}
if (Input.GetKeyDown(KeyCode.F9))
{
FindObjectOfType<SaveSystem>()?.Load();
}
if (Input.GetKeyDown(KeyCode.Backspace))
{
FindObjectOfType<SaveSystem>()?.ResetSave();
}
if (currentWindow == UIWindowType.Dialogue)
{
return;
}
if (Input.GetKeyDown(KeyCode.I)) ToggleInventory();
if (Input.GetKeyDown(KeyCode.C)) ToggleCrafting();
if (Input.GetKeyDown(KeyCode.J)) ToggleQuestLog();
if (Input.GetKeyDown(KeyCode.K)) ToggleTalentTree();
if (Input.GetKeyDown(KeyCode.Escape))
{
if (currentWindow == UIWindowType.None)
{
OpenPause();
}
else
{
CloseCurrentWindow();
}
}
}
public void ToggleInventory() => ToggleWindow(UIWindowType.Inventory);
public void ToggleCrafting() => ToggleWindow(UIWindowType.Crafting);
public void ToggleQuestLog() => ToggleWindow(UIWindowType.QuestLog);
public void ToggleTalentTree() => ToggleWindow(UIWindowType.TalentTree);
public void OpenCrafting() => OpenWindow(UIWindowType.Crafting);
public void OpenPause() => OpenWindow(UIWindowType.Pause);
public void OpenDialogue()
{
CloseCurrentWindow(false);
currentWindow = UIWindowType.Dialogue;
SetPanel(dialoguePanel, true);
ApplyWindowFocus();
}
public void CloseDialogue()
{
if (currentWindow == UIWindowType.Dialogue)
{
SetPanel(dialoguePanel, false);
currentWindow = UIWindowType.None;
ApplyGameplayFocus();
}
}
public void CloseCurrentWindow()
{
CloseCurrentWindow(true);
}
public void OpenWindow(UIWindowType windowType)
{
if (windowType == UIWindowType.None)
{
CloseCurrentWindow();
return;
}
CloseCurrentWindow(false);
currentWindow = windowType;
SetPanel(GetPanel(windowType), true);
ApplyWindowFocus();
}
private void ToggleWindow(UIWindowType windowType)
{
if (currentWindow == windowType)
{
CloseCurrentWindow();
}
else
{
OpenWindow(windowType);
}
}
private void CloseCurrentWindow(bool restoreGameplay)
{
SetPanel(GetPanel(currentWindow), false);
currentWindow = UIWindowType.None;
if (restoreGameplay)
{
ApplyGameplayFocus();
}
}
private GameObject GetPanel(UIWindowType windowType)
{
switch (windowType)
{
case UIWindowType.Inventory:
return inventoryPanel;
case UIWindowType.Crafting:
return craftingPanel;
case UIWindowType.QuestLog:
return questLogPanel;
case UIWindowType.TalentTree:
return talentTreePanel;
case UIWindowType.Dialogue:
return dialoguePanel;
case UIWindowType.Pause:
return pausePanel;
default:
return null;
}
}
private void HideAllPanels()
{
SetPanel(inventoryPanel, false);
SetPanel(craftingPanel, false);
SetPanel(questLogPanel, false);
SetPanel(talentTreePanel, false);
SetPanel(dialoguePanel, false);
SetPanel(pausePanel, false);
currentWindow = UIWindowType.None;
}
private void SetPanel(GameObject panel, bool active)
{
if (panel != null)
{
panel.SetActive(active);
}
}
private void ApplyWindowFocus()
{
if (playerController != null)
{
playerController.SetInputEnabled(false);
}
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
private void ApplyGameplayFocus()
{
if (playerController != null)
{
playerController.SetInputEnabled(true);
}
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ce956048a6d4346d9f0b97163cd08af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+29
View File
@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEngine.UI;
namespace OTGIntegrated.UI
{
public static class UITheme
{
public static readonly Color Panel = new Color(0.04f, 0.05f, 0.065f, 0.88f);
public static readonly Color PanelSoft = new Color(0.07f, 0.085f, 0.11f, 0.82f);
public static readonly Color Gold = new Color(0.92f, 0.68f, 0.24f, 1f);
public static readonly Color Blue = new Color(0.18f, 0.48f, 0.84f, 1f);
public static readonly Color Red = new Color(0.78f, 0.18f, 0.16f, 1f);
public static readonly Color Text = new Color(0.94f, 0.93f, 0.88f, 1f);
public static readonly Color MutedText = new Color(0.68f, 0.7f, 0.74f, 1f);
public static void ApplyText(Text text, Font font, int size, TextAnchor anchor)
{
if (text == null)
{
return;
}
text.font = font != null ? font : Resources.GetBuiltinResource<Font>("Arial.ttf");
text.fontSize = size;
text.alignment = anchor;
text.color = Text;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e0eff769ffbfc345287f21b9af9ef876
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: