Files
bl/logic/controller/pet_skill.go
昔念 82bb99d141 ```
refactor(common/rpc): 移除Redis PubSub心跳机制并优化连接管理

移除Redis PubSub连接的心跳保活功能,因为PubSub连接只应负责订阅和接收,
避免在同一连接上并发执行PING操作。更新了ListenFunc和ListenFight函数,
统一代码结构,移除了context包依赖,并添加了相关注释说明。

feat(logic/pet): 新增宠物技能提交功能

新增CommitPetSkills接口用于一次性提交宠物技能学习/替换/排序结果。
实现技能验证、费用计算和状态更新逻辑,包括新技能学习成本和排序费用。
添加isSameUint32Slice辅助函数用于比较技能数组。
```
2026-04-12 19:14:18 +08:00

284 lines
7.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package controller
import (
"blazing/common/data/xmlres"
"blazing/common/socket/errorcode"
"blazing/common/utils"
"blazing/logic/service/fight"
"blazing/logic/service/pet"
"blazing/logic/service/player"
"blazing/modules/player/model"
)
type GetPetLearnableSkillsOutboundInfo struct {
SkillListLen uint32 `struc:"sizeof=SkillList"`
SkillList []uint32 `json:"skillList"`
}
func isSameUint32Slice(a []uint32, b []uint32) bool {
if len(a) != len(b) {
return false
}
for index := range a {
if a[index] != b[index] {
return false
}
}
return true
}
func collectPetLearnableSkillList(currentPet *model.PetInfo) []uint32 {
skillSet := make(map[uint32]struct{})
skills := make([]uint32, 0)
appendSkill := func(skillID uint32) {
if skillID == 0 {
return
}
if _, exists := skillSet[skillID]; exists {
return
}
skillSet[skillID] = struct{}{}
skills = append(skills, skillID)
}
for _, skillID := range currentPet.GetLevelRangeCanLearningSkills(1, currentPet.Level) {
appendSkill(skillID)
}
for _, skillID := range currentPet.ExtSKill {
appendSkill(skillID)
}
for _, skill := range currentPet.SkillList {
delete(skillSet, skill.ID)
}
result := make([]uint32, 0, len(skillSet))
for _, skillID := range skills {
if _, exists := skillSet[skillID]; exists {
result = append(result, skillID)
}
}
return result
}
// GetPetLearnableSkills 查询当前精灵可学习技能(等级技能 + 额外技能ExtSKill
func (h Controller) GetPetLearnableSkills(
data *GetPetLearnableSkillsInboundInfo,
c *player.Player,
) (result *GetPetLearnableSkillsOutboundInfo, err errorcode.ErrorCode) {
_, currentPet, ok := c.FindPet(data.CatchTime)
if !ok {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
return &GetPetLearnableSkillsOutboundInfo{
SkillList: collectPetLearnableSkillList(currentPet),
}, 0
}
// SetPetSkill 设置宠物技能消耗50赛尔豆
func (h Controller) SetPetSkill(data *ChangeSkillInfo, c *player.Player) (result *pet.ChangeSkillOutInfo, err errorcode.ErrorCode) {
const setSkillCost = 50
_, currentPet, ok := c.FindPet(data.CatchTime)
if !ok {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
canLearnSkillSet := make(map[uint32]struct{})
for _, skillID := range collectPetLearnableSkillList(currentPet) {
canLearnSkillSet[skillID] = struct{}{}
}
if _, exists := canLearnSkillSet[data.ReplaceSkill]; !exists {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
skillInfo, exists := xmlres.SkillMap[int(data.ReplaceSkill)]
if !exists {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
_, _, ok = utils.FindWithIndex(currentPet.SkillList, func(item model.SkillInfo) bool {
return item.ID == data.ReplaceSkill
})
if ok {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
if data.HasSkill == 0 && len(currentPet.SkillList) >= 4 {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
if data.HasSkill != 0 {
_, _, found := utils.FindWithIndex(currentPet.SkillList, func(item model.SkillInfo) bool {
return item.ID == data.HasSkill
})
if !found {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
}
if !c.GetCoins(setSkillCost) {
return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
}
c.Info.Coins -= setSkillCost
maxPP := uint32(skillInfo.MaxPP)
if data.HasSkill != 0 {
_, targetSkill, _ := utils.FindWithIndex(currentPet.SkillList, func(item model.SkillInfo) bool {
return item.ID == data.HasSkill
})
targetSkill.ID = data.ReplaceSkill
targetSkill.PP = maxPP
} else {
currentPet.SkillList = append(currentPet.SkillList, model.SkillInfo{
ID: data.ReplaceSkill,
PP: maxPP,
})
}
return &pet.ChangeSkillOutInfo{
CatchTime: data.CatchTime,
}, 0
}
// SortPetSkills 排序宠物技能消耗50赛尔豆
func (h Controller) SortPetSkills(data *C2S_Skill_Sort, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
const skillSortCost = 50
_, currentPet, ok := c.FindPet(data.CapTm)
if !ok {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
usedSkillSet := make(map[uint32]struct{})
newSkillList := make([]model.SkillInfo, 0, 4)
for _, skillID := range data.Skill {
if skillID == 0 {
continue
}
if _, used := usedSkillSet[skillID]; used {
continue
}
_, skill, found := utils.FindWithIndex(currentPet.SkillList, func(item model.SkillInfo) bool {
return item.ID == skillID
})
if !found {
continue
}
newSkillList = append(newSkillList, *skill)
usedSkillSet[skillID] = struct{}{}
}
for _, skill := range currentPet.SkillList {
if skill.ID == 0 {
continue
}
if _, used := usedSkillSet[skill.ID]; used {
continue
}
newSkillList = append(newSkillList, skill)
usedSkillSet[skill.ID] = struct{}{}
}
if len(newSkillList) > 4 {
newSkillList = newSkillList[:4]
}
if !c.GetCoins(skillSortCost) {
return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
}
c.Info.Coins -= skillSortCost
currentPet.SkillList = newSkillList
return nil, 0
}
// CommitPetSkills 按最终技能列表一次性提交学习/替换/排序结果。
func (h Controller) CommitPetSkills(
data *CommitPetSkillsInboundInfo,
c *player.Player,
) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
const setSkillCost = 50
const skillSortCost = 50
_, currentPet, ok := c.FindPet(data.CatchTime)
if !ok {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
currentSkillSet := make(map[uint32]model.SkillInfo, len(currentPet.SkillList))
currentSkillOrder := make([]uint32, 0, len(currentPet.SkillList))
for _, skill := range currentPet.SkillList {
if skill.ID == 0 {
continue
}
currentSkillSet[skill.ID] = skill
currentSkillOrder = append(currentSkillOrder, skill.ID)
}
finalSkillIDs := make([]uint32, 0, 4)
usedSkillSet := make(map[uint32]struct{}, 4)
for _, skillID := range data.Skill {
if skillID == 0 {
continue
}
if _, exists := usedSkillSet[skillID]; exists {
continue
}
usedSkillSet[skillID] = struct{}{}
finalSkillIDs = append(finalSkillIDs, skillID)
}
if len(finalSkillIDs) == 0 {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
if len(finalSkillIDs) > 4 {
finalSkillIDs = finalSkillIDs[:4]
}
if isSameUint32Slice(currentSkillOrder, finalSkillIDs) {
return nil, 0
}
learnableSkillSet := make(map[uint32]struct{})
for _, skillID := range collectPetLearnableSkillList(currentPet) {
learnableSkillSet[skillID] = struct{}{}
}
newSkillCount := 0
finalSkillList := make([]model.SkillInfo, 0, len(finalSkillIDs))
for _, skillID := range finalSkillIDs {
if skill, exists := currentSkillSet[skillID]; exists {
finalSkillList = append(finalSkillList, skill)
continue
}
if _, exists := learnableSkillSet[skillID]; !exists {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
skillInfo, exists := xmlres.SkillMap[int(skillID)]
if !exists {
return nil, errorcode.ErrorCodes.ErrSystemBusy
}
newSkillCount++
finalSkillList = append(finalSkillList, model.SkillInfo{
ID: skillID,
PP: uint32(skillInfo.MaxPP),
})
}
totalCost := int64(newSkillCount * setSkillCost)
if newSkillCount == 0 {
totalCost += int64(skillSortCost)
}
if totalCost > 0 && !c.GetCoins(totalCost) {
return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
}
c.Info.Coins -= totalCost
currentPet.SkillList = finalSkillList
return nil, 0
}