feat(pet): 实现精灵融合功能并优化相关数据结构

- 新增精灵融合主服务和材料服务,支持根据主副精灵ID查询融合结果
- 调整融合接口参数结构,将物品字段统一为数组形式
- 修改融合材料模型字段类型,提升数据一致性
- 重构融合配置相关逻辑,移除旧融合配置模型及服务
- 增加特性随机选择逻辑,确保融合产物具备有效特性
- 添加材料合法性校验,防止非法材料参与融合
```
This commit is contained in:
2025-12-02 03:59:28 +08:00
parent aad1e2f44c
commit f73c11e571
9 changed files with 160 additions and 85 deletions

View File

@@ -6,6 +6,7 @@ import (
"blazing/logic/service/pet"
"blazing/logic/service/player"
"blazing/modules/blazing/model"
"blazing/modules/blazing/service"
)
func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result *pet.PetFusionInfo, err errorcode.ErrorCode) {
@@ -34,10 +35,26 @@ func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result
if xmlres.PetMAP[int(Auxpetinfo.ID)].FuseSub == 0 {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
r := model.GenPetInfo(1, -1, -1, -1, -1, 1)
///性格生成
var natureId int32 = -1
if Auxpetinfo.Nature == Mcatchpetinfo.Nature {
natureId = int32(Auxpetinfo.Nature)
}
resid := int(service.NewPetFusionService().Data(Mcatchpetinfo.ID, Auxpetinfo.ID))
if resid == 0 {
//todo失败降低等级
return &pet.PetFusionInfo{}, 0
}
effect := int(service.NewPetFusionMaterialService().Data(data.Item1))
r := model.GenPetInfo(resid, -1, int(natureId), effect, -1, 1)
c.Service.Pet.PetAdd(*r)
c.Pet_del(Auxpetinfo.CatchTime)
c.Pet_del(Mcatchpetinfo.CatchTime)
//todo材料扣除
return &pet.PetFusionInfo{
ObtainTime: r.CatchTime,
SoulID: 1000017,

View File

@@ -27,12 +27,10 @@ type C2S_PetFusion struct {
Head common.TomeeHeader `cmd:"2351" struc:"skip"`
Mcatchtime uint32 `json:"mcatchtime" msgpack:"mcatchtime"` // 主精灵的时间戳
Auxcatchtime uint32 `json:"auxcatchtime" msgpack:"auxcatchtime"` // 副精灵的时间戳
Item1 uint32 `json:"item1" msgpack:"item1"` // 物品序号1
Item2 uint32 `json:"item2" msgpack:"item2"` // 物品序号2
Item3 uint32 `json:"item3" msgpack:"item3"` // 物品序号3
Item4 uint32 `json:"item4" msgpack:"item4"` // 物品序号4
GoldItem1 uint32 `json:"gold_item1" msgpack:"gold_item1"` // 0代表未放置 金豆物品1C#:gold_item1
GoldItem2 uint32 `json:"gold_item2" msgpack:"gold_item2"` // 0代表未放置 金豆物品2C#:gold_item2
Item1 [4]uint32 `json:"item1" msgpack:"item1"` // 物品序号1
GoldItem1 uint32 `json:"gold_item1" msgpack:"gold_item1"` // 0代表未放置 金豆物品1C#:gold_item1
GoldItem2 uint32 `json:"gold_item2" msgpack:"gold_item2"` // 0代表未放置 金豆物品2C#:gold_item2
}
// PetFusionInfo 精灵融合结果详情(后端回包嵌套结构体)

View File

@@ -12,13 +12,13 @@ const (
type PetFusion struct {
*cool.Model // 嵌入通用ModelID/创建时间/更新时间等)
MainPetID int32 `gorm:"not null;comment:'主宠物ID尼尔'" json:"main_pet_id"`
SubPetID int32 `gorm:"not null;comment:'副宠物ID闪皮'" json:"sub_pet_id"`
Probability float32 `gorm:"not null;comment:'融合成功率百分比如80代表80%'" json:"probability"`
ResultPetID int32 `gorm:"not null;comment:'融合结果宠物ID卡鲁、闪尼'" json:"result_pet_id"`
Remark string `gorm:"type:varchar(255);default:'';comment:'融合配方备注(如:尼尔+闪皮=闪尼)'" json:"remark"`
IsEnable int32 `gorm:"not null;default:1;comment:'是否启用1:启用0:禁用)'" json:"is_enable"`
IsDefault int32 `gorm:"not null;default:0;comment:'是否默认配方1:默认配方0:非默认;所有配方不匹配时随机选默认配方)'" json:"is_default"`
MainPetID int32 `gorm:"not null;comment:'主宠物ID尼尔'" json:"main_pet_id"`
SubPetID int32 `gorm:"not null;comment:'副宠物ID闪皮'" json:"sub_pet_id"`
Probability int32 `gorm:"not null;comment:'融合成功率百分比如80代表80%'" json:"probability"`
ResultPetID int32 `gorm:"not null;comment:'融合结果宠物ID卡鲁、闪尼'" json:"result_pet_id"`
Remark string `gorm:"type:varchar(255);default:'';comment:'融合配方备注(如:尼尔+闪皮=闪尼)'" json:"remark"`
IsEnable int32 `gorm:"not null;default:1;comment:'是否启用1:启用0:禁用)'" json:"is_enable"`
IsDefault int32 `gorm:"not null;default:0;comment:'是否默认配方1:默认配方0:非默认;所有配方不匹配时随机选默认配方)'" json:"is_default"`
// 关联一个配方对应多个材料gorm 一对多关联,查询时可预加载)
//Materials []*PetFusionMaterial `gorm:"foreignKey:PetFusionID;references:ID" json:"materials,omitempty"`

View File

@@ -11,16 +11,16 @@ type PetFusionMaterial struct {
*cool.Model // 嵌入通用ModelID/创建时间/更新时间等)
// 4个材料ID对应XML ItemGroup的Idx=1-4无材料填0
Material1 int64 `gorm:"not null;comment:'材料1ID如400030'" json:"material1"`
Material2 int64 `gorm:"not null;comment:'材料2ID如400030'" json:"material2"`
Material3 int64 `gorm:"not null;comment:'材料3ID如400028'" json:"material3"`
Material4 int64 `gorm:"not null;comment:'材料4ID如400030'" json:"material4"`
Material1 uint32 `gorm:"not null;comment:'材料1ID如400030'" json:"material1"`
Material2 uint32 `gorm:"not null;comment:'材料2ID如400030'" json:"material2"`
Material3 uint32 `gorm:"not null;comment:'材料3ID如400028'" json:"material3"`
Material4 uint32 `gorm:"not null;comment:'材料4ID如400030'" json:"material4"`
// 4个特性ID对应XML MaterialGroup的特性索引4-4对应
Trait1Idx int32 `gorm:"not null;comment:'特性1索引如1008'" json:"trait1_idx"`
Trait2Idx int32 `gorm:"not null;comment:'特性2索引如1018'" json:"trait2_idx"`
Trait3Idx int32 `gorm:"not null;comment:'特性3索引如1023'" json:"trait3_idx"`
Trait4Idx int32 `gorm:"not null;comment:'特性4索引如1031'" json:"trait4_idx"`
Trait1Idx uint32 `gorm:"not null;comment:'特性1索引如1008'" json:"trait1_idx"`
Trait2Idx uint32 `gorm:"not null;comment:'特性2索引如1018'" json:"trait2_idx"`
Trait3Idx uint32 `gorm:"not null;comment:'特性3索引如1023'" json:"trait3_idx"`
Trait4Idx uint32 `gorm:"not null;comment:'特性4索引如1031'" json:"trait4_idx"`
IsEnable int32 `gorm:"not null;default:1;comment:'是否启用该组合1:启用0:禁用)'" json:"is_enable"`
}

View File

@@ -1,45 +0,0 @@
package model
import (
"blazing/cool"
)
const (
TableNamePetFusionConfig = "pet_fusion_config" // 宠物融合配置表(尼尔/闪皮融合规则)
)
// PetFusionConfig 宠物融合配置模型(对应尼尔/闪皮融合卡鲁/闪尼的规则)
type PetFusionConfig struct {
*cool.Model
MainPetID int32 `gorm:"not null;comment:'主精灵ID尼尔'" json:"main_pet_id"`
AuxPetID int32 `gorm:"not null;comment:'副精灵ID闪皮'" json:"aux_pet_id"`
FusionMaterial1 int32 `gorm:"not null;default:0;comment:'融合材料1编号'" json:"fusion_material1"`
FusionMaterial2 int32 `gorm:"not null;default:0;comment:'融合材料2编号'" json:"fusion_material2"`
FusionMaterial3 int32 `gorm:"not null;default:0;comment:'融合材料3编号'" json:"fusion_material3"`
FusionMaterial4 int32 `gorm:"not null;default:0;comment:'融合材料4编号'" json:"fusion_material4"`
FusionProbability float64 `gorm:"not null;comment:'融合成功概率百分比0-100'" json:"fusion_probability"`
ResultPetID int32 `gorm:"not null;comment:'融合结果精灵ID卡鲁/闪尼)'" json:"result_pet_id"`
}
// TableName PetFusionConfig's table name
func (*PetFusionConfig) TableName() string {
return TableNamePetFusionConfig
}
// GroupName PetFusionConfig's table group
func (*PetFusionConfig) GroupName() string {
return "default" // 与原模型保持一致的分组名
}
// NewPetFusionConfig create a new PetFusionConfig
func NewPetFusionConfig() *PetFusionConfig {
return &PetFusionConfig{
Model: cool.NewModel(),
}
}
// 初始化表结构(与原模型保持一致的初始化方式)
func init() {
cool.CreateTable(&PetFusionConfig{})
}

View File

@@ -1,8 +1,15 @@
package service
import (
"blazing/common/utils"
"blazing/cool"
"blazing/modules/blazing/model"
dictmodel "blazing/modules/dict/model"
"blazing/modules/dict/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
)
// PetFusionMaterialService 宠物融合材料子表Service对应pet_fusion_material表
@@ -18,3 +25,60 @@ func NewPetFusionMaterialService() *PetFusionMaterialService {
},
}
}
// 获取融合材料的特性,返回两个值,一个是指定的特性,另一个是如果配方没找到的情况下,默认的配置
func (s *PetFusionMaterialService) Data(Material1 [4]uint32) uint32 {
fusion, _ := service.NewDictInfoService().DataOne("fusion")
fusions := utils.ToMap(fusion, func(t dictmodel.DictInfo) uint32 {
return gconv.Uint32(t.Remark)
})
for _, v := range Material1 {
_, ok := fusions[v]
if !ok {
//todo使用了非法材料
return 0
}
}
m := cool.DBM(s.Model)
var effect model.PetFusionMaterial //一个特性应该是唯一的,但是我们要获取默认随机特性
condition := g.Map{
"material1": fusions[Material1[0]].ID,
"material2": fusions[Material1[1]].ID,
"material3": fusions[Material1[2]].ID,
"material4": fusions[Material1[3]].ID,
"is_enable": 1,
}
m.Where(condition).Scan(&effect)
//这时候有可能效果是空的,那么这时候就再次查询默认的特性,保证每次必会生成一个数据库有的特性
//也许这个时候的特性配方就是随机从数据库中查找一个特性
effect2, _ := service.NewDictInfoService().DataOne("effect")
effect2s := utils.ToMap(effect2, func(t dictmodel.DictInfo) uint32 {
return gconv.Uint32(t.ID)
})
if effect.Trait1Idx != 0 {
r := grand.Intn(3)
switch r {
case 0:
return gconv.Uint32(effect2s[effect.Trait1Idx].Remark)
case 1:
return gconv.Uint32(effect2s[effect.Trait2Idx].Remark)
case 2:
return gconv.Uint32(effect2s[effect.Trait3Idx].Remark)
case 3:
return gconv.Uint32(effect2s[effect.Trait4Idx].Remark)
}
}
r := grand.Intn(len(effect2) - 1)
return gconv.Uint32(effect2[r].Remark)
}

View File

@@ -3,6 +3,9 @@ package service
import (
"blazing/cool"
"blazing/modules/blazing/model"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/grand"
)
// PetFusionService 宠物融合配方主表Service对应pet_fusion表
@@ -15,6 +18,42 @@ func NewPetFusionService() *PetFusionService {
return &PetFusionService{
&cool.Service{
Model: model.NewPetFusion(), // 绑定PetFusion模型
},
}
}
//获取主副精灵融合的id,如果不存在,那就给一个保底的id
func (s *PetFusionService) Data(p1, p2 uint32) uint32 {
m := cool.DBM(s.Model)
var pet []model.PetFusion //一个特性应该是唯一的,但是我们要获取默认随机特性
condition := g.Map{
"main_pet_id": p1,
"sub_pet_id": p2,
"is_enable": 1,
// "hits between ? and ?" : g.Slice{1, 10},
// "exp > 0" : nil,
// "category" : g.Slice{100, 200},
}
m.Where(condition).Scan(&pet)
for _, v := range pet {
rr := grand.Intn(100)
if rr < int(v.Probability) {
return uint32(v.ResultPetID)
}
}
var pets []model.PetFusion
m.Where("is_enable", 1).Where("is_default", 1).Scan(&pets)
if len(pets) == 0 {
return 0
}
return uint32(pets[grand.Intn(len(pets)-1)].ID)
}

View File

@@ -1,18 +0,0 @@
package service
import (
"blazing/cool"
"blazing/modules/blazing/model"
)
type FusionService struct {
*cool.Service
}
func NewFusionService() *FusionService {
return &FusionService{
&cool.Service{
Model: model.NewPetFusionConfig(),
},
}
}

View File

@@ -53,6 +53,26 @@ func (s *DictInfoService) Data(ctx context.Context, types []string) (data interf
}
return
}
func (s *DictInfoService) DataOne(types string) (data []model.DictInfo, err error) {
var (
dictInfoModel = model.NewDictInfo()
dictTypeModel = model.NewDictType()
)
// 如果typeData为空, 则返回空
var ty *model.DictType
mType := cool.DBM(dictTypeModel)
mType.Where("key in (?)", types).Scan(&ty)
// 如果typeData为空, 则返回空
if ty == nil {
return []model.DictInfo{}, nil
}
m := cool.DBM(dictInfoModel)
var ress []model.DictInfo
m.Where("typeId", ty.ID).Scan(&ress)
return ress, nil
}
// ModifyAfter 修改后
func (s *DictInfoService) ModifyAfter(ctx context.Context, method string, param map[string]interface{}) (err error) {