636 lines
22 KiB
C#
636 lines
22 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using UnityEngine;
|
||
using TcgEngine;
|
||
using TcgEngine.Gameplay;
|
||
using TcgEngine.Client;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace TcgEngine.Gameplay
|
||
{
|
||
/// <summary>
|
||
/// 任务管理器,负责任务的分配、进度追踪和奖励发放等功能
|
||
/// </summary>
|
||
public class TaskManager : MonoBehaviour
|
||
{
|
||
public static TaskManager Instance;
|
||
|
||
[Header("Player Data")]
|
||
public List<PlayerTask> playerTasks = new List<PlayerTask>();
|
||
|
||
public long lastDailyTaskAssigned = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
public int maxTasks = 5; // 玩家最多持有任务数
|
||
|
||
private GameLogic gameLogic;
|
||
private System.Random random = new System.Random();
|
||
|
||
public Action<List<PlayerTask>> refreshTaskUI;
|
||
|
||
private GameClient gameClient;
|
||
|
||
public string userID;
|
||
|
||
private void Awake()
|
||
{
|
||
|
||
if (Instance == null)
|
||
{
|
||
Instance = this;
|
||
DontDestroyOnLoad(gameObject);
|
||
}
|
||
else
|
||
{
|
||
Destroy(gameObject);
|
||
}
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
userID = ApiClient.Get().UserID;
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
// 订阅游戏事件
|
||
gameClient = FindFirstObjectByType<GameClient>();
|
||
gameClient = GameClient.Get();
|
||
if (gameClient != null)
|
||
{
|
||
LoadTasks();
|
||
LoadPlayerData();
|
||
gameClient.onGameStart += OnGameStart;
|
||
gameClient.onGameEnd += OnGameEnd;
|
||
// gameClient.onConnectServer?.Invoke();
|
||
}
|
||
|
||
// 移除对GameLogic.Instance的错误引用,改为检查gameLogic变量
|
||
if (gameLogic != null)
|
||
{
|
||
SubscribeToGameEvents();
|
||
}
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
// 取消订阅游戏事件
|
||
gameClient = FindFirstObjectByType<GameClient>();
|
||
gameClient = GameClient.Get();
|
||
if (gameClient != null)
|
||
{
|
||
gameClient.onGameStart -= OnGameStart;
|
||
gameClient.onGameEnd -= OnGameEnd;
|
||
}
|
||
|
||
// 移除对GameLogic.Instance的错误引用,改为检查gameLogic变量
|
||
if (gameLogic != null)
|
||
{
|
||
UnsubscribeFromGameEvents();
|
||
}
|
||
}
|
||
|
||
public void SetGameLogic(GameLogic logic)
|
||
{
|
||
gameLogic = logic;
|
||
SubscribeToGameEvents();
|
||
}
|
||
|
||
private void SubscribeToGameEvents()
|
||
{
|
||
if (gameLogic != null)
|
||
{
|
||
gameLogic.onCardPlayed += OnCardPlayed;
|
||
gameLogic.onCardSummoned += OnCardSummoned;
|
||
gameLogic.onAbilityStart += OnAbilityUsed;
|
||
gameLogic.onCardDiscarded += OnCardDiscarded; // 用于检测击败英雄
|
||
}
|
||
}
|
||
|
||
private void UnsubscribeFromGameEvents()
|
||
{
|
||
if (gameLogic != null)
|
||
{
|
||
gameLogic.onCardPlayed -= OnCardPlayed;
|
||
gameLogic.onCardSummoned -= OnCardSummoned;
|
||
gameLogic.onAbilityStart -= OnAbilityUsed;
|
||
gameLogic.onCardDiscarded -= OnCardDiscarded;
|
||
}
|
||
}
|
||
|
||
private async void LoadTasks()
|
||
{
|
||
// 从服务器加载所有任务配置
|
||
await LoadTasksFromServer();
|
||
}
|
||
|
||
private async Task LoadTasksFromServer()
|
||
{
|
||
if (ApiClient.Get() != null && ApiClient.Get().IsLoggedIn())
|
||
{
|
||
// 从服务器API获取任务配置
|
||
string url = ApiClient.ServerURL + "/api/tasks";
|
||
WebResponse res = await ApiClient.Get().SendGetRequest(url);
|
||
Debug.Log($"<color=red>LoadTasksFromServer:{url}</color>");
|
||
Debug.LogError("从服务器API获取任务配置" + res.data + res.status);
|
||
Debug.LogError(res.success);
|
||
if (res.success)
|
||
{
|
||
// 解析任务配置数据
|
||
try
|
||
{
|
||
TaskDataResponse[] taskResponses = ApiTool.JsonToObject<TaskDataResponse[]>(res.data);
|
||
Debug.Log("<color=red>Loaded </color>" + taskResponses.Length + " tasks from server");
|
||
// 在实际项目中,这里应该将服务器数据转换为TaskData对象并存储在内存中
|
||
// 供后续使用,而不是每次都从Resources加载
|
||
|
||
foreach (var item in taskResponses)
|
||
{
|
||
Debug.Log($"<color=pink>{item.id}</color>");
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError("Failed to parse task data from server: " + e.Message);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("Failed to load tasks from server: " + res.error);
|
||
}
|
||
}
|
||
|
||
// 如果无法从服务器加载,则使用本地Resources作为后备
|
||
TaskData[] localTasks = Resources.LoadAll<TaskData>("Tasks");
|
||
Debug.Log("Loaded " + localTasks.Length + " tasks from local resources as fallback");
|
||
}
|
||
|
||
private async void LoadPlayerData()
|
||
{
|
||
// 从服务器加载玩家任务数据
|
||
await LoadPlayerTasksFromServer();
|
||
}
|
||
|
||
private async Task LoadPlayerTasksFromServer()
|
||
{
|
||
if (ApiClient.Get() != null && ApiClient.Get().IsLoggedIn())
|
||
{
|
||
// 从服务器获取玩家任务数据
|
||
string url = ApiClient.ServerURL + $"/api/tasks/"+ApiClient.Get().UserID;
|
||
WebResponse res = await ApiClient.Get().SendGetRequest(url);
|
||
|
||
Debug.Log($"<color=red>LoadPlayerTasksFromServer:{url}</color>");
|
||
Debug.LogWarning($"从服务器获取玩家任务数据res_data--:{res.data},{res.status}");
|
||
if (res.success)
|
||
{
|
||
// 解析玩家任务数据
|
||
try
|
||
{
|
||
PlayerTasksResponse response = ApiTool.JsonToObject<PlayerTasksResponse>(res.data);
|
||
lastDailyTaskAssigned = response.lastDailyTaskAssigned;
|
||
|
||
// 清除现有任务
|
||
playerTasks.Clear();
|
||
|
||
// 转换并添加任务
|
||
foreach (var taskResponse in response.tasks)
|
||
{
|
||
PlayerTask playerTask = new PlayerTask(taskResponse);
|
||
playerTasks.Add(playerTask);
|
||
Debug.Log(playerTask);
|
||
}
|
||
|
||
Debug.Log("Player tasks loaded from server: " + playerTasks.Count + " tasks");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError("Failed to parse player tasks from server: " + e.Message);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("Failed to load player tasks from server: " + res.error);
|
||
}
|
||
}
|
||
|
||
// 简化处理,首次登录时分配任务
|
||
if (playerTasks.Count == 0)
|
||
{
|
||
AssignDailyTaskIfNeeded();
|
||
}
|
||
}
|
||
|
||
public async void SavePlayerData()
|
||
{
|
||
// 将玩家任务数据保存到服务器
|
||
if (ApiClient.Get() != null)
|
||
{
|
||
// 准备要发送的数据
|
||
PlayerTasksResponse saveData = new PlayerTasksResponse();
|
||
// 转换任务数据
|
||
PlayerTaskResponse[] taskResponses = new PlayerTaskResponse[playerTasks.Count];
|
||
for (int i = 0; i < playerTasks.Count; i++)
|
||
{
|
||
taskResponses[i] = playerTasks[i].ToResponse();
|
||
Debug.Log($"数据[{i}]:{taskResponses[i].taskId}");
|
||
}
|
||
|
||
foreach (PlayerTaskResponse item in taskResponses)
|
||
{
|
||
Debug.Log($"<color=red>id:{item.taskId}" +
|
||
$"开始时间:{item.assignedTime}" +
|
||
$"结束时间:{item.expireTime}" +
|
||
$"状态:{item.status}" +
|
||
$"进度:{item.progress}" +
|
||
$"</color>");
|
||
}
|
||
|
||
|
||
Debug.Log($"<color=red>{taskResponses.Length}</color>");
|
||
|
||
saveData.tasks = taskResponses;
|
||
saveData.lastDailyTaskAssigned = lastDailyTaskAssigned;
|
||
|
||
Debug.Log($"saveData.tasks---{saveData.tasks.Length}");
|
||
|
||
string json = ApiTool.ToJson(saveData);
|
||
string url = ApiClient.ServerURL + $"/api/tasks/{userID}";
|
||
WebResponse res = await ApiClient.Get().SendPostRequest(url, json);
|
||
|
||
Debug.Log(json);
|
||
|
||
Debug.Log($"<color=red>SavePlayerData:{url}</color>");
|
||
|
||
if (res.success)
|
||
{
|
||
Debug.Log("玩家任务已保存到服务器");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("未能将玩家任务保存到服务器:" + res.error+"错误码:"+res.status);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 每日任务分配
|
||
public void AssignDailyTaskIfNeeded()
|
||
{
|
||
long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
long secondsSinceLastTask = now - lastDailyTaskAssigned;
|
||
|
||
// 转换为小时
|
||
double hoursSinceLastTask = secondsSinceLastTask / 3600.0;
|
||
|
||
if (hoursSinceLastTask < 24)
|
||
return;
|
||
|
||
// 检查玩家是否已达到最大任务数
|
||
if (playerTasks.Count >= maxTasks)
|
||
return;
|
||
|
||
// 获取所有每日任务
|
||
TaskData[] allTasks = Resources.LoadAll<TaskData>("Tasks");
|
||
var dailyTasks = allTasks
|
||
.Where(t => t.isDailyTask &&
|
||
t.condition != 0 &&
|
||
!string.IsNullOrEmpty(t.id))
|
||
.ToArray();
|
||
|
||
if (dailyTasks.Length == 0)
|
||
return;
|
||
|
||
// 避免重复分配相同任务
|
||
int maxRetries = 3;
|
||
TaskData selectedTask = null;
|
||
HashSet<string> triedTasks = new HashSet<string>();
|
||
|
||
while (maxRetries-- > 0 && selectedTask == null)
|
||
{
|
||
// 随机选择一个任务
|
||
TaskData candidateTask = dailyTasks[random.Next(dailyTasks.Length)];
|
||
|
||
// 如果任务已经尝试过则跳过
|
||
if (triedTasks.Contains(candidateTask.id))
|
||
continue;
|
||
|
||
triedTasks.Add(candidateTask.id);
|
||
|
||
// 检查任务是否有效(未被玩家完成过或未在进度中)
|
||
if (!playerTasks.Any(pt => pt.taskId == candidateTask.id))
|
||
{
|
||
selectedTask = candidateTask;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果没有找到合适的任务则返回
|
||
if (selectedTask == null)
|
||
{
|
||
Debug.LogWarning("Failed to assign daily task after multiple retries");
|
||
return;
|
||
}
|
||
|
||
// 创建玩家任务实例
|
||
PlayerTask playerTask = new PlayerTask(selectedTask);
|
||
playerTasks.Add(playerTask);
|
||
|
||
lastDailyTaskAssigned = now;
|
||
SavePlayerData();
|
||
|
||
Debug.Log($"分配任务: {selectedTask.name}");
|
||
}
|
||
|
||
// 检查并更新过期任务
|
||
public void CheckExpiredTasks()
|
||
{
|
||
long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); // 当前时间戳(秒)
|
||
foreach (var task in playerTasks)
|
||
{
|
||
if (task.status == TaskStatus.Active && now > task.expireTime)
|
||
{
|
||
task.status = TaskStatus.Expired;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查任务是否完成
|
||
public bool CheckTaskCompletion(PlayerTask task)
|
||
{
|
||
TaskData taskConfig = GetTaskConfig(task.taskId);
|
||
if (taskConfig == null)
|
||
return false;
|
||
|
||
return task.progress >= taskConfig.value1;
|
||
}
|
||
|
||
// 领取任务奖励
|
||
public bool ClaimTaskReward(PlayerTask task)
|
||
{
|
||
if (task.status != TaskStatus.Completed)
|
||
return false;
|
||
|
||
TaskData taskConfig = GetTaskConfig(task.taskId);
|
||
if (taskConfig == null)
|
||
return false;
|
||
|
||
// 发放奖励
|
||
for (int i = 0; i < taskConfig.rewardTypes.Length && i < taskConfig.rewardNums.Length; i++)
|
||
{
|
||
GiveReward(taskConfig.rewardTypes[i], taskConfig.rewardNums[i]);
|
||
}
|
||
|
||
task.status = TaskStatus.Claimed;
|
||
SavePlayerData();
|
||
return true;
|
||
}
|
||
|
||
private void GiveReward(TaskRewardType rewardType, int amount)
|
||
{
|
||
// 根据奖励类型发放奖励
|
||
switch (rewardType)
|
||
{
|
||
case TaskRewardType.Coins:
|
||
// 增加金币(需要与游戏现有金币系统集成)
|
||
UserData userData = Authenticator.Get().UserData;
|
||
if (userData != null)
|
||
{
|
||
userData.coins += amount;
|
||
Debug.Log($"Gave {amount} coins as reward");
|
||
|
||
// 保存用户数据更新
|
||
if (ApiClient.Get() != null && ApiClient.Get().IsLoggedIn())
|
||
{
|
||
Authenticator.Get().SaveUserData();
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
private TaskData GetTaskConfig(string taskId)
|
||
{
|
||
TaskData[] allTasks = Resources.LoadAll<TaskData>("Tasks");
|
||
return allTasks.FirstOrDefault(t => t.id == taskId);
|
||
}
|
||
|
||
// 更新任务进度
|
||
private void UpdateTaskProgress(TaskConditionType conditionType, string parameter1 = null, string parameter2 = null)
|
||
{
|
||
bool progressUpdated = false;
|
||
refreshTaskUI?.Invoke(playerTasks);
|
||
foreach (var task in playerTasks)
|
||
{
|
||
if (task.status != TaskStatus.Active)
|
||
continue;
|
||
|
||
TaskData taskConfig = GetTaskConfig(task.taskId);
|
||
if (taskConfig == null || taskConfig.condition != conditionType)
|
||
continue;
|
||
|
||
bool shouldUpdate = false;
|
||
|
||
// 根据任务类型检查条件
|
||
switch (conditionType)
|
||
{
|
||
case TaskConditionType.LoginGame:
|
||
case TaskConditionType.PlayGames:
|
||
case TaskConditionType.WinGames:
|
||
// 这些任务类型直接增加进度
|
||
shouldUpdate = true;
|
||
break;
|
||
|
||
case TaskConditionType.DefeatHeroWithAttributes:
|
||
case TaskConditionType.SummonHeroWithAttributes:
|
||
case TaskConditionType.UseHeroSkillWithAttributes:
|
||
// 检查参数是否匹配(支持多个参数匹配)
|
||
if (!string.IsNullOrEmpty(parameter1) &&
|
||
(!string.IsNullOrEmpty(taskConfig.value2) && taskConfig.value2 == parameter1 ||
|
||
!string.IsNullOrEmpty(taskConfig.value3) && taskConfig.value3 == parameter1))
|
||
{
|
||
shouldUpdate = true;
|
||
}
|
||
else if (!string.IsNullOrEmpty(parameter2) &&
|
||
(!string.IsNullOrEmpty(taskConfig.value2) && taskConfig.value2 == parameter2 ||
|
||
!string.IsNullOrEmpty(taskConfig.value3) && taskConfig.value3 == parameter2))
|
||
{
|
||
shouldUpdate = true;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (shouldUpdate && task.progress < taskConfig.value1)
|
||
{
|
||
task.progress++;
|
||
progressUpdated = true;
|
||
|
||
// 检查任务是否完成
|
||
if (CheckTaskCompletion(task))
|
||
{
|
||
task.status = TaskStatus.Completed;
|
||
Debug.Log($"Task completed: {taskConfig.name}");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (progressUpdated)
|
||
{
|
||
Debug.LogError("读到了");
|
||
SavePlayerData();
|
||
}
|
||
}
|
||
|
||
// 事件处理方法
|
||
private void OnGameStart()
|
||
{
|
||
|
||
// 进行对战任务进度+1
|
||
UpdateTaskProgress(TaskConditionType.PlayGames);
|
||
}
|
||
|
||
private void OnGameEnd(int winner)
|
||
{
|
||
int player_id = GameClient.Get().GetPlayerID();
|
||
|
||
// 胜利任务进度+1
|
||
if (winner == player_id)
|
||
{
|
||
UpdateTaskProgress(TaskConditionType.WinGames);
|
||
}
|
||
|
||
// 检查是否有任务完成并自动领取奖励
|
||
CheckAndClaimCompletedTasks();
|
||
}
|
||
|
||
private void OnCardPlayed(Card card, Slot slot)
|
||
{
|
||
// 召唤特定属性英雄任务
|
||
if (card.CardData.type == CardType.Character)
|
||
{
|
||
string cardCamp = card.CardData.camp.ToString();
|
||
UpdateTaskProgress(TaskConditionType.SummonHeroWithAttributes, cardCamp);
|
||
}
|
||
}
|
||
|
||
private void OnCardSummoned(Card card, Slot slot)
|
||
{
|
||
// 召唤特定属性英雄任务
|
||
if (card.CardData.type == CardType.Character)
|
||
{
|
||
string cardCamp = card.CardData.camp.ToString();
|
||
UpdateTaskProgress(TaskConditionType.SummonHeroWithAttributes, cardCamp);
|
||
}
|
||
}
|
||
|
||
private void OnAbilityUsed(AbilityData ability, Card caster)
|
||
{
|
||
// 使用特定属性英雄技能任务
|
||
string cardCamp = caster.CardData.camp.ToString();
|
||
UpdateTaskProgress(TaskConditionType.UseHeroSkillWithAttributes, cardCamp);
|
||
}
|
||
|
||
private void OnCardDiscarded(Card card)
|
||
{
|
||
// 击败特定属性英雄任务
|
||
if (card.CardData.type == CardType.Character)
|
||
{
|
||
string cardCamp = card.CardData.camp.ToString();
|
||
UpdateTaskProgress(TaskConditionType.DefeatHeroWithAttributes, cardCamp);
|
||
}
|
||
}
|
||
|
||
// 检查并自动领取已完成任务的奖励
|
||
public void CheckAndClaimCompletedTasks()
|
||
{
|
||
bool claimedAny = false;
|
||
foreach (var task in playerTasks)
|
||
{
|
||
if (task.status == TaskStatus.Completed)
|
||
{
|
||
if (ClaimTaskReward(task))
|
||
{
|
||
claimedAny = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (claimedAny)
|
||
{
|
||
SavePlayerData();
|
||
}
|
||
}
|
||
|
||
// 玩家登录时检查任务
|
||
public void OnPlayerLogin()
|
||
{
|
||
// 检查是否已有登录任务
|
||
bool hasLoginTask = playerTasks.Any(t =>
|
||
{
|
||
TaskData config = GetTaskConfig(t.taskId);
|
||
return config != null && config.condition == TaskConditionType.LoginGame;
|
||
});
|
||
|
||
// 如果没有登录任务,就分配一个
|
||
if (!hasLoginTask)
|
||
{
|
||
AssignLoginTask();
|
||
}
|
||
|
||
// 登录任务完成
|
||
UpdateTaskProgress(TaskConditionType.LoginGame);
|
||
|
||
// 检查是否有任务完成并自动领取奖励
|
||
CheckAndClaimCompletedTasks();
|
||
|
||
// 分配每日任务(如果需要)
|
||
AssignDailyTaskIfNeeded();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 专门分配一个登录任务
|
||
/// </summary>
|
||
private void AssignLoginTask()
|
||
{
|
||
TaskData[] allTasks = Resources.LoadAll<TaskData>("Tasks");
|
||
var loginTask = allTasks.FirstOrDefault(t => t.isDailyTask && t.condition == TaskConditionType.LoginGame);
|
||
|
||
if (loginTask != null)
|
||
{
|
||
PlayerTask playerTask = new PlayerTask(loginTask);
|
||
playerTasks.Add(playerTask);
|
||
lastDailyTaskAssigned = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||
SavePlayerData();
|
||
Debug.Log($"分配登录任务: {loginTask.name}");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("未找到登录任务配置!");
|
||
}
|
||
}
|
||
|
||
|
||
// 获取活跃任务
|
||
public List<PlayerTask> GetActiveTasks()
|
||
{
|
||
return playerTasks.Where(t => t.status == TaskStatus.Active).ToList();
|
||
}
|
||
|
||
// 获取已完成任务
|
||
public List<PlayerTask> GetCompletedTasks()
|
||
{
|
||
return playerTasks.Where(t => t.status == TaskStatus.Completed).ToList();
|
||
}
|
||
|
||
// 获取所有任务
|
||
public List<PlayerTask> GetAllTasks()
|
||
{
|
||
return playerTasks;
|
||
}
|
||
}
|
||
|
||
// 玩家任务保存数据结构
|
||
[Serializable]
|
||
public struct PlayerTaskSaveData
|
||
{
|
||
public PlayerTask[] tasks;
|
||
public DateTime lastDailyTaskAssigned;
|
||
}
|
||
} |