feat: 重构任务奖励系统并增加宠物技能和皮肤奖励
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

将任务奖励逻辑重构到单独的文件中,增加对宠物技能和皮肤奖励的支持,优化任务完成处理流程
This commit is contained in:
xinian
2026-04-11 19:25:59 +08:00
parent ecc483a11a
commit f6aa0c3339
13 changed files with 536 additions and 188 deletions

View File

@@ -43,7 +43,7 @@ func (Controller) PlayerFightBoss(req *ChallengeBossInboundInfo, p *player.Playe
}
p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
p.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE
p.Fightinfo.Mode = resolveMapNodeFightMode(mapNode)
ai := player.NewAI_player(monsterInfo)
ai.CanCapture = resolveBossCaptureRate(bossConfigs[0].IsCapture, leadMonsterID)
@@ -76,12 +76,23 @@ func startMapBossFight(
oppPets := ai.GetPetInfo(0)
if mapNode != nil && mapNode.IsGroupBoss != 0 {
if len(ourPets) > 0 && len(oppPets) > 0 {
return fight.NewLegacyGroupFightSingleController(p, ai, ourPets, oppPets, groupBossSlotLimit, fn)
slotLimit := groupBossSlotLimit
if mapNode.PkFlag != 0 {
slotLimit = 1
}
return fight.NewLegacyGroupFightSingleController(p, ai, ourPets, oppPets, slotLimit, fn)
}
}
return fight.NewFight(p, ai, ourPets, oppPets, fn)
}
func resolveMapNodeFightMode(mapNode *configmodel.MapNode) uint32 {
if mapNode != nil && mapNode.PkFlag != 0 {
return fightinfo.BattleMode.SINGLE_MODE
}
return fightinfo.BattleMode.MULTI_MODE
}
// OnPlayerFightNpcMonster 战斗野怪
func (Controller) OnPlayerFightNpcMonster(req *FightNpcMonsterInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
if err = p.CanFight(); err != 0 {

View File

@@ -77,31 +77,16 @@ func (h Controller) CompleteTask(data1 *CompleteTaskInboundInfo, c *player.Playe
// if service.NewTaskService().IsAcceptable(data1.TaskId) == nil {
// return nil, errorcode.ErrorCodes.ErrSystemError
// }
c.Info.SetTask(int(data1.TaskId), model.Completed)
result = &task.CompleteTaskOutboundInfo{
TaskId: data1.TaskId,
ItemList: make([]data.ItemInfo, 0),
}
taskInfo := task.GetTaskInfo(int(data1.TaskId), int(data1.OutState))
if taskInfo == nil {
return nil, errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize
if _, err = c.ApplyTaskCompletion(data1.TaskId, int(data1.OutState), result); err != 0 {
return nil, err
}
if taskInfo.Pet != nil {
c.Service.Pet.PetAdd(taskInfo.Pet, 0)
result.CaptureTime = taskInfo.Pet.CatchTime
result.PetTypeId = taskInfo.Pet.ID
}
for _, item := range taskInfo.ItemList {
success := c.ItemAdd(item.ItemId, item.ItemCnt)
if success {
result.ItemList = append(result.ItemList, item)
}
if taskErr := c.Info.SetTask(int(data1.TaskId), model.Completed); taskErr != nil {
return nil, errorcode.ErrorCodes.ErrSystemError
}
return result, 0 //通过PUB/SUB回包

View File

@@ -1,23 +1,11 @@
package player
import (
"blazing/logic/service/fight/info"
"blazing/logic/service/task"
"blazing/modules/player/model"
"github.com/pointernil/bitset32"
)
// 辅助函数:获取任务奖励,封装逻辑便于复用和统一检查
// 返回nil表示无奖励
func (p *Player) getTaskGift(taskID int, ot int) *task.TaskResult {
// 防御性检查taskID非法时直接返回nil
if taskID <= 0 {
return nil
}
return task.GetTaskInfo(taskID, ot)
}
// SptCompletedTask 完成任务(单分支)
// 优化点:仅当奖励存在时,才完成任务并发放奖励
func (p *Player) SptCompletedTask(taskID int, ot int) {
@@ -29,15 +17,17 @@ func (p *Player) SptCompletedTask(taskID int, ot int) {
return
}
// 2. 核心逻辑:先检查奖励是否存在,无奖励则直接返回(不完成任务)
gift := p.getTaskGift(taskID, ot)
if gift == nil {
if !p.canCompleteTaskReward(taskID, ot) {
return
}
granted, err := p.ApplyTaskCompletion(uint32(taskID), ot, nil)
if err != 0 {
return
}
// 3. 奖励存在时,才标记任务完成 + 发放奖励
p.Info.SetTask(taskID, model.Completed)
p.bossgive(taskID, ot)
p.SendTaskCompletionBonus(uint32(taskID), granted)
}
// TawerCompletedTask 完成塔类任务(多分支)
@@ -48,10 +38,12 @@ func (p *Player) TawerCompletedTask(taskID int, ot int) {
}
// 处理默认分支ot=-1仅奖励存在时才完成主任务
if p.Info.GetTask(taskID) != model.Completed {
defaultGift := p.getTaskGift(taskID, -1)
if defaultGift != nil { // 奖励存在才标记主任务完成
p.Info.SetTask(taskID, model.Completed)
p.bossgive(taskID, -1)
if p.canCompleteTaskReward(taskID, -1) {
granted, err := p.ApplyTaskCompletion(uint32(taskID), -1, nil)
if err == 0 {
p.Info.SetTask(taskID, model.Completed)
p.SendTaskCompletionBonus(uint32(taskID), granted)
}
}
}
@@ -61,53 +53,19 @@ func (p *Player) TawerCompletedTask(taskID int, ot int) {
return
}
// 核心检查:指定分支的奖励是否存在
branchGift := p.getTaskGift(taskID, ot)
if branchGift == nil {
if !p.canCompleteTaskReward(taskID, ot) {
return
}
r := bitset32.From(taskData.Data)
if !r.Test(uint(ot)) {
r.Set(uint(ot))
p.bossgive(taskID, ot)
granted, rewardErr := p.ApplyTaskCompletion(uint32(taskID), ot, nil)
if rewardErr != 0 {
return
}
p.SendTaskCompletionBonus(uint32(taskID), granted)
taskData.Data = r.Bytes()
_ = p.Service.Task.SetTask(taskData)
}
}
// bossgive 发放任务奖励(逻辑保持不变,仅补充注释)
func (p *Player) bossgive(taskID int, ot int) {
gift := p.getTaskGift(taskID, ot)
if gift == nil {
return
}
res := &info.S2C_GET_BOSS_MONSTER{
BonusID: uint32(taskID),
}
// 发放宠物奖励
if gift.Pet != nil {
p.Service.Pet.PetAdd(gift.Pet, 0)
res.PetID = gift.Pet.ID
res.CaptureTm = gift.Pet.CatchTime
}
// 发放道具奖励(仅成功添加的道具才返回给前端)
for _, item := range gift.ItemList {
if success := p.ItemAdd(item.ItemId, item.ItemCnt); success {
res.AddItemInfo(item)
}
}
// 发放称号奖励
if gift.Title != 0 {
p.GiveTitle(gift.Title)
}
// 发送奖励通知给前端
if res.HasReward() {
p.SendPackCmd(8004, res)
}
}

View File

@@ -0,0 +1,193 @@
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"
)
type TaskCompletionContext struct {
TaskID uint32
OutState int
Config *configmodel.TaskConfig
Reward *tasklogic.TaskResult
Result *tasklogic.CompleteTaskOutboundInfo
SkipDefaultReward bool
}
type TaskCompletionHandler func(*Player, *TaskCompletionContext) errorcode.ErrorCode
var taskCompletionRegistry = struct {
sync.RWMutex
handlers map[uint32]TaskCompletionHandler
}{
handlers: make(map[uint32]TaskCompletionHandler),
}
type taskRewardGrantResult struct {
Pet *playermodel.PetInfo
Items []data.ItemInfo
}
func RegisterTaskCompletionHandler(taskID uint32, handler TaskCompletionHandler) {
if taskID == 0 || handler == nil {
return
}
taskCompletionRegistry.Lock()
taskCompletionRegistry.handlers[taskID] = handler
taskCompletionRegistry.Unlock()
}
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)
}
func hasTaskCompletionHandler(taskID uint32) bool {
taskCompletionRegistry.RLock()
_, ok := taskCompletionRegistry.handlers[taskID]
taskCompletionRegistry.RUnlock()
return ok
}
func getTaskCompletionHandler(taskID uint32) TaskCompletionHandler {
taskCompletionRegistry.RLock()
handler := taskCompletionRegistry.handlers[taskID]
taskCompletionRegistry.RUnlock()
return handler
}
func (p *Player) canCompleteTaskReward(taskID, outState int) bool {
if taskID <= 0 {
return false
}
return p.getTaskGift(taskID, outState) != nil || hasTaskCompletionHandler(uint32(taskID))
}
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
}
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
}
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)
}
}

View File

@@ -0,0 +1,115 @@
package player
import "blazing/modules/player/model"
func applyTaskRewardToPetInfo(pet *model.PetInfo, trainSkillIDs, skinIDs []uint32) bool {
if pet == nil {
return false
}
changed := false
mergedSkills := mergeTaskRewardIDs(pet.ExtSKill, trainSkillIDs)
if len(mergedSkills) != len(pet.ExtSKill) {
pet.ExtSKill = mergedSkills
changed = true
}
mergedSkins := mergeTaskRewardIDs(pet.ExtSkin, skinIDs)
if len(mergedSkins) != len(pet.ExtSkin) {
pet.ExtSkin = mergedSkins
changed = true
}
if pet.SkinID == 0 {
for _, skinID := range mergedSkins {
if skinID == 0 {
continue
}
pet.SkinID = skinID
changed = true
break
}
}
return changed
}
func mergeTaskRewardIDs(dst []uint32, src []uint32) []uint32 {
if len(src) == 0 {
return dst
}
seen := make(map[uint32]struct{}, len(dst)+len(src))
result := make([]uint32, 0, len(dst)+len(src))
for _, id := range dst {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
for _, id := range src {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
return result
}
func (p *Player) GrantTaskPetRewards(targetPetID uint32, trainSkillIDs, skinIDs []uint32) bool {
if p == nil || targetPetID == 0 || (len(trainSkillIDs) == 0 && len(skinIDs) == 0) {
return false
}
changed := false
for i := range p.Info.PetList {
if p.Info.PetList[i].ID != targetPetID {
continue
}
if applyTaskRewardToPetInfo(&p.Info.PetList[i], trainSkillIDs, skinIDs) {
changed = true
}
}
for i := range p.Info.BackupPetList {
if p.Info.BackupPetList[i].ID != targetPetID {
continue
}
if applyTaskRewardToPetInfo(&p.Info.BackupPetList[i], trainSkillIDs, skinIDs) {
changed = true
}
}
if p.Service == nil || p.Service.Pet == nil {
return changed
}
allPets := p.Service.Pet.PetInfo(0)
for i := range allPets {
if allPets[i].Data.ID != targetPetID {
continue
}
if !applyTaskRewardToPetInfo(&allPets[i].Data, trainSkillIDs, skinIDs) {
continue
}
allPets[i].Data.CatchTime = allPets[i].CatchTime
if p.Service.Pet.Update(allPets[i].Data) {
changed = true
}
}
return changed
}

View File

@@ -10,8 +10,11 @@ import (
type TaskResult struct {
Pet *model.PetInfo `json:"petTypeId" description:"发放的精灵ID"` // 发放的精灵ID
ItemList []data.ItemInfo `json:"itemList" description:"发放物品的数组"` // 发放物品的数组,
Title uint32 `json:"title" description:"称号奖励"`
ItemList []data.ItemInfo `json:"itemList" description:"发放物品的数组"` // 发放物品的数组,
Title uint32 `json:"title" description:"称号奖励"`
RewardPetID uint32 `json:"rewardPetId" description:"宠物相关奖励目标精灵ID"`
TrainSkillIDs []uint32 `json:"trainSkillIds" description:"特训技能奖励"`
SkinIDs []uint32 `json:"skinIds" description:"皮肤奖励"`
}
func GetTaskInfo(id, ot int) *TaskResult {
@@ -26,6 +29,12 @@ func GetTaskInfo(id, ot int) *TaskResult {
if pet != nil {
ret.Pet = model.GenPetInfo(int(pet.MonID), int(pet.DV), int(pet.Nature), int(pet.Effect), int(pet.Lv), nil, 0)
}
ret.RewardPetID = r.RewardPetID
ret.TrainSkillIDs = append(ret.TrainSkillIDs, r.TrainSkillIDs...)
ret.SkinIDs = append(ret.SkinIDs, r.SkinIDs...)
if ret.Pet != nil {
applyTaskRewardPetExtras(ret.Pet, ret.TrainSkillIDs, ret.SkinIDs)
}
for _, itemID := range r.ItemRewardIds {
iteminfo := service.NewItemService().GetItemCount(itemID)
@@ -39,3 +48,59 @@ func GetTaskInfo(id, ot int) *TaskResult {
return ret
}
func applyTaskRewardPetExtras(pet *model.PetInfo, trainSkillIDs, skinIDs []uint32) {
if pet == nil {
return
}
if merged := mergeTaskRewardIDs(pet.ExtSKill, trainSkillIDs); len(merged) != len(pet.ExtSKill) {
pet.ExtSKill = merged
}
mergedSkins := mergeTaskRewardIDs(pet.ExtSkin, skinIDs)
if len(mergedSkins) != len(pet.ExtSkin) {
pet.ExtSkin = mergedSkins
}
if pet.SkinID == 0 {
for _, skinID := range mergedSkins {
if skinID != 0 {
pet.SkinID = skinID
break
}
}
}
}
func mergeTaskRewardIDs(dst []uint32, src []uint32) []uint32 {
if len(src) == 0 {
return dst
}
seen := make(map[uint32]struct{}, len(dst)+len(src))
result := make([]uint32, 0, len(dst)+len(src))
for _, id := range dst {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
for _, id := range src {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
return result
}