package player import ( "blazing/common/data" "blazing/common/socket/errorcode" fightinfo "blazing/logic/service/fight/info" tasklogic "blazing/logic/service/task" configmodel "blazing/modules/config/model" configservice "blazing/modules/config/service" playermodel "blazing/modules/player/model" "sync" ) // TaskCompletionContext 封装任务完成时的上下文。 // 这里除了保留任务配置和默认奖励,也给自定义任务完成逻辑暴露了返回包与开关位, // 用来兼容“固定发物品/精灵”之外的奖励场景。 // 这套扩展最初是为任务发放特训技能、皮肤而补上的: // 特训奖励不能完全按静态表直发,需要结合额外条件做特判, // 例如通过挖矿/对话进度限制特训次数,满足条件后再允许完成任务。 type TaskCompletionContext struct { TaskID uint32 OutState int Config *configmodel.TaskConfig Reward *tasklogic.TaskResult Result *tasklogic.CompleteTaskOutboundInfo SkipDefaultReward bool } // TaskCompletionHandler 定义任务完成前的自定义处理器。 // 处理器可用于补充校验、写入额外奖励,或在任务完全走自定义发奖时跳过默认奖励流程。 type TaskCompletionHandler func(*Player, *TaskCompletionContext) errorcode.ErrorCode // taskCompletionRegistry 按任务 ID 维护自定义完成处理器。 // 默认任务仍然走 task 配表里的固定奖励;只有存在特判需求的任务才在这里注册。 var taskCompletionRegistry = struct { sync.RWMutex handlers map[uint32]TaskCompletionHandler }{ handlers: make(map[uint32]TaskCompletionHandler), } // taskRewardGrantResult 汇总本次任务实际发放的奖励, // 便于后续统一推送给前端展示。 type taskRewardGrantResult struct { Pet *playermodel.PetInfo Items []data.ItemInfo } // RegisterTaskCompletionHandler 注册任务完成时的自定义处理器。 // 用于覆盖“任务奖励固定为物品和精灵”的旧模型,让指定任务在完成前后插入额外逻辑。 // 当前这套机制主要服务于特训技能、皮肤等特殊奖励,以及需要额外次数/进度校验的任务。 func RegisterTaskCompletionHandler(taskID uint32, handler TaskCompletionHandler) { if taskID == 0 || handler == nil { return } taskCompletionRegistry.Lock() taskCompletionRegistry.handlers[taskID] = handler taskCompletionRegistry.Unlock() } // RegisterTaskTalkLimitHandler 注册一个基于挖矿/采集对话进度的完成限制。 // 历史上特训任务需要通过挖矿次数限制可领取次数,因此复用了 Talk 进度作为准入条件。 // 当指定 talkID 的进度不足 needCount 时,任务不能完成也不能领奖。 func RegisterTaskTalkLimitHandler(taskID, talkID, needCount uint32) { RegisterTaskCompletionHandler(taskID, func(p *Player, _ *TaskCompletionContext) errorcode.ErrorCode { if p == nil || p.Service == nil || p.Service.Talk == nil { return errorcode.ErrorCodes.ErrSystemError } currentCount, ok := p.Service.Talk.Progress(int(talkID)) if !ok || currentCount < needCount { return errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize } return 0 }) } func (p *Player) getTaskGift(taskID int, outState int) *tasklogic.TaskResult { if taskID <= 0 { return nil } return tasklogic.GetTaskInfo(taskID, outState) } // hasTaskCompletionHandler 判断任务是否存在自定义完成处理器。 func hasTaskCompletionHandler(taskID uint32) bool { taskCompletionRegistry.RLock() _, ok := taskCompletionRegistry.handlers[taskID] taskCompletionRegistry.RUnlock() return ok } // getTaskCompletionHandler 获取任务的自定义完成处理器。 func getTaskCompletionHandler(taskID uint32) TaskCompletionHandler { taskCompletionRegistry.RLock() handler := taskCompletionRegistry.handlers[taskID] taskCompletionRegistry.RUnlock() return handler } // canCompleteTaskReward 判断任务是否具备可执行的奖励逻辑。 // 只要存在默认奖励,或已注册自定义处理器,就允许进入完成流程。 func (p *Player) canCompleteTaskReward(taskID, outState int) bool { if taskID <= 0 { return false } return p.getTaskGift(taskID, outState) != nil || hasTaskCompletionHandler(uint32(taskID)) } // ApplyTaskCompletion 执行任务完成时的奖励发放入口。 // 流程分两层: // 1. 先执行自定义处理器,处理特训/皮肤/额外次数校验等特殊逻辑; // 2. 若未要求跳过默认奖励,再回落到原有的物品/精灵发奖逻辑。 func (p *Player) ApplyTaskCompletion(taskID uint32, outState int, result *tasklogic.CompleteTaskOutboundInfo) (*taskRewardGrantResult, errorcode.ErrorCode) { if p == nil { return nil, errorcode.ErrorCodes.ErrSystemError } ctx := &TaskCompletionContext{ TaskID: taskID, OutState: outState, Config: configservice.NewTaskService().Get(int(taskID), outState), Reward: tasklogic.GetTaskInfo(int(taskID), outState), Result: result, } if ctx.Reward == nil && !hasTaskCompletionHandler(taskID) { return nil, errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize } if handler := getTaskCompletionHandler(taskID); handler != nil { if err := handler(p, ctx); err != 0 { return nil, err } } if ctx.SkipDefaultReward { if result != nil { result.ItemLen = uint32(len(result.ItemList)) } return &taskRewardGrantResult{Items: make([]data.ItemInfo, 0)}, 0 } if ctx.Reward == nil { return nil, errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize } return p.grantTaskReward(ctx.Reward, result), 0 } // grantTaskReward 发放 task 配表里的默认奖励。 // 这里仍负责原有的固定奖励模型:物品、精灵、称号,以及配置里声明的任务宠奖励。 func (p *Player) grantTaskReward(reward *tasklogic.TaskResult, result *tasklogic.CompleteTaskOutboundInfo) *taskRewardGrantResult { granted := &taskRewardGrantResult{ Items: make([]data.ItemInfo, 0), } if reward == nil { if result != nil { result.ItemLen = uint32(len(result.ItemList)) } return granted } if reward.Pet != nil { p.Service.Pet.PetAdd(reward.Pet, 0) granted.Pet = reward.Pet if result != nil { result.CaptureTime = reward.Pet.CatchTime result.PetTypeId = reward.Pet.ID } } for _, item := range reward.ItemList { if !p.ItemAdd(item.ItemId, item.ItemCnt) { continue } granted.Items = append(granted.Items, item) if result != nil { result.ItemList = append(result.ItemList, item) } } if reward.Title != 0 { p.GiveTitle(reward.Title) } if reward.RewardPetID != 0 { p.GrantTaskPetRewards(reward.RewardPetID, reward.TrainSkillIDs, reward.SkinIDs) } if result != nil { result.ItemLen = uint32(len(result.ItemList)) } return granted } // SendTaskCompletionBonus 将任务奖励转换为旧的奖励展示协议并推送给前端。 func (p *Player) SendTaskCompletionBonus(bonusID uint32, granted *taskRewardGrantResult) { if p == nil { return } res := &fightinfo.S2C_GET_BOSS_MONSTER{ BonusID: bonusID, } if granted != nil && granted.Pet != nil { res.PetID = granted.Pet.ID res.CaptureTm = granted.Pet.CatchTime } if granted != nil { for _, item := range granted.Items { res.AddItemInfo(item) } } if res.HasReward() { p.SendPackCmd(8004, res) } }