All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat(fight): 添加旧组队协议支持并优化战斗系统 - 实现了旧组队协议相关功能,包括GroupReadyFightFinish、GroupUseSkill、 GroupUseItem、GroupChangePet和GroupEscape方法 - 新增组队战斗相关的入站信息结构体定义 - 实现了组队BOSS战斗逻辑,添加groupBossSlotLimit常量 - 重构宠物技能设置逻辑,调整金币消耗时机 - 优化战斗循环逻辑,添加对无行动槽位的处理 - 改进AI行动逻辑,增加多位置目标选择
429 lines
11 KiB
Go
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
|
|
}
|