From 2860bcfa5cbcac6cc9baa75e4dd9204dfc37abb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=94=E5=BF=B5?= <12574910+72wo@users.noreply.github.com> Date: Mon, 9 Feb 2026 01:29:33 +0800 Subject: [PATCH] 1 --- common/utils/help.go | 90 ++++++++----- logic/controller/active_寒流枪.go | 5 + logic/controller/fight_boss.go | 3 + logic/controller/{pet_egg.go => pet_繁殖.go} | 91 +++++++++++-- logic/service/fight/loop.go | 12 +- modules/config/model/egg.go | 4 +- modules/config/service/egg.go | 38 +++++- modules/config/service/pet_fusion_service.go | 2 +- modules/player/model/egg.go | 1 + modules/player/model/pet.go | 6 + modules/player/service/egg.go | 130 ++++++++++++++++++- modules/player/service/room.go | 1 - 12 files changed, 319 insertions(+), 64 deletions(-) rename logic/controller/{pet_egg.go => pet_繁殖.go} (64%) diff --git a/common/utils/help.go b/common/utils/help.go index 7aaaea9ea..f54aa6473 100644 --- a/common/utils/help.go +++ b/common/utils/help.go @@ -2,6 +2,8 @@ package utils import ( "errors" + "fmt" + "math/rand/v2" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/grand" @@ -32,53 +34,71 @@ func RemoveLast(s string) string { return string(runes[:len(runes)-1]) } -// ************************** 函数1:核心函数(双数组+int型概率,泛型)************************** -// randomByIntProbs 核心概率计算函数:接收任意类型元素数组 + int型概率数组,实现权重随机 -// T any:支持任意类型的元素切片,职责单一,只处理纯int概率的核心逻辑 -func RandomByIntProbs[T any](natureSet []T, probs []int) (T, error) { +// RandomByWeight 根据整数权重随机选择元素(优化后的泛型版本) +// 入参: +// - elements: 待随机的元素集合(非空) +// - weights: 对应元素的权重(非负整数,长度需与elements一致;长度不匹配/总权重为0时降级为等概率随机) +// +// 返回: +// - 随机选中的元素 +// - 错误(仅当elements为空/权重值为负时返回) +func RandomByWeight[Element any, Weight integer](elements []Element, weights []Weight) (Element, error) { // 定义泛型零值,用于错误返回 - var zeroT T + var zero Element - // 1. 合法性校验:元素数组为空 或 概率数组与元素数组长度不匹配 - if len(natureSet) == 0 { - return zeroT, errors.New("natureSet is empty") - } - if len(probs) == 0 || len(natureSet) != len(probs) { - // 长度不匹配,降级为等概率随机(兼容原有逻辑) - return natureSet[grand.Intn(len(natureSet))], nil + // 1. 核心合法性校验:元素集合不能为空 + if len(elements) == 0 { + return zero, errors.New("elements set is empty (cannot random from empty slice)") } - // 2. 校验概率值非负,并计算总概率 - totalProb := 0 - for i, p := range probs { - if p < 0 { - return zeroT, errors.New("invalid prob value: index " + gconv.String(i) + " (must be non-negative integer)") + // 2. 权重数组合法性校验:长度不匹配/为空时,降级为等概率随机(兼容原逻辑) + elemLen := len(elements) + if len(weights) == 0 || len(weights) != elemLen { + return elements[rand.IntN(elemLen)], nil + } + + // 3. 校验权重非负,并计算总权重(统一转为int64避免溢出) + var totalWeight int64 + // 预转换权重为int64,避免重复类型转换 + intWeights := make([]int64, elemLen) + for i, w := range weights { + intW := int64(w) + if intW < 0 { + return zero, fmt.Errorf("invalid negative weight at index %d (value: %d)", i, w) } - totalProb += p + intWeights[i] = intW + totalWeight += intW } - // 3. 总概率为0,降级为等概率随机 - if totalProb == 0 { - return natureSet[grand.Intn(len(natureSet))], nil + // 4. 总权重为0时,降级为等概率随机 + if totalWeight == 0 { + return elements[rand.IntN(elemLen)], nil } - // 4. 计算前缀和(权重随机核心逻辑) - prefixSum := make([]int, len(probs)) - prefixSum[0] = probs[0] - for i := 1; i < len(probs); i++ { - prefixSum[i] = prefixSum[i-1] + probs[i] + // 5. 计算前缀和(权重随机核心逻辑,复用预转换的int64权重) + prefixWeights := make([]int64, elemLen) + prefixWeights[0] = intWeights[0] + for i := 1; i < elemLen; i++ { + prefixWeights[i] = prefixWeights[i-1] + intWeights[i] } - // 5. 生成随机数匹配前缀和,返回对应元素 - randVal := grand.Intn(totalProb) - for i, sum := range prefixSum { - if randVal < sum { - return natureSet[i], nil + // 6. 生成随机数并匹配前缀和(用int64避免溢出) + randomValue := grand.Intn(int(totalWeight)) + for i, sum := range prefixWeights { + if randomValue < int(sum) { + return elements[i], nil } } - // 极端情况兜底,返回第一个元素 - return natureSet[0], nil + // 极端兜底:理论上不会走到这里(randomValue < totalWeight),返回第一个元素保证不panic + return elements[0], nil +} + +// integer 自定义泛型约束:匹配所有整数类型(扩展原~int的限制) +// 包含:int/int8/int16/int32/int64/uint/uint8/uint16/uint32/uint64/uintptr +type integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr } // ************************** 函数2:封装函数(兼容string型概率,泛型)************************** @@ -89,7 +109,7 @@ func RandomByProbs[T any](natureSet []T, probs []string) (T, error) { // 1. 若string概率数组为空,直接调用核心函数(核心函数会处理降级逻辑) if len(probs) == 0 { - return RandomByIntProbs(natureSet, []int{}) + return RandomByWeight(natureSet, []int{}) } // 2. string[] 转换为 int[](使用 gconv.Int 完成转换) @@ -100,5 +120,5 @@ func RandomByProbs[T any](natureSet []T, probs []string) (T, error) { } // 3. 调用核心函数,复用概率计算逻辑 - return RandomByIntProbs(natureSet, probInts) + return RandomByWeight(natureSet, probInts) } diff --git a/logic/controller/active_寒流枪.go b/logic/controller/active_寒流枪.go index ee2d70212..7a3d5c7a9 100644 --- a/logic/controller/active_寒流枪.go +++ b/logic/controller/active_寒流枪.go @@ -2,6 +2,7 @@ package controller import ( "blazing/common/socket/errorcode" + "blazing/cool" "blazing/logic/service/fight" "blazing/logic/service/pet" "blazing/logic/service/player" @@ -16,5 +17,9 @@ func (h Controller) HanLiuQiang(data *pet.C2S_2608, c *player.Player) (result *f if c.ItemAdd(100245, 1) { return } + + if cool.Config.ServerInfo.IsVip != 0 { + c.ItemAdd(500655, 1) + } return result, -1 } diff --git a/logic/controller/fight_boss.go b/logic/controller/fight_boss.go index a827a4248..06ed974dd 100644 --- a/logic/controller/fight_boss.go +++ b/logic/controller/fight_boss.go @@ -157,6 +157,9 @@ func (Controller) OnPlayerFightNpcMonster(data1 *fight.FightNpcMonsterInboundInf if !p.CanFight() { return nil, errorcode.ErrorCodes.ErrSystemError } + if data1.Number > 9 { + return nil, errorcode.ErrorCodes.ErrSystemError + } refPet := p.OgreInfo.Data[data1.Number] if refPet.ID == 0 { diff --git a/logic/controller/pet_egg.go b/logic/controller/pet_繁殖.go similarity index 64% rename from logic/controller/pet_egg.go rename to logic/controller/pet_繁殖.go index db96d2fc8..282dcd23b 100644 --- a/logic/controller/pet_egg.go +++ b/logic/controller/pet_繁殖.go @@ -5,7 +5,13 @@ import ( "blazing/logic/service/fight" "blazing/logic/service/pet" "blazing/logic/service/player" + "blazing/modules/config/service" "blazing/modules/player/model" + "time" + + "github.com/gogf/gf/v2/util/grand" + "github.com/samber/lo" + "github.com/yudeguang/ratelimit" ) // GetBreedInfo 获取繁殖信息协议 @@ -18,6 +24,7 @@ func (ctl Controller) GetBreedInfo( if r == nil { return } + result = &r.Data // TODO: 实现获取繁殖信息的具体逻辑 return result, 0 @@ -28,11 +35,38 @@ func (ctl Controller) GetBreedInfo( // 前端到后端 func (ctl Controller) GetBreedPet( data *pet.C2S_GET_BREED_PET, playerObj *player.Player) (result *pet.S2C_GET_BREED_PET, err errorcode.ErrorCode) { //这个时候player应该是空的 - + _, fPet, found := playerObj.FindPet(data.MaleCatchTime) + if !found { + return nil, errorcode.ErrorCodes.ErrPokemonNotExists + } + if fPet.Gender != 1 { + return nil, errorcode.ErrorCodes.ErrPokemonNotExists + } + if fPet.Generation > 9 { + return nil, errorcode.ErrorCodes.ErrPokemonNotExists + } result = &pet.S2C_GET_BREED_PET{} - // TODO: 实现获取可繁殖雌性精灵列表的逻辑 + + MPETS := service.NewEggService().GetData(fPet.ID) + // 这里只是示例,实际应该根据雄性精灵的catchTime查找可繁殖的雌性精灵 for _, v := range playerObj.Info.PetList { + if v.Level < 50 { + continue + } + //不是雌性 + if v.Gender != 2 { + continue + } + if v.Generation > 9 { + continue + } + _, ok := lo.Find(MPETS, func(item int32) bool { + return item == int32(v.ID) + }) + if !ok { + continue + } // 如果是雌性精灵,且可以繁殖,则添加到列表 result.FemaleList = append(result.FemaleList, v.CatchTime) } @@ -44,6 +78,11 @@ func (ctl Controller) GetBreedPet( // 前端到后端 func (ctl Controller) StartBreed( data *pet.C2S_START_BREED, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的 + + if !player.GetCoins(1000) { + return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016 + } + player.Info.Coins -= 1000 _, MalePet, found := player.FindPet(data.Male) if !found { return nil, errorcode.ErrorCodes.ErrPokemonNotExists @@ -58,6 +97,16 @@ func (ctl Controller) StartBreed( if !r { return nil, errorcode.ErrorCodes.ErrCannotPerformAction } + MalePet.Generation++ + + Female.Generation++ + + r1 := grand.Meet(1, 2) + if r1 { + MalePet.Gender = 0 + } else { + Female.Gender = 0 + } return result, 0 } @@ -75,22 +124,36 @@ func (ctl Controller) GetEggList( return } - for _, v := range r.EggList { - result.EggList = append(result.EggList, v) - } + result.EggList = append(result.EggList, r.EggList...) return result, 0 } +var limiter *ratelimit.Rule = ratelimit.NewRule() + +// 简单规则案例 +func init() { + + //步骤二:增加一条或者多条规则组成复合规则,此复合规则必须至少包含一条规则 + limiter.AddRule(time.Second*1, 1) + //步骤三:调用函数判断某用户是否允许访问 allow:= r.AllowVisit(user) + +} + // EffectHatch 精灵蛋互动协议 // 前端到后端 func (ctl Controller) EffectHatch( data *pet.C2S_EFFECT_HATCH, playerObj *player.Player) (result *pet.S2C_EFFECT_HATCH, err errorcode.ErrorCode) { - + if !limiter.AllowVisit(data.Head.UserID) { + return nil, errorcode.ErrorCodes.ErrCannotPerformAction + } result = &pet.S2C_EFFECT_HATCH{} - // TODO: 实现精灵蛋互动逻辑,根据互动ID更新亲密度 - result.Intimacy = 1 // 悲伤 + tt := uint32(grand.N(1, 4)) + if tt == data.Index { + result.Intimacy = playerObj.Service.Egg.EffectHatch() + } + return result, 0 } @@ -102,6 +165,10 @@ func (ctl Controller) StartHatch( // TODO: 实现开始孵化精灵蛋的具体逻辑 result = &pet.S2C_START_HATCH{} + r := playerObj.Service.Egg.StartEgg(data.OwnerID, data.EggCatchTime) + if !r { + return nil, errorcode.ErrorCodes.ErrCannotPerformAction + } return result, 0 } @@ -113,8 +180,12 @@ func (ctl Controller) GetHatchPet( result = &pet.S2C_GET_HATCH_PET{} // TODO: 实现获得孵化精灵的具体逻辑,这里暂时返回默认值 - result.PetID = 0 - result.CatchTime = 0 + + r := playerObj.Service.Egg.GetEgg() + playerObj.Service.Pet.PetAdd(r) + + result.PetID = r.ID + result.CatchTime = r.CatchTime return result, 0 } diff --git a/logic/service/fight/loop.go b/logic/service/fight/loop.go index 967cc39d4..5bd7a1c15 100644 --- a/logic/service/fight/loop.go +++ b/logic/service/fight/loop.go @@ -247,21 +247,24 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat //fmt.Println("玩家执行动作:", pid, paction.Priority()) case <-timeout: - f.handleTimeout(ourID, oppID, actions) - return actions + r := f.handleTimeout(ourID, oppID, actions) + if r { + return actions + } + } } return actions } // 超时处理逻辑 -func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.BattleActionI) { +func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.BattleActionI) bool { if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC { // f.WinnerId = 0 go f.Our.GetAction() //boss出手后获取出招 - + return false } else { var pid uint32 for _, pid = range []uint32{ourID, oppID} { @@ -275,6 +278,7 @@ func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.Ba f.WinnerId = f.GetInputByPlayer(f.getPlayerByID(pid), false).Player.GetInfo().UserID f.Reason = info.BattleOverReason.PlayerOVerTime f.closefight = true + return true } } diff --git a/modules/config/model/egg.go b/modules/config/model/egg.go index 1907be754..8edc9858b 100644 --- a/modules/config/model/egg.go +++ b/modules/config/model/egg.go @@ -11,9 +11,9 @@ const ( // Egg 性别配置模型(替换原宠物融合配方模型) type Egg struct { *cool.Model // 保留通用Model(ID/创建时间/更新时间等) - //雄性 - MalePetIDs []int32 `gorm:"type:int[];comment:'雄性宠物ID列表(如:[1001,1002])'" json:"male_pet_ids"` //雌性 + MalePetIDs []int32 `gorm:"type:int[];comment:'雄性宠物ID列表(如:[1001,1002])'" json:"male_pet_ids"` + //雄性 FemalePetIDs []int32 `gorm:"type:int[];comment:'雌性宠物ID列表(如:[1001,1002])'" json:"female_pet_ids"` //子代指定性别配置表名 OutputMons []int32 `gorm:"type:int[];not null;comment:'宠物类型ID'" json:"pet_type_id"` diff --git a/modules/config/service/egg.go b/modules/config/service/egg.go index 519833420..7acd38c99 100644 --- a/modules/config/service/egg.go +++ b/modules/config/service/egg.go @@ -1,6 +1,7 @@ package service import ( + "blazing/common/utils" "blazing/cool" "blazing/modules/config/model" ) @@ -20,13 +21,36 @@ func NewEggService() *EggService { } } -func (s *EggService) Get(id uint32) *model.Egg { - if id == 0 { - return nil - } - var item *model.Egg - dbm(s.Model).Where("id", id).Scan(&item) +func (s *EggService) GetData(p1 uint32) []int32 { + //cacheKey := strings.Join([]string{fmt.Sprintf("%d", p1), fmt.Sprintf("%d", p2)}, ":") + m := dbm(s.Model) - return item + var pet []model.Egg //一个特性应该是唯一的,但是我们要获取默认随机特性 + m.Wheref(`male_pet_ids @> ARRAY[?]::integer[]`, p1). + Where("is_enable", 1).Scan(&pet) + var petIDs []int32 + for _, p := range pet { + petIDs = append(petIDs, p.FemalePetIDs...) + } + + return petIDs + +} +func (s *EggService) GetResult(m, f uint32) uint32 { + //cacheKey := strings.Join([]string{fmt.Sprintf("%d", p1), fmt.Sprintf("%d", p2)}, ":") + md := dbm(s.Model) + + var pet *model.Egg //一个特性应该是唯一的,但是我们要获取默认随机特性 + md.Wheref(`male_pet_ids @> ARRAY[?]::integer[]`, m). + Wheref(`female_pet_ids @> ARRAY[?]::integer[]`, f). + Where("is_enable", 1).Scan(&pet) + if pet != nil { + + t, _ := utils.RandomByWeight(pet.OutputMons, pet.Probs) + return uint32(t) + + } + + return 0 } diff --git a/modules/config/service/pet_fusion_service.go b/modules/config/service/pet_fusion_service.go index aef094de0..c74b8727d 100644 --- a/modules/config/service/pet_fusion_service.go +++ b/modules/config/service/pet_fusion_service.go @@ -44,7 +44,7 @@ func (s *PetFusionService) Data(p1, p2, rand uint32) uint32 { props = append(props, int(v.Probability)) } - t,_:=utils.RandomByIntProbs(pets, props) + t, _ := utils.RandomByWeight(pets, props) return uint32(t) //说明是失败,直接返回失败 } else { diff --git a/modules/player/model/egg.go b/modules/player/model/egg.go index c9a39299a..10743ebbb 100644 --- a/modules/player/model/egg.go +++ b/modules/player/model/egg.go @@ -51,6 +51,7 @@ type EggInfo struct { EggID uint32 `json:"eggID"` // 精灵蛋ID MalePetID uint32 `json:"male"` // 雄性精灵ID FeMalePetID uint32 `json:"female"` // 雌性精灵ID + //ShinyCode uint32 `struc:"skip"` //返回记录 } // TableName 返回表名 diff --git a/modules/player/model/pet.go b/modules/player/model/pet.go index fdbbaa372..2a971a567 100644 --- a/modules/player/model/pet.go +++ b/modules/player/model/pet.go @@ -422,6 +422,9 @@ func GenPetInfo( dv, natureId, abilityTypeEnum, level int, shinyid []data.GlowFilter, gen int, ) *PetInfo { + if id == 0 { + return nil + } // 创建随机源 //rng := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -447,6 +450,9 @@ func GenPetInfo( if gen == -1 { p.Gender = grand.N(1, 4) + if cool.Config.ServerInfo.IsVip != 0 { + p.Gender = grand.N(1, 2) + } if p.Gender == 3 || p.Gender == 4 { p.Gender = 0 diff --git a/modules/player/service/egg.go b/modules/player/service/egg.go index c0b4e4e74..522a71bde 100644 --- a/modules/player/service/egg.go +++ b/modules/player/service/egg.go @@ -1,9 +1,13 @@ package service import ( + "blazing/common/utils" "blazing/cool" + "blazing/modules/config/service" "blazing/modules/player/model" "time" + + "github.com/gogf/gf/v2/util/grand" ) type EggService struct { @@ -23,6 +27,16 @@ func NewEggService(id uint32) *EggService { func (s *EggService) Get() (out *model.Egg) { s.TestModel(s.Model).Scan(&out) + if out != nil { + BreedLeftTime := int64(out.Data.StartTime+out.CurEgg.EggID*uint32(time.Hour/1000000000)) - (time.Now().Unix()) + //判断是否繁殖完成 + if BreedLeftTime < 0 && out.Data.HatchState == 1 { + out.Data.HatchState = 2 + } else { + out.Data.HatchLeftTime = uint32(BreedLeftTime) + } + s.TestModel(s.Model).Save(out) + } return @@ -33,16 +47,124 @@ func (s *EggService) StartBreed(m, f *model.PetInfo) bool { s.TestModel(s.Model).Scan(&tt) if tt == nil { tt = &model.Egg{} + tt.IsVip = cool.Config.ServerInfo.IsVip + } + //如果正在孵化中或者蛋列表超过4个了,就不能再开始新的繁殖了 + if len(tt.EggList) > 4 { + return false + } + + cureff := model.EggInfo{FeMalePetID: f.ID, MalePetID: m.ID} + + cureff.EggCatchTime = uint32(time.Now().Unix()) + + cureff.EggID = (uint32(m.Generation) + uint32(f.Generation)) / 2 + if cureff.EggID == 0 { + cureff.EggID = 1 + } + cureff.OwnerID = uint32(s.userid) + + cureff.FeMalePetID = f.ID + + cureff.MalePetID = m.ID + tt.PlayerID = uint64(s.userid) + //tt.CurEgg = cureff + tt.EggList = append(tt.EggList, cureff) + + s.TestModel(s.Model).Save(tt) + return true +} +func (s *EggService) StartEgg(owner, eggc uint32) bool { + + var tt *model.Egg + s.TestModel(s.Model).Scan(&tt) + if tt == nil { + return false } if tt.Data.HatchState != 0 { return false } + + index, v, ok := utils.FindWithIndex(tt.EggList, func(item model.EggInfo) bool { + return item.EggCatchTime == eggc + }) + if !ok { + return false + } + tt.EggList = append(tt.EggList[:index], tt.EggList[index+1:]...) //删除精灵蛋 tt.Data.StartTime = uint32(time.Now().Unix()) + tt.Data.HatchState = 1 - tt.Data.FeMalePetCatchTime = f.CatchTime - tt.Data.MalePetCatchTime = m.CatchTime - tt.Data.FeMalePetID = f.ID - tt.Data.MalePetID = m.ID + + tt.Data.EggID = v.EggID + + tt.Data.FeMalePetID = v.FeMalePetID + + tt.Data.MalePetID = v.MalePetID + + tt.PlayerID = uint64(s.userid) + tt.CurEgg = *v + + tt.Data.Intimacy = 1 + s.TestModel(s.Model).Save(tt) return true + +} +func (s *EggService) EffectHatch() uint32 { + + var tt *model.Egg + s.TestModel(s.Model).Scan(&tt) + if tt == nil { + return 1 + } + if tt.Data.HatchState != 1 { + return 1 + + } + tt.Data.Intimacy += 1 + if tt.Data.Intimacy > 5 { + tt.Data.Intimacy = 5 + } + s.TestModel(s.Model).Save(tt) + return tt.Data.Intimacy +} +func (s *EggService) GetEgg() *model.PetInfo { + + var tt *model.Egg + s.TestModel(s.Model).Scan(&tt) + if tt == nil { + return nil + } + if tt.Data.HatchState != 2 { + return nil + + } + if tt.CurEgg.EggCatchTime == 0 { + return nil + + } + mpets := service.NewEggService().GetResult(tt.CurEgg.MalePetID, tt.CurEgg.FeMalePetID) + if mpets == 0 { + return nil + } + + dv := grand.N(int(tt.Data.Intimacy+tt.CurEgg.EggID*2), 31) + p := model.GenPetInfo(int(mpets), dv, -1, 0, 1, nil, -1) + shinycont := 0 + + if mpets != tt.CurEgg.MalePetID && mpets != tt.CurEgg.FeMalePetID { + shinycont = int(tt.CurEgg.EggID) + } + + if grand.Meet(shinycont, 100) { + p.FixShiny() + } + //直接覆盖当前蛋 + tt.Data.HatchState = 0 + p.Generation = uint16(tt.CurEgg.EggID) + 1 + tt.CurEgg = model.EggInfo{} + s.TestModel(s.Model).Save(tt) + + return p } diff --git a/modules/player/service/room.go b/modules/player/service/room.go index abd5d08b0..6b79d3347 100644 --- a/modules/player/service/room.go +++ b/modules/player/service/room.go @@ -18,7 +18,6 @@ func (s *RoomService) Get(userid uint32) model.BaseHouseEx { } - func (s *RoomService) Set(id []model.FitmentShowInfo) { //todo待测试 if cool.Config.ServerInfo.IsVip != 0 {