feat: 重构任务奖励系统并增加宠物技能和皮肤奖励
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
将任务奖励逻辑重构到单独的文件中,增加对宠物技能和皮肤奖励的支持,优化任务完成处理流程
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
193
logic/service/player/task_completion.go
Normal file
193
logic/service/player/task_completion.go
Normal 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)
|
||||
}
|
||||
}
|
||||
115
logic/service/player/task_reward.go
Normal file
115
logic/service/player/task_reward.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user