352 lines
15 KiB
C#

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace BulletHellTemplate
{
/// <summary>
/// Manages the display of quests in the UI.
/// Displays available quests, handles quest completion logic,
/// and can optionally hide completed quests.
/// </summary>
public class UIQuestMenu : MonoBehaviour
{
#region Singleton and Public Fields
[Tooltip("Prefab for individual quest entries")]
public QuestEntry questEntryPrefab;
[Tooltip("Container where quest entries will be instantiated")]
public Transform container;
/// <summary>
/// Singleton reference for global access to UIQuestMenu.
/// </summary>
public static UIQuestMenu Singleton;
[Tooltip("Whether or not to hide completed quests from the UI")]
public bool hideCompletedQuests;
/// <summary>
/// Current language code or identifier used by the UI.
/// </summary>
[HideInInspector]
public string currentLang;
#endregion
#region Unity Callbacks
/// <summary>
/// Initializes the singleton reference on startup.
/// </summary>
private void Start()
{
Singleton = this;
}
/// <summary>
/// Called when the object becomes enabled and active.
/// Sets the current language and loads quests into the UI.
/// </summary>
private void OnEnable()
{
currentLang = LanguageManager.LanguageManager.Instance.GetCurrentLanguage();
LoadQuests();
}
#endregion
#region Public Methods
/// <summary>
/// Loads and displays the available quests in the UI.
/// If 'hideCompletedQuests' is true, completed quests (except repeat) will be skipped.
/// Otherwise, all quests are shown, with an indicator for those that are completed.
/// Additionally, it checks if the player's level or characters' levels
/// exceed any quest requirements to set progress to 100%.
/// </summary>
public void LoadQuests()
{
// Clear existing children in the container to avoid duplication
foreach (Transform child in container)
{
Destroy(child.gameObject);
}
// Retrieve all available quests from the game instance
List<QuestItem> allQuests = GameInstance.Singleton.questData.ToList();
// Adjust progress for level-based quests (but don't auto-complete)
SetLevelBasedQuestsToMaxProgress(allQuests);
// Display each quest
foreach (QuestItem quest in allQuests)
{
// If quest is not repeat and is completed, skip if hideCompletedQuests is true
bool isCompleted = PlayerSave.IsQuestCompleted(quest.questId);
if (hideCompletedQuests && quest.questType != QuestType.Repeat && isCompleted)
{
continue;
}
// Instantiate a new QuestEntry
QuestEntry newQuestEntry = Instantiate(questEntryPrefab, container);
// Translate title and description
string questTitleTranslated = GetTranslatedString(quest.titleTranslated, quest.title, currentLang);
string questDescriptionTranslated = GetTranslatedString(quest.descriptionTranslated, quest.description, currentLang);
// Set up the QuestEntry
newQuestEntry.Setup(
questTitleTranslated,
questDescriptionTranslated,
quest,
isCompleted
);
// Load current progress from player data
int currentProgress = PlayerSave.LoadQuestProgress(quest.questId);
// Retrieve target amount from quest requirements
int targetAmount = quest.requirement.targetAmount;
// Update the progress bar and button interactability
newQuestEntry.UpdateProgress(currentProgress, targetAmount);
}
}
/// <summary>
/// Called by the QuestEntry when the player tries to complete the quest.
/// Checks if the quest is indeed completable, then applies rewards.
/// Marks the quest as completed or resets its progress if it's a repeat quest.
/// Finally, reloads the quest UI for updated feedback.
/// </summary>
/// <param name="quest">The quest item the player is attempting to complete.</param>
public void OnUserAttemptCompleteQuest(QuestItem quest)
{
if (quest == null) return;
// Check if already completed (or if it's repeat, we don't care, we keep resetting)
bool isQuestCompleted = PlayerSave.IsQuestCompleted(quest.questId);
if (isQuestCompleted && quest.questType != QuestType.Repeat)
{
Debug.Log("Quest is already completed.");
return;
}
// Verify if progress is sufficient
int currentProgress = PlayerSave.LoadQuestProgress(quest.questId);
int targetAmount = quest.requirement.targetAmount;
if (currentProgress < targetAmount)
{
Debug.Log($"Quest '{quest.title}' is not yet complete. Current progress: {currentProgress}/{targetAmount}.");
return;
}
// Progress is enough to conclude
ApplyQuestReward(quest);
// If it's a repeat quest, reset progress to 0
if (quest.questType == QuestType.Repeat)
{
PlayerSave.SaveQuestProgress(quest.questId, 0);
}
else
{
// Mark quest as permanently completed
PlayerSave.SaveQuestCompletion(quest.questId);
}
// Reload the UI to show updated quest states
LoadQuests();
}
/// <summary>
/// Applies the quest's direct EXP and currency to the player,
/// then unlocks items via the BattlePass system if necessary.
/// This includes: AccountExp, BattlePassExp, and CharacterExp
/// for the currently selected character if 'selectedCharacterExp' > 0.
/// </summary>
/// <param name="questItem">The quest to apply rewards from.</param>
public void ApplyQuestReward(QuestItem questItem)
{
if (questItem == null) return;
// Add account EXP (if > 0)
if (questItem.accountExp > 0)
{
GameManager.Singleton.AddAccountExp(questItem.accountExp);
}
// Add battle pass EXP (if > 0)
if (questItem.battlePassExp > 0)
{
GameManager.Singleton.AddBattlePassExp(questItem.battlePassExp);
}
// Add character EXP to the currently selected character (if > 0)
if (questItem.selectedCharacterExp > 0)
{
int selectedCharId = PlayerSave.GetSelectedCharacter();
GameManager.Singleton.AddCharacterExp(selectedCharId, questItem.selectedCharacterExp);
GameManager.Singleton.SaveCharacterBasicInfo(selectedCharId);
}
// Add currency
if (questItem.currencyAmount > 0 && !string.IsNullOrEmpty(questItem.currencyReward))
{
int currentCurrency = MonetizationManager.GetCurrency(questItem.currencyReward);
currentCurrency += questItem.currencyAmount;
MonetizationManager.SetCurrency(questItem.currencyReward, currentCurrency);
}
// Unlock item reward if any (CharacterReward, IconReward, FrameReward, or ItemReward)
if (questItem.questReward != QuestReward.None)
{
BattlePassItem tempBattlePassItem = ScriptableObject.CreateInstance<BattlePassItem>();
// Basic info
tempBattlePassItem.passId = "Quest_" + questItem.questId.ToString();
tempBattlePassItem.itemTitle = questItem.title;
tempBattlePassItem.itemIcon = questItem.icon;
tempBattlePassItem.rewardTier = BattlePassItem.RewardTier.Free; // or Paid, if desired
switch (questItem.questReward)
{
case QuestReward.CharacterReward:
tempBattlePassItem.rewardType = BattlePassItem.RewardType.CharacterReward;
tempBattlePassItem.characterData = new CharacterData[] { questItem.characterData };
break;
case QuestReward.IconReward:
tempBattlePassItem.rewardType = BattlePassItem.RewardType.IconReward;
tempBattlePassItem.iconReward = questItem.iconItem;
break;
case QuestReward.FrameReward:
tempBattlePassItem.rewardType = BattlePassItem.RewardType.FrameReward;
tempBattlePassItem.frameReward = questItem.frameItem;
break;
case QuestReward.ItemReward:
tempBattlePassItem.rewardType = BattlePassItem.RewardType.InventoryItemReward;
tempBattlePassItem.inventoryItems = new InventoryItem[] { questItem.inventoryItem };
break;
}
// Unlock via MonetizationManager
MonetizationManager.Singleton.BattlePassUnlockItem(tempBattlePassItem);
}
}
/// <summary>
/// Sets any level-based quests' progress to 100% if the player or their characters
/// already meet or exceed the required level, but does not complete them automatically.
/// </summary>
/// <param name="allQuests">List of all available quests.</param>
public void SetLevelBasedQuestsToMaxProgress(List<QuestItem> allQuests)
{
if (allQuests == null) return;
int accountLevel = PlayerSave.GetAccountLevel();
// Create a lookup for character levels
Dictionary<int, int> characterLevels = new Dictionary<int, int>();
foreach (var charData in GameInstance.Singleton.characterData)
{
int level = PlayerSave.GetCharacterLevel(charData.characterId);
characterLevels[charData.characterId] = level;
}
foreach (var quest in allQuests)
{
if (quest == null) continue;
// If it's completed (and not repeat) skip
if (PlayerSave.IsQuestCompleted(quest.questId) && quest.questType != QuestType.Repeat)
continue;
QuestRequirementType reqType = quest.requirement.requirementType;
// We only care about LevelUpAccount or LevelUpCharacter
if (reqType == QuestRequirementType.LevelUpAccount)
{
// If player account level >= required target
if (accountLevel >= quest.requirement.targetAmount)
{
// Just set progress to the target amount (not completing automatically)
PlayerSave.SaveQuestProgress(quest.questId, quest.requirement.targetAmount);
}
}
else if (reqType == QuestRequirementType.LevelUpCharacter)
{
if (quest.requirement.targetCharacter != null)
{
int targetCharId = quest.requirement.targetCharacter.characterId;
if (characterLevels.ContainsKey(targetCharId))
{
int currentCharLevel = characterLevels[targetCharId];
if (currentCharLevel >= quest.requirement.targetAmount)
{
// Set progress to target
PlayerSave.SaveQuestProgress(quest.questId, quest.requirement.targetAmount);
}
}
}
}
}
}
/// <summary>
/// Returns a translated string according to the current language
/// or a fallback string if no translation is found.
/// </summary>
/// <param name="translations">An array of translations for different languages.</param>
/// <param name="fallback">The default (fallback) string if no matching translation is found.</param>
/// <param name="currentLang">The language code or identifier currently in use.</param>
/// <returns>A string in the user's selected language, or the fallback if none is found.</returns>
public string GetTranslatedString(NameTranslatedByLanguage[] translations, string fallback, string currentLang)
{
if (translations != null)
{
foreach (var trans in translations)
{
if (!string.IsNullOrEmpty(trans.LanguageId)
&& trans.LanguageId.Equals(currentLang)
&& !string.IsNullOrEmpty(trans.Translate))
{
return trans.Translate;
}
}
}
return fallback;
}
/// <summary>
/// Returns a translated description string according to the current language
/// or a fallback string if no translation is found.
/// </summary>
/// <param name="translations">An array of description translations for different languages.</param>
/// <param name="fallback">The default (fallback) string if no matching translation is found.</param>
/// <param name="currentLang">The language code or identifier currently in use.</param>
/// <returns>A string in the user's selected language, or the fallback if none is found.</returns>
public string GetTranslatedString(DescriptionTranslatedByLanguage[] translations, string fallback, string currentLang)
{
if (translations != null)
{
foreach (var trans in translations)
{
if (!string.IsNullOrEmpty(trans.LanguageId)
&& trans.LanguageId.Equals(currentLang)
&& !string.IsNullOrEmpty(trans.Translate))
{
return trans.Translate;
}
}
}
return fallback;
}
#endregion
}
}