using BulletHellTemplate.PVP; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace BulletHellTemplate { /// /// Manages game data and settings. /// public class GameInstance : MonoBehaviour { [Tooltip("Array of currency GameData.")] public List currencyData = new List(); [Tooltip("Array of character data scriptable objects.")] public CharacterData[] characterData; [Tooltip("Array of icon items.")] public IconItem[] iconItems; [Tooltip("Array of frame items.")] public FrameItem[] frameItems; [Tooltip("Array of inventory items.")] public InventoryItem[] inventoryItems; [Tooltip("List of all character types.")] public CharacterTypeData[] characterTypes; [Tooltip("Array of all map info data in the game.")] public MapInfoData[] mapInfoData; [Tooltip("Array of quest items.")] public QuestItem[] questData; [Tooltip("Array of coupon items.")] public CouponItem[] couponData; [Tooltip("Array of shop items.")] public ShopItem[] shopData; [Tooltip("Array of battle pass items.")] public BattlePassItem[] battlePassData; [Tooltip("Rewards for new players, typically one per day.")] public RewardItem[] newPlayerRewardItems; [Tooltip("All daily reward items for each day.")] public RewardItem[] dailyRewardItems; [Header("PVP")] public PvpModeData[] pvpModes; [Header("Password Length Rules")] [Tooltip("Minimum number of characters required in the password.")] public int minPasswordLength = 8; [Tooltip("Maximum number of characters allowed in the password.")] public int maxPasswordLength = 20; [Header("Password Complexity Requirements")] [Tooltip("Whether at least one uppercase letter is required.")] public bool requireUppercase = false; [Tooltip("Whether at least one lowercase letter is required.")] public bool requireLowercase = false; [Tooltip("Whether at least one numeric digit is required.")] public bool requireNumbers = false; [Tooltip("Whether at least one special character (!@#...) is required.")] public bool requireSpecial = false; [Header("Controller Reference")] [Tooltip("Reference to the main character entity.")] public CharacterEntity characterEntity; [Header("Account Levels")] [Tooltip("Information about account levels.")] public AccountLevel accountLevels; [Header("Character Mastery")] [Tooltip("Settings for character mastery.")] public CharacterMastery characterMastery; [Header("Mastery Levels")] [Tooltip("Array of mastery level details.")] public CharacterMasteryLevel[] masteryLevels; [Header("Elements Settings")] [Tooltip("Percentage increase in damage when advantage element.")] public float advantageDamageIncrease = 0.2f; [Tooltip("Percentage reduction in damage when weakness element.")] public float weaknessDamageReduction = 0.2f; [Header("BattlePass earn EXP for winning?")] [Tooltip("Battle Pass EXP awarded for winning a match")] public int BattlePassEXP = 210; public int battlePassDurationDays = 60; [Header("Map Name MainMenu")] [Tooltip("Name of the main menu scene")] public string mainMenuScene = "Home"; [Header("Currency earned in matches")] [Tooltip("Currency used in the game")] public string goldCurrency = "GO"; [Header("Battle Pass Settings")] public int maxLevelPass = 100; // Maximum level for the Battle Pass public float baseExpPass = 1000; public int SeasonLengthInDays = 30; [Header("0.1 = 10% more at each level // 1 = 100% more")] public float incPerLevelPass = 0.1f; public string battlePassCurrencyID = "DM"; // Currency ID for purchasing the Battle Pass public int battlePassPrice = 1000; // Price of the Battle Pass [Tooltip("Currency ID used for name change.")] public string changeNameTick = "TKN"; [Tooltip("Minimum length for the player's name.")] public int minNameLength = 3; [Tooltip("Maximum length for the player's name.")] public int maxNameLength = 20; [Tooltip("Number of tickets required to change the player's name.")] public int ticketsToChange = 1; [Tooltip("Indicates if a ticket is required for name change.")] public bool needTicket; [Header("Platform Selection")] [Tooltip("Specifies the platform type to determine the correct UIGameplay.")] public PlatformType platformType; public WorldType worldType = WorldType.World3D; [Header("Prefab References")] [Tooltip("Reference to the UIGameplay for mobile platform.")] public UIGameplay mobileGameplay; [Tooltip("Reference to the UIGameplay for desktop platform.")] public UIGameplay desktopGameplay; public static GameInstance Singleton; private bool isInitialized; /// /// Initializes the GameInstance as a singleton. /// private void Awake() { if (Singleton == null) { Singleton = this; DontDestroyOnLoad(gameObject); isInitialized = true; } else { Destroy(gameObject); } } /// /// Checks if the GameInstance has been initialized. /// /// True if initialized; otherwise, false. public bool IsInitialized() { return isInitialized; } /// /// Gets the CharacterData for the currently selected character. /// /// The matching CharacterData, or null if not found. public CharacterData GetCharacterData() { int selectedId = PlayerSave.GetSelectedCharacter(); var character = characterData.FirstOrDefault(c => c.characterId == selectedId); if (character == null) Debug.LogWarning($"CharacterData with ID '{selectedId}' not found."); return character; } /// /// Returns the character data that matches the given ID. /// public CharacterData GetCharacterDataById(int charId) => characterData.FirstOrDefault(c => c.characterId == charId); /// /// Returns the inventory item that matches the given ID. /// public InventoryItem GetInventoryItemById(string id) => inventoryItems.FirstOrDefault(i => i.itemId == id); /// /// Returns the quest item that matches the given ID. /// public QuestItem GetQuestItemById(int id) => questData.FirstOrDefault(q => q.questId == id); /// /// Returns the IconItem that matches the given ID. /// public IconItem GetIconItemById(string id) => iconItems.FirstOrDefault(i => i.iconId == id); /// /// Returns the FrameItem that matches the given ID. /// public FrameItem GetFrameItemById(string id) => frameItems.FirstOrDefault(f => f.frameId == id); /// /// Returns the CouponItem that matches the given ID. /// public CouponItem GetCouponItemById(string id) => couponData.FirstOrDefault(c => c.idCoupon == id); /// /// Returns the MapInfoData that matches the given ID. /// public MapInfoData GetMapInfoDataById(int mapId) => mapInfoData.FirstOrDefault(m => m.mapId == mapId); public PvpModeData GetPvpModeByKey(string key) { foreach (var m in pvpModes) if (m && m.GetModeKey() == key) return m; return null; } /// /// Gets the icon for a given character type. /// /// The character type data. /// The corresponding icon sprite. public Sprite GetIconForCharacterType(CharacterTypeData characterType) => characterType.icon; /// /// Gets the required account experience for a specific level. /// /// The account level (1-based index). /// The required experience, or -1 if level is invalid. public int GetAccountExpForLevel(int level) { if (accountLevels.accountExpPerLevel != null && level >= 1 && level <= accountLevels.accountExpPerLevel.Length) return accountLevels.accountExpPerLevel[level - 1]; Debug.LogWarning("Invalid account level requested."); return -1; } /// /// Gets the required mastery experience for a specific mastery level. /// /// The mastery level (0-based index). /// The required experience, or -1 if level is invalid. public int GetMasteryExpForLevel(int level) { if (characterMastery.masteryExpPerLevel != null && level >= 0 && level < characterMastery.masteryExpPerLevel.Length) return characterMastery.masteryExpPerLevel[level]; Debug.LogWarning("Invalid mastery level requested."); return 0; } /// /// Gets the mastery level details for a specific level. /// /// The mastery level (1-based index). /// The mastery level details, or a default value if level is invalid. public CharacterMasteryLevel GetMasteryLevel(int level) { if (masteryLevels != null && level >= 0 && level < masteryLevels.Length) { return masteryLevels[level]; } Debug.LogWarning("Invalid mastery level requested."); return masteryLevels[masteryLevels.Length - 1]; } /// /// Gets the UIGameplay instance based on the current platform. /// /// The corresponding UIGameplay instance for PC or Mobile. public UIGameplay GetUIGameplayForPlatform() { switch (platformType) { case PlatformType.PC: return desktopGameplay; case PlatformType.Mobile: return mobileGameplay; default: Debug.LogWarning("Unsupported platform type specified."); return null; } } /// /// Gets the current platform type. /// /// The platform type. public PlatformType GetCurrentPlatform() => platformType; public WorldType GetCurrentWorldType() => worldType; /// /// Automatically fills the mastery experience array based on the calculation method set in 'characterMastery.characterExpCalculationMethod'. /// public void AutoFillMasteryExp() { if (characterMastery.Equals(null)) { Debug.LogWarning("CharacterMastery is not assigned."); return; } ExpCalculationMethod method = characterMastery.characterExpCalculationMethod; int maxLevel = characterMastery.maxMasteryLevel; characterMastery.masteryExpPerLevel = new int[maxLevel]; for (int i = 0; i < maxLevel; i++) { int value = 0; float t = (maxLevel > 1) ? (float)i / (maxLevel - 1) : 0f; switch (method) { case ExpCalculationMethod.Linear: value = Mathf.RoundToInt(Mathf.Lerp(characterMastery.initialExp, characterMastery.finalExp, t)); break; case ExpCalculationMethod.Exponential: if (characterMastery.initialExp <= 0) characterMastery.initialExp = 1; value = Mathf.RoundToInt(characterMastery.initialExp * Mathf.Pow((float)characterMastery.finalExp / characterMastery.initialExp, t)); break; case ExpCalculationMethod.Custom: default: value = Mathf.RoundToInt(Mathf.Lerp(characterMastery.initialExp, characterMastery.finalExp, t)); break; } characterMastery.masteryExpPerLevel[i] = value; } } /// /// Calculates the total damage with elemental advantages and disadvantages. /// /// The element of the attacker. /// The element of the target. /// The base damage before elemental adjustments. /// The final damage after applying elemental adjustments. public float TotalDamageWithElements(CharacterTypeData attackerElement, CharacterTypeData targetElement, float baseDamage) { if (attackerElement == null || targetElement == null) return baseDamage; bool advantage = Array.Exists(attackerElement.strengths, e => e == targetElement) || Array.Exists(targetElement.weaknesses, e => e == attackerElement); bool disadvantage = Array.Exists(attackerElement.weaknesses, e => e == targetElement) || Array.Exists(targetElement.strengths, e => e == attackerElement); if (advantage && !disadvantage) return baseDamage * (1f + advantageDamageIncrease); if (disadvantage && !advantage) return baseDamage * (1f - weaknessDamageReduction); return baseDamage; } /// /// Automatically fills the account experience array based on the calculation method set in 'accountLevels.accountExpCalculationMethod'. /// public void AutoFillAccountExp() { if (accountLevels.Equals(null)) { Debug.LogWarning("AccountLevel is not assigned."); return; } ExpCalculationMethod method = accountLevels.accountExpCalculationMethod; int maxLevel = accountLevels.accountMaxLevel; accountLevels.accountExpPerLevel = new int[maxLevel]; for (int i = 0; i < maxLevel; i++) { int value = 0; float t = (maxLevel > 1) ? (float)i / (maxLevel - 1) : 0f; switch (method) { case ExpCalculationMethod.Linear: value = Mathf.RoundToInt(Mathf.Lerp(accountLevels.initialExp, accountLevels.finalExp, t)); break; case ExpCalculationMethod.Exponential: if (accountLevels.initialExp <= 0) accountLevels.initialExp = 1; value = Mathf.RoundToInt(accountLevels.initialExp * Mathf.Pow((float)accountLevels.finalExp / accountLevels.initialExp, t)); break; case ExpCalculationMethod.Custom: default: value = Mathf.RoundToInt(Mathf.Lerp(accountLevels.initialExp, accountLevels.finalExp, t)); break; } accountLevels.accountExpPerLevel[i] = value; } } } /// /// Represents the available platform types. /// public enum PlatformType { Mobile, PC } [System.Serializable] public struct AccountLevel { [Tooltip("Maximum account level.")] public int accountMaxLevel; [Tooltip("Initial EXP for account leveling.")] public int initialExp; [Tooltip("Final EXP for the highest account level.")] public int finalExp; [Tooltip("Selected calculation method for account EXP progression.")] public ExpCalculationMethod accountExpCalculationMethod; [Tooltip("EXP required per account level.")] public int[] accountExpPerLevel; } /// /// Represents the character mastery settings. /// [System.Serializable] public struct CharacterMastery { [Tooltip("Maximum mastery level.")] public int maxMasteryLevel; [Tooltip("Initial EXP for mastery leveling.")] public int initialExp; [Tooltip("Final EXP for the highest mastery level.")] public int finalExp; [Tooltip("Selected calculation method for mastery EXP progression.")] public ExpCalculationMethod characterExpCalculationMethod; [Tooltip("Experience points required per mastery level.")] public int[] masteryExpPerLevel; } /// /// Represents the details for a specific mastery level. /// [System.Serializable] public struct CharacterMasteryLevel { [Tooltip("Name of the mastery level.")] public string masteryName; [Tooltip("Translated name of the mastery level.")] public NameTranslatedByLanguage[] masteryNameTranslated; [Tooltip("Icon representing the mastery level.")] public Sprite masteryIcon; } /// /// Contains translation data for a name by language. /// [System.Serializable] public struct NameTranslatedByLanguage { public string LanguageId; public string Translate; } /// /// Contains translation data for a description by language. /// [System.Serializable] public struct DescriptionTranslatedByLanguage { public string LanguageId; [TextArea] public string Translate; } /// /// Methods for calculating experience progression. /// [System.Serializable] public enum ExpCalculationMethod { Linear, Exponential, Custom } [System.Serializable] public enum WorldType { World3D, World2D } }