214 lines
8.1 KiB
C#
214 lines
8.1 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.Networking;
|
|||
|
|
using Newtonsoft.Json;
|
|||
|
|
using System.Linq;
|
|||
|
|
|
|||
|
|
public class MANAGER_Mail : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
public static MANAGER_Mail Instance { get; private set; }
|
|||
|
|
|
|||
|
|
private const string MAIL_URL = "http://mc1.live-on.pro:64093/api/mail/mail.json";
|
|||
|
|
private const string REWARDS_URL = "http://mc1.live-on.pro:64093/api/other/rewards_table_id.json";
|
|||
|
|
|
|||
|
|
// Данные сервера
|
|||
|
|
public List<MailEntry> ActiveMails { get; private set; } = new List<MailEntry>();
|
|||
|
|
public Dictionary<string, List<RewardItem>> RewardsTable { get; private set; } = new Dictionary<string, List<RewardItem>>();
|
|||
|
|
|
|||
|
|
// Состояние инициализации
|
|||
|
|
public bool IsDataLoaded { get; private set; } = false;
|
|||
|
|
|
|||
|
|
private void Awake()
|
|||
|
|
{
|
|||
|
|
if (Instance == null)
|
|||
|
|
{
|
|||
|
|
Instance = this;
|
|||
|
|
DontDestroyOnLoad(gameObject);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Destroy(gameObject);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Start()
|
|||
|
|
{
|
|||
|
|
StartCoroutine(LoadMailSystemData());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private IEnumerator LoadMailSystemData()
|
|||
|
|
{
|
|||
|
|
IsDataLoaded = false;
|
|||
|
|
|
|||
|
|
// 1. Загрузка конфигурации писем
|
|||
|
|
UnityWebRequest mailReq = UnityWebRequest.Get(MAIL_URL);
|
|||
|
|
yield return mailReq.SendWebRequest();
|
|||
|
|
|
|||
|
|
if (mailReq.result != UnityWebRequest.Result.Success)
|
|||
|
|
{
|
|||
|
|
yield break; // В реальном проекте здесь нужна логика ретрая
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. Загрузка таблицы наград
|
|||
|
|
UnityWebRequest rewardsReq = UnityWebRequest.Get(REWARDS_URL);
|
|||
|
|
yield return rewardsReq.SendWebRequest();
|
|||
|
|
|
|||
|
|
if (rewardsReq.result != UnityWebRequest.Result.Success)
|
|||
|
|
{
|
|||
|
|
yield break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. Десериализация
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 1. Десериализуем во временную обертку
|
|||
|
|
var wrapper = JsonConvert.DeserializeObject<MailDataWrapper>(mailReq.downloadHandler.text);
|
|||
|
|
|
|||
|
|
// 2. Присваиваем список из обертки в ActiveMails
|
|||
|
|
ActiveMails = wrapper != null ? wrapper.mails : new List<MailEntry>();
|
|||
|
|
|
|||
|
|
// Для наград (если там такая же структура с корневым объектом, проверьте rewards_table_id.json)
|
|||
|
|
RewardsTable = JsonConvert.DeserializeObject<Dictionary<string, List<RewardItem>>>(rewardsReq.downloadHandler.text);
|
|||
|
|
|
|||
|
|
IsDataLoaded = true;
|
|||
|
|
MANAGER_GameEvents.TriggerUIUpdate();
|
|||
|
|
}
|
|||
|
|
catch (System.Exception e)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public DateTime GetCurrentTime()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// Пытаемся получить локальное время устройства
|
|||
|
|
return DateTime.Now;
|
|||
|
|
}
|
|||
|
|
catch
|
|||
|
|
{
|
|||
|
|
// Если по какой-то причине на Linux или другой ОС произошел сбой,
|
|||
|
|
// берем UTC и прибавляем 7 часов (Красноярск)
|
|||
|
|
return DateTime.UtcNow.AddHours(7);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool IsMailDeleted(string mailId)
|
|||
|
|
{
|
|||
|
|
return PlayerPrefs.GetInt($"Mail_Deleted_{mailId}", 0) == 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void DeleteMail(string mailId)
|
|||
|
|
{
|
|||
|
|
PlayerPrefs.SetInt($"Mail_Deleted_{mailId}", 1);
|
|||
|
|
PlayerPrefs.Save();
|
|||
|
|
MANAGER_GameEvents.TriggerUIUpdate(); // Обновляем список, чтобы письмо исчезло
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// --- ЛОГИКА СОСТОЯНИЙ (ПРОЧИТАНО / ЗАБРАНО) ---
|
|||
|
|
|
|||
|
|
public bool IsMailRead(string mailId)
|
|||
|
|
{
|
|||
|
|
return PlayerPrefs.GetInt($"Mail_Read_{mailId}", 0) == 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void MarkAsRead(string mailId)
|
|||
|
|
{
|
|||
|
|
if (!IsMailRead(mailId))
|
|||
|
|
{
|
|||
|
|
PlayerPrefs.SetInt($"Mail_Read_{mailId}", 1);
|
|||
|
|
PlayerPrefs.Save();
|
|||
|
|
|
|||
|
|
// Отправляем сигнал для визуального снятия метки "Новое"
|
|||
|
|
MANAGER_GameEvents.TriggerUIUpdate();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Добавьте это в MANAGER_Mail.cs
|
|||
|
|
public string GetFormattedBody(MailEntry mail)
|
|||
|
|
{
|
|||
|
|
if (mail == null || mail.content == null) return "";
|
|||
|
|
|
|||
|
|
string body = mail.content.body;
|
|||
|
|
|
|||
|
|
// Проверяем наличие плейсхолдера
|
|||
|
|
if (body.Contains("{today_reward}"))
|
|||
|
|
{
|
|||
|
|
string rewardText = "";
|
|||
|
|
|
|||
|
|
// Проверяем, есть ли ID таблицы наград и загружена ли она
|
|||
|
|
if (!string.IsNullOrEmpty(mail.rewards_table_id) && RewardsTable.TryGetValue(mail.rewards_table_id, out var rewards))
|
|||
|
|
{
|
|||
|
|
// Собираем строку наград (например: "Золото x15, Меч x1")
|
|||
|
|
// Здесь r.name — это то самое поле, которое мы договорились добавить в RewardItem
|
|||
|
|
rewardText = string.Join(", ", rewards.Select(r => $"[g]x{r.amount} {r.name}[/c]"));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// Если награда не найдена, выводим заглушку или пустую строку
|
|||
|
|
rewardText = "награды отсутствуют";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body = body.Replace("{today_reward}", rewardText);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return body;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private const int TEST_COOLDOWN_SECONDS = 86400;
|
|||
|
|
|
|||
|
|
public void ClaimReward(string mailId)
|
|||
|
|
{
|
|||
|
|
if (IsRewardClaimed(mailId)) return;
|
|||
|
|
|
|||
|
|
MailEntry mail = ActiveMails.FirstOrDefault(m => m.id == mailId);
|
|||
|
|
if (mail != null)
|
|||
|
|
{
|
|||
|
|
// Сохраняем текущее время в формате Total Seconds
|
|||
|
|
double totalSecondsNow = GetCurrentTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
|||
|
|
PlayerPrefs.SetString($"Mail_LastClaimedTime_{mailId}", totalSecondsNow.ToString());
|
|||
|
|
|
|||
|
|
PlayerPrefs.Save();
|
|||
|
|
MarkAsRead(mailId);
|
|||
|
|
MANAGER_GameEvents.TriggerUIUpdate();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool IsRewardClaimed(string mailId)
|
|||
|
|
{
|
|||
|
|
string lastClaimedStr = PlayerPrefs.GetString($"Mail_LastClaimedTime_{mailId}", "0");
|
|||
|
|
double lastClaimedSeconds = double.Parse(lastClaimedStr);
|
|||
|
|
|
|||
|
|
double currentSeconds = GetCurrentTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
|||
|
|
|
|||
|
|
// Письмо "забрано", если с момента последнего клика прошло МЕНЬШЕ 60 секунд
|
|||
|
|
return (currentSeconds - lastClaimedSeconds) < TEST_COOLDOWN_SECONDS;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public List<MailEntry> GetAvailableMails()
|
|||
|
|
{
|
|||
|
|
return ActiveMails
|
|||
|
|
.Where(mail =>
|
|||
|
|
{
|
|||
|
|
// Базовая фильтрация: удаленные и не подходящие по дате
|
|||
|
|
if (IsMailDeleted(mail.id)) return false;
|
|||
|
|
|
|||
|
|
DateTime now = GetCurrentTime();
|
|||
|
|
if (mail.logic.StartDate.HasValue && now < mail.logic.StartDate.Value) return false;
|
|||
|
|
if (mail.logic.EndDate.HasValue && now > mail.logic.EndDate.Value) return false;
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
})
|
|||
|
|
/* СОРТИРОВКА:
|
|||
|
|
1. Сначала считаем приоритет:
|
|||
|
|
Если награда ЗАБРАНА (IsRewardClaimed == true), ставим -99.
|
|||
|
|
Если награда ДОСТУПНА, ставим 0 (или можно брать mail.priority, если он есть в классе).
|
|||
|
|
*/
|
|||
|
|
.OrderByDescending(mail => IsRewardClaimed(mail.id) ? -99 : 0)
|
|||
|
|
// 2. Внутри групп (сначала новые/доступные) сортируем по дате начала
|
|||
|
|
.ThenByDescending(mail => mail.logic.StartDate)
|
|||
|
|
.ToList();
|
|||
|
|
}
|
|||
|
|
}
|