feat(pet): 实现精灵融合功能并优化相关逻辑

新增精灵融合接口及处理逻辑,支持主副精灵融合生成新精灵,并消耗金币与材料。
同时调整了战斗技能选择流程、修复地图热度统计安全问题以及完善宠物删除机制。

- 添加 `PetFusion` 控制器方法实现融合核心逻辑
- 新增 `C2S_PetFusion` 和 `PetFusionInfo` 结构体用于通信
- 修正战斗中技能随机选取后立即返回的问题
- 修复太空站进入/离开时对地图热度的并发访问风险
-
This commit is contained in:
2025-11-26 01:33:48 +08:00
parent f682abe537
commit f76587f952
14 changed files with 198 additions and 18 deletions

View File

@@ -1 +1,47 @@
package controller
import (
"blazing/common/data/xmlres"
"blazing/common/socket/errorcode"
"blazing/logic/service/pet"
"blazing/logic/service/player"
"blazing/modules/blazing/model"
)
func (h Controller) PetFusion(data *pet.C2S_PetFusion, c *player.Player) (result *pet.PetFusionInfo, err errorcode.ErrorCode) {
if !c.UseCoins(1000) {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
//防止同一只
if data.Mcatchtime == data.Auxcatchtime {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
_, Mcatchpetinfo, ok := c.FindPet(data.Mcatchtime)
if !ok {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
if xmlres.PetMAP[int(Mcatchpetinfo.ID)].FuseMaster == 0 {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
_, Auxpetinfo, ok := c.FindPet(data.Auxcatchtime)
if !ok {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
if xmlres.PetMAP[int(Auxpetinfo.ID)].FuseSub == 0 {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
r := model.GenPetInfo(1, -1, -1, -1, -1, 1)
c.Service.Pet.PetAdd(*r)
c.Pet_del(Auxpetinfo.CatchTime)
c.Pet_del(Mcatchpetinfo.CatchTime)
return &pet.PetFusionInfo{
ObtainTime: r.CatchTime,
SoulID: 1000017,
StarterCpTm: r.ID,
CostItemFlag: 0,
}, 0
}

View File

@@ -186,9 +186,6 @@ func (h *Controller) PetOneCure(
return result, errorcode.ErrorCodes.ErrChampionCannotHeal
}
if !c.UseCoins(20) {
return result, errorcode.ErrorCodes.ErrSystemBusy
}
_, onpet, ok := c.FindPet(data.CatchTime)
if ok {
onpet.Cure()

View File

@@ -0,0 +1,17 @@
package controller
import (
"blazing/common/socket/errorcode"
"blazing/logic/service/pet"
"blazing/logic/service/player"
)
func (h *Controller) IS_COLLECT(
data *pet.C2S_IS_COLLECT, c *player.Player) (result *pet.S2C_IS_COLLECT, err errorcode.ErrorCode) { //这个时候player应该是空的
result = &pet.S2C_IS_COLLECT{
ID: data.Type,
IsCom: 0,
}
return result, 0
}

View File

@@ -267,6 +267,8 @@ func (our *Input) GetAction(opp *Input) {
for i := 0; i < len(allSkills); i++ {
if randomIdx == int(i) {
our.FightC.UseSkill(our.Player, uint32(allSkills[i].ID))
return
}
}

View File

@@ -188,11 +188,11 @@ func (f *FightC) resolveRound(p1Action, p2Action action.BattleActionI) {
f.GetInputByAction(a, false).CurrentPet.Info.Hp = 1
}
if b2k, ok := b2.(*action.SelectSkillAction); ok {
if b2k.CD != nil {
f.waittime = *b2k.CD
if b2k.SkillEntity != nil {
if b2k.CD != nil {
f.waittime = *b2k.CD
}
}
f.enterturn(b2.(*action.SelectSkillAction), nil)
} else {

View File

@@ -21,3 +21,24 @@ type PetShortInfo struct {
SkinID uint32 // 精灵皮肤ID
Shiny uint32 // 精灵是否闪光0=否1=是)
}
// C2S_PetFusion 前端(Client)到后端(Server)的精灵融合请求包
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
}
// PetFusionInfo 精灵融合结果详情(后端回包嵌套结构体)
type PetFusionInfo struct {
ObtainTime uint32 `json:"obtainTime" msgpack:"obtainTime"` // 如果获得时间为0 则代表融合失败
SoulID uint32 `json:"soulID" msgpack:"soulID"` // 元神珠的id 融合失败为0
StarterCpTm uint32 `json:"starterCpTm" msgpack:"starterCpTm"` // 融合失败为0
CostItemFlag uint32 `json:"costItemFlag" msgpack:"costItemFlag"` // 是不是消耗掉了金豆道具 1代表消耗道具
}

View File

@@ -78,3 +78,19 @@ type PetDefaultInboundInfo struct {
type PetDefaultOutboundInfo struct {
IsDefault uint32 `json:"isDefault" fieldDescription:"0: 首发设置失败1: 首发设置成功" uint:"true" autoCodec:"true" outboundMessageType:"Pet_Default"`
}
// C2S_IS_COLLECT 前端(Client)→后端(Server):精灵收集任务状态查询请求包
type C2S_IS_COLLECT struct {
Head common.TomeeHeader `cmd:"2313" struc:"skip"`
Type uint32 `json:"type" msgpack:"type"` // 收集类型301=太空站10胜1=精灵收集计划一期第一种2=精灵收集计划一期第二种
}
// S2C_IS_COLLECT 后端(Server)→前端(Client):精灵收集任务状态查询回包
// 补充说明:
// 1. 此包同时适用于「精灵收集计划」和「太空站10胜送主宠」场景
// 2. isCom=0未领取才可调用 PRIZE_OF_PETKING(2317) 协议领取精灵
// 3. isCom=1已完成/已领取)时,代表奖励已领取,无需再调用领取协议
type S2C_IS_COLLECT struct {
ID uint32 `json:"id" msgpack:"id"` // 收集类型301=太空站10胜1=精灵收集计划一期第一种2=精灵收集计划一期第二种
IsCom uint32 `json:"isCom" msgpack:"isCom"` // 是否完成/是否已领取0=未完成/未领取1=已完成/已领取
}

View File

@@ -50,6 +50,15 @@ func (f *baseplayer) FindPet(CatchTime uint32) (int, *model.PetInfo, bool) {
return item.CatchTime == CatchTime
})
}
func (f *Player) Pet_del(CatchTime uint32) {
index, _, ok := f.FindPet(CatchTime)
if ok {
f.Info.PetList = append(f.Info.PetList[:index], f.Info.PetList[index+1:]...)
}
f.Service.Pet.Pet_del(CatchTime)
}
// // 计算整数的二进制1的个数Integer.bitCount
// func bitsCount(n int) int {

View File

@@ -46,8 +46,10 @@ func (s *Space) LeaveMap(c common.PlayerI) {
s.User.Delete(c.GetInfo().UserID)
s.UserInfo.Delete(c.GetInfo().UserID)
atomic.AddInt32(maphot[s.Super], -1)
_, ok := maphot[s.Super]
if ok {
atomic.AddInt32(maphot[s.Super], -1)
}
}
@@ -59,8 +61,10 @@ func (s *Space) EnterMap(c common.PlayerI) {
s.User.Store(c.GetInfo().UserID, c)
s.UserInfo.Store(c.GetInfo().UserID, *out)
s.Broadcast(c, 2001, out)
atomic.AddInt32(maphot[s.Super], 1)
_, ok := maphot[s.Super]
if ok {
atomic.AddInt32(maphot[s.Super], 1)
}
}
func (s *Space) GetInfo() []maps.OutInfo {

View File

@@ -74,11 +74,11 @@ func GetSpace(id uint32) *Space {
for _, v := range xmlres.MapConfig.Maps {
if v.ID == int(id) { //找到这个地图
// t.Super = uint32(v.Super)
// if t.Super == 0 {
// t.Super = uint32(v.ID)
// }
t.Super = uint32(v.ID)
t.Super = uint32(v.Super)
if t.Super == 0 {
t.Super = uint32(v.ID)
}
_, ok := maphot[t.Super]
if !ok {
var t1 int32

View File

@@ -0,0 +1,45 @@
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

@@ -36,7 +36,7 @@ func (s *PetService) PetInfo_One_exec(cachetime uint32, t func(*model.PetEX)) {
}
tt.Data.CatchTime = tt.CatchTime
t(&tt)
_,err := m.OnConflict("catch_time").Update(tt)
_, err := m.OnConflict("catch_time").Update(tt)
if err != nil {
panic(err)
}
@@ -50,6 +50,11 @@ func (s *PetService) PetInfo_One(cachetime uint32) model.PetEX {
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()
}
func (s *PetService) PetAdd(y model.PetInfo) {
for {

View File

@@ -0,0 +1,18 @@
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

@@ -431,7 +431,7 @@ eg:
<Map ID="8" Name="机械舱" InitX="456" InitY="143">
<Bosses>
<Boss TaskID="4" AppearTime="0 23" BossVisible="0" >
<Boss AppearTime="0 23" BossVisible="0" >
<BossMon MonID="1 4 7" Hp="10" Lv="2" />
</Boss>
<Boss AppearTime="0 23" BossVisible="0" >