feat(item): 添加 NatureProbs 字段并优化宠物道具使用逻辑
- 在 `Item` 结构体中新增 `NatureProbs` 字段,用于支持性格概率配置 - 重构 `ItemUsePet` 控制器方法,引入处理器注册机制统一管理道具效果 - 修复神经元相关道具的特殊处理逻辑,增强代码可维护性 - 调整 `S2C_USE_PET_ITEM_OUT_OF_FIGHT` 响应结构体,增加技能列表长度字段 - 修改 `ResetNature` 方法命名及判断条件,提升语义清晰度与健壮性 - 新增 `PetInfo_One_Unscoped` 查询方法以支持软删除数据访问 - 实
This commit is contained in:
@@ -47,10 +47,11 @@ type Item struct {
|
||||
YuanshenDegrade int `xml:"YuanshenDegrade,attr,omitempty"` // 融合精灵还原标识(融合精灵还原药剂)
|
||||
EvRemove int `xml:"EvRemove,attr,omitempty"` // 学习力遗忘类型(各类学习力遗忘剂)
|
||||
//bShowPetBag int `xml:"bShowPetBag,attr,omitempty"` // 宠物背包显示标识(副融合精灵保留药剂等)
|
||||
Nature *string `xml:"Nature,attr"` // 指向int的指针,无值时为nil
|
||||
NatureSet *string `xml:"NatureSet,attr"` // 指向string的指针,无值时为nil
|
||||
Pet *Pet `xml:"pet,omitempty"` // 精灵属性子节点
|
||||
TeamPK *TeamPK `xml:"teamPK,omitempty"` // 要塞保卫战子节点
|
||||
Nature *string `xml:"Nature,attr"` // 指向int的指针,无值时为nil
|
||||
NatureSet *string `xml:"NatureSet,attr"` // 指向string的指针,无值时为nil
|
||||
NatureProbs *string `xml:"NatureProbs,attr"` // 指向string的指针,无值时为nil
|
||||
Pet *Pet `xml:"pet,omitempty"` // 精灵属性子节点
|
||||
TeamPK *TeamPK `xml:"teamPK,omitempty"` // 要塞保卫战子节点
|
||||
}
|
||||
|
||||
// Pet 精灵属性子节点,对应<pet>标签
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/common/socket/errorcode"
|
||||
"blazing/logic/service/fight"
|
||||
"blazing/logic/service/item"
|
||||
"blazing/logic/service/player"
|
||||
"blazing/modules/blazing/model"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
@@ -35,40 +31,33 @@ func (h Controller) ItemUsePet(data *item.C2S_USE_PET_ITEM_OUT_OF_FIGHT, c *play
|
||||
if !ok {
|
||||
return nil, errorcode.ErrorCodes.Err10401
|
||||
}
|
||||
// 绑定变量到switch,显式匹配true
|
||||
switch itemID := data.ItemID; true {
|
||||
//这是学习力遗忘
|
||||
case itemID >= 300037 && itemID <= 300041:
|
||||
|
||||
onpet.Ev[itemID-300037+1] = 0
|
||||
// 体力遗忘
|
||||
case itemID == 300042:
|
||||
onpet.Ev[0] = 0
|
||||
// 全能遗忘
|
||||
case itemID == 300650:
|
||||
onpet.Ev = [6]uint32{}
|
||||
|
||||
//性格随机
|
||||
case itemID == 300025:
|
||||
|
||||
onpet.Nature = (onpet.Nature + uint32(grand.Intn(25))) % 25
|
||||
|
||||
//性格转换
|
||||
case itemID >= 300081 && itemID <= 300086:
|
||||
onpet.Nature = gconv.Uint32(*xmlres.ItemsMAP[int(itemID)].Nature)
|
||||
|
||||
//性格转换
|
||||
case (itemID >= 300602 && itemID <= 300605) || itemID == 300119 || itemID == 300120:
|
||||
rr := strings.Split(*xmlres.ItemsMAP[int(itemID)].NatureSet, " ")
|
||||
onpet.Nature = gconv.Uint32(rr[grand.Intn(len(rr))-1])
|
||||
default:
|
||||
// 无效ID处理
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
|
||||
if c.Service.Item.CheakItem(data.ItemID) == 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
hd := item.PetItemRegistry.GetHandler(data.ItemID)
|
||||
if hd == nil {
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
if data.ItemID == 300025 {
|
||||
//神经元需要特殊处理
|
||||
|
||||
if onpet.OldCatchTime == 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
oldpetc := onpet.CatchTime
|
||||
oldpet := c.Service.Pet.PetInfo_One_Unscoped(onpet.OldCatchTime)
|
||||
|
||||
copier.Copy(onpet, oldpet.Data)
|
||||
onpet.CatchTime = oldpetc
|
||||
} else {
|
||||
r := hd(data.ItemID, onpet)
|
||||
if !r {
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c.Service.Item.SubItem(data.ItemID, 1)
|
||||
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
|
||||
onpet.CalculatePetPane()
|
||||
@@ -77,8 +66,8 @@ func (h Controller) ItemUsePet(data *item.C2S_USE_PET_ITEM_OUT_OF_FIGHT, c *play
|
||||
return result, 0
|
||||
}
|
||||
|
||||
func (h Controller) RESET_NATURE(data *item.C2S_PET_RESET_NATURE, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||
if c.Service.Item.CheakItem(data.ItemId) == 0 {
|
||||
func (h Controller) ResetNature(data *item.C2S_PET_RESET_NATURE, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||
if c.Service.Item.CheakItem(data.ItemId) <= 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
_, onpet, ok := c.FindPet(data.CatchTime)
|
||||
|
||||
@@ -26,8 +26,9 @@ type S2C_USE_PET_ITEM_OUT_OF_FIGHT struct {
|
||||
// * ev:生命学习力,攻击学习力,防御学习力,特攻学习力,特防学习力,速度学习力
|
||||
Ev [6]uint32 `fieldDesc:"属性" `
|
||||
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
|
||||
Prop [5]uint32 `fieldDesc:"属性" `
|
||||
SkillList []model.SkillInfo `json:"skilllist"` // 技能数组
|
||||
Prop [5]uint32 `fieldDesc:"属性" `
|
||||
SkillListLen uint32 `struc:"sizeof=SkillList"`
|
||||
SkillList []model.SkillInfo `json:"skilllist"` // 技能数组
|
||||
}
|
||||
type C2S_PET_RESET_NATURE struct {
|
||||
Head common.TomeeHeader `cmd:"2343" struc:"skip"`
|
||||
|
||||
258
logic/service/item/petuse.go
Normal file
258
logic/service/item/petuse.go
Normal file
@@ -0,0 +1,258 @@
|
||||
package item
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/modules/blazing/model"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
type PetItemHandler func(itemid uint32, ctx *model.PetInfo) bool
|
||||
|
||||
// RangeHandler 范围ID处理器(用于ID区间匹配)
|
||||
type RangeHandler struct {
|
||||
Start uint32
|
||||
End uint32
|
||||
Handler PetItemHandler
|
||||
}
|
||||
|
||||
// SetHandler 集合ID处理器(用于离散ID匹配)
|
||||
type SetHandler struct {
|
||||
IDs []uint32
|
||||
Handler PetItemHandler
|
||||
}
|
||||
|
||||
// PetItemHandlerRegistry 道具处理器注册器
|
||||
type PetItemHandlerRegistry struct {
|
||||
exactHandlers map[uint32]PetItemHandler // 精确ID映射
|
||||
rangeHandlers []RangeHandler // 范围ID映射
|
||||
setHandlers []SetHandler // 集合ID映射
|
||||
}
|
||||
|
||||
// 全局注册器实例
|
||||
var PetItemRegistry = &PetItemHandlerRegistry{
|
||||
exactHandlers: make(map[uint32]PetItemHandler),
|
||||
}
|
||||
|
||||
// -------------------------- 4. 注册器方法 --------------------------
|
||||
// RegisterExact 注册精确ID的处理函数
|
||||
func (r *PetItemHandlerRegistry) RegisterExact(itemID uint32, handler PetItemHandler) {
|
||||
r.exactHandlers[itemID] = handler
|
||||
}
|
||||
|
||||
// RegisterRange 注册ID范围的处理函数
|
||||
func (r *PetItemHandlerRegistry) RegisterRange(start, end uint32, handler PetItemHandler) {
|
||||
r.rangeHandlers = append(r.rangeHandlers, RangeHandler{
|
||||
Start: start,
|
||||
End: end,
|
||||
Handler: handler,
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterSet 注册ID集合的处理函数
|
||||
func (r *PetItemHandlerRegistry) RegisterSet(ids []uint32, handler PetItemHandler) {
|
||||
r.setHandlers = append(r.setHandlers, SetHandler{
|
||||
IDs: ids,
|
||||
Handler: handler,
|
||||
})
|
||||
}
|
||||
|
||||
// GetHandler 根据ItemID获取对应的处理函数
|
||||
func (r *PetItemHandlerRegistry) GetHandler(itemID uint32) PetItemHandler {
|
||||
// 1. 优先匹配精确ID
|
||||
if handler, ok := r.exactHandlers[itemID]; ok {
|
||||
return handler
|
||||
}
|
||||
|
||||
// 2. 匹配范围ID
|
||||
for _, rh := range r.rangeHandlers {
|
||||
if itemID >= rh.Start && itemID <= rh.End {
|
||||
return rh.Handler
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 匹配集合ID
|
||||
for _, sh := range r.setHandlers {
|
||||
for _, id := range sh.IDs {
|
||||
if itemID == id {
|
||||
return sh.Handler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 无匹配的处理器
|
||||
return nil
|
||||
}
|
||||
func nvfunc(itemid uint32, onpet *model.PetInfo) bool {
|
||||
// 1. 取出配置(保留你原有的取值方式)
|
||||
itemConf := xmlres.ItemsMAP[int(itemid)]
|
||||
|
||||
// 2. 分割NatureSet(保留你原有的写法)
|
||||
rr := strings.Split(*itemConf.NatureSet, " ")
|
||||
|
||||
// 3. 分割NatureProbs(新增概率解析)
|
||||
var probList []string
|
||||
if itemConf.NatureProbs != nil {
|
||||
probList = strings.Split(*itemConf.NatureProbs, " ")
|
||||
}
|
||||
|
||||
// 4. 按概率选值(核心改造:替换原有随机逻辑)
|
||||
selectedVal, err := randomByProbs(rr, probList)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 5. 赋值(保留你原有的类型转换)
|
||||
onpet.Nature = gconv.Uint32(selectedVal)
|
||||
return true
|
||||
}
|
||||
|
||||
// -------------------------- 6. 初始化注册器(注册所有处理器) --------------------------
|
||||
func init() {
|
||||
|
||||
PetItemRegistry.RegisterRange(300037, 300041, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
onpet.Ev[itemid-300037+1] = 0
|
||||
return true
|
||||
|
||||
})
|
||||
|
||||
PetItemRegistry.RegisterExact(300042, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
onpet.Ev[0] = 0
|
||||
return true
|
||||
})
|
||||
PetItemRegistry.RegisterExact(300650, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
onpet.Ev = [6]uint32{}
|
||||
return true
|
||||
})
|
||||
PetItemRegistry.RegisterExact(300025, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
onpet.Nature = (onpet.Nature + uint32(grand.Intn(25))) % 25
|
||||
return true
|
||||
})
|
||||
|
||||
PetItemRegistry.RegisterRange(300081, 300086, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
onpet.Nature = gconv.Uint32(*xmlres.ItemsMAP[int(itemid)].Nature)
|
||||
return true
|
||||
|
||||
})
|
||||
|
||||
PetItemRegistry.RegisterRange(300602, 300605, nvfunc)
|
||||
|
||||
//特判转换
|
||||
PetItemRegistry.RegisterSet([]uint32{300119, 300120}, nvfunc)
|
||||
|
||||
PetItemRegistry.RegisterExact(300790, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
r := grand.Intn(2)
|
||||
if r == 0 {
|
||||
onpet.Dv--
|
||||
} else {
|
||||
onpet.Dv++
|
||||
}
|
||||
return true
|
||||
})
|
||||
//基因重组
|
||||
PetItemRegistry.RegisterExact(300024, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
oldcat := onpet.CatchTime
|
||||
|
||||
ab := 0
|
||||
for _, v := range onpet.EffectInfo {
|
||||
if v.Type == 1 {
|
||||
ab = int(v.Idx)
|
||||
}
|
||||
|
||||
}
|
||||
r := model.GenPetInfo(int(onpet.ID), -1, -1, ab, 0, 1)
|
||||
|
||||
copier.Copy(onpet, r)
|
||||
onpet.CatchTime = oldcat
|
||||
return true
|
||||
})
|
||||
|
||||
PetItemRegistry.RegisterExact(300668, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
|
||||
onpet.Dv = uint32(grand.Intn(32))
|
||||
return true
|
||||
})
|
||||
PetItemRegistry.RegisterExact(300791, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
|
||||
onpet.Dv = 31
|
||||
return true
|
||||
})
|
||||
PetItemRegistry.RegisterExact(300792, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
|
||||
onpet.Dv = uint32(grand.Intn(31-int(onpet.Dv)) + 1)
|
||||
return true
|
||||
})
|
||||
PetItemRegistry.RegisterExact(300053, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
|
||||
if !onpet.HaveAN() {
|
||||
onpet.RnadAN()
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
PetItemRegistry.RegisterExact(300054, func(itemid uint32, onpet *model.PetInfo) bool {
|
||||
|
||||
if onpet.HaveAN() {
|
||||
onpet.RnadAN()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// 替换你原有性格转换的代码段,新增概率解析+权重随机逻辑
|
||||
// 注:保留你原有的变量名(如rr)、函数调用(grand.Intn),仅扩展概率逻辑
|
||||
|
||||
// 先定义一个辅助函数:按概率从列表中选值(贴合你的代码风格)
|
||||
func randomByProbs(natureSet []string, probs []string) (string, error) {
|
||||
// 1. 若概率配置为空/长度不匹配,降级为等概率随机(兼容原有逻辑)
|
||||
if len(probs) == 0 || len(natureSet) != len(probs) {
|
||||
if len(natureSet) == 0 {
|
||||
return "", errors.New("natureSet is empty")
|
||||
}
|
||||
// 修复你原有代码的索引越界问题:grand.Intn(len(rr))-1 → 改为grand.Intn(len(rr))
|
||||
// 因为grand.Intn(n)返回0~n-1,直接作为索引即可,无需-1
|
||||
return natureSet[grand.Intn(len(natureSet))], nil
|
||||
}
|
||||
|
||||
// 2. 解析概率为int数组,校验非负
|
||||
probInts := make([]int, len(probs))
|
||||
totalProb := 0
|
||||
for i, p := range probs {
|
||||
val, err := strconv.Atoi(p)
|
||||
if err != nil || val < 0 {
|
||||
return "", errors.New("invalid prob value: " + p)
|
||||
}
|
||||
probInts[i] = val
|
||||
totalProb += val
|
||||
}
|
||||
|
||||
// 3. 总概率为0,降级为等概率
|
||||
if totalProb == 0 {
|
||||
return natureSet[grand.Intn(len(natureSet))], nil
|
||||
}
|
||||
|
||||
// 4. 计算前缀和(权重随机核心)
|
||||
prefixSum := make([]int, len(probInts))
|
||||
prefixSum[0] = probInts[0]
|
||||
for i := 1; i < len(probInts); i++ {
|
||||
prefixSum[i] = prefixSum[i-1] + probInts[i]
|
||||
}
|
||||
|
||||
// 5. 生成随机数匹配前缀和
|
||||
randVal := grand.Intn(totalProb)
|
||||
for i, sum := range prefixSum {
|
||||
if randVal < sum {
|
||||
return natureSet[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
// 理论上不会走到这里
|
||||
return natureSet[0], nil
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@@ -89,7 +90,7 @@ type PetInfo struct {
|
||||
// 捕获等级默认0(@UInt long → uint32)
|
||||
CatchLevel uint32 `fieldDesc:"捕获等级 默认为0" `
|
||||
EffectInfoLen uint16 `struc:"sizeof=EffectInfo"`
|
||||
// 特性列表:长度用UShort存储(变长List → []PetEffectInfo + 长度前缀规则)
|
||||
// 特性列表:长度用UShort存储(变长List → []PetEffectInfo + 长度前缀规则) 第一个一定是特性
|
||||
EffectInfo []PetEffectInfo `fieldDesc:"特性列表, 长度在头部以UShort存储" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
|
||||
|
||||
// 皮肤ID默认0(@UInt long → uint32)
|
||||
@@ -196,6 +197,42 @@ func (pet *PetInfo) Cure() {
|
||||
}
|
||||
}
|
||||
|
||||
// 随机特性
|
||||
func (pet *PetInfo) RnadAN() {
|
||||
// 随机特性
|
||||
randomIndex := rand.Intn(len(xmlres.PlayerEffectMAP))
|
||||
|
||||
var i int
|
||||
for _, v := range xmlres.PlayerEffectMAP {
|
||||
|
||||
if i == randomIndex {
|
||||
ret := &PetEffectInfo{
|
||||
Idx: uint16(gconv.Int16(v.Idx)),
|
||||
Status: 1,
|
||||
EID: uint16(gconv.Int16(v.Eid)),
|
||||
Args: v.ArgsS,
|
||||
}
|
||||
_, eff1, ok := utils.FindWithIndex(pet.EffectInfo, func(item PetEffectInfo) bool {
|
||||
return uint16(item.Type) == 1
|
||||
})
|
||||
if ok {
|
||||
copier.Copy(eff1, ret)
|
||||
}
|
||||
pet.EffectInfo = append(pet.EffectInfo, *ret)
|
||||
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func (pet *PetInfo) HaveAN() bool {
|
||||
|
||||
_, _, ok := utils.FindWithIndex(pet.EffectInfo, func(item PetEffectInfo) bool {
|
||||
return uint16(item.Type) == 1
|
||||
})
|
||||
return ok
|
||||
}
|
||||
func (pet *PetInfo) Downgrade(level uint32) {
|
||||
|
||||
for pet.Level > uint32(level) {
|
||||
|
||||
@@ -50,6 +50,15 @@ func (s *PetService) PetInfo_One(cachetime uint32) model.PetEX {
|
||||
tt.Data.CatchTime = tt.CatchTime
|
||||
return tt
|
||||
}
|
||||
func (s *PetService) PetInfo_One_Unscoped(cachetime uint32) model.PetEX {
|
||||
|
||||
m := cool.DBM(s.Model).Where("player_id", s.userid).Where("catch_time", cachetime).Unscoped()
|
||||
var tt model.PetEX
|
||||
|
||||
m.Scan(&tt)
|
||||
tt.Data.CatchTime = tt.CatchTime
|
||||
return tt
|
||||
}
|
||||
func (s *PetService) Pet_del(cachetime uint32) {
|
||||
|
||||
cool.DBM(s.Model).Where("player_id", s.userid).Where("catch_time", cachetime).Delete()
|
||||
|
||||
Reference in New Issue
Block a user