Files
bl/logic/service/fight/group_legacy.go
昔念 0051ac0be8
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
```
feat(fight): 添加旧组队协议支持并优化战斗系统

- 实现了旧组队协议相关功能,包括GroupReadyFightFinish、GroupUseSkill、
  GroupUseItem、GroupChangePet和GroupEscape方法
- 新增组队战斗相关的入站信息结构体定义
- 实现了组队BOSS战斗逻辑,添加groupBossSlotLimit常量
- 重构宠物技能设置逻辑,调整金币消耗时机
- 优化战斗循环逻辑,添加对无行动槽位的处理
- 改进AI行动逻辑,增加多位置目标选择
2026-04-08 01:28:55 +08:00

429 lines
11 KiB
Go

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"
"bytes"
"encoding/binary"
)
const (
groupCmdReadyToFight uint32 = 7555
groupCmdReadyFightFinish uint32 = 7556
groupCmdStartFight uint32 = 7557
groupCmdUseSkill uint32 = 7558
groupCmdSkillHurt uint32 = 7559
groupCmdFightOver uint32 = 7560
groupCmdSpriteDie uint32 = 7561
groupCmdUseItem uint32 = 7562
groupCmdChangePet uint32 = 7563
groupCmdEscape uint32 = 7565
groupCmdBoutDone uint32 = 7566
groupCmdChangePetSuc uint32 = 7567
groupCmdEscapeSuc uint32 = 7568
groupCmdChat uint32 = 7569
groupCmdLoadPercent uint32 = 7571
groupCmdLoadPercentNotice uint32 = 7572
groupCmdSpriteNotice uint32 = 7573
groupCmdFightWinClose uint32 = 7574
groupCmdFightOvertime uint32 = 7585
groupCmdSkillPlayOver uint32 = 7586
groupCmdFightTimeoutExit uint32 = 7587
groupCmdFightRelation uint32 = 7588
groupModelNPC uint32 = 3
groupModelBoss uint32 = 4
groupModelPlayerSingle uint32 = 5
groupModelPlayerMulti uint32 = 6
)
func groupModelByFight(f *FightC) uint32 {
if f == nil {
return groupModelBoss
}
switch {
case f.Info.Status == 0:
return groupModelNPC
case f.Info.Status == 1 && f.Info.Mode == 1:
return groupModelPlayerSingle
case f.Info.Status == 1:
return groupModelPlayerMulti
default:
return groupModelBoss
}
}
func writeUint8(buf *bytes.Buffer, v uint8) {
_ = buf.WriteByte(v)
}
func writeUint32(buf *bytes.Buffer, v uint32) {
_ = binary.Write(buf, binary.BigEndian, v)
}
func writeInt32(buf *bytes.Buffer, v int32) {
_ = binary.Write(buf, binary.BigEndian, v)
}
func writeFixedString16(buf *bytes.Buffer, s string) {
raw := make([]byte, 16)
copy(raw, []byte(s))
_, _ = buf.Write(raw)
}
func buildPacket(cmd uint32, userID uint32, payload []byte) []byte {
header := common.NewTomeeHeader(cmd, userID)
totalLen := uint32(17 + len(payload))
header.Len = totalLen
buf := make([]byte, totalLen)
binary.BigEndian.PutUint32(buf[0:4], totalLen)
buf[4] = header.Version
binary.BigEndian.PutUint32(buf[5:9], cmd)
binary.BigEndian.PutUint32(buf[9:13], userID)
binary.BigEndian.PutUint32(buf[13:17], 0)
copy(buf[17:], payload)
return buf
}
func (f *FightC) sendLegacyGroupReady() {
f.Broadcast(func(ff *input.Input) {
if ff == nil || ff.Player == nil {
return
}
sendLegacyPacket(ff.Player, groupCmdReadyToFight, f.buildLegacyGroupReadyPayload())
})
}
func (f *FightC) sendLegacyGroupStart(player common.PlayerI) {
if player == nil {
return
}
sendLegacyPacket(player, groupCmdStartFight, f.buildLegacyGroupStartPayload())
}
func (f *FightC) sendLegacyGroupOver(player common.PlayerI, over *model.FightOverInfo) {
if player == nil {
return
}
sendLegacyPacket(player, groupCmdFightOver, f.buildLegacyGroupOverPayload(over))
}
func sendLegacyPacket(player common.PlayerI, cmd uint32, payload []byte) {
if player == nil {
return
}
if sender, ok := player.(interface{ SendPack([]byte) error }); ok {
_ = sender.SendPack(buildPacket(cmd, player.GetInfo().UserID, payload))
}
}
func (f *FightC) buildLegacyGroupReadyPayload() []byte {
var (
buf bytes.Buffer
users [][]*input.Input
)
writeUint32(&buf, groupModelByFight(f))
users = [][]*input.Input{f.Our, f.Opp}
for sideIndex, sideInputs := range users {
writeUint8(&buf, 1)
var leaderID uint32
if len(sideInputs) > 0 && sideInputs[0] != nil && sideInputs[0].Player != nil {
leaderID = sideInputs[0].Player.GetInfo().UserID
}
writeUint32(&buf, leaderID)
writeUint8(&buf, 1)
if leaderID == 0 {
writeUint32(&buf, 0)
writeFixedString16(&buf, "boss")
} else {
writeUint32(&buf, leaderID)
writeFixedString16(&buf, sideInputs[0].Player.GetInfo().Nick)
}
writeUint32(&buf, uint32(len(sideInputs)))
for _, slot := range sideInputs {
if slot == nil || slot.CurrentPet() == nil {
continue
}
currentPet := slot.CurrentPet()
writeUint32(&buf, currentPet.Info.ID)
writeUint32(&buf, uint32(len(currentPet.Info.SkillList)))
for _, skill := range currentPet.Info.SkillList {
writeUint32(&buf, skill.ID)
}
}
if sideIndex == 0 && len(sideInputs) == 0 {
writeUint32(&buf, 0)
writeFixedString16(&buf, "")
writeUint32(&buf, 0)
}
}
return buf.Bytes()
}
func (f *FightC) buildLegacyGroupStartPayload() []byte {
var (
buf bytes.Buffer
sides [][]*input.Input
)
writeUint8(&buf, 0)
sides = [][]*input.Input{f.Our, f.Opp}
for sideIndex, sideInputs := range sides {
writeUint8(&buf, uint8(len(sideInputs)))
for pos, slot := range sideInputs {
if slot == nil || slot.CurrentPet() == nil {
continue
}
currentPet := slot.CurrentPet()
writeUint8(&buf, uint8(sideIndex+1))
writeUint8(&buf, uint8(pos))
if slot.Player != nil {
writeUint32(&buf, slot.Player.GetInfo().UserID)
} else {
writeUint32(&buf, 0)
}
writeUint8(&buf, 0)
writeUint32(&buf, currentPet.Info.ID)
writeUint32(&buf, currentPet.Info.CatchTime)
writeUint32(&buf, currentPet.Info.Hp)
writeUint32(&buf, currentPet.Info.MaxHp)
writeUint32(&buf, currentPet.Info.Level)
writeUint32(&buf, 0)
writeUint32(&buf, 1)
}
}
return buf.Bytes()
}
func (f *FightC) buildLegacyGroupOverPayload(over *model.FightOverInfo) []byte {
var (
buf bytes.Buffer
winnerID uint32
endReason uint32
playerInfo *model.PlayerInfo
)
if over != nil {
winnerID = over.WinnerId
endReason = uint32(over.Reason)
}
if our := f.primaryOurPlayer(); our != nil {
playerInfo = our.GetInfo()
}
writeUint8(&buf, 0)
writeUint32(&buf, endReason)
writeUint32(&buf, winnerID)
writeUint32(&buf, 0)
if playerInfo != nil {
writeUint32(&buf, uint32(playerInfo.TwoTimes))
writeUint32(&buf, uint32(playerInfo.ThreeTimes))
writeUint32(&buf, playerInfo.AutoFightTime)
writeUint32(&buf, 0)
writeUint32(&buf, uint32(playerInfo.EnergyTime))
writeUint32(&buf, playerInfo.LearnTimes)
} else {
for i := 0; i < 6; i++ {
writeUint32(&buf, 0)
}
}
return buf.Bytes()
}
func (f *FightC) SendLegacyEscapeSuccess(player common.PlayerI, actorIndex int) {
if f == nil || !f.LegacyGroupProtocol || player == nil {
return
}
payload := f.buildLegacyEscapePayload(player, actorIndex)
f.Broadcast(func(ff *input.Input) {
if ff == nil || ff.Player == nil {
return
}
sendLegacyPacket(ff.Player, groupCmdEscapeSuc, payload)
})
}
func (f *FightC) buildLegacyEscapePayload(player common.PlayerI, actorIndex int) []byte {
var buf bytes.Buffer
side := uint8(1)
if !f.isOurPlayerID(player.GetInfo().UserID) {
side = 2
}
writeUint32(&buf, player.GetInfo().UserID)
writeFixedString16(&buf, player.GetInfo().Nick)
writeUint8(&buf, side)
writeUint8(&buf, uint8(actorIndex))
return buf.Bytes()
}
func (f *FightC) sendLegacyRoundBroadcast(firstAttack, secondAttack *action.SelectSkillAction) {
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) {
var payload []byte
if skillAction == nil {
return
}
payload = f.buildLegacyGroupSkillHurtPayload(skillAction)
if len(payload) == 0 {
return
}
f.Broadcast(func(ff *input.Input) {
if ff == nil || ff.Player == nil {
return
}
sendLegacyPacket(ff.Player, groupCmdSkillHurt, payload)
})
}
func (f *FightC) buildLegacyGroupSkillHurtPayload(skillAction *action.SelectSkillAction) []byte {
var buf bytes.Buffer
attacker := f.GetInputByAction(skillAction, false)
defender := f.GetInputByAction(skillAction, true)
if attacker == nil || defender == nil || attacker.AttackValue == nil || defender.AttackValue == nil {
return nil
}
writeUint8(&buf, 0)
f.writeLegacySkillHurtInfo(&buf, skillAction, attacker, defender, true)
f.writeLegacySkillHurtInfo(&buf, skillAction, defender, attacker, false)
return buf.Bytes()
}
func (f *FightC) writeLegacySkillHurtInfo(buf *bytes.Buffer, skillAction *action.SelectSkillAction, self *input.Input, opponent *input.Input, isAttacker bool) {
var (
moveID uint32
attackVal *model.AttackValue
currentPet *info.BattlePetEntity
side uint8
pos uint8
)
if self == nil || buf == nil {
return
}
if self.AttackValue == nil {
attackVal = info.NewAttackValue(self.UserID)
} else {
attackVal = self.AttackValue
}
currentPet = self.CurrentPet()
side = 1
if !f.isOurPlayerID(self.UserID) {
side = 2
}
pos = uint8(self.TeamSlotIndex())
moveID = attackVal.SkillID
if isAttacker {
if skillAction != nil && skillAction.SkillEntity != nil {
moveID = uint32(skillAction.SkillEntity.XML.ID)
}
writeUint8(buf, 0)
} else {
writeUint8(buf, 1)
moveID = 0
}
writeUint8(buf, side)
writeUint8(buf, pos)
writeUint32(buf, self.UserID)
for i := 0; i < 20; i++ {
writeUint8(buf, uint8(attackVal.Status[i]))
}
writeUint8(buf, 0)
writeUint8(buf, 0)
for i := 0; i < 6; i++ {
writeUint8(buf, uint8(attackVal.Prop[i]))
}
if currentPet != nil {
writeUint32(buf, currentPet.Info.ID)
} else {
writeUint32(buf, 0)
}
writeUint32(buf, moveID)
if currentPet != nil {
writeUint32(buf, currentPet.Info.Hp)
writeUint32(buf, currentPet.Info.MaxHp)
writeUint32(buf, uint32(len(currentPet.Info.SkillList)))
for _, skill := range currentPet.Info.SkillList {
writeUint32(buf, skill.ID)
writeUint32(buf, skill.PP)
}
} else {
writeUint32(buf, uint32(maxInt32(attackVal.RemainHp)))
writeUint32(buf, attackVal.MaxHp)
writeUint32(buf, uint32(len(attackVal.SkillList)))
for _, skill := range attackVal.SkillList {
writeUint32(buf, skill.ID)
writeUint32(buf, skill.PP)
}
}
writeUint32(buf, attackVal.State)
if isAttacker {
writeUint32(buf, attackVal.IsCritical)
writeUint32(buf, attackVal.State)
writeUint32(buf, 1)
writeInt32(buf, int32(attackVal.LostHp))
writeInt32(buf, attackVal.GainHp)
}
writeUint32(buf, 0)
}
func (f *FightC) sendLegacyGroupBoutDone() {
var buf bytes.Buffer
if f == nil || !f.LegacyGroupProtocol {
return
}
writeUint32(&buf, f.Round)
f.Broadcast(func(ff *input.Input) {
if ff == nil || ff.Player == nil {
return
}
sendLegacyPacket(ff.Player, groupCmdBoutDone, buf.Bytes())
})
}
func (f *FightC) sendLegacySpriteDie(in *input.Input, hasBackup bool) {
var (
buf bytes.Buffer
side uint8
data uint32
)
if f == nil || !f.LegacyGroupProtocol || in == nil {
return
}
side = 1
if !f.isOurPlayerID(in.UserID) {
side = 2
}
if hasBackup {
data = 1
}
writeUint8(&buf, 1)
writeUint8(&buf, side)
writeUint8(&buf, uint8(in.TeamSlotIndex()))
writeUint8(&buf, 1)
writeUint32(&buf, data)
f.Broadcast(func(ff *input.Input) {
if ff == nil || ff.Player == nil {
return
}
sendLegacyPacket(ff.Player, groupCmdSpriteDie, buf.Bytes())
})
}
func maxInt32(v int32) int32 {
if v < 0 {
return 0
}
return v
}