Files
bl/logic/service/player/task_completion.go
昔念 e1a994ba11
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
```
feat(fight): 添加效果工厂模式支持以解决闭包变量捕获问题

- 新增initskillFactory函数用于注册效果工厂
- 修改技能效果注册逻辑从直接实例化改为工厂模式
- 解决循环中闭包捕获变量导致的潜在问题

feat(fight): 实现对手输入获取逻辑优化回合处理

- 添加roundOpponentInput方法获取对手输入
- 重构enterturn方法中的先后手逻辑
- 确保攻击方和被攻击
2026-04-12 22:44:13 +08:00

223 lines
7.1 KiB
Go

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)
}
}