feat(fight): 添加旧组队协议支持并优化战斗系统 - 实现了旧组队协议相关功能,包括GroupReadyFightFinish、GroupUseSkill、 GroupUseItem、GroupChangePet和GroupEscape方法 - 新增组队战斗相关的入站信息结构体定义 - 实现了组队BOSS战斗逻辑,添加groupBossSlotLimit常量 - 重构宠物技能设置逻辑,调整金币消耗时机 - 优化战斗循环逻辑,添加对无行动槽位的处理 - 改进AI行动逻辑,增加多位置目标选择机制 - 完善捕获系统上下文处理,修复空指针问题 - 添加战斗状态更新和数据同步机制 fix(pet-skill): 修复宠物技能设置中的金币扣除逻辑错误 - 将金币扣除逻辑移到验证之后 - 修正宠物技能数量限制检查的顺序 - 防止重复添加已有技能的情况 refactor(fight): 重构战斗系统代码结构 - 分离新旧组队协议的战斗创建逻辑 - 优化战斗输入验证和处理流程 - 改进战斗循环中的错误处理机制 ```
This commit is contained in:
@@ -43,7 +43,8 @@ func (h Controller) GroupUseSkill(data *GroupUseSkillInboundInfo, c *player.Play
|
||||
targetRelation = fight.SkillTargetAlly
|
||||
}
|
||||
h.dispatchFightActionEnvelope(c, fight.NewSkillActionEnvelope(data.SkillId, int(data.ActorIndex), int(data.TargetPos), targetRelation, 0))
|
||||
return nil, 0
|
||||
c.SendPackCmd(7558, nil)
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func (h Controller) GroupUseItem(data *GroupUseItemInboundInfo, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||
|
||||
@@ -75,54 +75,13 @@ func startMapBossFight(
|
||||
ourPets := p.GetPetInfo(100)
|
||||
oppPets := ai.GetPetInfo(0)
|
||||
if mapNode != nil && mapNode.IsGroupBoss != 0 {
|
||||
ourSlots := buildGroupBossPetSlots(ourPets, groupBossSlotLimit)
|
||||
oppSlots := buildGroupBossPetSlots(oppPets, groupBossSlotLimit)
|
||||
if len(ourSlots) > 0 && len(oppSlots) > 0 {
|
||||
return fight.NewLegacyGroupFightSingleControllerN(p, ai, ourSlots, oppSlots, fn)
|
||||
if len(ourPets) > 0 && len(oppPets) > 0 {
|
||||
return fight.NewLegacyGroupFightSingleController(p, ai, ourPets, oppPets, groupBossSlotLimit, fn)
|
||||
}
|
||||
}
|
||||
return fight.NewFight(p, ai, ourPets, oppPets, fn)
|
||||
}
|
||||
|
||||
func buildGroupBossPetSlots(pets []model.PetInfo, slotLimit int) [][]model.PetInfo {
|
||||
if len(pets) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
slots := make([][]model.PetInfo, 0, slotLimit)
|
||||
for _, pet := range pets {
|
||||
if pet.Hp == 0 {
|
||||
continue
|
||||
}
|
||||
if slotLimit <= 0 {
|
||||
slotLimit = 3
|
||||
}
|
||||
if len(slots) < slotLimit {
|
||||
slots = append(slots, []model.PetInfo{pet})
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if len(slots) == 0 {
|
||||
return nil
|
||||
}
|
||||
var idx int = 0
|
||||
for _, pet := range pets[len(slots):] {
|
||||
if pet.Hp == 0 {
|
||||
continue
|
||||
}
|
||||
for step := 0; step < len(slots); step++ {
|
||||
slotIdx := (idx + step) % len(slots)
|
||||
if len(slots[slotIdx]) < 6 {
|
||||
slots[slotIdx] = append(slots[slotIdx], pet)
|
||||
idx = (slotIdx + 1) % len(slots)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return slots
|
||||
}
|
||||
|
||||
// OnPlayerFightNpcMonster 战斗野怪
|
||||
func (Controller) OnPlayerFightNpcMonster(req *FightNpcMonsterInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
|
||||
if err = p.CanFight(); err != 0 {
|
||||
|
||||
@@ -64,6 +64,7 @@ func (h *Controller) SwitchFlying(data *SwitchFlyingInboundInfo, c *player.Playe
|
||||
// PlayerPetCure 处理控制器请求。
|
||||
func (h *Controller) PlayerPetCure(data *PetCureInboundInfo, c *player.Player) (result *nono.PetCureOutboundEmpty, err errorcode.ErrorCode) { //这个时候player应该是空的
|
||||
_ = data
|
||||
result = &nono.PetCureOutboundEmpty{}
|
||||
if c.IsArenaHealLocked() {
|
||||
return result, errorcode.ErrorCodes.ErrChampionCannotHeal
|
||||
}
|
||||
@@ -73,6 +74,9 @@ func (h *Controller) PlayerPetCure(data *PetCureInboundInfo, c *player.Player) (
|
||||
for i := range c.Info.PetList {
|
||||
c.Info.PetList[i].Cure()
|
||||
}
|
||||
for i := range c.Info.BackupPetList {
|
||||
c.Info.BackupPetList[i].Cure()
|
||||
}
|
||||
c.Info.Coins -= nonoPetCureCost
|
||||
return
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ func (f *FightC) submitAction(act action.BattleActionI) {
|
||||
break
|
||||
}
|
||||
if replaceIndex >= 0 {
|
||||
if f.LegacyGroupProtocol {
|
||||
f.actionMu.Unlock()
|
||||
return
|
||||
}
|
||||
f.pendingActions[replaceIndex] = act
|
||||
} else {
|
||||
f.pendingActions = append(f.pendingActions, act)
|
||||
@@ -295,9 +299,8 @@ func (f *FightC) ReadyFight(c common.PlayerI) {
|
||||
}
|
||||
return
|
||||
}
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
|
||||
ff.Player.SendPackCmd(2404, &info.S2C_2404{UserID: c.GetInfo().UserID})
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
p.SendPackCmd(2404, &info.S2C_2404{UserID: c.GetInfo().UserID})
|
||||
})
|
||||
// 2. 标记当前玩家已准备完成
|
||||
input := f.GetInputByPlayer(c, false)
|
||||
@@ -369,8 +372,12 @@ func (f *FightC) startBattle(startInfo info.FightStartOutboundInfo) {
|
||||
go f.battleLoop()
|
||||
|
||||
// 向双方广播战斗开始信息
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
f.sendFightPacket(ff.Player, fightPacketStart, &startInfo)
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
if f.LegacyGroupProtocol {
|
||||
f.sendLegacyGroupStart(p)
|
||||
return
|
||||
}
|
||||
f.sendFightPacket(p, fightPacketStart, &startInfo)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package fight
|
||||
import (
|
||||
"blazing/common/utils"
|
||||
|
||||
"blazing/logic/service/common"
|
||||
"blazing/logic/service/fight/action"
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
@@ -422,11 +423,19 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
||||
|
||||
attackValueResult := f.buildNoteUseSkillOutboundInfo()
|
||||
//因为切完才能广播,所以必须和回合结束分开结算
|
||||
f.Broadcast(func(fighter *input.Input) {
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
for _, switchAction := range f.Switch {
|
||||
if fighter.Player.GetInfo().UserID != switchAction.Reason.UserId {
|
||||
if p.GetInfo().UserID != switchAction.Reason.UserId {
|
||||
// println("切精灵", switchAction.Reason.UserId, switchAction.Reason.ID)
|
||||
f.sendFightPacket(fighter.Player, fightPacketChangePetSuccess, &switchAction.Reason)
|
||||
if f.LegacyGroupProtocol {
|
||||
switchedInput := f.getInputByUserID(switchAction.Reason.UserId, int(switchAction.Reason.ActorIndex), false)
|
||||
if switchedInput == nil {
|
||||
switchedInput = f.getInputByUserID(switchAction.Reason.UserId, int(switchAction.ActorIndex), false)
|
||||
}
|
||||
f.sendLegacyGroupChangePetSuccess(p, switchedInput, &switchAction.Reason)
|
||||
} else {
|
||||
f.sendFightPacket(p, fightPacketChangePetSuccess, &switchAction.Reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -440,8 +449,12 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
||||
// })
|
||||
return
|
||||
}
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
if !f.LegacyGroupProtocol {
|
||||
f.sendFightPacket(p, fightPacketSkillResult, &attackValueResult)
|
||||
}
|
||||
})
|
||||
f.Broadcast(func(fighter *input.Input) {
|
||||
f.sendFightPacket(fighter.Player, fightPacketSkillResult, &attackValueResult)
|
||||
fighter.CanChange = 0
|
||||
})
|
||||
if f.closefight {
|
||||
|
||||
@@ -3,6 +3,7 @@ package fight
|
||||
import (
|
||||
"blazing/logic/service/common"
|
||||
"blazing/logic/service/fight/action"
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/modules/player/model"
|
||||
)
|
||||
@@ -38,6 +39,17 @@ const (
|
||||
|
||||
type fightPacketKind uint8
|
||||
|
||||
const (
|
||||
fightPacketReady fightPacketKind = iota
|
||||
fightPacketStart
|
||||
fightPacketSkillResult
|
||||
fightPacketOver
|
||||
fightPacketChangePetSuccess
|
||||
fightPacketUseItem
|
||||
fightPacketChat
|
||||
fightPacketLoadPercentNotice
|
||||
)
|
||||
|
||||
type legacyEscapeSuccessInfo struct {
|
||||
UserID uint32 `struc:"uint32"`
|
||||
Nick string `struc:"[16]byte"`
|
||||
@@ -50,6 +62,14 @@ type legacyBoutDoneInfo struct {
|
||||
}
|
||||
|
||||
type legacySpriteDieInfo struct {
|
||||
Count uint8 `struc:"uint8"`
|
||||
Side uint8 `struc:"uint8"`
|
||||
ActorIndex uint8 `struc:"uint8"`
|
||||
Flag uint8 `struc:"uint8"`
|
||||
HasBackup uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
type legacyLegacySpriteDieItem struct {
|
||||
Flag uint8 `struc:"uint8"`
|
||||
Side uint8 `struc:"uint8"`
|
||||
ActorIndex uint8 `struc:"uint8"`
|
||||
@@ -57,16 +77,132 @@ type legacySpriteDieInfo struct {
|
||||
HasBackup uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
const (
|
||||
fightPacketReady fightPacketKind = iota
|
||||
fightPacketStart
|
||||
fightPacketSkillResult
|
||||
fightPacketOver
|
||||
fightPacketChangePetSuccess
|
||||
fightPacketUseItem
|
||||
fightPacketChat
|
||||
fightPacketLoadPercentNotice
|
||||
)
|
||||
type legacyGroupReadyToFightInfo struct {
|
||||
Model uint32 `struc:"uint32"`
|
||||
GroupOneInfo legacyReadyToFightTeam `struc:""`
|
||||
GroupTwoInfo legacyReadyToFightTeam `struc:""`
|
||||
}
|
||||
|
||||
type legacyReadyToFightTeam struct {
|
||||
InvitorID uint8 `struc:"uint8"`
|
||||
LeaderID uint32 `struc:"uint32"`
|
||||
GroupMembCnt uint8 `struc:"sizeof=GroupList"`
|
||||
GroupList []legacyReadyFightUser `struc:""`
|
||||
}
|
||||
|
||||
type legacyReadyFightUser struct {
|
||||
UserID uint32 `struc:"uint32"`
|
||||
Nick string `struc:"[16]byte"`
|
||||
MonCnt uint32 `struc:"sizeof=MonList"`
|
||||
MonList []legacyReadyFightPet `struc:""`
|
||||
}
|
||||
|
||||
type legacyReadyFightPet struct {
|
||||
ID uint32 `struc:"uint32"`
|
||||
MoveCnt uint32 `struc:"sizeof=MoveList"`
|
||||
MoveList []uint32 `struc:"[]uint32"`
|
||||
}
|
||||
|
||||
type legacyGroupStartInfo struct {
|
||||
IsGank uint8 `struc:"uint8"`
|
||||
GroupOneN uint8 `struc:"sizeof=GroupOne"`
|
||||
GroupOne []legacyGroupStartPet `struc:""`
|
||||
GroupTwoN uint8 `struc:"sizeof=GroupTwo"`
|
||||
GroupTwo []legacyGroupStartPet `struc:""`
|
||||
}
|
||||
|
||||
type legacyGroupStartPet struct {
|
||||
Side uint8 `struc:"uint8"`
|
||||
Pos uint8 `struc:"uint8"`
|
||||
UserID uint32 `struc:"uint32"`
|
||||
IsChange uint8 `struc:"uint8"`
|
||||
PetID uint32 `struc:"uint32"`
|
||||
CatchTime uint32 `struc:"uint32"`
|
||||
Hp uint32 `struc:"uint32"`
|
||||
MaxHp uint32 `struc:"uint32"`
|
||||
Level uint32 `struc:"uint32"`
|
||||
Reserve uint32 `struc:"uint32"`
|
||||
Flag uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
type legacyGroupSkillHurtPacket struct {
|
||||
IsGank uint8 `struc:"uint8"`
|
||||
Attack legacyGroupSkillAttackInfo `struc:""`
|
||||
Attacked legacyGroupSkillDefendInfo `struc:""`
|
||||
}
|
||||
|
||||
type legacyGroupSkillAttackInfo struct {
|
||||
IsAttackor uint8 `struc:"uint8"`
|
||||
Side uint8 `struc:"uint8"`
|
||||
Pos uint8 `struc:"uint8"`
|
||||
UserID uint32 `struc:"uint32"`
|
||||
StatusList [20]uint8 `struc:"[20]byte"`
|
||||
Reserve1 uint8 `struc:"uint8"`
|
||||
Reserve2 uint8 `struc:"uint8"`
|
||||
BatLvList [6]uint8 `struc:"[6]byte"`
|
||||
PetID uint32 `struc:"uint32"`
|
||||
MoveID uint32 `struc:"uint32"`
|
||||
Hp uint32 `struc:"uint32"`
|
||||
MaxHp uint32 `struc:"uint32"`
|
||||
MoveCnt uint32 `struc:"sizeof=MoveMap"`
|
||||
MoveMap []legacyGroupSkillMoveInfo `struc:""`
|
||||
Flag uint32 `struc:"uint32"`
|
||||
IsCrit uint32 `struc:"uint32"`
|
||||
EffectName uint32 `struc:"uint32"`
|
||||
AtkTimes uint32 `struc:"uint32"`
|
||||
Dmg int32 `struc:"int32"`
|
||||
ChgHp int32 `struc:"int32"`
|
||||
SideEffectLen uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
type legacyGroupSkillDefendInfo struct {
|
||||
IsAttackor uint8 `struc:"uint8"`
|
||||
Side uint8 `struc:"uint8"`
|
||||
Pos uint8 `struc:"uint8"`
|
||||
UserID uint32 `struc:"uint32"`
|
||||
StatusList [20]uint8 `struc:"[20]byte"`
|
||||
Reserve1 uint8 `struc:"uint8"`
|
||||
Reserve2 uint8 `struc:"uint8"`
|
||||
BatLvList [6]uint8 `struc:"[6]byte"`
|
||||
PetID uint32 `struc:"uint32"`
|
||||
MoveID uint32 `struc:"uint32"`
|
||||
Hp uint32 `struc:"uint32"`
|
||||
MaxHp uint32 `struc:"uint32"`
|
||||
MoveCnt uint32 `struc:"sizeof=MoveMap"`
|
||||
MoveMap []legacyGroupSkillMoveInfo `struc:""`
|
||||
Flag uint32 `struc:"uint32"`
|
||||
SideEffectLen uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
type legacyGroupSkillMoveInfo struct {
|
||||
MoveID uint32 `struc:"uint32"`
|
||||
PP uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
type legacyGroupFightOverInfo struct {
|
||||
IsGank uint8 `struc:"uint8"`
|
||||
Reason uint32 `struc:"uint32"`
|
||||
WinnerID uint32 `struc:"uint32"`
|
||||
Reserve uint32 `struc:"uint32"`
|
||||
TwoTimes uint32 `struc:"uint32"`
|
||||
ThreeTimes uint32 `struc:"uint32"`
|
||||
AutoFightTime uint32 `struc:"uint32"`
|
||||
Reserve2 uint32 `struc:"uint32"`
|
||||
EnergyTime uint32 `struc:"uint32"`
|
||||
LearnTimes uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
type legacyGroupChangePetSuccessInfo struct {
|
||||
Side uint8 `struc:"uint8"`
|
||||
Pos uint8 `struc:"uint8"`
|
||||
UserID uint32 `struc:"uint32"`
|
||||
PetID uint32 `struc:"uint32"`
|
||||
CatchTime uint32 `struc:"uint32"`
|
||||
Level uint32 `struc:"uint32"`
|
||||
Hp uint32 `struc:"uint32"`
|
||||
MaxHp uint32 `struc:"uint32"`
|
||||
SkinID uint32 `struc:"uint32"`
|
||||
}
|
||||
|
||||
func groupModelByFight(f *FightC) uint32 {
|
||||
if f == nil {
|
||||
@@ -84,37 +220,15 @@ func groupModelByFight(f *FightC) uint32 {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FightC) sendLegacyGroupOver(player common.PlayerI, over *model.FightOverInfo) {
|
||||
if player == nil {
|
||||
return
|
||||
}
|
||||
if over == nil {
|
||||
over = &model.FightOverInfo{}
|
||||
}
|
||||
f.sendFightPacket(player, fightPacketOver, over)
|
||||
}
|
||||
|
||||
func (f *FightC) fightPacketCmd(kind fightPacketKind) uint32 {
|
||||
switch kind {
|
||||
case fightPacketReady:
|
||||
if f != nil && f.LegacyGroupProtocol {
|
||||
return groupCmdReadyToFight
|
||||
}
|
||||
return 2503
|
||||
case fightPacketStart:
|
||||
if f != nil && f.LegacyGroupProtocol {
|
||||
return groupCmdStartFight
|
||||
}
|
||||
return 2504
|
||||
case fightPacketSkillResult:
|
||||
if f != nil && f.LegacyGroupProtocol {
|
||||
return groupCmdSkillHurt
|
||||
}
|
||||
return 2505
|
||||
case fightPacketOver:
|
||||
if f != nil && f.LegacyGroupProtocol {
|
||||
return groupCmdFightOver
|
||||
}
|
||||
return 2506
|
||||
case fightPacketChangePetSuccess:
|
||||
if f != nil && f.LegacyGroupProtocol {
|
||||
@@ -152,6 +266,232 @@ func (f *FightC) sendFightPacket(player common.PlayerI, kind fightPacketKind, pa
|
||||
player.SendPackCmd(cmd, payload)
|
||||
}
|
||||
|
||||
func (f *FightC) sendLegacyGroupReady(player common.PlayerI) {
|
||||
if f == nil || !f.LegacyGroupProtocol || player == nil {
|
||||
return
|
||||
}
|
||||
player.SendPackCmd(groupCmdReadyToFight, f.buildLegacyGroupReadyInfo())
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyGroupReadyInfo() *legacyGroupReadyToFightInfo {
|
||||
return &legacyGroupReadyToFightInfo{
|
||||
Model: groupModelByFight(f),
|
||||
GroupOneInfo: f.buildLegacyReadyTeam(f.OurPlayers, f.Our),
|
||||
GroupTwoInfo: f.buildLegacyReadyTeam(f.OppPlayers, f.Opp),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyReadyTeam(players []common.PlayerI, inputs []*input.Input) legacyReadyToFightTeam {
|
||||
team := legacyReadyToFightTeam{InvitorID: 1}
|
||||
users := make([]legacyReadyFightUser, 0, len(players))
|
||||
for _, p := range players {
|
||||
if p == nil || p.GetInfo() == nil {
|
||||
continue
|
||||
}
|
||||
users = append(users, legacyReadyFightUser{
|
||||
UserID: p.GetInfo().UserID,
|
||||
Nick: p.GetInfo().Nick,
|
||||
MonList: collectLegacyReadyPetsByController(inputs, p.GetInfo().UserID),
|
||||
})
|
||||
}
|
||||
if len(users) == 0 {
|
||||
if fallback := firstNonNilInput(inputs); fallback != nil && fallback.Player != nil && fallback.Player.GetInfo() != nil {
|
||||
info := fallback.Player.GetInfo()
|
||||
users = append(users, legacyReadyFightUser{
|
||||
UserID: info.UserID,
|
||||
Nick: info.Nick,
|
||||
MonList: collectLegacyReadyPetsByController(inputs, info.UserID),
|
||||
})
|
||||
}
|
||||
}
|
||||
for idx := range users {
|
||||
users[idx].MonCnt = uint32(len(users[idx].MonList))
|
||||
}
|
||||
if len(users) > 0 {
|
||||
team.LeaderID = users[0].UserID
|
||||
}
|
||||
team.GroupList = users
|
||||
return team
|
||||
}
|
||||
|
||||
func collectLegacyReadyPetsByController(inputs []*input.Input, controllerID uint32) []legacyReadyFightPet {
|
||||
pets := make([]legacyReadyFightPet, 0, 6)
|
||||
for _, in := range inputs {
|
||||
if in == nil || !in.ControlledBy(controllerID) {
|
||||
continue
|
||||
}
|
||||
currentPet := in.CurrentPet()
|
||||
if currentPet == nil {
|
||||
continue
|
||||
}
|
||||
pets = append(pets, buildLegacyReadyFightPet(currentPet))
|
||||
}
|
||||
return pets
|
||||
}
|
||||
|
||||
func buildLegacyReadyFightPet(pet *info.BattlePetEntity) legacyReadyFightPet {
|
||||
result := legacyReadyFightPet{}
|
||||
if pet == nil {
|
||||
return result
|
||||
}
|
||||
moves := make([]uint32, 0, len(pet.Info.SkillList))
|
||||
for _, skill := range pet.Info.SkillList {
|
||||
if skill.ID == 0 {
|
||||
continue
|
||||
}
|
||||
moves = append(moves, skill.ID)
|
||||
}
|
||||
result.ID = pet.Info.ID
|
||||
result.MoveList = moves
|
||||
return result
|
||||
}
|
||||
|
||||
func firstNonNilInput(inputs []*input.Input) *input.Input {
|
||||
for _, in := range inputs {
|
||||
if in != nil {
|
||||
return in
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FightC) sendLegacyGroupStart(player common.PlayerI) {
|
||||
if f == nil || !f.LegacyGroupProtocol || player == nil {
|
||||
return
|
||||
}
|
||||
player.SendPackCmd(groupCmdStartFight, f.buildLegacyGroupStartInfo())
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyGroupStartInfo() *legacyGroupStartInfo {
|
||||
return &legacyGroupStartInfo{
|
||||
IsGank: 0,
|
||||
GroupOne: f.collectLegacyGroupStartPets(f.Our, 1),
|
||||
GroupTwo: f.collectLegacyGroupStartPets(f.Opp, 2),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FightC) collectLegacyGroupStartPets(inputs []*input.Input, side uint8) []legacyGroupStartPet {
|
||||
ret := make([]legacyGroupStartPet, 0, len(inputs))
|
||||
for pos, in := range inputs {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
currentPet := in.CurrentPet()
|
||||
if currentPet == nil {
|
||||
continue
|
||||
}
|
||||
userID := uint32(0)
|
||||
if in.Player != nil && in.Player.GetInfo() != nil {
|
||||
userID = in.Player.GetInfo().UserID
|
||||
}
|
||||
ret = append(ret, legacyGroupStartPet{
|
||||
Side: side,
|
||||
Pos: uint8(pos),
|
||||
UserID: userID,
|
||||
IsChange: 0,
|
||||
PetID: currentPet.Info.ID,
|
||||
CatchTime: currentPet.Info.CatchTime,
|
||||
Hp: currentPet.Info.Hp,
|
||||
MaxHp: currentPet.Info.MaxHp,
|
||||
Level: currentPet.Info.Level,
|
||||
Reserve: 0,
|
||||
Flag: 1,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (f *FightC) sendLegacyGroupOver(player common.PlayerI, over *model.FightOverInfo) {
|
||||
if f == nil || !f.LegacyGroupProtocol || player == nil {
|
||||
return
|
||||
}
|
||||
player.SendPackCmd(groupCmdFightOver, f.buildLegacyGroupOverInfo(over))
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyGroupOverInfo(over *model.FightOverInfo) *legacyGroupFightOverInfo {
|
||||
result := &legacyGroupFightOverInfo{}
|
||||
if over != nil {
|
||||
result.Reason = resolveLegacyGroupFightOverReason(over)
|
||||
result.WinnerID = over.WinnerId
|
||||
}
|
||||
if our := f.primaryOurPlayer(); our != nil && our.GetInfo() != nil {
|
||||
playerInfo := our.GetInfo()
|
||||
result.TwoTimes = uint32(playerInfo.TwoTimes)
|
||||
result.ThreeTimes = uint32(playerInfo.ThreeTimes)
|
||||
result.AutoFightTime = playerInfo.AutoFightTime
|
||||
result.EnergyTime = uint32(playerInfo.EnergyTime)
|
||||
result.LearnTimes = playerInfo.LearnTimes
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func mapLegacyGroupFightOverReason(reason model.EnumBattleOverReason) uint32 {
|
||||
switch reason {
|
||||
case model.BattleOverReason.PlayerOffline:
|
||||
return 2
|
||||
case model.BattleOverReason.PlayerOVerTime:
|
||||
return 3
|
||||
case model.BattleOverReason.NOTwind:
|
||||
return 4
|
||||
case model.BattleOverReason.DefaultEnd:
|
||||
return 1
|
||||
case model.BattleOverReason.PlayerEscape:
|
||||
return 6
|
||||
default:
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
func resolveLegacyGroupFightOverReason(over *model.FightOverInfo) uint32 {
|
||||
if over == nil {
|
||||
return 5
|
||||
}
|
||||
switch over.Reason {
|
||||
case model.BattleOverReason.PlayerOffline:
|
||||
return 2
|
||||
case model.BattleOverReason.PlayerOVerTime:
|
||||
return 3
|
||||
case model.BattleOverReason.PlayerEscape:
|
||||
return 6
|
||||
case model.BattleOverReason.NOTwind:
|
||||
return 4
|
||||
}
|
||||
if over.WinnerId != 0 {
|
||||
return 1
|
||||
}
|
||||
return mapLegacyGroupFightOverReason(over.Reason)
|
||||
}
|
||||
|
||||
func (f *FightC) sendLegacyGroupChangePetSuccess(player common.PlayerI, in *input.Input, reason *info.ChangePetInfo) {
|
||||
if f == nil || !f.LegacyGroupProtocol || player == nil || in == nil || reason == nil {
|
||||
return
|
||||
}
|
||||
player.SendPackCmd(groupCmdChangePetSuc, f.buildLegacyGroupChangePetSuccessInfo(in, reason))
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyGroupChangePetSuccessInfo(in *input.Input, reason *info.ChangePetInfo) *legacyGroupChangePetSuccessInfo {
|
||||
result := &legacyGroupChangePetSuccessInfo{}
|
||||
if in == nil || reason == nil {
|
||||
return result
|
||||
}
|
||||
if !f.isOurPlayerID(in.UserID) {
|
||||
result.Side = 2
|
||||
} else {
|
||||
result.Side = 1
|
||||
}
|
||||
result.Pos = uint8(in.TeamSlotIndex())
|
||||
result.UserID = reason.UserId
|
||||
result.PetID = reason.ID
|
||||
result.CatchTime = reason.CatchTime
|
||||
result.Level = reason.Level
|
||||
result.Hp = reason.Hp
|
||||
result.MaxHp = reason.MaxHp
|
||||
if currentPet := in.CurrentPet(); currentPet != nil {
|
||||
result.SkinID = currentPet.Info.SkinID
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *FightC) SendLegacyEscapeSuccess(player common.PlayerI, actorIndex int) {
|
||||
if f == nil || !f.LegacyGroupProtocol || player == nil {
|
||||
return
|
||||
@@ -166,11 +506,8 @@ func (f *FightC) SendLegacyEscapeSuccess(player common.PlayerI, actorIndex int)
|
||||
Side: side,
|
||||
ActorIndex: uint8(actorIndex),
|
||||
}
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
if ff == nil || ff.Player == nil {
|
||||
return
|
||||
}
|
||||
ff.Player.SendPackCmd(groupCmdEscapeSuc, &payload)
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
p.SendPackCmd(groupCmdEscapeSuc, &payload)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,19 +515,147 @@ func (f *FightC) sendLegacyRoundBroadcast(firstAttack, secondAttack *action.Sele
|
||||
if f == nil || !f.LegacyGroupProtocol {
|
||||
return
|
||||
}
|
||||
if firstAttack != nil {
|
||||
f.sendLegacyGroupSkillHurt(firstAttack)
|
||||
}
|
||||
if secondAttack != nil {
|
||||
f.sendLegacyGroupSkillHurt(secondAttack)
|
||||
}
|
||||
f.sendLegacyGroupBoutDone()
|
||||
}
|
||||
|
||||
func (f *FightC) sendLegacyGroupSkillHurt(skillAction *action.SelectSkillAction) {
|
||||
if f == nil || !f.LegacyGroupProtocol || skillAction == nil {
|
||||
return
|
||||
}
|
||||
packet := f.buildLegacyGroupSkillHurtPacket(skillAction)
|
||||
if packet == nil {
|
||||
return
|
||||
}
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
p.SendPackCmd(groupCmdSkillHurt, packet)
|
||||
})
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyGroupSkillHurtPacket(skillAction *action.SelectSkillAction) *legacyGroupSkillHurtPacket {
|
||||
attacker := f.GetInputByAction(skillAction, false)
|
||||
defender := f.GetInputByAction(skillAction, true)
|
||||
if attacker == nil || defender == nil {
|
||||
return nil
|
||||
}
|
||||
return &legacyGroupSkillHurtPacket{
|
||||
IsGank: 0,
|
||||
Attack: f.buildLegacyGroupSkillAttackInfo(skillAction, attacker),
|
||||
Attacked: f.buildLegacyGroupSkillDefendInfo(defender),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FightC) fillLegacyGroupSkillCommonFields(
|
||||
self *input.Input,
|
||||
isAttackor uint8,
|
||||
statusList *[20]uint8,
|
||||
batLvList *[6]uint8,
|
||||
) (side uint8, pos uint8, userID uint32, petID uint32, hp uint32, maxHP uint32, moveMap []legacyGroupSkillMoveInfo, flag uint32) {
|
||||
if self == nil {
|
||||
return
|
||||
}
|
||||
if !f.isOurPlayerID(self.UserID) {
|
||||
side = 2
|
||||
} else {
|
||||
side = 1
|
||||
}
|
||||
pos = uint8(self.TeamSlotIndex())
|
||||
userID = self.UserID
|
||||
attackValue := self.AttackValue
|
||||
if attackValue == nil {
|
||||
attackValue = info.NewAttackValue(self.UserID)
|
||||
}
|
||||
for i := 0; i < len(attackValue.Status) && i < 20; i++ {
|
||||
statusList[i] = uint8(attackValue.Status[i])
|
||||
}
|
||||
for i := 0; i < len(attackValue.Prop) && i < len(batLvList); i++ {
|
||||
batLvList[i] = uint8(attackValue.Prop[i])
|
||||
}
|
||||
currentPet := self.CurrentPet()
|
||||
if currentPet != nil {
|
||||
petID = currentPet.Info.ID
|
||||
hp = currentPet.Info.Hp
|
||||
maxHP = currentPet.Info.MaxHp
|
||||
moveMap = collectLegacyGroupSkillMoves(currentPet.Info.SkillList)
|
||||
} else {
|
||||
hp = clampLegacyInt32ToUint32(attackValue.RemainHp)
|
||||
maxHP = attackValue.MaxHp
|
||||
moveMap = collectLegacyGroupSkillMoves(attackValue.SkillList)
|
||||
}
|
||||
flag = attackValue.State
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyGroupSkillAttackInfo(skillAction *action.SelectSkillAction, self *input.Input) legacyGroupSkillAttackInfo {
|
||||
result := legacyGroupSkillAttackInfo{}
|
||||
if self == nil {
|
||||
return result
|
||||
}
|
||||
result.IsAttackor = 0
|
||||
result.Side, result.Pos, result.UserID, result.PetID, result.Hp, result.MaxHp, result.MoveMap, result.Flag =
|
||||
f.fillLegacyGroupSkillCommonFields(self, result.IsAttackor, &result.StatusList, &result.BatLvList)
|
||||
attackValue := self.AttackValue
|
||||
if attackValue == nil {
|
||||
attackValue = info.NewAttackValue(self.UserID)
|
||||
}
|
||||
if skillAction != nil && skillAction.SkillEntity != nil {
|
||||
result.MoveID = uint32(skillAction.SkillEntity.XML.ID)
|
||||
} else {
|
||||
result.MoveID = attackValue.SkillID
|
||||
}
|
||||
result.IsCrit = attackValue.IsCritical
|
||||
result.EffectName = attackValue.State
|
||||
result.AtkTimes = 1
|
||||
result.Dmg = int32(attackValue.LostHp)
|
||||
result.ChgHp = attackValue.GainHp
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *FightC) buildLegacyGroupSkillDefendInfo(self *input.Input) legacyGroupSkillDefendInfo {
|
||||
result := legacyGroupSkillDefendInfo{}
|
||||
if self == nil {
|
||||
return result
|
||||
}
|
||||
result.IsAttackor = 1
|
||||
result.Side, result.Pos, result.UserID, result.PetID, result.Hp, result.MaxHp, result.MoveMap, result.Flag =
|
||||
f.fillLegacyGroupSkillCommonFields(self, result.IsAttackor, &result.StatusList, &result.BatLvList)
|
||||
result.MoveID = 0
|
||||
return result
|
||||
}
|
||||
|
||||
func collectLegacyGroupSkillMoves(skills []model.SkillInfo) []legacyGroupSkillMoveInfo {
|
||||
moves := make([]legacyGroupSkillMoveInfo, 0, len(skills))
|
||||
for _, skill := range skills {
|
||||
if skill.ID == 0 {
|
||||
continue
|
||||
}
|
||||
moves = append(moves, legacyGroupSkillMoveInfo{
|
||||
MoveID: skill.ID,
|
||||
PP: skill.PP,
|
||||
})
|
||||
}
|
||||
return moves
|
||||
}
|
||||
|
||||
func clampLegacyInt32ToUint32(v int32) uint32 {
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
return uint32(v)
|
||||
}
|
||||
|
||||
func (f *FightC) sendLegacyGroupBoutDone() {
|
||||
if f == nil || !f.LegacyGroupProtocol {
|
||||
return
|
||||
}
|
||||
payload := legacyBoutDoneInfo{Round: f.Round}
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
if ff == nil || ff.Player == nil {
|
||||
return
|
||||
}
|
||||
ff.Player.SendPackCmd(groupCmdBoutDone, &payload)
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
p.SendPackCmd(groupCmdBoutDone, &payload)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -207,16 +672,13 @@ func (f *FightC) sendLegacySpriteDie(in *input.Input, hasBackup bool) {
|
||||
data = 1
|
||||
}
|
||||
payload := legacySpriteDieInfo{
|
||||
Flag: 1,
|
||||
Count: 1,
|
||||
Side: side,
|
||||
ActorIndex: uint8(in.TeamSlotIndex()),
|
||||
Reserve: 1,
|
||||
Flag: 1,
|
||||
HasBackup: data,
|
||||
}
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
if ff == nil || ff.Player == nil {
|
||||
return
|
||||
}
|
||||
ff.Player.SendPackCmd(groupCmdSpriteDie, &payload)
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
p.SendPackCmd(groupCmdSpriteDie, &payload)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -528,6 +528,27 @@ func (f *FightC) Broadcast(t func(ff *input.Input)) {
|
||||
|
||||
}
|
||||
|
||||
func (f *FightC) BroadcastPlayers(t func(common.PlayerI)) {
|
||||
if f == nil || t == nil {
|
||||
return
|
||||
}
|
||||
seen := make(map[uint32]struct{}, len(f.OurPlayers)+len(f.OppPlayers))
|
||||
visit := func(players []common.PlayerI) {
|
||||
for _, p := range players {
|
||||
if p == nil || p.GetInfo() == nil {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[p.GetInfo().UserID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[p.GetInfo().UserID] = struct{}{}
|
||||
t(p)
|
||||
}
|
||||
}
|
||||
visit(f.OurPlayers)
|
||||
visit(f.OppPlayers)
|
||||
}
|
||||
|
||||
func (f *FightC) GetOverChan() chan struct{} {
|
||||
return f.over
|
||||
|
||||
|
||||
@@ -173,10 +173,14 @@ func (f *FightC) battleLoop() {
|
||||
|
||||
//大乱斗,给个延迟
|
||||
//<-time.After(1000)
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
f.sendFightPacket(ff.Player, fightPacketOver, &f.FightOverInfo)
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
if f.LegacyGroupProtocol {
|
||||
f.sendLegacyGroupOver(p, &f.FightOverInfo)
|
||||
} else {
|
||||
f.sendFightPacket(p, fightPacketOver, &f.FightOverInfo)
|
||||
}
|
||||
|
||||
ff.Player.QuitFight()
|
||||
p.QuitFight()
|
||||
|
||||
//待退出玩家战斗状态
|
||||
})
|
||||
@@ -260,7 +264,11 @@ func (f *FightC) collectPlayerActions(expectedSlots map[actionSlotKey]struct{})
|
||||
ret.Reason = reason
|
||||
ret.Reason.ActorIndex = uint32(ret.ActorIndex)
|
||||
|
||||
if f.LegacyGroupProtocol {
|
||||
f.sendLegacyGroupChangePetSuccess(selfinput.Player, selfinput, &ret.Reason)
|
||||
} else {
|
||||
f.sendFightPacket(selfinput.Player, fightPacketChangePetSuccess, &ret.Reason)
|
||||
}
|
||||
|
||||
f.Switch[key] = ret
|
||||
|
||||
@@ -281,7 +289,11 @@ func (f *FightC) collectPlayerActions(expectedSlots map[actionSlotKey]struct{})
|
||||
selfinput.CanChange = 0
|
||||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && paction.GetPlayerID() == 0 {
|
||||
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
|
||||
if f.LegacyGroupProtocol {
|
||||
f.sendLegacyGroupChangePetSuccess(f.Our[0].Player, selfinput, &ret.Reason)
|
||||
} else {
|
||||
f.sendFightPacket(f.Our[0].Player, fightPacketChangePetSuccess, &ret.Reason)
|
||||
}
|
||||
//println("AI出手死切")
|
||||
f.triggerNPCActions() // boss出手后获取出招
|
||||
|
||||
@@ -597,12 +609,12 @@ func (f *FightC) handleItemAction(a *action.UseItemAction) {
|
||||
case gconv.Int(item.HP) != 0:
|
||||
addhp := item.HP
|
||||
source.Heal(source, a, alpacadecimal.NewFromInt(int64(addhp)))
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
currentPet := source.PrimaryCurPet()
|
||||
if currentPet == nil {
|
||||
return
|
||||
}
|
||||
f.sendFightPacket(ff.Player, fightPacketUseItem, &info.UsePetIteminfo{
|
||||
f.sendFightPacket(p, fightPacketUseItem, &info.UsePetIteminfo{
|
||||
UserID: source.UserID,
|
||||
ChangeHp: int32(addhp),
|
||||
ItemID: uint32(item.ID),
|
||||
@@ -612,12 +624,12 @@ func (f *FightC) handleItemAction(a *action.UseItemAction) {
|
||||
})
|
||||
case gconv.Int(item.PP) != 0:
|
||||
source.HealPP(item.PP)
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
currentPet := source.PrimaryCurPet()
|
||||
if currentPet == nil {
|
||||
return
|
||||
}
|
||||
f.sendFightPacket(ff.Player, fightPacketUseItem, &info.UsePetIteminfo{
|
||||
f.sendFightPacket(p, fightPacketUseItem, &info.UsePetIteminfo{
|
||||
UserID: source.UserID,
|
||||
|
||||
ItemID: uint32(item.ID),
|
||||
|
||||
@@ -47,6 +47,100 @@ func NewFightSingleControllerN(
|
||||
)
|
||||
}
|
||||
|
||||
// ArrangePetsBySlotLimit 按站位上限切分宠物。
|
||||
// 规则:
|
||||
// 1. 前 slotLimit 只存活宠物优先占据出战位。
|
||||
// 2. 其余宠物按 1..slotLimit 轮转挂到对应站位作为后备。
|
||||
// 3. 每个站位最多保留 6 只宠物。
|
||||
func ArrangePetsBySlotLimit(pets []model.PetInfo, slotLimit int) [][]model.PetInfo {
|
||||
var (
|
||||
alivePets []model.PetInfo
|
||||
slots [][]model.PetInfo
|
||||
idx int
|
||||
)
|
||||
for _, pet := range pets {
|
||||
if pet.Hp == 0 {
|
||||
continue
|
||||
}
|
||||
alivePets = append(alivePets, pet)
|
||||
}
|
||||
if len(alivePets) == 0 {
|
||||
return nil
|
||||
}
|
||||
if slotLimit <= 0 {
|
||||
slotLimit = 1
|
||||
}
|
||||
if slotLimit > len(alivePets) {
|
||||
slotLimit = len(alivePets)
|
||||
}
|
||||
slots = make([][]model.PetInfo, 0, slotLimit)
|
||||
for i := 0; i < slotLimit; i++ {
|
||||
slots = append(slots, []model.PetInfo{alivePets[i]})
|
||||
}
|
||||
for _, pet := range alivePets[slotLimit:] {
|
||||
for step := 0; step < len(slots); step++ {
|
||||
slotIdx := (idx + step) % len(slots)
|
||||
if len(slots[slotIdx]) >= 6 {
|
||||
continue
|
||||
}
|
||||
slots[slotIdx] = append(slots[slotIdx], pet)
|
||||
idx = (slotIdx + 1) % len(slots)
|
||||
break
|
||||
}
|
||||
}
|
||||
return slots
|
||||
}
|
||||
|
||||
// ExpandPlayersWithSlotLimit 将“每位玩家的宠物列表”按站位限制展开为扁平站位列表。
|
||||
// 例如:
|
||||
// 1. slotLimit=1: 每位玩家占 1 个站位,其余为该站位后备。
|
||||
// 2. slotLimit=3: 每位玩家最多展开 3 个站位,每个站位带各自后备。
|
||||
func ExpandPlayersWithSlotLimit(
|
||||
players []common.PlayerI,
|
||||
petsByPlayer [][]model.PetInfo,
|
||||
slotLimit int,
|
||||
) ([]common.PlayerI, [][]model.PetInfo, errorcode.ErrorCode) {
|
||||
var (
|
||||
flatPlayers []common.PlayerI
|
||||
flatSlots [][]model.PetInfo
|
||||
)
|
||||
if len(players) == 0 || len(players) != len(petsByPlayer) {
|
||||
return nil, nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
|
||||
}
|
||||
for idx, p := range players {
|
||||
if p == nil {
|
||||
return nil, nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
|
||||
}
|
||||
slots := ArrangePetsBySlotLimit(petsByPlayer[idx], slotLimit)
|
||||
for _, slotPets := range slots {
|
||||
flatPlayers = append(flatPlayers, p)
|
||||
flatSlots = append(flatSlots, slotPets)
|
||||
}
|
||||
}
|
||||
if len(flatPlayers) == 0 || len(flatPlayers) != len(flatSlots) {
|
||||
return nil, nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
|
||||
}
|
||||
return flatPlayers, flatSlots, 0
|
||||
}
|
||||
|
||||
// NewFightSingleController 使用站位限制规则创建单人控制多站位战斗。
|
||||
func NewFightSingleController(
|
||||
ourController common.PlayerI,
|
||||
oppController common.PlayerI,
|
||||
ourPets []model.PetInfo,
|
||||
oppPets []model.PetInfo,
|
||||
slotLimit int,
|
||||
fn func(model.FightOverInfo),
|
||||
) (*FightC, errorcode.ErrorCode) {
|
||||
return NewFightSingleControllerN(
|
||||
ourController,
|
||||
oppController,
|
||||
ArrangePetsBySlotLimit(ourPets, slotLimit),
|
||||
ArrangePetsBySlotLimit(oppPets, slotLimit),
|
||||
fn,
|
||||
)
|
||||
}
|
||||
|
||||
// NewLegacyGroupFightSingleControllerN 创建旧组队协议的单人控制多站位战斗。
|
||||
func NewLegacyGroupFightSingleControllerN(
|
||||
ourController common.PlayerI,
|
||||
@@ -82,6 +176,24 @@ func NewLegacyGroupFightSingleControllerN(
|
||||
)
|
||||
}
|
||||
|
||||
// NewLegacyGroupFightSingleController 使用站位限制规则创建旧组队协议战斗。
|
||||
func NewLegacyGroupFightSingleController(
|
||||
ourController common.PlayerI,
|
||||
oppController common.PlayerI,
|
||||
ourPets []model.PetInfo,
|
||||
oppPets []model.PetInfo,
|
||||
slotLimit int,
|
||||
fn func(model.FightOverInfo),
|
||||
) (*FightC, errorcode.ErrorCode) {
|
||||
return NewLegacyGroupFightSingleControllerN(
|
||||
ourController,
|
||||
oppController,
|
||||
ArrangePetsBySlotLimit(ourPets, slotLimit),
|
||||
ArrangePetsBySlotLimit(oppPets, slotLimit),
|
||||
fn,
|
||||
)
|
||||
}
|
||||
|
||||
// NewFightPerSlotControllerN 创建 N 打战斗(多人各控制一个站位)。
|
||||
// ourPlayers/oppPlayers 与 ourPetsBySlot/oppPetsBySlot 按站位一一对应。
|
||||
func NewFightPerSlotControllerN(
|
||||
@@ -117,25 +229,32 @@ func NewFightPerSlotControllerN(
|
||||
)
|
||||
}
|
||||
|
||||
// NewFightPerPlayerControllers 使用“每位玩家 + 站位限制”创建多人战斗。
|
||||
func NewFightPerPlayerControllers(
|
||||
ourPlayers []common.PlayerI,
|
||||
oppPlayers []common.PlayerI,
|
||||
ourPetsByPlayer [][]model.PetInfo,
|
||||
oppPetsByPlayer [][]model.PetInfo,
|
||||
slotLimit int,
|
||||
fn func(model.FightOverInfo),
|
||||
) (*FightC, errorcode.ErrorCode) {
|
||||
flatOurPlayers, flatOurSlots, err := ExpandPlayersWithSlotLimit(ourPlayers, ourPetsByPlayer, slotLimit)
|
||||
if err > 0 {
|
||||
return nil, err
|
||||
}
|
||||
flatOppPlayers, flatOppSlots, err := ExpandPlayersWithSlotLimit(oppPlayers, oppPetsByPlayer, slotLimit)
|
||||
if err > 0 {
|
||||
return nil, err
|
||||
}
|
||||
return NewFightPerSlotControllerN(flatOurPlayers, flatOppPlayers, flatOurSlots, flatOppSlots, fn)
|
||||
}
|
||||
|
||||
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
|
||||
func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.FightOverInfo)) (*FightC, errorcode.ErrorCode) {
|
||||
if p1 == nil || p2 == nil {
|
||||
return nil, errorcode.ErrorCodes.ErrSystemBusyTryLater
|
||||
}
|
||||
fightInfo := p1.Getfightinfo()
|
||||
ourInput, err := buildInputFromPets(p1, b1, fightInfo.Mode)
|
||||
if err > 0 {
|
||||
return nil, err
|
||||
}
|
||||
oppInput, err := buildInputFromPets(p2, b2, fightInfo.Mode)
|
||||
if err > 0 {
|
||||
return nil, err
|
||||
}
|
||||
return NewFightWithOptions(
|
||||
WithFightInputs([]*input.Input{ourInput}, []*input.Input{oppInput}),
|
||||
WithFightCallback(fn),
|
||||
WithFightInfo(fightInfo),
|
||||
)
|
||||
return NewFightSingleController(p1, p2, b1, b2, 1, fn)
|
||||
}
|
||||
|
||||
// buildFight 基于已准备好的双方 Inputs 构建战斗实例。
|
||||
@@ -196,8 +315,12 @@ func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
|
||||
}
|
||||
f.FightStartOutboundInfo = f.buildFightStartInfo()
|
||||
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
f.sendFightPacket(ff.Player, fightPacketReady, &f.ReadyInfo)
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
if f.LegacyGroupProtocol {
|
||||
f.sendLegacyGroupReady(p)
|
||||
return
|
||||
}
|
||||
f.sendFightPacket(p, fightPacketReady, &f.ReadyInfo)
|
||||
})
|
||||
|
||||
cool.Cron.AfterFunc(loadtime, func() {
|
||||
@@ -215,9 +338,13 @@ func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
|
||||
case !our.Finished:
|
||||
f.WinnerId = opp.Player.GetInfo().UserID
|
||||
}
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
f.sendFightPacket(ff.Player, fightPacketOver, &f.FightOverInfo)
|
||||
ff.Player.QuitFight()
|
||||
f.BroadcastPlayers(func(p common.PlayerI) {
|
||||
if f.LegacyGroupProtocol {
|
||||
f.sendLegacyGroupOver(p, &f.FightOverInfo)
|
||||
} else {
|
||||
f.sendFightPacket(p, fightPacketOver, &f.FightOverInfo)
|
||||
}
|
||||
p.QuitFight()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
60
logic/service/fight/slot_alloc_test.go
Normal file
60
logic/service/fight/slot_alloc_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package fight
|
||||
|
||||
import (
|
||||
"blazing/logic/service/common"
|
||||
"blazing/modules/player/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestArrangePetsBySlotLimit(t *testing.T) {
|
||||
pets := []model.PetInfo{
|
||||
{ID: 1, Hp: 10},
|
||||
{ID: 2, Hp: 10},
|
||||
{ID: 3, Hp: 10},
|
||||
{ID: 4, Hp: 10},
|
||||
{ID: 5, Hp: 10},
|
||||
{ID: 6, Hp: 10},
|
||||
}
|
||||
|
||||
slots := ArrangePetsBySlotLimit(pets, 3)
|
||||
if len(slots) != 3 {
|
||||
t.Fatalf("expected 3 slots, got %d", len(slots))
|
||||
}
|
||||
if len(slots[0]) != 2 || slots[0][0].ID != 1 || slots[0][1].ID != 4 {
|
||||
t.Fatalf("slot 0 mismatch: %+v", slots[0])
|
||||
}
|
||||
if len(slots[1]) != 2 || slots[1][0].ID != 2 || slots[1][1].ID != 5 {
|
||||
t.Fatalf("slot 1 mismatch: %+v", slots[1])
|
||||
}
|
||||
if len(slots[2]) != 2 || slots[2][0].ID != 3 || slots[2][1].ID != 6 {
|
||||
t.Fatalf("slot 2 mismatch: %+v", slots[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandPlayersWithSlotLimit(t *testing.T) {
|
||||
players := []common.PlayerI{&stubPlayer{}, &stubPlayer{}}
|
||||
petsByPlayer := [][]model.PetInfo{
|
||||
{
|
||||
{ID: 1, Hp: 10},
|
||||
{ID: 2, Hp: 10},
|
||||
{ID: 3, Hp: 10},
|
||||
},
|
||||
{
|
||||
{ID: 11, Hp: 10},
|
||||
{ID: 12, Hp: 10},
|
||||
},
|
||||
}
|
||||
flatPlayers, flatSlots, err := ExpandPlayersWithSlotLimit(players, petsByPlayer, 1)
|
||||
if err != 0 {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if len(flatPlayers) != 2 || len(flatSlots) != 2 {
|
||||
t.Fatalf("unexpected flatten result: players=%d slots=%d", len(flatPlayers), len(flatSlots))
|
||||
}
|
||||
if len(flatSlots[0]) != 3 || flatSlots[0][0].ID != 1 || flatSlots[0][1].ID != 2 || flatSlots[0][2].ID != 3 {
|
||||
t.Fatalf("player0 slot mismatch: %+v", flatSlots[0])
|
||||
}
|
||||
if len(flatSlots[1]) != 2 || flatSlots[1][0].ID != 11 || flatSlots[1][1].ID != 12 {
|
||||
t.Fatalf("player1 slot mismatch: %+v", flatSlots[1])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user