This commit is contained in:
@@ -82,7 +82,9 @@ func (h Controller) DASHIbeiR(req *C2s_MASTER_REWARDSR, c *player.Player) (resul
|
||||
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrAwardAlreadyClaimed)
|
||||
}
|
||||
|
||||
consumeMasterCupItems(c, requiredItems)
|
||||
if err := consumeMasterCupItems(c, requiredItems); err != nil {
|
||||
return nil, errorcode.ErrorCode(errorcode.ErrorCodes.ErrInsufficientItems)
|
||||
}
|
||||
progress.Set(uint(req.ElementType))
|
||||
taskData.Data = progress.Bytes()
|
||||
if taskErr = c.Service.Task.SetTask(taskData); taskErr != nil {
|
||||
@@ -130,10 +132,13 @@ func hasEnoughMasterCupItems(c *player.Player, requiredItems []ItemS) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func consumeMasterCupItems(c *player.Player, requiredItems []ItemS) {
|
||||
func consumeMasterCupItems(c *player.Player, requiredItems []ItemS) error {
|
||||
for _, item := range requiredItems {
|
||||
c.Service.Item.UPDATE(item.ItemId, -int(item.ItemCnt))
|
||||
if err := c.Service.Item.UPDATE(item.ItemId, -int(item.ItemCnt)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func appendMasterCupRewardItems(c *player.Player, result *S2C_MASTER_REWARDSR, itemList []data.ItemInfo) {
|
||||
|
||||
@@ -18,10 +18,13 @@ func (h Controller) ItemSale(data *C2S_ITEM_SALE, c *player.Player) (result *fig
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
|
||||
if err := c.Service.Item.UPDATE(data.ItemId, -gconv.Int(data.Amount)); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
|
||||
itemConfig := xmlres.ItemsMAP[int(data.ItemId)]
|
||||
if itemConfig.SellPrice != 0 {
|
||||
c.Info.Coins += int64(int64(data.Amount) * int64(itemConfig.SellPrice))
|
||||
}
|
||||
c.Service.Item.UPDATE(data.ItemId, -gconv.Int(data.Amount))
|
||||
return result, 0
|
||||
}
|
||||
|
||||
@@ -58,7 +58,10 @@ func (h Controller) UsePetItemOutOfFight(data *C2S_USE_PET_ITEM_OUT_OF_FIGHT, c
|
||||
return nil, errcode
|
||||
}
|
||||
refreshPetPaneKeepHP(currentPet, oldHP)
|
||||
c.Service.Item.UPDATE(itemID, -1)
|
||||
if err := c.Service.Item.UPDATE(itemID, -1); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
c.Service.Info.Save(*c.Info)
|
||||
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
|
||||
copier.Copy(&result, currentPet)
|
||||
return result, 0
|
||||
@@ -90,7 +93,10 @@ func (h Controller) UsePetItemOutOfFight(data *C2S_USE_PET_ITEM_OUT_OF_FIGHT, c
|
||||
return nil, errcode
|
||||
}
|
||||
|
||||
c.Service.Item.UPDATE(itemID, -1)
|
||||
if err := c.Service.Item.UPDATE(itemID, -1); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
c.Service.Info.Save(*c.Info)
|
||||
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
|
||||
copier.Copy(&result, currentPet)
|
||||
return result, 0
|
||||
@@ -133,7 +139,9 @@ func (h Controller) handlexuancaiItem(currentPet *model.PetInfo, c *player.Playe
|
||||
return errorcode.ErrorCodes.ErrItemUnusable
|
||||
}
|
||||
|
||||
c.Service.Item.UPDATE(itemid, -100)
|
||||
if err := c.Service.Item.UPDATE(itemid, -100); err != nil {
|
||||
return errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -214,7 +222,10 @@ func (h Controller) ResetNature(data *C2S_PET_RESET_NATURE, c *player.Player) (r
|
||||
currentHP := currentPet.Hp
|
||||
currentPet.Nature = data.Nature
|
||||
refreshPetPaneKeepHP(currentPet, currentHP)
|
||||
c.Service.Item.UPDATE(data.ItemId, -1)
|
||||
if err := c.Service.Item.UPDATE(data.ItemId, -1); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
c.Service.Info.Save(*c.Info)
|
||||
return result, 0
|
||||
}
|
||||
|
||||
@@ -242,29 +253,38 @@ func (h Controller) UseSpeedupItem(data *C2S_USE_SPEEDUP_ITEM, c *player.Player)
|
||||
if c.Info.TwoTimes != 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||
}
|
||||
c.Info.TwoTimes += 50 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
||||
case 300067:
|
||||
if c.Info.TwoTimes != 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||
}
|
||||
c.Info.TwoTimes += 25 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
||||
case 300051: // 假设1002是三倍经验加速器道具ID
|
||||
if c.Info.ThreeTimes != 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||
}
|
||||
c.Info.ThreeTimes += 50 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
||||
case 300115:
|
||||
if c.Info.ThreeTimes != 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||
}
|
||||
c.Info.ThreeTimes += 30 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
||||
|
||||
default:
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError // 未知道具ID
|
||||
}
|
||||
|
||||
// 3. 扣减道具(数量-1)
|
||||
c.Service.Item.UPDATE(data.ItemID, -1)
|
||||
if err := c.Service.Item.UPDATE(data.ItemID, -1); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
|
||||
switch data.ItemID {
|
||||
case 300027: // 假设1001是双倍经验加速器道具ID
|
||||
c.Info.TwoTimes += 50 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
||||
case 300067:
|
||||
c.Info.TwoTimes += 25 // 玩家对象新增 TwoTimesExp 字段存储双倍剩余次数
|
||||
case 300051: // 假设1002是三倍经验加速器道具ID
|
||||
c.Info.ThreeTimes += 50 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
||||
case 300115:
|
||||
c.Info.ThreeTimes += 30 // 玩家对象新增 ThreeTimesExp 字段存储三倍剩余次数
|
||||
}
|
||||
result.ThreeTimes = uint32(c.Info.ThreeTimes) // 返回三倍经验剩余次数
|
||||
result.TwoTimes = uint32(c.Info.TwoTimes) // 返回双倍经验剩余次数
|
||||
|
||||
@@ -295,10 +315,11 @@ func (h Controller) UseEnergyXishou(data *C2S_USE_ENERGY_XISHOU, c *player.Playe
|
||||
}
|
||||
// 2. 核心业务逻辑:更新能量吸收器剩余次数
|
||||
// (注:可根据道具ID配置不同的次数加成,此处默认+1)
|
||||
if err := c.Service.Item.UPDATE(data.ItemID, -1); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
c.Info.EnergyTime += 40 // 玩家对象新增 EnergyTimes 字段存储能量吸收剩余次数
|
||||
|
||||
// 3. 扣减道具(数量-1)
|
||||
c.Service.Item.UPDATE(data.ItemID, -1)
|
||||
result = &item.S2C_USE_ENERGY_XISHOU{
|
||||
EnergyTimes: uint32(c.Info.EnergyTime),
|
||||
}
|
||||
@@ -329,6 +350,9 @@ func (h Controller) UseAutoFightItem(data *C2S_USE_AUTO_FIGHT_ITEM, c *player.Pl
|
||||
if c.Info.AutoFightTime != 0 {
|
||||
return nil, errorcode.ErrorCodes.ErrItemInUse
|
||||
}
|
||||
if err := c.Service.Item.UPDATE(data.ItemID, -1); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
}
|
||||
result = &item.S2C_USE_AUTO_FIGHT_ITEM{}
|
||||
// 2. 核心业务逻辑:开启自动战斗 + 更新剩余次数
|
||||
c.Info.AutoFight = 3 // 按需求设置自动战斗flag为3(需测试)
|
||||
@@ -344,8 +368,6 @@ func (h Controller) UseAutoFightItem(data *C2S_USE_AUTO_FIGHT_ITEM, c *player.Pl
|
||||
c.Info.AutoFightTime += 50
|
||||
}
|
||||
result.AutoFightTimes = c.Info.AutoFightTime
|
||||
// 3. 扣减道具(数量-1)
|
||||
c.Service.Item.UPDATE(data.ItemID, -1)
|
||||
|
||||
return result, 0
|
||||
}
|
||||
|
||||
60
logic/controller/item_use_test.go
Normal file
60
logic/controller/item_use_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/logic/service/player"
|
||||
playermodel "blazing/modules/player/model"
|
||||
blservice "blazing/modules/player/service"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUsePetItemOutOfFightAppliesToBackupPetInMemory(t *testing.T) {
|
||||
petID := firstPetIDForControllerTest(t)
|
||||
backupPet := playermodel.GenPetInfo(petID, 31, 0, 0, 50, nil, 0)
|
||||
if backupPet == nil {
|
||||
t.Fatal("failed to generate backup pet")
|
||||
}
|
||||
if backupPet.MaxHp <= 1 {
|
||||
t.Fatalf("expected generated pet to have max hp > 1, got %d", backupPet.MaxHp)
|
||||
}
|
||||
backupPet.Hp = 1
|
||||
|
||||
testPlayer := player.NewPlayer(nil)
|
||||
testPlayer.Info = &playermodel.PlayerInfo{
|
||||
UserID: 1,
|
||||
PetList: []playermodel.PetInfo{},
|
||||
BackupPetList: []playermodel.PetInfo{*backupPet},
|
||||
}
|
||||
testPlayer.Service = blservice.NewUserService(testPlayer.Info.UserID)
|
||||
|
||||
itemID, recoverHP := firstRecoverHPItemForControllerTest(t)
|
||||
if recoverHP <= 0 {
|
||||
t.Fatalf("expected positive recover hp for item %d, got %d", itemID, recoverHP)
|
||||
}
|
||||
|
||||
_, err := (Controller{}).UsePetItemOutOfFight(&C2S_USE_PET_ITEM_OUT_OF_FIGHT{
|
||||
CatchTime: backupPet.CatchTime,
|
||||
ItemID: int32(itemID),
|
||||
}, testPlayer)
|
||||
if err != 0 {
|
||||
t.Fatalf("expected backup pet item use to succeed in-memory, got err=%d", err)
|
||||
}
|
||||
|
||||
updatedPet := testPlayer.Info.BackupPetList[0]
|
||||
if updatedPet.Hp <= 1 {
|
||||
t.Fatalf("expected backup pet hp to increase in memory, got hp=%d", updatedPet.Hp)
|
||||
}
|
||||
}
|
||||
|
||||
func firstRecoverHPItemForControllerTest(t *testing.T) (uint32, int) {
|
||||
t.Helper()
|
||||
|
||||
for id, cfg := range xmlres.ItemsMAP {
|
||||
if cfg.HP > 0 {
|
||||
return uint32(id), cfg.HP
|
||||
}
|
||||
}
|
||||
|
||||
t.Fatal("xmlres.ItemsMAP has no HP recovery item")
|
||||
return 0, 0
|
||||
}
|
||||
@@ -37,7 +37,9 @@ func (h Controller) PetELV(data *C2S_PET_EVOLVTION, c *player.Player) (result *f
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItemsMulti
|
||||
}
|
||||
if branch.EvolvItem != 0 {
|
||||
c.Service.Item.UPDATE(uint32(branch.EvolvItem), -branch.EvolvItemCount)
|
||||
if err := c.Service.Item.UPDATE(uint32(branch.EvolvItem), -branch.EvolvItemCount); err != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItemsMulti
|
||||
}
|
||||
}
|
||||
|
||||
currentPet.ID = uint32(branch.MonTo)
|
||||
|
||||
@@ -65,16 +65,33 @@ func (h Controller) PetFusion(data *C2S_PetFusion, c *player.Player) (result *pe
|
||||
return result, errorcode.ErrorCodes.ErrSunDouInsufficient10016
|
||||
}
|
||||
|
||||
consumeItems(c, materialCounts)
|
||||
c.Info.Coins -= petFusionCost
|
||||
|
||||
if resultPetID == 0 {
|
||||
if useOptionalItem(c, data.GoldItem1[:], petFusionFailureItemID) {
|
||||
result.CostItemFlag = 1
|
||||
} else if auxPet.Level > 5 {
|
||||
auxPet.Downgrade(auxPet.Level - 5)
|
||||
failedAux := *auxPet
|
||||
if auxPet.Level > 5 {
|
||||
failedAux.Downgrade(auxPet.Level - 5)
|
||||
} else {
|
||||
auxPet.Downgrade(1)
|
||||
failedAux.Downgrade(1)
|
||||
}
|
||||
txResult, errCode := c.Service.PetFusionTx(
|
||||
*c.Info,
|
||||
data.Mcatchtime,
|
||||
data.Auxcatchtime,
|
||||
materialCounts,
|
||||
data.GoldItem1[:],
|
||||
petFusionKeepAuxItemID,
|
||||
petFusionFailureItemID,
|
||||
petFusionCost,
|
||||
nil,
|
||||
&failedAux,
|
||||
)
|
||||
if errCode != 0 {
|
||||
return result, errCode
|
||||
}
|
||||
c.Info.Coins -= petFusionCost
|
||||
if txResult.CostItemUsed {
|
||||
result.CostItemFlag = 1
|
||||
} else if txResult.UpdatedAux != nil {
|
||||
*auxPet = *txResult.UpdatedAux
|
||||
}
|
||||
return &pet.PetFusionInfo{}, 0
|
||||
}
|
||||
@@ -101,18 +118,37 @@ func (h Controller) PetFusion(data *C2S_PetFusion, c *player.Player) (result *pe
|
||||
newPet.RandomByWeightShiny()
|
||||
}
|
||||
|
||||
c.Service.Pet.PetAdd(newPet, 0)
|
||||
//println(c.Info.UserID, "进行融合", len(c.Info.PetList), masterPet.ID, auxPet.ID, newPet.ID)
|
||||
|
||||
c.PetDel(data.Mcatchtime)
|
||||
if useOptionalItem(c, data.GoldItem1[:], petFusionKeepAuxItemID) {
|
||||
result.CostItemFlag = 1
|
||||
} else {
|
||||
c.PetDel(data.Auxcatchtime)
|
||||
txResult, errCode := c.Service.PetFusionTx(
|
||||
*c.Info,
|
||||
data.Mcatchtime,
|
||||
data.Auxcatchtime,
|
||||
materialCounts,
|
||||
data.GoldItem1[:],
|
||||
petFusionKeepAuxItemID,
|
||||
petFusionFailureItemID,
|
||||
petFusionCost,
|
||||
newPet,
|
||||
nil,
|
||||
)
|
||||
if errCode != 0 {
|
||||
return result, errCode
|
||||
}
|
||||
|
||||
result.ObtainTime = newPet.CatchTime
|
||||
result.StarterCpTm = newPet.ID
|
||||
c.Info.Coins -= petFusionCost
|
||||
if txResult.CostItemUsed {
|
||||
result.CostItemFlag = 1
|
||||
} else {
|
||||
removePetFromPlayerInfo(c, data.Auxcatchtime)
|
||||
}
|
||||
removePetFromPlayerInfo(c, data.Mcatchtime)
|
||||
|
||||
if txResult.NewPet == nil {
|
||||
return result, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
c.Info.PetList = append(c.Info.PetList, *txResult.NewPet)
|
||||
|
||||
result.ObtainTime = txResult.NewPet.CatchTime
|
||||
result.StarterCpTm = txResult.NewPet.ID
|
||||
return result, 0
|
||||
}
|
||||
|
||||
@@ -149,21 +185,10 @@ func hasEnoughItems(c *player.Player, itemCounts map[uint32]int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func consumeItems(c *player.Player, itemCounts map[uint32]int) {
|
||||
for itemID, count := range itemCounts {
|
||||
_ = c.Service.Item.UPDATE(itemID, -count)
|
||||
func removePetFromPlayerInfo(c *player.Player, catchTime uint32) {
|
||||
index, _, ok := c.FindPet(catchTime)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func useOptionalItem(c *player.Player, itemIDs []uint32, target uint32) bool {
|
||||
if c.Service.Item.CheakItem(target) <= 0 {
|
||||
return false
|
||||
}
|
||||
for _, itemID := range itemIDs {
|
||||
if itemID == target {
|
||||
_ = c.Service.Item.UPDATE(target, -1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
c.Info.PetList = append(c.Info.PetList[:index], c.Info.PetList[index+1:]...)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,42 @@ import (
|
||||
"blazing/logic/service/fight"
|
||||
"blazing/logic/service/pet"
|
||||
"blazing/logic/service/player"
|
||||
playermodel "blazing/modules/player/model"
|
||||
)
|
||||
|
||||
func petSetExpLimit(currentPet *playermodel.PetInfo) int64 {
|
||||
if currentPet == nil || currentPet.Level >= 100 {
|
||||
return 0
|
||||
}
|
||||
|
||||
simulatedPet := *currentPet
|
||||
var allowedExp int64
|
||||
|
||||
for simulatedPet.Level < 100 {
|
||||
needExp := simulatedPet.NextLvExp - simulatedPet.Exp
|
||||
if needExp <= 0 {
|
||||
simulatedPet.Level++
|
||||
simulatedPet.Exp = 0
|
||||
simulatedPet.Update(true)
|
||||
continue
|
||||
}
|
||||
|
||||
allowedExp += needExp
|
||||
simulatedPet.Level++
|
||||
simulatedPet.Exp = 0
|
||||
simulatedPet.Update(true)
|
||||
}
|
||||
|
||||
return allowedExp
|
||||
}
|
||||
|
||||
func minInt64(a, b int64) int64 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// PetReleaseToWarehouse 将精灵从仓库包中放生
|
||||
func (h Controller) PetReleaseToWarehouse(
|
||||
data *PET_ROWEI, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||
@@ -70,6 +104,11 @@ func (h Controller) SetPetExp(
|
||||
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
|
||||
player.AddPetExp(currentPet, data.Exp)
|
||||
allowedExp := petSetExpLimit(currentPet)
|
||||
if allowedExp <= 0 {
|
||||
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
|
||||
player.AddPetExp(currentPet, minInt64(data.Exp, allowedExp))
|
||||
return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, 0
|
||||
}
|
||||
|
||||
75
logic/controller/pet_manage_test.go
Normal file
75
logic/controller/pet_manage_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"blazing/common/data/xmlres"
|
||||
"blazing/common/socket/errorcode"
|
||||
"blazing/logic/service/player"
|
||||
playermodel "blazing/modules/player/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func firstPetIDForControllerTest(t *testing.T) int {
|
||||
t.Helper()
|
||||
|
||||
for id := range xmlres.PetMAP {
|
||||
return id
|
||||
}
|
||||
|
||||
t.Fatal("xmlres.PetMAP is empty")
|
||||
return 0
|
||||
}
|
||||
|
||||
func TestSetPetExpCapsLevelAt100(t *testing.T) {
|
||||
petID := firstPetIDForControllerTest(t)
|
||||
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 99, nil, 0)
|
||||
if petInfo == nil {
|
||||
t.Fatal("failed to generate test pet")
|
||||
}
|
||||
|
||||
expPool := petInfo.NextLvExp + 10_000
|
||||
testPlayer := player.NewPlayer(nil)
|
||||
testPlayer.Info = &playermodel.PlayerInfo{
|
||||
ExpPool: expPool,
|
||||
PetList: []playermodel.PetInfo{*petInfo},
|
||||
}
|
||||
|
||||
currentPet := &testPlayer.Info.PetList[0]
|
||||
result, err := (Controller{}).SetPetExp(&PetSetExpInboundInfo{
|
||||
CatchTime: currentPet.CatchTime,
|
||||
Exp: expPool,
|
||||
}, testPlayer)
|
||||
if err != 0 {
|
||||
t.Fatalf("expected SetPetExp to succeed, got err=%d", err)
|
||||
}
|
||||
if currentPet.Level != 100 {
|
||||
t.Fatalf("expected pet level to stop at 100, got %d", currentPet.Level)
|
||||
}
|
||||
if result.Exp != 10_000 {
|
||||
t.Fatalf("expected overflow exp to remain in pool, got %d", result.Exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetPetExpRejectsPetAtLevel100(t *testing.T) {
|
||||
petID := firstPetIDForControllerTest(t)
|
||||
petInfo := playermodel.GenPetInfo(petID, 31, 0, 0, 100, nil, 0)
|
||||
if petInfo == nil {
|
||||
t.Fatal("failed to generate test pet")
|
||||
}
|
||||
|
||||
testPlayer := player.NewPlayer(nil)
|
||||
testPlayer.Info = &playermodel.PlayerInfo{
|
||||
ExpPool: 50_000,
|
||||
PetList: []playermodel.PetInfo{*petInfo},
|
||||
}
|
||||
|
||||
result, err := (Controller{}).SetPetExp(&PetSetExpInboundInfo{
|
||||
CatchTime: petInfo.CatchTime,
|
||||
Exp: 12_345,
|
||||
}, testPlayer)
|
||||
if err != errorcode.ErrorCodes.ErrSystemError {
|
||||
t.Fatalf("expected level-100 pet to be rejected, got err=%d", err)
|
||||
}
|
||||
if result.Exp != 50_000 {
|
||||
t.Fatalf("expected exp pool to remain unchanged, got %d", result.Exp)
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,7 @@ func (p *Player) AddPetExp(petInfo *model.PetInfo, addExp int64) {
|
||||
p.Info.ExpPool -= addExp //减去已使用的经验
|
||||
gainedExp := exp //已获得的经验
|
||||
for exp >= int64(petInfo.NextLvExp) {
|
||||
|
||||
petInfo.Level++
|
||||
|
||||
exp -= int64(petInfo.LvExp)
|
||||
petInfo.Update(true)
|
||||
}
|
||||
|
||||
@@ -419,7 +419,15 @@ func (p *Player) ItemAdd(ItemId, ItemCnt int64) (result bool) {
|
||||
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
||||
return false
|
||||
}
|
||||
p.Service.Item.UPDATE(uint32(ItemId), gconv.Int(ItemCnt))
|
||||
if err := p.Service.Item.UPDATE(uint32(ItemId), gconv.Int(ItemCnt)); err != nil {
|
||||
cool.Logger.Error(context.TODO(), "item add update failed", p.Info.UserID, ItemId, ItemCnt, err)
|
||||
|
||||
t1 := common.NewTomeeHeader(2601, p.Info.UserID)
|
||||
t1.Result = uint32(errorcode.ErrorCodes.ErrSystemError200007)
|
||||
|
||||
p.SendPack(t1.Pack(nil)) //准备包由各自发,因为协议不一样
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
269
modules/player/service/pet_fusion_tx.go
Normal file
269
modules/player/service/pet_fusion_tx.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"blazing/common/socket/errorcode"
|
||||
"blazing/cool"
|
||||
baseservice "blazing/modules/base/service"
|
||||
"blazing/modules/player/model"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
var (
|
||||
errPetFusionInsufficientItems = errors.New("pet fusion insufficient items")
|
||||
errPetFusionPetNotFound = errors.New("pet fusion pet not found")
|
||||
errPetFusionPlayerNotFound = errors.New("pet fusion player not found")
|
||||
)
|
||||
|
||||
type PetFusionTxResult struct {
|
||||
NewPet *model.PetInfo
|
||||
CostItemUsed bool
|
||||
UpdatedAux *model.PetInfo
|
||||
}
|
||||
|
||||
func (s *UserService) PetFusionTx(
|
||||
currentInfo model.PlayerInfo,
|
||||
masterCatchTime uint32,
|
||||
auxCatchTime uint32,
|
||||
materialCounts map[uint32]int,
|
||||
goldItemIDs []uint32,
|
||||
keepAuxItemID uint32,
|
||||
failureItemID uint32,
|
||||
cost int64,
|
||||
newPet *model.PetInfo,
|
||||
failedAux *model.PetInfo,
|
||||
) (*PetFusionTxResult, errorcode.ErrorCode) {
|
||||
if s == nil || s.Pet == nil {
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
|
||||
userID := s.Pet.userid
|
||||
nextInfo := currentInfo
|
||||
if nextInfo.Coins < cost {
|
||||
return nil, errorcode.ErrorCodes.ErrSunDouInsufficient10016
|
||||
}
|
||||
nextInfo.Coins -= cost
|
||||
|
||||
result := &PetFusionTxResult{}
|
||||
err := g.DB().Transaction(context.TODO(), func(ctx context.Context, tx gdb.TX) error {
|
||||
if err := updatePlayerInfoTx(tx, userID, nextInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := consumeItemCountsTx(tx, userID, materialCounts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if newPet == nil {
|
||||
used, err := consumeOptionalItemTx(tx, userID, goldItemIDs, failureItemID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result.CostItemUsed = used
|
||||
if !used && failedAux != nil {
|
||||
if err := updatePetDataTx(tx, userID, auxCatchTime, *failedAux); err != nil {
|
||||
return err
|
||||
}
|
||||
auxCopy := *failedAux
|
||||
result.UpdatedAux = &auxCopy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := deletePetTx(tx, userID, masterCatchTime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
used, err := consumeOptionalItemTx(tx, userID, goldItemIDs, keepAuxItemID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result.CostItemUsed = used
|
||||
if !used {
|
||||
if err := deletePetTx(tx, userID, auxCatchTime); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
catchTime, err := addPetTx(tx, s.Pet, newPet, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
petCopy := *newPet
|
||||
petCopy.CatchTime = catchTime
|
||||
result.NewPet = &petCopy
|
||||
return nil
|
||||
})
|
||||
if err == nil {
|
||||
return result, 0
|
||||
}
|
||||
|
||||
switch {
|
||||
case errors.Is(err, errPetFusionInsufficientItems):
|
||||
return nil, errorcode.ErrorCodes.ErrInsufficientItems
|
||||
case errors.Is(err, errPetFusionPetNotFound):
|
||||
return nil, errorcode.ErrorCodes.ErrPokemonNotFusionReady2
|
||||
case errors.Is(err, errPetFusionPlayerNotFound):
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
default:
|
||||
cool.Logger.Error(context.TODO(), "pet fusion tx failed", userID, err)
|
||||
return nil, errorcode.ErrorCodes.ErrSystemError
|
||||
}
|
||||
}
|
||||
|
||||
func updatePlayerInfoTx(tx gdb.TX, userID uint32, info model.PlayerInfo) error {
|
||||
res, err := tx.Model(model.NewPlayer()).Where("player_id", userID).Data("data", info).Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return errPetFusionPlayerNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func consumeItemCountsTx(tx gdb.TX, userID uint32, itemCounts map[uint32]int) error {
|
||||
for itemID, count := range itemCounts {
|
||||
if err := updateItemCountTx(tx, userID, itemID, -count); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func consumeOptionalItemTx(tx gdb.TX, userID uint32, itemIDs []uint32, target uint32) (bool, error) {
|
||||
for _, itemID := range itemIDs {
|
||||
if itemID != target {
|
||||
continue
|
||||
}
|
||||
if err := updateItemCountTx(tx, userID, target, -1); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func updateItemCountTx(tx gdb.TX, userID uint32, id uint32, count int) error {
|
||||
if cool.Config.ServerInfo.IsVip != 0 && count < 0 {
|
||||
return nil
|
||||
}
|
||||
if id == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
baseModel := tx.Model(model.NewPlayerBag()).
|
||||
Where("player_id", userID).
|
||||
Where("is_vip", cool.Config.ServerInfo.IsVip)
|
||||
|
||||
ok, err := baseModel.Where("item_id", id).Exist()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok {
|
||||
updateModel := tx.Model(model.NewPlayerBag()).
|
||||
Where("player_id", userID).
|
||||
Where("is_vip", cool.Config.ServerInfo.IsVip).
|
||||
Where("item_id", id)
|
||||
if count < 0 {
|
||||
updateModel = updateModel.Where("item_cnt + ? > 0", count)
|
||||
}
|
||||
|
||||
result, err := updateModel.Increment("item_cnt", count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return errPetFusionInsufficientItems
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if count <= 0 {
|
||||
return errPetFusionInsufficientItems
|
||||
}
|
||||
|
||||
_, err = tx.Model(model.NewPlayerBag()).Data(g.Map{
|
||||
"player_id": userID,
|
||||
"item_id": id,
|
||||
"item_cnt": count,
|
||||
"is_vip": cool.Config.ServerInfo.IsVip,
|
||||
}).Insert()
|
||||
return err
|
||||
}
|
||||
|
||||
func updatePetDataTx(tx gdb.TX, userID uint32, catchTime uint32, pet model.PetInfo) error {
|
||||
res, err := tx.Model(model.NewPet()).
|
||||
Where("player_id", userID).
|
||||
Where("is_vip", cool.Config.ServerInfo.IsVip).
|
||||
Where("catch_time", catchTime).
|
||||
Data("data", pet).
|
||||
Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return errPetFusionPetNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deletePetTx(tx gdb.TX, userID uint32, catchTime uint32) error {
|
||||
res, err := tx.Model(model.NewPet()).
|
||||
Where("player_id", userID).
|
||||
Where("is_vip", cool.Config.ServerInfo.IsVip).
|
||||
Where("catch_time", catchTime).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return errPetFusionPetNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addPetTx(tx gdb.TX, petService *PetService, petInfo *model.PetInfo, saleCount uint32) (uint32, error) {
|
||||
if petInfo == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
catchTime, err := petService.nextCatchTime(tx.Model(baseservice.NewBaseSysUserService().Model))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
petCopy := *petInfo
|
||||
petCopy.CatchTime = catchTime
|
||||
playerPet := model.Pet{
|
||||
PlayerID: petService.userid,
|
||||
Data: petCopy,
|
||||
CatchTime: catchTime,
|
||||
Free: 0,
|
||||
SaleCount: saleCount,
|
||||
}
|
||||
playerPet.IsVip = cool.Config.ServerInfo.IsVip
|
||||
if _, err := tx.Model(model.NewPet()).Insert(playerPet); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return catchTime, nil
|
||||
}
|
||||
Reference in New Issue
Block a user