diff --git a/logic/controller/pet_bag.go b/logic/controller/pet_bag.go new file mode 100644 index 00000000..991b265b --- /dev/null +++ b/logic/controller/pet_bag.go @@ -0,0 +1,199 @@ +package controller + +import ( + "blazing/common/socket/errorcode" + "blazing/logic/service/fight" + "blazing/logic/service/pet" + "blazing/logic/service/player" +) + +// SavePetBagOrder 保存当前主背包和备用背包顺序 +func (h Controller) SavePetBagOrder( + data *pet.SavePetBagOrderInboundInfo, + player *player.Player) (result *fight.NullOutboundInfo, + err errorcode.ErrorCode) { + syncBackupPetList(player) + + if len(data.PetList) > 6 || len(data.BackupPetList) > 6 { + return nil, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + + totalPetCount := len(player.Info.PetList) + len(player.Info.BackupPetList) + if len(data.PetList)+len(data.BackupPetList) != totalPetCount { + return nil, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + + petMap := buildPetInfoMap(player.Info.PetList, player.Info.BackupPetList) + used := make(map[uint32]struct{}, totalPetCount) + + battleList, ok := buildOrderedPetList(data.PetList, petMap, used) + if !ok { + return nil, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + + backupList, ok := buildOrderedPetList(data.BackupPetList, petMap, used) + if !ok { + return nil, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + + if len(used) != totalPetCount { + return nil, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + + player.Info.PetList = battleList + player.Info.BackupPetList = backupList + player.Service.Info.Save(*player.Info) + return nil, 0 +} + +// PetRetrieveFromWarehouse 领回仓库精灵 +func (h Controller) PetRetrieveFromWarehouse( + data *pet.PET_RETRIEVE, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { + if _, ok := findPetListSlot(player, data.CatchTime); ok { + return nil, 0 + } + + petInfo := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime) + if petInfo == nil { + return nil, 0 + } + + syncBackupPetList(player) + changed := false + if len(player.Info.PetList) < 6 { + player.Info.PetList = append(player.Info.PetList, petInfo.Data) + changed = true + } else if len(player.Info.BackupPetList) < 6 { + player.Info.BackupPetList = append(player.Info.BackupPetList, petInfo.Data) + changed = true + } + if changed { + player.Service.Info.Save(*player.Info) + } + return nil, 0 +} + +// TogglePetBagWarehouse 精灵背包仓库切换 +func (h Controller) TogglePetBagWarehouse( + data *pet.PetReleaseInboundInfo, + player *player.Player) (result *pet.PetReleaseOutboundInfo, err errorcode.ErrorCode) { + result = &pet.PetReleaseOutboundInfo{ + Flag: uint32(data.Flag), + } + + if player.IsArenaSwitchLocked() { + return result, errorcode.ErrorCodes.ErrChampionCannotSwitch + } + + syncBackupPetList(player) + + switch data.Flag { + case 0: + slot, ok := findPetListSlot(player, data.CatchTime) + if !ok { + return result, errorcode.ErrorCodes.ErrPokemonNotExists + } + if !slot.isValid() { + return result, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + if !player.Service.Pet.Update(slot.info) { + return result, errorcode.ErrorCodes.ErrSystemError + } + + slot.remove() + if slot.kind == petListKindMain { + player.Service.Info.Save(*player.Info) + } + + case 1: + if len(player.Info.PetList) >= 6 && len(player.Info.BackupPetList) >= 6 { + return result, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + if _, ok := findPetListSlot(player, data.CatchTime); ok { + return result, 0 + } + + petInfo := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime) + if petInfo == nil { + return result, errorcode.ErrorCodes.ErrPokemonNotExists + } + if len(player.Info.PetList) < 6 { + player.Info.PetList = append(player.Info.PetList, petInfo.Data) + } else { + player.Info.BackupPetList = append(player.Info.BackupPetList, petInfo.Data) + } + result.PetInfo = petInfo.Data + } + + if len(player.Info.PetList) > 0 { + result.FirstPetTime = player.Info.PetList[0].CatchTime + } + + return result, 0 +} + +// TogglePetBagWarehouseLegacy 旧版精灵背包仓库切换 +func (h Controller) TogglePetBagWarehouseLegacy( + data *pet.PetReleaseLegacyInboundInfo, + player *player.Player) (result *pet.PetReleaseOutboundInfo, err errorcode.ErrorCode) { + result = &pet.PetReleaseOutboundInfo{ + Flag: uint32(data.Flag), + } + + if player.IsArenaSwitchLocked() { + return result, errorcode.ErrorCodes.ErrChampionCannotSwitch + } + + switch data.Flag { + case 0: + index, currentPet, ok := player.FindPet(data.CatchTime) + if !ok { + break + } + if index < 0 || index >= len(player.Info.PetList) { + return result, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + if !player.Service.Pet.Update(*currentPet) { + return result, errorcode.ErrorCodes.ErrSystemError + } + + player.Info.PetList = append(player.Info.PetList[:index], player.Info.PetList[index+1:]...) + player.Info.BackupPetList = removePetByCatchTime(player.Info.BackupPetList, data.CatchTime) + player.Info.BackupPetList = append(player.Info.BackupPetList, *currentPet) + + case 1: + if len(player.Info.PetList) >= 6 { + break + } + + if _, _, ok := player.FindPet(data.CatchTime); ok { + player.Info.BackupPetList = removePetByCatchTime(player.Info.BackupPetList, data.CatchTime) + break + } + + if index, backupPet, ok := findBackupPet(player, data.CatchTime); ok { + if index < 0 || index >= len(player.Info.BackupPetList) { + return result, errorcode.ErrorCodes.ErrPokemonIDMismatch + } + + result.PetInfo = *backupPet + player.Info.PetList = append(player.Info.PetList, *backupPet) + player.Info.BackupPetList = append(player.Info.BackupPetList[:index], player.Info.BackupPetList[index+1:]...) + break + } + + petInfo := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime) + if petInfo == nil { + return result, errorcode.ErrorCodes.ErrPokemonNotExists + } + + player.Info.PetList = append(player.Info.PetList, petInfo.Data) + result.PetInfo = petInfo.Data + } + + if len(player.Info.PetList) > 0 { + result.FirstPetTime = player.Info.PetList[0].CatchTime + } + + return result, 0 +} diff --git a/logic/controller/pet_info.go b/logic/controller/pet_info.go index 08a65286..71c8b804 100644 --- a/logic/controller/pet_info.go +++ b/logic/controller/pet_info.go @@ -52,187 +52,10 @@ func (h Controller) GetPetReleaseList( return buildPetListOutboundInfo(buildWarehousePetList(player)), 0 } -<<<<<<< ours -// PetReleaseToWarehouse 将精灵从仓库包中放生 -// data: 包含精灵ID和捕获时间的输入信息 -// player: 当前玩家对象 -// 返回: 无数据和错误码 -func (h Controller) PetReleaseToWarehouse( - data *pet.PET_ROWEI, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { - _, _, inBag := player.FindPet(data.CatchTime) - _, _, inBackup := findBackupPet(player, data.CatchTime) - freeForbidden := xmlres.PetMAP[int(data.ID)].FreeForbidden - // 如果背包没找到,再放入背包 - if inBag || inBackup || freeForbidden == 1 { - return nil, errorcode.ErrorCodes.ErrCannotReleaseNonWarehouse - } - - if !player.Service.Pet.UpdateFree(data.CatchTime, 1) { - return nil, errorcode.ErrorCodes.ErrSystemError - } - return nil, err - -} - -// PetRetrieveFromWarehouse 领回包 -func (h Controller) PetRetrieveFromWarehouse( - data *pet.PET_RETRIEVE, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的 - - //如果背包没找到,再放入背包 - if _, _, ok := player.FindPet(data.CatchTime); !ok { - if petInfo := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime); petInfo != nil { - syncBackupPetList(player) - if len(player.Info.PetList) < 6 { - player.Info.PetList = append(player.Info.PetList, petInfo.Data) - } else if len(player.Info.BackupPetList) < 6 { - player.Info.BackupPetList = append(player.Info.BackupPetList, petInfo.Data) - } - player.Service.Info.Save(*player.Info) - } - } - - return nil, 0 - -} - -// TogglePetBagWarehouse 精灵背包仓库切换 -func (h Controller) TogglePetBagWarehouse( - data *pet.PetReleaseInboundInfo, - player *player.Player) (result *pet.PetReleaseOutboundInfo, err errorcode.ErrorCode) { - result = &pet.PetReleaseOutboundInfo{} - result.Flag = uint32(data.Flag) - - if player.IsArenaSwitchLocked() { - return result, errorcode.ErrorCodes.ErrChampionCannotSwitch - } - - syncBackupPetList(player) - - switch data.Flag { - case 0: - if index, currentPet, ok := player.FindPet(data.CatchTime); ok { - if index < 0 || index >= len(player.Info.PetList) { - return result, errorcode.ErrorCodes.ErrPokemonIDMismatch - } - if !player.Service.Pet.Update(*currentPet) { - return result, errorcode.ErrorCodes.ErrSystemError - } - player.Info.PetList = append(player.Info.PetList[:index], player.Info.PetList[index+1:]...) - player.Service.Info.Save(*player.Info) - break - } - - index, currentPet, ok := findBackupPet(player, data.CatchTime) - if !ok { - return result, errorcode.ErrorCodes.ErrPokemonNotExists - } - if index < 0 || index >= len(player.Info.BackupPetList) { - return result, errorcode.ErrorCodes.ErrPokemonIDMismatch - } - if !player.Service.Pet.Update(*currentPet) { - return result, errorcode.ErrorCodes.ErrSystemError - } - player.Info.BackupPetList = append(player.Info.BackupPetList[:index], player.Info.BackupPetList[index+1:]...) - - case 1: - if len(player.Info.PetList) >= 6 && len(player.Info.BackupPetList) >= 6 { - return result, errorcode.ErrorCodes.ErrPokemonIDMismatch - } - if _, _, ok := player.FindPet(data.CatchTime); ok { - return result, 0 - } - if _, _, ok := findBackupPet(player, data.CatchTime); ok { - return result, 0 - } - - petInfo := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime) - if petInfo == nil { - return result, errorcode.ErrorCodes.ErrPokemonNotExists - } - if len(player.Info.PetList) < 6 { - player.Info.PetList = append(player.Info.PetList, petInfo.Data) - } else { - player.Info.BackupPetList = append(player.Info.BackupPetList, petInfo.Data) - } - result.PetInfo = petInfo.Data - - } - - if len(player.Info.PetList) > 0 { - result.FirstPetTime = player.Info.PetList[0].CatchTime - } - - return nil, 0 -} - -func (h Controller) TogglePetBagWarehouseLegacy( - data *pet.PetReleaseLegacyInboundInfo, - player *player.Player) ( - result *pet.PetReleaseOutboundInfo, - err errorcode.ErrorCode) { //这个时候player应该是空的 - //放入背包=数据库置1+添加到背包+pet release发包 仓库=数据库置0+移除背包 设置首发等于取到首发精灵后重新排序 - //这里只修改,因为添加和移除背包在宠物获取时已经做了 - - result = &pet.PetReleaseOutboundInfo{} - result.Flag = uint32(data.Flag) - //擂台住不能换精灵 - if player.IsArenaSwitchLocked() { - return result, errorcode.ErrorCodes.ErrChampionCannotSwitch - } - switch data.Flag { - case 0: - - index, pet, ok := player.FindPet(data.CatchTime) - if ok { - // ========== 新增:index合法性校验 ========== - if index < 0 || index >= len(player.Info.PetList) { - return result, errorcode.ErrorCodes.ErrPokemonIDMismatch - } - - if !player.Service.Pet.Update(*pet) { - return result, errorcode.ErrorCodes.ErrSystemError - } - - player.Info.BackupPetList = removePetByCatchTime(player.Info.BackupPetList, data.CatchTime) - player.Info.BackupPetList = append(player.Info.BackupPetList, *pet) - player.Info.PetList = append(player.Info.PetList[:index], player.Info.PetList[index+1:]...) - } - - // break // 只移除第一个匹配值,若需移除所有,可省略 break 继续循环 - case 1: - if len(player.Info.PetList) < 6 { - //todo 背包 - _, _, ok := player.FindPet(data.CatchTime) - - //如果背包没找到,再放入背包 - if !ok { - r := player.Service.Pet.PetInfoOneByCatchTime(data.CatchTime) - if r == nil { - return result, errorcode.ErrorCodes.ErrPokemonNotExists - } - player.Info.PetList = append(player.Info.PetList, r.Data) - player.Info.BackupPetList = removePetByCatchTime(player.Info.BackupPetList, data.CatchTime) - result.PetInfo = r.Data - } else { - player.Info.BackupPetList = removePetByCatchTime(player.Info.BackupPetList, data.CatchTime) - } - - } - - } - - if len(player.Info.PetList) > 0 { - result.FirstPetTime = player.Info.PetList[0].CatchTime //设置首发 - } - - return result, 0 -} - -======= ->>>>>>> theirs // PlayerShowPet 精灵展示 func (h Controller) PlayerShowPet( - data *pet.PetShowInboundInfo, player *player.Player) (result *pet.PetShowOutboundInfo, err errorcode.ErrorCode) { + data *pet.PetShowInboundInfo, + player *player.Player) (result *pet.PetShowOutboundInfo, err errorcode.ErrorCode) { result = &pet.PetShowOutboundInfo{ UserID: data.Head.UserID, CatchTime: data.CatchTime, @@ -256,60 +79,4 @@ func (h Controller) PlayerShowPet( result = buildPetShowOutboundInfo(data.Head.UserID, data.Flag, currentPet) defer player.GetSpace().Broadcast(player, data.Head.CMD, result) return -<<<<<<< ours - -} - -// PetOneCure 单体治疗 -func (h Controller) PetOneCure( - data *pet.PetOneCureInboundInfo, player *player.Player) (result *pet.PetOneCureOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的 - if player.IsArenaHealLocked() { - return result, errorcode.ErrorCodes.ErrChampionCannotHeal - } - - _, currentPet, ok := player.FindPet(data.CatchTime) - if ok { - defer currentPet.Cure() - - } - - return &pet.PetOneCureOutboundInfo{ - CatchTime: data.CatchTime, - }, 0 - -} - -// PetFirst 精灵首发 -func (h Controller) PetFirst( - data *pet.PetDefaultInboundInfo, player *player.Player) (result *pet.PetDefaultOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的 - //擂台住不能换精灵 - if player.IsArenaSwitchLocked() { - return result, errorcode.ErrorCodes.ErrChampionCannotSwitch - } - - result = &pet.PetDefaultOutboundInfo{} - - index, _, ok := player.FindPet(data.CatchTime) - if ok && index != 0 { - - player.Info.PetList[index], player.Info.PetList[0] = player.Info.PetList[0], player.Info.PetList[index] - - result.IsDefault = 1 - } - - return result, 0 - -} - -// SetPetExp 设置宠物经验 -func (h Controller) SetPetExp(data *pet.PetSetExpInboundInfo, player *player.Player) (result *pet.PetSetExpOutboundInfo, err errorcode.ErrorCode) { - _, currentPet, found := player.FindPet(data.CatchTime) - if found && currentPet.Level < 100 { - player.AddPetExp(currentPet, data.Exp) - return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, 0 - } - - return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, errorcode.ErrorCodes.ErrSystemError -======= ->>>>>>> theirs } diff --git a/logic/controller/pet_info_helpers.go b/logic/controller/pet_info_helpers.go new file mode 100644 index 00000000..2b368c68 --- /dev/null +++ b/logic/controller/pet_info_helpers.go @@ -0,0 +1,220 @@ +package controller + +import ( + "blazing/logic/service/pet" + "blazing/logic/service/player" + "blazing/modules/player/model" +) + +type petListKind uint8 + +const ( + petListKindMain petListKind = iota + petListKindBackup +) + +type petListSlot struct { + list *[]model.PetInfo + index int + info model.PetInfo + kind petListKind +} + +func (slot petListSlot) isValid() bool { + return slot.list != nil && slot.index >= 0 && slot.index < len(*slot.list) +} + +func (slot petListSlot) remove() { + *slot.list = append((*slot.list)[:slot.index], (*slot.list)[slot.index+1:]...) +} + +func buildPetShortInfo(info model.PetInfo) pet.PetShortInfo { + return pet.PetShortInfo{ + ID: info.ID, + CatchTime: info.CatchTime, + Level: info.Level, + SkinID: info.SkinID, + ShinyLen: info.ShinyLen, + ShinyInfo: info.ShinyInfo, + } +} + +func buildPetListOutboundInfo(petList []model.PetInfo) *pet.GetPetListOutboundInfo { + result := &pet.GetPetListOutboundInfo{ + ShortInfoList: make([]pet.PetShortInfo, len(petList)), + } + for i := range petList { + result.ShortInfoList[i] = buildPetShortInfo(petList[i]) + } + return result +} + +func removePetByCatchTime(petList []model.PetInfo, catchTime uint32) []model.PetInfo { + for i := range petList { + if petList[i].CatchTime == catchTime { + return append(petList[:i], petList[i+1:]...) + } + } + return petList +} + +func buildCatchTimeSet(petLists ...[]model.PetInfo) map[uint32]struct{} { + total := 0 + for _, petList := range petLists { + total += len(petList) + } + + catchTimes := make(map[uint32]struct{}, total) + for _, petList := range petLists { + for _, petInfo := range petList { + catchTimes[petInfo.CatchTime] = struct{}{} + } + } + return catchTimes +} + +func buildPetInfoMap(petLists ...[]model.PetInfo) map[uint32]model.PetInfo { + total := 0 + for _, petList := range petLists { + total += len(petList) + } + + petMap := make(map[uint32]model.PetInfo, total) + for _, petList := range petLists { + for _, petInfo := range petList { + petMap[petInfo.CatchTime] = petInfo + } + } + return petMap +} + +func buildWarehousePetList(player *player.Player) []model.PetInfo { + allPets := player.Service.Pet.PetInfo(0) + if len(allPets) == 0 { + return make([]model.PetInfo, 0) + } + + usedCatchTimes := buildCatchTimeSet(player.Info.PetList, player.Info.BackupPetList) + result := make([]model.PetInfo, 0, len(allPets)) + for i := range allPets { + catchTime := allPets[i].Data.CatchTime + if _, exists := usedCatchTimes[catchTime]; exists { + continue + } + result = append(result, allPets[i].Data) + } + return result +} + +func findBackupPet(player *player.Player, catchTime uint32) (int, *model.PetInfo, bool) { + for i := range player.Info.BackupPetList { + if player.Info.BackupPetList[i].CatchTime == catchTime { + return i, &player.Info.BackupPetList[i], true + } + } + return -1, nil, false +} + +func findPetListSlot(player *player.Player, catchTime uint32) (petListSlot, bool) { + if index, petInfo, ok := player.FindPet(catchTime); ok { + return petListSlot{ + list: &player.Info.PetList, + index: index, + info: *petInfo, + kind: petListKindMain, + }, true + } + + if index, petInfo, ok := findBackupPet(player, catchTime); ok { + return petListSlot{ + list: &player.Info.BackupPetList, + index: index, + info: *petInfo, + kind: petListKindBackup, + }, true + } + + return petListSlot{}, false +} + +func syncBackupPetList(player *player.Player) { + if player.Info.BackupPetList == nil { + player.Info.BackupPetList = make([]model.PetInfo, 0) + return + } + + bagPets := player.Service.Pet.PetInfo(0) + if len(bagPets) == 0 { + player.Info.BackupPetList = make([]model.PetInfo, 0) + return + } + + bagCatchTimes := make(map[uint32]struct{}, len(bagPets)) + for i := range bagPets { + bagCatchTimes[bagPets[i].Data.CatchTime] = struct{}{} + } + + mainPetCatchTimes := buildCatchTimeSet(player.Info.PetList) + nextBackupList := make([]model.PetInfo, 0, len(player.Info.BackupPetList)) + for _, petInfo := range player.Info.BackupPetList { + if _, inBag := bagCatchTimes[petInfo.CatchTime]; !inBag { + continue + } + if _, inMain := mainPetCatchTimes[petInfo.CatchTime]; inMain { + continue + } + nextBackupList = append(nextBackupList, petInfo) + } + + player.Info.BackupPetList = nextBackupList +} + +func buildUserBagPetInfo(player *player.Player) *pet.GetUserBagPetInfoOutboundInfo { + syncBackupPetList(player) + + result := &pet.GetUserBagPetInfoOutboundInfo{ + PetList: make([]model.PetInfo, len(player.Info.PetList)), + BackupPetList: make([]model.PetInfo, len(player.Info.BackupPetList)), + } + copy(result.PetList, player.Info.PetList) + copy(result.BackupPetList, player.Info.BackupPetList) + return result +} + +func buildOrderedPetList( + catchTimes []uint32, + petMap map[uint32]model.PetInfo, + used map[uint32]struct{}, +) ([]model.PetInfo, bool) { + result := make([]model.PetInfo, 0, len(catchTimes)) + for _, catchTime := range catchTimes { + if catchTime == 0 { + return nil, false + } + if _, exists := used[catchTime]; exists { + return nil, false + } + + petInfo, exists := petMap[catchTime] + if !exists { + return nil, false + } + + used[catchTime] = struct{}{} + result = append(result, petInfo) + } + return result, true +} + +func buildPetShowOutboundInfo(userID, flag uint32, info *model.PetInfo) *pet.PetShowOutboundInfo { + return &pet.PetShowOutboundInfo{ + UserID: userID, + CatchTime: info.CatchTime, + ID: info.ID, + Flag: flag, + Dv: info.Dv, + ShinyLen: info.ShinyLen, + ShinyInfo: info.ShinyInfo, + SkinID: info.SkinID, + } +} diff --git a/logic/controller/pet_manage.go b/logic/controller/pet_manage.go new file mode 100644 index 00000000..be11be9d --- /dev/null +++ b/logic/controller/pet_manage.go @@ -0,0 +1,73 @@ +package controller + +import ( + "blazing/common/data/xmlres" + "blazing/common/socket/errorcode" + "blazing/logic/service/fight" + "blazing/logic/service/pet" + "blazing/logic/service/player" +) + +// PetReleaseToWarehouse 将精灵从仓库包中放生 +func (h Controller) PetReleaseToWarehouse( + data *pet.PET_ROWEI, player *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { + _, _, inBag := player.FindPet(data.CatchTime) + _, _, inBackup := findBackupPet(player, data.CatchTime) + freeForbidden := xmlres.PetMAP[int(data.ID)].FreeForbidden + if inBag || inBackup || freeForbidden == 1 { + return nil, errorcode.ErrorCodes.ErrCannotReleaseNonWarehouse + } + + if !player.Service.Pet.UpdateFree(data.CatchTime, 1) { + return nil, errorcode.ErrorCodes.ErrSystemError + } + + return nil, 0 +} + +// PetOneCure 单体治疗 +func (h Controller) PetOneCure( + data *pet.PetOneCureInboundInfo, player *player.Player) (result *pet.PetOneCureOutboundInfo, err errorcode.ErrorCode) { + if player.IsArenaHealLocked() { + return result, errorcode.ErrorCodes.ErrChampionCannotHeal + } + + _, currentPet, ok := player.FindPet(data.CatchTime) + if ok { + defer currentPet.Cure() + } + + return &pet.PetOneCureOutboundInfo{ + CatchTime: data.CatchTime, + }, 0 +} + +// PetFirst 精灵首发 +func (h Controller) PetFirst( + data *pet.PetDefaultInboundInfo, player *player.Player) (result *pet.PetDefaultOutboundInfo, err errorcode.ErrorCode) { + if player.IsArenaSwitchLocked() { + return result, errorcode.ErrorCodes.ErrChampionCannotSwitch + } + + result = &pet.PetDefaultOutboundInfo{} + index, _, ok := player.FindPet(data.CatchTime) + if ok && index != 0 { + player.Info.PetList[index], player.Info.PetList[0] = player.Info.PetList[0], player.Info.PetList[index] + result.IsDefault = 1 + } + + return result, 0 +} + +// SetPetExp 设置宠物经验 +func (h Controller) SetPetExp( + data *pet.PetSetExpInboundInfo, + player *player.Player) (result *pet.PetSetExpOutboundInfo, err errorcode.ErrorCode) { + _, currentPet, found := player.FindPet(data.CatchTime) + if !found || currentPet.Level >= 100 { + return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, errorcode.ErrorCodes.ErrSystemError + } + + player.AddPetExp(currentPet, data.Exp) + return &pet.PetSetExpOutboundInfo{Exp: player.Info.ExpPool}, 0 +} diff --git a/logic/service/fight/action.go b/logic/service/fight/action.go index 7d6f8403..bf4f906a 100644 --- a/logic/service/fight/action.go +++ b/logic/service/fight/action.go @@ -6,10 +6,6 @@ import ( "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" "blazing/modules/player/model" -<<<<<<< ours -======= - "context" ->>>>>>> theirs "github.com/jinzhu/copier" ) @@ -33,6 +29,7 @@ func (f *FightC) openActionWindow() { f.actionMu.Lock() f.acceptActions = true f.pendingActions = f.pendingActions[:0] + f.actionRound.Store(uint32(f.Round)) f.actionMu.Unlock() } @@ -40,6 +37,7 @@ func (f *FightC) closeActionWindow() { f.actionMu.Lock() f.acceptActions = false f.pendingActions = f.pendingActions[:0] + f.actionRound.Store(0) f.actionMu.Unlock() } @@ -48,19 +46,14 @@ func (f *FightC) submitAction(act action.BattleActionI) { return } -<<<<<<< ours -======= round := f.actionRound.Load() if round == 0 { - cool.Logger.Printf(context.Background(), logMsg, userID, targetID) return } - act.SetRound(round) ->>>>>>> theirs f.actionMu.Lock() - if !f.acceptActions { + if !f.acceptActions || act.GetRound() != f.actionRound.Load() { f.actionMu.Unlock() return } @@ -90,7 +83,6 @@ func (f *FightC) submitAction(act action.BattleActionI) { default: } } -<<<<<<< ours func (f *FightC) nextAction() action.BattleActionI { f.actionMu.Lock() @@ -98,8 +90,6 @@ func (f *FightC) nextAction() action.BattleActionI { f.actionMu.Unlock() return nil } -======= ->>>>>>> theirs act := f.pendingActions[0] copy(f.pendingActions, f.pendingActions[1:]) @@ -218,21 +208,11 @@ func (f *FightC) UseItem(c common.PlayerI, cacthid, itemid uint32) { return } -<<<<<<< ours - -======= ->>>>>>> theirs if f.Info.Mode == info.BattleMode.PET_MELEE { go f.UseSkill(c, 0) return } -<<<<<<< ours f.submitAction(&action.UseItemAction{BaseAction: action.NewBaseAction(c.GetInfo().UserID), ItemID: itemid, CacthTime: cacthid}) -======= - f.submitAction(&action.UseItemAction{BaseAction: action.NewBaseAction(c.GetInfo().UserID), ItemID: itemid, CacthTime: cacthid}, - "action round is not ready, failed to send UseItem, userID: %d, skillID: %d", - c.GetInfo().UserID, cacthid) ->>>>>>> theirs } // ReadyFight 处理玩家战斗准备逻辑,当满足条件时启动战斗循环 @@ -285,13 +265,9 @@ func (f *FightC) startBattle(startInfo info.FightStartOutboundInfo) { // } go f.battleLoop() + // 向双方广播战斗开始信息 f.Broadcast(func(ff *input.Input) { - - // 通知双方玩家准备完成,即将开始战斗 - ff.Player.SendPackCmd(2504, &startInfo) - }) }) - } diff --git a/logic/service/fight/action/BattleAction.go b/logic/service/fight/action/BattleAction.go index 88aeba0e..3ba6f0e0 100644 --- a/logic/service/fight/action/BattleAction.go +++ b/logic/service/fight/action/BattleAction.go @@ -24,8 +24,9 @@ var PlayerOperations = enum.New[struct { // BattleActionI 战斗动作接口 type BattleActionI interface { GetPlayerID() uint32 + GetRound() uint32 + SetRound(uint32) Priority() int // 优先级 - } // SelectSkillAction 选择技能的战斗动作 @@ -43,21 +44,29 @@ func (*SelectSkillAction) Priority() int { type BaseAction struct { PlayerID uint32 // 玩家ID - + Round uint32 // 所属回合 } func NewBaseAction(t uint32) BaseAction { return BaseAction{ PlayerID: t, } - } + func (a *BaseAction) GetPlayerID() uint32 { return a.PlayerID // fmt.Printf("玩家[%d]主动切换宠物:从%s切换到%s(原因:%s)\n", // // a.PlayerID, a.CurrentPet.Name, a.TargetPet.Name, a.SwitchReason) } +func (a *BaseAction) GetRound() uint32 { + return a.Round +} + +func (a *BaseAction) SetRound(round uint32) { + a.Round = round +} + // ActiveSwitchAction 主动切换宠物的战斗动作 type ActiveSwitchAction struct { BaseAction diff --git a/logic/service/fight/effect/1438_1442.go b/logic/service/fight/effect/1438_1442.go new file mode 100644 index 00000000..ce99a15a --- /dev/null +++ b/logic/service/fight/effect/1438_1442.go @@ -0,0 +1,177 @@ +package effect + +import ( + "blazing/logic/service/fight/action" + "blazing/logic/service/fight/info" + "blazing/logic/service/fight/input" + "blazing/logic/service/fight/node" +) + +// Effect 1438: 若对手处于能力下降状态,则{0}回合内令对手使用的属性技能无效 +type Effect1438 struct{ node.EffectNode } + +func (e *Effect1438) Skill_Use() bool { + if len(e.Args()) == 0 || e.Args()[0].Cmp(zero) <= 0 || !e.Ctx().Opp.HasPropSub() { + return true + } + + sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1438, int(e.Args()[0].IntPart())) + if sub != nil { + e.Ctx().Opp.AddEffect(e.Ctx().Our, sub) + } + return true +} + +type Effect1438Sub struct{ RoundEffectArg0Base } + +func (e *Effect1438Sub) ActionStart(a, b *action.SelectSkillAction) bool { + if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS { + return true + } + + e.Ctx().SkillEntity.SetMiss() + return true +} + +// Effect 1439: 对手处于能力下降状态造成的伤害提升{0}% +type Effect1439 struct{ node.EffectNode } + +func (e *Effect1439) Damage_Mul(zone *info.DamageZone) bool { + if len(e.Args()) == 0 || zone == nil || zone.Type != info.DamageType.Red || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS { + return true + } + if !e.Ctx().Opp.HasPropSub() { + return true + } + + zone.Damage = zone.Damage.Mul(hundred.Add(e.Args()[0])).Div(hundred) + return true +} + +// Effect 1440: 免疫下{0}次自身受到的异常状态 +type Effect1440 struct{ node.EffectNode } + +func (e *Effect1440) Skill_Use() bool { + if len(e.Args()) == 0 || e.Args()[0].Cmp(zero) <= 0 { + return true + } + + sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1440, int(e.Args()[0].IntPart())) + if sub != nil { + e.Ctx().Our.AddEffect(e.Ctx().Our, sub) + } + return true +} + +type Effect1440Sub struct { + node.EffectNode + remaining int +} + +func (e *Effect1440Sub) SetArgs(t *input.Input, a ...int) { + e.EffectNode.SetArgs(t, a...) + e.Duration(-1) + e.CanStack(false) + if len(a) > 0 { + e.remaining = a[0] + } +} + +func (e *Effect1440Sub) EFFect_Befer(in *input.Input, effEffect input.Effect) bool { + if e.remaining <= 0 { + e.Alive(false) + return true + } + if in != e.Ctx().Opp || !input.IS_Stat(effEffect) { + return true + } + + e.remaining-- + if e.remaining <= 0 { + e.Alive(false) + } + return false +} + +// Effect 1441: 解除自身能力下降状态,解除成功则使对手随机{0}个技能PP值归零 +type Effect1441 struct{ node.EffectNode } + +func (e *Effect1441) Skill_Use() bool { + cleared := false + for i, v := range e.Ctx().Our.Prop[:] { + if v >= 0 { + continue + } + if e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), 0) { + cleared = true + } + } + if cleared && len(e.Args()) > 0 { + zeroRandomSkillPP(e.Ctx().Opp, int(e.Args()[0].IntPart())) + } + return true +} + +// Effect 1442: {0}回合内每回合{1}%闪避对手攻击,未触发则回合结束时{2}%令对手{3} +type Effect1442 struct{ node.EffectNode } + +func (e *Effect1442) Skill_Use() bool { + if len(e.Args()) < 4 || e.Args()[0].Cmp(zero) <= 0 { + return true + } + + sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1442, e.SideEffectArgs...) + if sub != nil { + e.Ctx().Our.AddEffect(e.Ctx().Our, sub) + } + return true +} + +type Effect1442Sub struct { + node.EffectNode + triggered bool +} + +func (e *Effect1442Sub) SetArgs(t *input.Input, a ...int) { + e.EffectNode.SetArgs(t, a...) + e.CanStack(false) + if len(a) > 0 { + e.Duration(a[0]) + } +} + +func (e *Effect1442Sub) SkillHit_ex() bool { + if len(e.Args()) < 4 || e.triggered || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 || e.Ctx().SkillEntity.AttackTime == 2 { + return true + } + + if ok, _, _ := e.Input.Player.Roll(int(e.Args()[1].IntPart()), 100); ok && e.Ctx().SkillEntity.SetMiss() { + e.triggered = true + } + return true +} + +func (e *Effect1442Sub) TurnEnd() { + if !e.triggered && len(e.Args()) >= 4 { + statusID := int(e.Args()[3].IntPart()) + if statusID > 0 { + if ok, _, _ := e.Input.Player.Roll(int(e.Args()[2].IntPart()), 100); ok { + addStatusByID(e.Ctx().Our, e.Ctx().Opp, statusID) + } + } + } + + e.triggered = false + e.EffectNode.TurnEnd() +} + +func init() { + input.InitEffect(input.EffectType.Skill, 1438, &Effect1438{}) + input.InitEffect(input.EffectType.Sub, 1438, &Effect1438Sub{}) + input.InitEffect(input.EffectType.Skill, 1439, &Effect1439{}) + input.InitEffect(input.EffectType.Skill, 1440, &Effect1440{}) + input.InitEffect(input.EffectType.Sub, 1440, &Effect1440Sub{}) + input.InitEffect(input.EffectType.Skill, 1441, &Effect1441{}) + input.InitEffect(input.EffectType.Skill, 1442, &Effect1442{}) + input.InitEffect(input.EffectType.Sub, 1442, &Effect1442Sub{}) +} diff --git a/logic/service/fight/effect/1443_1447.go b/logic/service/fight/effect/1443_1447.go new file mode 100644 index 00000000..266caf1a --- /dev/null +++ b/logic/service/fight/effect/1443_1447.go @@ -0,0 +1,170 @@ +package effect + +import ( + "blazing/logic/service/fight/action" + "blazing/logic/service/fight/info" + "blazing/logic/service/fight/input" + "blazing/logic/service/fight/node" + + "github.com/alpacahq/alpacadecimal" +) + +// Effect 1443: 连续使用时威力提升{0}%,最高提升{1}% +type Effect1443 struct{ AddLvelEffect } + +func (e *Effect1443) SkillHit() bool { + if len(e.Args()) < 2 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS { + return true + } + if (e.Skillid != 0 && e.Ctx().SkillEntity.XML.ID != e.Skillid) || e.Ctx().SkillEntity.AttackTime == 0 { + return e.AddLvelEffect.SkillHit() + } + if e.UseSkillCount > 0 { + addSkillPowerPercent(e.Ctx().SkillEntity, e.GetADD(alpacadecimal.Zero, e.Args()[0], e.Args()[1])) + } + return e.AddLvelEffect.SkillHit() +} + +// Effect 1444: 威力根据自身体力变化,当前剩余体力越少则威力越大 +type Effect1444 struct{ node.EffectNode } + +func (e *Effect1444) SkillHit() bool { + if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().Our.CurrentPet == nil { + return true + } + + maxHP := e.Ctx().Our.CurrentPet.GetMaxHP() + if maxHP.Cmp(alpacadecimal.Zero) <= 0 { + return true + } + + power := alpacadecimal.NewFromInt(165).Sub(e.Ctx().Our.CurrentPet.GetHP().Div(maxHP).Mul(alpacadecimal.NewFromInt(65))) + if power.Cmp(alpacadecimal.Zero) > 0 { + e.Ctx().SkillEntity.XML.Power = int(power.IntPart()) + } + return true +} + +// Effect 1445: 吸取对手能力提升状态,吸取成功则{0}回合内对手属性技能无效,若对手不处于能力提升状态则自身下{1}回合先制+{2} +type Effect1445 struct{ node.EffectNode } + +func (e *Effect1445) Skill_Use() bool { + if len(e.Args()) < 3 { + return true + } + + absorbed := false + for i, v := range e.Ctx().Opp.Prop[:] { + if v <= 0 { + continue + } + if !e.Ctx().Opp.SetProp(e.Ctx().Our, int8(i), 0) { + continue + } + absorbed = true + e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), v) + } + if absorbed { + sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 14450, int(e.Args()[0].IntPart())) + if sub != nil { + e.Ctx().Opp.AddEffect(e.Ctx().Our, sub) + } + return true + } + + sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 14451, int(e.Args()[1].IntPart()), int(e.Args()[2].IntPart())) + if sub != nil { + e.Ctx().Our.AddEffect(e.Ctx().Our, sub) + } + return true +} + +type Effect1445StatusDisableSub struct{ RoundEffectArg0Base } + +func (e *Effect1445StatusDisableSub) ActionStart(a, b *action.SelectSkillAction) bool { + if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS { + return true + } + + e.Ctx().SkillEntity.SetMiss() + return true +} + +type Effect1445PrioritySub struct{ RoundEffectArg0Base } + +func (e *Effect1445PrioritySub) ComparePre(fattack, sattack *action.SelectSkillAction) bool { + if !shouldAdjustNextAttackPriority(e, fattack, sattack) || len(e.Args()) < 2 { + return true + } + + sattack.SkillEntity.XML.Priority += int(e.Args()[1].IntPart()) + return true +} + +// Effect 1446: 获得{0}点护罩,护罩消失时使对手全属性-{1} +type Effect1446 struct{ node.EffectNode } + +func (e *Effect1446) Skill_Use() bool { + if len(e.Args()) < 2 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 { + return true + } + + e.Ctx().Our.AddShield(e.Args()[0]) + sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1446, int(e.Args()[1].IntPart())) + if sub != nil { + e.Ctx().Our.AddEffect(e.Ctx().Our, sub) + } + return true +} + +type Effect1446Sub struct{ node.EffectNode } + +func (e *Effect1446Sub) SetArgs(t *input.Input, a ...int) { + e.EffectNode.SetArgs(t, a...) + e.Duration(-1) + e.CanStack(false) +} + +func (e *Effect1446Sub) ShieldChange(before, after alpacadecimal.Decimal) bool { + if before.Cmp(alpacadecimal.Zero) <= 0 || after.Cmp(alpacadecimal.Zero) > 0 { + return true + } + if len(e.Args()) > 0 { + applyAllPropDown(e.Ctx().Our, e.Ctx().Opp, int8(e.Args()[0].IntPart())) + } + e.Alive(false) + return true +} + +// Effect 1447: 自身体力高于最大体力的1/{0}时{1}%令对手{2} +type Effect1447 struct{ node.EffectNode } + +func (e *Effect1447) Skill_Use() bool { + if len(e.Args()) < 3 || e.Ctx().Our.CurrentPet == nil || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 { + return true + } + + maxHP := e.Ctx().Our.CurrentPet.GetMaxHP() + if maxHP.Cmp(alpacadecimal.Zero) <= 0 { + return true + } + if e.Ctx().Our.CurrentPet.GetHP().Mul(e.Args()[0]).Cmp(maxHP) <= 0 { + return true + } + + if ok, _, _ := e.Input.Player.Roll(int(e.Args()[1].IntPart()), 100); ok { + addStatusByID(e.Ctx().Our, e.Ctx().Opp, int(e.Args()[2].IntPart())) + } + return true +} + +func init() { + input.InitEffect(input.EffectType.Skill, 1443, &Effect1443{}) + input.InitEffect(input.EffectType.Skill, 1444, &Effect1444{}) + input.InitEffect(input.EffectType.Skill, 1445, &Effect1445{}) + input.InitEffect(input.EffectType.Sub, 14450, &Effect1445StatusDisableSub{}) + input.InitEffect(input.EffectType.Sub, 14451, &Effect1445PrioritySub{}) + input.InitEffect(input.EffectType.Skill, 1446, &Effect1446{}) + input.InitEffect(input.EffectType.Sub, 1446, &Effect1446Sub{}) + input.InitEffect(input.EffectType.Skill, 1447, &Effect1447{}) +} diff --git a/logic/service/fight/fightc.go b/logic/service/fight/fightc.go index cb7583d5..ae83d027 100644 --- a/logic/service/fight/fightc.go +++ b/logic/service/fight/fightc.go @@ -319,7 +319,6 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) FAttack: *f.First.AttackValue, SAttack: *f.Second.AttackValue, } - f.actionRound.Store(uint32(f.Round + 1)) //因为切完才能广播,所以必须和回合结束分开结算 f.Broadcast(func(fighter *input.Input) { for _, switchAction := range f.Switch { diff --git a/logic/service/fight/input.go b/logic/service/fight/input.go index 2192ca13..af3552f8 100644 --- a/logic/service/fight/input.go +++ b/logic/service/fight/input.go @@ -11,6 +11,7 @@ import ( "blazing/logic/service/fight/input" "blazing/logic/service/user" "sync" + "sync/atomic" "time" "github.com/gogf/gf/v2/util/grand" @@ -35,8 +36,8 @@ type FightC struct { actionNotify chan struct{} acceptActions bool pendingActions []action.BattleActionI // 待处理动作队列,同一玩家最多保留两段动作 + actionRound atomic.Uint32 -<<<<<<< ours quit chan struct{} over chan struct{} First *input.Input @@ -45,17 +46,6 @@ type FightC struct { closefight bool overl sync.Once waittime int -======= - quit chan struct{} - over chan struct{} - First *input.Input - TrueFirst *input.Input - Second *input.Input - closefight bool - overl sync.Once - waittime int - actionRound atomic.Uint32 ->>>>>>> theirs model.FightOverInfo //战斗结束的插装 callback func(model.FightOverInfo)