refactor(fight): 重构战斗逻辑
- 新增 AI_player 结构体和相关方法,用于创建和管理 AI 玩家 - 重构 FightC 结构体,增加 Input 结构体用于封装玩家输入 - 优化战斗流程,包括回合处理、技能使用、伤害计算等 - 改进广播机制,使用函数回调替代直接调用方法 - 优化玩家和 AI 的动作处理逻辑
This commit is contained in:
@@ -51,8 +51,9 @@ func (h Controller) OnPlayerFightNpcMonster(data *fight.FightNpcMonsterInboundIn
|
||||
if c.FightC != nil {
|
||||
return nil, errorcode.ErrorCodes.ErrOnlineOver6HoursCannotFight
|
||||
}
|
||||
c.FightC = &service.FightC{}
|
||||
c.FightC.NewFight(&ttt, c, mo) //把两个玩家都传进去
|
||||
ai := service.NewAI_player(*mo)
|
||||
service.NewFight(ttt, c, ai)
|
||||
|
||||
c.FightC.OwnerID = c.Info.UserID
|
||||
|
||||
return nil, -1
|
||||
|
||||
@@ -7,15 +7,24 @@ import (
|
||||
)
|
||||
|
||||
type AI_player struct {
|
||||
fightinfo info.NoteReadyToFightInfo
|
||||
id uint32 //0
|
||||
FightC *FightC //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool
|
||||
//petinfo []model.PlayerInfo //精灵信息
|
||||
FightC *FightC //绑定战斗标识 替代本身的是否战斗标记 //IsFighting bool
|
||||
petinfo []model.PetInfo //精灵信息
|
||||
|
||||
}
|
||||
|
||||
func NewAI_player(m model.PetInfo) *AI_player {
|
||||
ret := &AI_player{}
|
||||
ret.petinfo = make([]model.PetInfo, 0)
|
||||
ret.petinfo = append(ret.petinfo, m)
|
||||
return ret
|
||||
|
||||
}
|
||||
func (f *AI_player) SetFightC(ff *FightC) {
|
||||
f.FightC = ff
|
||||
}
|
||||
|
||||
func (f *AI_player) ID() uint32 {
|
||||
return f.id
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *AI_player) MapID() uint32 {
|
||||
@@ -38,16 +47,16 @@ func (f *AI_player) SendReadyToFightInfo(gg info.FightStartOutboundInfo) {
|
||||
//这时候给个出招
|
||||
}
|
||||
|
||||
func (f *AI_player) SendNoteReadyToFightInfo(_ info.NoteReadyToFightInfo) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
func (f *AI_player) SendNoteReadyToFightInfo(fs info.NoteReadyToFightInfo) {
|
||||
fmt.Println(fs)
|
||||
}
|
||||
|
||||
func (f *AI_player) SendFightEndInfo(_ info.FightOverInfo) {
|
||||
fmt.Println("战斗结束")
|
||||
//fmt.Println("战斗结束")
|
||||
|
||||
}
|
||||
func (f *AI_player) GetAction() {
|
||||
f.FightC.UseSkill(f, int32(f.fightinfo.OpponentPetList[0].SkillList[0].ID)) //使用1#技能,实际上要按照四个技能权重去使用
|
||||
f.FightC.UseSkill(f, int32(f.FightC.Opp.CurrentPet.Skills[0].ID)) //使用1#技能,实际上要按照四个技能权重去使用
|
||||
}
|
||||
func (p *AI_player) End() {
|
||||
p.FightC = nil
|
||||
@@ -55,9 +64,9 @@ func (p *AI_player) End() {
|
||||
return
|
||||
}
|
||||
|
||||
func (p *AI_player) GetPetInfo() []*model.PetInfo {
|
||||
func (p *AI_player) GetPetInfo() []model.PetInfo {
|
||||
|
||||
return nil
|
||||
return p.petinfo
|
||||
}
|
||||
func (p *AI_player) SendAttackValue(info.AttackValueS) {
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ type EnumPlayerOperation int
|
||||
|
||||
// 定义读秒倒计时期间玩家可执行的操作枚举
|
||||
var PlayerOperations = enum.New[struct {
|
||||
SystemGiveUp EnumPlayerOperation `enum:"-1"` // 系统选择放弃出手(比如没有PP)
|
||||
SelectSkill EnumPlayerOperation `enum:"0"` // 选择技能-6到6
|
||||
ActiveSwitch EnumPlayerOperation `enum:"2"` // 主动切换(中切)
|
||||
UsePotion EnumPlayerOperation `enum:"3"` // 使用药剂(捕捉、逃跑等)
|
||||
Escape EnumPlayerOperation `enum:"4"` // 逃跑(等级最高,以及掉线)
|
||||
PlayerOffline EnumPlayerOperation `enum:"5"` // 玩家掉线
|
||||
BeExpelledSwitch EnumPlayerOperation `enum:"6"` // 被驱逐切换
|
||||
SystemGiveUp EnumPlayerOperation `enum:"-1"` // 系统选择放弃出手(比如没有PP)
|
||||
SelectSkill EnumPlayerOperation `enum:"0"` // 选择技能-6到6
|
||||
ActiveSwitch EnumPlayerOperation `enum:"2"` // 主动切换(中切)
|
||||
UsePotion EnumPlayerOperation `enum:"3"` // 使用药剂(捕捉、逃跑等)
|
||||
Escape EnumPlayerOperation `enum:"4"` // 逃跑(等级最高,以及掉线)
|
||||
PlayerOffline EnumPlayerOperation `enum:"5"` // 玩家掉线
|
||||
// BeExpelledSwitch EnumPlayerOperation `enum:"6"` // 被驱逐切换
|
||||
}]()
|
||||
|
||||
// Compare 比较两个1v1战斗动作的执行优先级(核心逻辑)
|
||||
@@ -84,8 +84,8 @@ func (s *SelectSkillAction) Priority() int {
|
||||
// ActiveSwitchAction 主动切换宠物的战斗动作
|
||||
type ActiveSwitchAction struct {
|
||||
PlayerID uint32 // 玩家ID
|
||||
Type bool //是否主动切换
|
||||
Reason ChangePetInfo
|
||||
|
||||
Reason ChangePetInfo
|
||||
// CurrentPet BattlePetEntity // 当前在场宠物
|
||||
// TargetPet BattlePetEntity // 要切换上场的宠物
|
||||
// SwitchReason string // 切换原因
|
||||
@@ -93,16 +93,7 @@ type ActiveSwitchAction struct {
|
||||
|
||||
// Priority 返回动作优先级
|
||||
func (a *ActiveSwitchAction) Priority() int {
|
||||
if a.Type {
|
||||
return int(PlayerOperations.ActiveSwitch)
|
||||
}
|
||||
return int(PlayerOperations.BeExpelledSwitch)
|
||||
}
|
||||
|
||||
// Broadcast 广播切换宠物的动作信息
|
||||
func (a *ActiveSwitchAction) Broadcast() {
|
||||
// fmt.Printf("玩家[%d]主动切换宠物:从%s切换到%s(原因:%s)\n",
|
||||
// // a.PlayerID, a.CurrentPet.Name, a.TargetPet.Name, a.SwitchReason)
|
||||
return int(PlayerOperations.ActiveSwitch)
|
||||
}
|
||||
|
||||
func (a *ActiveSwitchAction) GetPlayerID() uint32 {
|
||||
@@ -171,10 +162,9 @@ func (s *SystemGiveUpAction) Broadcast() {
|
||||
|
||||
// PlayerOfflineAction 玩家掉线的战斗动作
|
||||
type PlayerOfflineAction struct {
|
||||
PlayerID uint32 // 掉线玩家ID
|
||||
OfflineTime time.Time // 掉线时间
|
||||
CurrentPet BattlePetEntity // 掉线时在场的宠物
|
||||
IsReconnect bool // 是否后续重连(预留字段)
|
||||
PlayerID uint32 // 掉线玩家ID
|
||||
OfflineTime time.Time // 掉线时间
|
||||
Reason FightOverInfo
|
||||
}
|
||||
|
||||
// Priority 返回动作优先级
|
||||
@@ -182,6 +172,10 @@ func (p *PlayerOfflineAction) Priority() int {
|
||||
return int(PlayerOperations.PlayerOffline)
|
||||
}
|
||||
|
||||
func (p *PlayerOfflineAction) GetPlayerID() uint32 {
|
||||
return p.PlayerID
|
||||
}
|
||||
|
||||
// Broadcast 广播玩家掉线信息
|
||||
func (p *PlayerOfflineAction) Broadcast() {
|
||||
// /status := "未重连"
|
||||
|
||||
@@ -73,11 +73,11 @@ type BattlePetEntity struct {
|
||||
}
|
||||
|
||||
// 创建精灵实例
|
||||
func CreateBattlePetEntity(info *model.PetInfo, rand *rand.Rand) *BattlePetEntity {
|
||||
func CreateBattlePetEntity(info model.PetInfo, rand *rand.Rand) *BattlePetEntity {
|
||||
ret := &BattlePetEntity{}
|
||||
|
||||
ret.PetInfo = xmlres.PetMAP[int(info.ID)] //注入精灵信息
|
||||
ret.Info = *info
|
||||
ret.Info = info
|
||||
for i := 0; i < 4; i++ {
|
||||
//todo 技能信息应该每回合进行深拷贝,保证每次的技能效果都是不一样的
|
||||
ret.Skills[i] = CreateBattleSkillWithInfinity(&info.SkillList[i], rand, ret)
|
||||
|
||||
@@ -37,16 +37,11 @@ var Category = enum.New[struct {
|
||||
// 战斗中可以修改技能实体值,比如是否暴击,是否必中等
|
||||
type BattleSkillEntity struct {
|
||||
xmlres.Move
|
||||
// SideEffects []int
|
||||
// SideEffectArgs []int
|
||||
PP int
|
||||
DamageZone map[EnumCategory]map[EnumsZoneType]map[EnumsZoneType][]float64 // 三维map 伤害类型-》增还是减-》加还是乘-》值
|
||||
DamageValue decimal.Decimal // 伤害值
|
||||
Rand *rand.Rand
|
||||
Pet *BattlePetEntity
|
||||
// 技能类型属性
|
||||
//SkillType EnumCategory // 技能类型(物理/特殊/状态)
|
||||
|
||||
}
|
||||
|
||||
// CreateBattleSkillWithInfinity 创建战斗技能实例(可指定是否无限PP)
|
||||
@@ -241,14 +236,14 @@ func (s *BattleSkillEntity) PutDamageZone(e EnumCategory, dtype EnumsZoneType, v
|
||||
// }
|
||||
|
||||
// 暴击伤害 返回暴击率和是否暴击
|
||||
func (s *BattleSkillEntity) CriticalRate() (decimal.Decimal, uint32) {
|
||||
func (s *BattleSkillEntity) IsCritical() (decimal.Decimal, bool) {
|
||||
|
||||
return decimal.NewFromFloat(1), 1
|
||||
return decimal.NewFromFloat(1), true
|
||||
|
||||
}
|
||||
|
||||
// 计算是否命中
|
||||
func (s *BattleSkillEntity) CriticalisCritical() uint32 {
|
||||
func (s *BattleSkillEntity) AttackTime() uint32 {
|
||||
|
||||
if int64(s.Pet.Accuracy(int64(s.Accuracy))) > s.Rand.Int63n(100) {
|
||||
return 1
|
||||
@@ -343,8 +338,8 @@ func (s *BattleSkillEntity) CalculatePower(deftype int) uint32 {
|
||||
Mul(typeRate). // 克制系数
|
||||
|
||||
Mul(s.criticalrandom()) //随机波动
|
||||
v, ok := s.CriticalRate()
|
||||
if ok == 1 {
|
||||
v, ok := s.IsCritical() //获取暴击
|
||||
if ok {
|
||||
damage.Mul(v)
|
||||
}
|
||||
return uint32(damage.IntPart())
|
||||
|
||||
@@ -52,6 +52,24 @@ type AttackValueS struct {
|
||||
SAttack AttackValue
|
||||
}
|
||||
|
||||
func NewAttackValue(userid uint32) *AttackValue {
|
||||
return &AttackValue{
|
||||
userid,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
[]model.SkillInfo{},
|
||||
0,
|
||||
StatusDict{},
|
||||
PropDict{},
|
||||
}
|
||||
}
|
||||
|
||||
// AttackValue 战斗中的攻击数值信息
|
||||
type AttackValue struct {
|
||||
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
|
||||
@@ -142,6 +160,7 @@ type FightUserInfo struct {
|
||||
|
||||
// NoteReadyToFightInfo 战斗准备就绪消息结构体,NoteReadyToFightInfo
|
||||
type NoteReadyToFightInfo struct {
|
||||
MAXPET uint32 `struc:"skip"` // 最大精灵数 struc:"skip"`
|
||||
// 战斗类型ID(与野怪战斗为3,与人战斗为1,前端似乎未使用)
|
||||
// @UInt long
|
||||
FightId EnumBattleMode `fieldDesc:"战斗类型ID 但前端好像没有用到 与野怪战斗为3,与人战斗似乎是1" `
|
||||
|
||||
@@ -19,32 +19,64 @@ type PlayerI interface {
|
||||
ID() uint32
|
||||
MapID() uint32
|
||||
GetAction()
|
||||
GetPetInfo() []*model.PetInfo
|
||||
End() //结束战斗
|
||||
GetPetInfo() []model.PetInfo
|
||||
SendPack(b []byte) error
|
||||
SendReadyToFightInfo(info.FightStartOutboundInfo)
|
||||
SendNoteReadyToFightInfo(info.NoteReadyToFightInfo)
|
||||
SendFightEndInfo(info.FightOverInfo)
|
||||
SendAttackValue(info.AttackValueS)
|
||||
SendChangePet(info.ChangePetInfo)
|
||||
SetFightC(*FightC)
|
||||
}
|
||||
type Input struct {
|
||||
CanChange bool //是否可以死亡切换CanChange
|
||||
CurrentPet *info.BattlePetEntity //当前精灵
|
||||
AllPet []*info.BattlePetEntity
|
||||
Player PlayerI
|
||||
Finished bool //是否加载完成
|
||||
}
|
||||
|
||||
func (i *Input) GetPetInfo() *info.BattlePetEntity {
|
||||
|
||||
return i.CurrentPet
|
||||
|
||||
}
|
||||
|
||||
type FightC struct {
|
||||
Info *info.NoteReadyToFightInfo
|
||||
Our PlayerI
|
||||
OurCurrentPet *info.BattlePetEntity //我方精灵
|
||||
Opp PlayerI
|
||||
OppCurrentPet *info.BattlePetEntity //对方当前精灵
|
||||
Info info.NoteReadyToFightInfo
|
||||
|
||||
MAXPET uint32 // 最大精灵数
|
||||
OwnerID uint32 // 战斗发起者ID
|
||||
AFinished bool
|
||||
BFinished bool
|
||||
random *rand.Rand //随机数种子
|
||||
Our *Input //始终等于房主ID
|
||||
Opp *Input //对手ID
|
||||
rand *rand.Rand
|
||||
StartTime time.Time
|
||||
actionChan chan info.BattleActionI // 所有操作统一从这里进入
|
||||
Round int //回合数
|
||||
EffectS info.NodeManager //effects容器
|
||||
First *BPET
|
||||
Second *BPET
|
||||
}
|
||||
|
||||
func (f *FightC) GetInputByPlayer(c PlayerI, isOpposite bool) *Input {
|
||||
// 判断当前玩家是否为我方玩家
|
||||
isOurPlayer := c == f.Our.Player
|
||||
|
||||
// 逻辑简化:当"是否为我方玩家"与"是否需要对方"状态一致时,返回对方输入,否则返回我方输入
|
||||
if isOurPlayer == isOpposite {
|
||||
return f.Opp
|
||||
}
|
||||
return f.Our
|
||||
}
|
||||
|
||||
func (f *FightC) GetInputByAction(c info.BattleActionI, isOpposite bool) *Input {
|
||||
// 判断动作所属玩家是否为我方
|
||||
isOurAction := c.GetPlayerID() == f.Our.Player.ID()
|
||||
|
||||
// 根据isOpposite决定是否返回相反方向的输入
|
||||
if isOurAction == !isOpposite {
|
||||
return f.Our
|
||||
}
|
||||
return f.Opp
|
||||
}
|
||||
|
||||
// 玩家逃跑
|
||||
@@ -66,26 +98,20 @@ func (f *FightC) ChangePet(c PlayerI, id int32) {
|
||||
PlayerID: c.ID(),
|
||||
}
|
||||
|
||||
rett := func(t PlayerI) *info.BattlePetEntity {
|
||||
for _, v := range t.GetPetInfo() {
|
||||
if v.CatchTime == uint32(id) {
|
||||
fs := info.CreateBattlePetEntity(v, f.Random()) //存入自己的精灵信息
|
||||
rett := func(t *Input) *info.BattlePetEntity {
|
||||
for _, v := range t.AllPet {
|
||||
if v.Info.CatchTime == uint32(id) {
|
||||
copier.Copy(&ret.Reason, &v)
|
||||
ret.Reason.UserId = c.ID()
|
||||
return fs
|
||||
return v
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if c == f.Our {
|
||||
|
||||
f.OurCurrentPet = rett(f.Our)
|
||||
f.GetInputByPlayer(c, false).CurrentPet = rett(f.GetInputByPlayer(c, false))
|
||||
|
||||
} else {
|
||||
|
||||
f.OppCurrentPet = rett(f.Opp)
|
||||
}
|
||||
f.actionChan <- ret
|
||||
}
|
||||
|
||||
@@ -94,14 +120,7 @@ func (f *FightC) UseSkill(c PlayerI, id int32) {
|
||||
ret := &info.SelectSkillAction{
|
||||
PlayerID: c.ID(),
|
||||
}
|
||||
|
||||
if c == f.Our {
|
||||
ret.PetInfo = f.OurCurrentPet //存入自己的精灵信息
|
||||
|
||||
} else {
|
||||
|
||||
ret.PetInfo = f.OppCurrentPet
|
||||
}
|
||||
ret.PetInfo = f.GetInputByPlayer(c, false).CurrentPet
|
||||
|
||||
for _, v := range ret.PetInfo.Skills {
|
||||
|
||||
@@ -124,27 +143,18 @@ func (f *FightC) ReadyFight(c PlayerI) {
|
||||
|
||||
rrsult := func() { //传回函数
|
||||
|
||||
f.Our.SendReadyToFightInfo(rett)
|
||||
f.Opp.SendReadyToFightInfo(rett)
|
||||
f.Our.Player.SendReadyToFightInfo(rett)
|
||||
f.Opp.Player.SendReadyToFightInfo(rett)
|
||||
}
|
||||
|
||||
switch f.Info.FightId {
|
||||
case 1: // 1v1
|
||||
if c == f.Our {
|
||||
f.AFinished = true
|
||||
|
||||
if f.BFinished {
|
||||
|
||||
rrsult()
|
||||
}
|
||||
} else {
|
||||
f.Opp = c
|
||||
f.OppCurrentPet = info.CreateBattlePetEntity(c.GetPetInfo()[0], f.Random())
|
||||
f.BFinished = true
|
||||
if f.AFinished {
|
||||
rrsult()
|
||||
}
|
||||
f.GetInputByPlayer(c, false).Finished = true
|
||||
if f.GetInputByPlayer(c, true).Finished {
|
||||
rrsult()
|
||||
}
|
||||
|
||||
case 3: // 野怪战斗
|
||||
|
||||
//判断捕捉率大于0
|
||||
@@ -155,16 +165,6 @@ func (f *FightC) ReadyFight(c PlayerI) {
|
||||
rrsult()
|
||||
}
|
||||
}
|
||||
func (f *FightC) Random() *rand.Rand {
|
||||
//先产生战斗的随机数
|
||||
// 组合「时间戳(纳秒精度)+ 双方ID + 回合数」生成种子
|
||||
|
||||
seed := f.StartTime.UnixNano() ^ int64(f.OwnerID) ^ int64(f.Our.ID()) ^ int64(f.Round) // 用异或运算混合多维度信息
|
||||
ret := rand.New(rand.NewSource(seed))
|
||||
|
||||
return ret
|
||||
|
||||
}
|
||||
|
||||
var Fightpool *ants.Pool
|
||||
|
||||
@@ -173,104 +173,67 @@ func init() {
|
||||
//defer p.Release()
|
||||
}
|
||||
|
||||
// 创建新战斗
|
||||
func (f *FightC) NewFight(i *info.NoteReadyToFightInfo, plays PlayerI, mo *model.PetInfo) {
|
||||
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
|
||||
func NewFight(i info.NoteReadyToFightInfo, p1 PlayerI, p2 PlayerI) *FightC {
|
||||
f := &FightC{}
|
||||
f.OwnerID = p1.ID()
|
||||
f.Info = i //房主
|
||||
seed := f.StartTime.UnixNano() ^ int64(p1.ID()) ^ int64(p2.ID()) // ^ int64(f.Round) // 用异或运算混合多维度信息
|
||||
f.rand = rand.New(rand.NewSource(seed))
|
||||
|
||||
f.Our = plays
|
||||
f.Info = i
|
||||
f.StartTime = time.Now()
|
||||
f.actionChan = make(chan info.BattleActionI, 2) // 初始化全局操作通道
|
||||
fmt.Println("战斗开始精灵", plays.GetPetInfo()[0].CatchTime)
|
||||
f.Our = &Input{
|
||||
CurrentPet: info.CreateBattlePetEntity(p1.GetPetInfo()[0], f.rand),
|
||||
Player: p1,
|
||||
}
|
||||
for k, v := range p1.GetPetInfo() {
|
||||
if k < int(i.MAXPET) {
|
||||
f.Our.AllPet = append(f.Our.AllPet, info.CreateBattlePetEntity(v, f.rand))
|
||||
}
|
||||
|
||||
for _, v := range plays.GetPetInfo() {
|
||||
if v.Hp > 0 {
|
||||
f.OurCurrentPet = info.CreateBattlePetEntity(plays.GetPetInfo()[0], f.Random())
|
||||
}
|
||||
|
||||
f.Opp = &Input{
|
||||
CurrentPet: info.CreateBattlePetEntity(p2.GetPetInfo()[0], f.rand),
|
||||
Player: p2,
|
||||
}
|
||||
for k, v := range p2.GetPetInfo() {
|
||||
if k < int(i.MAXPET) {
|
||||
f.Opp.AllPet = append(f.Opp.AllPet, info.CreateBattlePetEntity(v, f.rand))
|
||||
}
|
||||
}
|
||||
// }
|
||||
// f.OurCurrentPet = info.CreateBattlePetEntity(plays.GetPetInfo()[0], f.Random())
|
||||
if mo != nil {
|
||||
f.OppCurrentPet = info.CreateBattlePetEntity(mo, f.Random())
|
||||
}
|
||||
|
||||
switch i.FightId {
|
||||
case 1:
|
||||
// 1v1,等双方进入
|
||||
case 3: // 野怪战斗
|
||||
p1.SetFightC(f) //给我方追加战斗容器
|
||||
p2.SetFightC(f) //给对方增加战斗容器
|
||||
defer func() {
|
||||
rr := Fightpool.Submit(f.battleLoop)
|
||||
if rr != nil {
|
||||
panic(rr)
|
||||
}
|
||||
f.Broadcast(func(ff *Input) {
|
||||
|
||||
f.Opp = &AI_player{fightinfo: *i, FightC: f} // 创建虚拟对手
|
||||
plays.SendNoteReadyToFightInfo(*i)
|
||||
}
|
||||
|
||||
rr := Fightpool.Submit(f.battleLoop)
|
||||
if rr != nil {
|
||||
panic(rr)
|
||||
}
|
||||
ff.Player.SendNoteReadyToFightInfo(i)
|
||||
})
|
||||
}()
|
||||
//go f.battleLoop() // 起战斗循环
|
||||
return f
|
||||
}
|
||||
|
||||
// 广播,并是否结束回合
|
||||
func (f *FightC) Broadcast(t info.BattleActionI) bool {
|
||||
switch ff := t.(type) {
|
||||
func (f *FightC) Broadcast(t func(ff *Input)) {
|
||||
|
||||
case *info.EscapeAction:
|
||||
t(f.Our)
|
||||
|
||||
f.Our.SendFightEndInfo(ff.Reason)
|
||||
f.Opp.SendFightEndInfo(ff.Reason)
|
||||
return true
|
||||
t(f.Opp)
|
||||
|
||||
case *info.ActiveSwitchAction: //切换精灵
|
||||
|
||||
f.Our.SendChangePet(ff.Reason)
|
||||
f.Opp.SendChangePet(ff.Reason)
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
func (f *FightC) BroadcastSkill(t info.AttackValueS) {
|
||||
f.Our.SendAttackValue(t)
|
||||
f.Opp.SendAttackValue(t)
|
||||
}
|
||||
|
||||
// 检查战斗是否结束
|
||||
func (f *FightC) CheakEnd(b uint32) bool {
|
||||
fmt.Println(f.Round, "检查战斗是否结束")
|
||||
var tt []info.ReadyFightPetInfo
|
||||
for _, v := range f.Info.OurPetList {
|
||||
if v.CatchTime == b {
|
||||
v.NotAlive = true
|
||||
|
||||
}
|
||||
tt = append(tt, v)
|
||||
}
|
||||
f.Info.OurPetList = tt
|
||||
var tt1 []info.ReadyFightPetInfo
|
||||
for _, v := range f.Info.OpponentPetList {
|
||||
if v.CatchTime == b {
|
||||
v.NotAlive = true
|
||||
|
||||
}
|
||||
tt1 = append(tt1, v)
|
||||
}
|
||||
f.Info.OpponentPetList = tt1
|
||||
for _, v := range f.Info.OurPetList {
|
||||
if !v.NotAlive {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
for _, v := range f.Info.OpponentPetList {
|
||||
if !v.NotAlive {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 战斗回合循环
|
||||
func (f *FightC) battleLoop() {
|
||||
f.StartTime = time.Now()
|
||||
f.actionChan = make(chan info.BattleActionI, 2) // 初始化全局操作通道
|
||||
fmt.Println("战斗开始精灵", f.Our.Player.GetPetInfo()[0].CatchTime)
|
||||
//战斗开始前操作
|
||||
|
||||
for {
|
||||
|
||||
f.Round++ //回合数自增
|
||||
@@ -284,23 +247,37 @@ func (f *FightC) battleLoop() {
|
||||
for len(actions) < 2 {
|
||||
select {
|
||||
case action := <-f.actionChan:
|
||||
// 只接受有效玩家 ID
|
||||
if action.GetPlayerID() != f.Our.ID() && action.GetPlayerID() != f.Opp.ID() {
|
||||
// 只接受有效玩家 ID\
|
||||
if action == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if action.GetPlayerID() != f.Our.Player.ID() && action.GetPlayerID() != f.Opp.Player.ID() {
|
||||
continue
|
||||
}
|
||||
|
||||
if a, isExpelled := action.(*info.ActiveSwitchAction); isExpelled {
|
||||
//fmt.Println("对方死亡切换")
|
||||
f.Broadcast(a)
|
||||
if !a.Type {
|
||||
f.Broadcast(func(ff *Input) {
|
||||
|
||||
ff.Player.SendChangePet(a.Reason)
|
||||
})
|
||||
|
||||
if f.GetInputByAction(action, false).CanChange {
|
||||
//如果是被动切换,不计入回合结算
|
||||
f.GetInputByAction(action, false).CanChange = false
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if action.GetPlayerID() == f.Our.ID() && f.Info.FightId == 3 {
|
||||
|
||||
go f.Opp.GetAction() //获取AI的动作
|
||||
if action.GetPlayerID() != 0 && f.Info.FightId == 3 {
|
||||
|
||||
f.GetInputByAction(action, true).Player.GetAction()
|
||||
|
||||
}
|
||||
|
||||
// 如果该玩家已经提交过,就忽略重复动作
|
||||
if _, exists := actions[uint32(action.GetPlayerID())]; exists {
|
||||
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", action.GetPlayerID())
|
||||
@@ -312,140 +289,241 @@ func (f *FightC) battleLoop() {
|
||||
|
||||
case <-timeout:
|
||||
fmt.Println("回合操作超时")
|
||||
if _, exists := actions[f.Our.ID()]; !exists {
|
||||
actions[f.Our.ID()] = &info.SystemGiveUpAction{PlayerID: f.Our.ID()} //系统选择出手
|
||||
|
||||
if _, exists := actions[f.Our.Player.ID()]; !exists {
|
||||
actions[f.Our.Player.ID()] = &info.SystemGiveUpAction{PlayerID: f.Our.Player.ID()} //系统选择出手
|
||||
}
|
||||
if _, exists := actions[f.Opp.ID()]; !exists {
|
||||
actions[f.Opp.ID()] = &info.SystemGiveUpAction{PlayerID: f.Opp.ID()} //系统选择出手
|
||||
if _, exists := actions[f.Opp.Player.ID()]; !exists {
|
||||
actions[f.Opp.Player.ID()] = &info.SystemGiveUpAction{PlayerID: f.Opp.Player.ID()} //系统选择出手
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 双方动作齐了,取出来结算
|
||||
p1Action := actions[f.Our.ID()]
|
||||
p2Action := actions[f.Opp.ID()]
|
||||
p1Action := actions[f.Our.Player.ID()]
|
||||
p2Action := actions[f.Opp.Player.ID()]
|
||||
fmt.Println("开始结算回合")
|
||||
var BattleActionI [2]info.BattleActionI
|
||||
BattleActionI[0], BattleActionI[1] = info.Compare(p1Action, p2Action)
|
||||
|
||||
if BattleActionI[0].Priority() > 0 { //如果优先级大于0,说明是在技能之上的
|
||||
t := f.Broadcast(BattleActionI[0])
|
||||
if t {
|
||||
switch faction := BattleActionI[0].(type) {
|
||||
case *info.EscapeAction: //优先逃跑
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if BattleActionI[0].Priority() < 0 && BattleActionI[1].Priority() < 0 { //双方都空过
|
||||
f.Broadcast(func(ff *Input) {
|
||||
ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
|
||||
})
|
||||
break
|
||||
|
||||
}
|
||||
case *info.PlayerOfflineAction: //单方掉线
|
||||
|
||||
var p_skill [2]*info.SelectSkillAction
|
||||
var currpet [2]*info.BattlePetEntity
|
||||
var currpetvale [2]int // 伤害值
|
||||
if BattleActionI[0].GetPlayerID() == f.OwnerID { //先反转战斗属性
|
||||
f.Broadcast(func(ff *Input) {
|
||||
ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
|
||||
})
|
||||
break
|
||||
case *info.ActiveSwitchAction: //切换上场的
|
||||
f.enterturn(BattleActionI[1], nil) //切换,相当于后手直接出手
|
||||
|
||||
currpet[0] = f.OurCurrentPet
|
||||
currpet[1] = f.OppCurrentPet
|
||||
case *info.SelectSkillAction: //选择技能
|
||||
//回合前操作,比如挂载buff
|
||||
f.enterturn(BattleActionI[0], BattleActionI[1])
|
||||
|
||||
} else {
|
||||
currpet[0] = f.OppCurrentPet
|
||||
currpet[1] = f.OurCurrentPet
|
||||
}
|
||||
fmt.Println("当前", currpet[0].Info.CatchTime, "敌方", currpet[1].Info.CatchTime)
|
||||
for i := 0; i < 2; i++ {
|
||||
|
||||
// TODO: 在这里调用技能结算逻辑
|
||||
|
||||
p_skill[i] = BattleActionI[i].(*info.SelectSkillAction)
|
||||
temparg := p_skill[i].Skill.SideEffectArgS
|
||||
for _, v := range p_skill[i].Skill.SideEffectS {
|
||||
|
||||
t, ok := info.NodeM[v]
|
||||
|
||||
if ok { //获取成功
|
||||
|
||||
args := t.GetArgSize()
|
||||
t.SetArgs(temparg[:args]) //设置入参
|
||||
|
||||
//如果不是是房主方,说明施加的对象是反的,比如本来是false,实际上是给邀请方施加的
|
||||
//所以这里要对target取反
|
||||
if BattleActionI[i].GetPlayerID() != f.OwnerID {
|
||||
t.SetTarget(!t.Target())
|
||||
}
|
||||
temparg = temparg[args:]
|
||||
f.EffectS.AddEffect(deepcopy.Copy(t).(info.Effect))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
spower := p_skill[i].Skill.CalculatePower(currpet[i].Type().ID)
|
||||
|
||||
//开始计算技能效果battleLoop
|
||||
_, ok := p_skill[i].Skill.CriticalRate()
|
||||
//CalculatePower()
|
||||
//记录伤害,到后手方出手时计算伤害
|
||||
currpetvale[i] = int(spower)
|
||||
|
||||
fmt.Println("技能伤害", spower, "剩余血量", currpet[i].Info.Hp)
|
||||
p_skill[i].Attack = info.AttackValue{
|
||||
p_skill[i].PlayerID,
|
||||
uint32(p_skill[i].Skill.ID),
|
||||
p_skill[i].Skill.CriticalisCritical(),
|
||||
spower,
|
||||
0,
|
||||
int32(currpet[i].Info.Hp),
|
||||
currpet[i].Info.MaxHp,
|
||||
0,
|
||||
0,
|
||||
[]model.SkillInfo{},
|
||||
ok,
|
||||
info.StatusDict{},
|
||||
info.PropDict{},
|
||||
}
|
||||
//回合后操作
|
||||
|
||||
}
|
||||
p_skill[0].Attack.RemainHp = int32(int(currpet[0].Info.Hp) - currpetvale[1])
|
||||
currpet[0].Info.Hp = uint32(p_skill[0].Attack.RemainHp)
|
||||
p_skill[1].Attack.RemainHp = int32(int(currpet[1].Info.Hp) - currpetvale[0])
|
||||
currpet[1].Info.Hp = uint32(p_skill[1].Attack.RemainHp)
|
||||
if p_skill[1].Attack.RemainHp <= 0 { //如果后手方被打死
|
||||
p_skill[1].Attack = info.AttackValue{
|
||||
p_skill[1].PlayerID,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
[]model.SkillInfo{},
|
||||
0,
|
||||
info.StatusDict{},
|
||||
info.PropDict{},
|
||||
}
|
||||
fmt.Println(currpet[1].Info.CatchTime, "被打死")
|
||||
if f.CheakEnd(currpet[1].Info.CatchTime) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if p_skill[0].Attack.RemainHp <= 0 {
|
||||
|
||||
if f.CheakEnd(currpet[0].Info.CatchTime) {
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Println("P1", p_skill[0].Attack.RemainHp, "P2", p_skill[1].Attack.RemainHp)
|
||||
f.BroadcastSkill(info.AttackValueS{
|
||||
p_skill[0].Attack, p_skill[1].Attack,
|
||||
})
|
||||
|
||||
}
|
||||
close(f.actionChan) //关闭战斗通道
|
||||
//取消战斗信息
|
||||
f.Our.End()
|
||||
f.Opp.End()
|
||||
|
||||
}
|
||||
|
||||
type BPET struct {
|
||||
*Input
|
||||
|
||||
//*info.SelectSkillAction //技能实体
|
||||
*info.BattlePetEntity //精灵实体
|
||||
*info.AttackValue
|
||||
*FightC
|
||||
Damage uint32 //造成伤害
|
||||
}
|
||||
|
||||
// 被击败的ID
|
||||
func (b *BPET) IsWin(cache uint32) bool {
|
||||
|
||||
fmt.Println("当前回合", b.FightC.Round, "开始检查战斗是否结束")
|
||||
|
||||
var tt []info.ReadyFightPetInfo
|
||||
bbb := b.FightC.Info.OurPetList
|
||||
|
||||
if b.Input.Player.ID() == b.FightC.OwnerID { //如果是房主
|
||||
bbb = b.FightC.Info.OpponentPetList
|
||||
} else {
|
||||
bbb = b.FightC.Info.OurPetList
|
||||
}
|
||||
|
||||
for _, v := range bbb {
|
||||
if v.CatchTime == cache {
|
||||
v.NotAlive = true
|
||||
|
||||
}
|
||||
tt = append(tt, v)
|
||||
}
|
||||
|
||||
for _, v := range tt {
|
||||
if !v.NotAlive { //如果存活
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 解析并 施加effect
|
||||
func (f *FightC) parseskill(id *info.SelectSkillAction) {
|
||||
temparg := id.Skill.SideEffectArgS
|
||||
for _, v := range id.Skill.SideEffectS {
|
||||
|
||||
t, ok := info.NodeM[v]
|
||||
|
||||
if ok { //获取成功
|
||||
|
||||
args := t.GetArgSize()
|
||||
t.SetArgs(temparg[:args]) //设置入参
|
||||
//如果不是是房主方,说明施加的对象是反的,比如本来是false,实际上是给邀请方施加的
|
||||
//所以这里要对target取反
|
||||
if id.GetPlayerID() != f.OwnerID {
|
||||
t.SetTarget(!t.Target())
|
||||
}
|
||||
temparg = temparg[args:]
|
||||
f.EffectS.AddEffect(deepcopy.Copy(t).(info.Effect))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
func (f *FightC) enterturn(fattack, sattack info.BattleActionI) {
|
||||
|
||||
// 伤害值
|
||||
|
||||
// 根据攻击方归属设置当前战斗的主/次攻击方属性
|
||||
var first, second *Input // 定义临时变量存储主/次攻击方
|
||||
if fattack.GetPlayerID() == f.OwnerID {
|
||||
first = f.Our // 攻击方为我方时,主攻击方是我方
|
||||
second = f.Opp // 次攻击方是对方
|
||||
} else {
|
||||
first = f.Opp // 攻击方为对方时,主攻击方是对方
|
||||
second = f.Our // 次攻击方是我方
|
||||
}
|
||||
|
||||
// 统一赋值,减少重复代码
|
||||
f.First = &BPET{
|
||||
FightC: f,
|
||||
Input: first,
|
||||
BattlePetEntity: first.CurrentPet,
|
||||
AttackValue: info.NewAttackValue(first.Player.ID()),
|
||||
Damage: 0}
|
||||
f.Second = &BPET{
|
||||
FightC: f,
|
||||
Input: second,
|
||||
BattlePetEntity: second.CurrentPet,
|
||||
AttackValue: info.NewAttackValue(second.Player.ID()),
|
||||
Damage: 0}
|
||||
|
||||
fmt.Println("先手", f.First.CurrentPet.Info.CatchTime, "后手", f.Second.CurrentPet.Info.CatchTime)
|
||||
|
||||
// TODO: 在这里调用技能结算逻辑
|
||||
|
||||
skill, _ := fattack.(*info.SelectSkillAction)
|
||||
|
||||
f.parseskill(skill)
|
||||
spower := skill.Skill.CalculatePower(f.Second.Type().ID)
|
||||
|
||||
//开始计算技能效果battleLoop
|
||||
_, ok := skill.Skill.IsCritical()
|
||||
|
||||
//CalculatePower()
|
||||
//记录伤害,到后手方出手时计算伤害
|
||||
f.First.Damage = (spower)
|
||||
|
||||
fmt.Println("先手技能伤害", spower, "先手剩余血量", f.First.CurrentPet.Info.Hp)
|
||||
|
||||
f.First.AttackValue.SkillID = uint32(skill.Skill.ID)
|
||||
f.First.AttackValue.AttackTime = skill.Skill.AttackTime()
|
||||
if ok { //判断暴击
|
||||
f.First.AttackValue.IsCritical = 1
|
||||
}
|
||||
fmt.Println(f.First.CurrentPet.Info.CatchTime, "先手出招结束")
|
||||
|
||||
if f.First.Damage > f.Second.CurrentPet.Info.Hp {
|
||||
f.Second.CurrentPet.Info.Hp = 0
|
||||
} else {
|
||||
f.Second.CurrentPet.Info.Hp = f.Second.CurrentPet.Info.Hp - f.First.Damage //扣减后手方血量
|
||||
}
|
||||
if f.Second.CurrentPet.Info.Hp > 0 { //如果后手方没被打死
|
||||
skill, ok := sattack.(*info.SelectSkillAction)
|
||||
if ok {
|
||||
|
||||
f.parseskill(skill)
|
||||
spower := skill.Skill.CalculatePower(f.First.Type().ID)
|
||||
|
||||
//开始计算技能效果battleLoop
|
||||
_, ok := skill.Skill.IsCritical()
|
||||
|
||||
//CalculatePower()
|
||||
//记录伤害,到后手方出手时计算伤害
|
||||
f.Second.Damage = (spower)
|
||||
|
||||
fmt.Println("后手技能伤害", spower, "后手剩余血量", f.Second.CurrentPet.Info.Hp)
|
||||
|
||||
f.Second.AttackValue.SkillID = uint32(skill.Skill.ID)
|
||||
f.Second.AttackValue.AttackTime = skill.Skill.AttackTime()
|
||||
if ok { //判断暴击
|
||||
f.Second.AttackValue.IsCritical = 1
|
||||
}
|
||||
fmt.Println(f.Second.CurrentPet.Info.CatchTime, "后手出招结束")
|
||||
if f.Second.Damage > f.First.CurrentPet.Info.Hp {
|
||||
f.First.CurrentPet.Info.Hp = 0
|
||||
} else {
|
||||
f.First.CurrentPet.Info.Hp = f.First.CurrentPet.Info.Hp - f.Second.Damage //扣减先手方血量
|
||||
}
|
||||
|
||||
} //还有系统选择放弃出手的
|
||||
|
||||
if f.First.CurrentPet.Info.Hp == 0 {
|
||||
f.First.CanChange = true //被打死就可以切精灵了
|
||||
if f.Second.IsWin(f.First.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
|
||||
defer f.Broadcast(func(ff *Input) {
|
||||
ff.Player.SendFightEndInfo(info.FightOverInfo{
|
||||
WinnerId: f.Second.UserID,
|
||||
}) //广播逃跑原因
|
||||
})
|
||||
defer close(f.actionChan) //关闭战斗通道
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
f.Second.AttackValue = info.NewAttackValue(second.Player.ID()) //已经被打死了,直接将后手方输出归0
|
||||
f.Second.CanChange = true //被打死就可以切精灵了
|
||||
if f.First.IsWin(f.Second.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
|
||||
defer f.Broadcast(func(ff *Input) {
|
||||
ff.Player.SendFightEndInfo(info.FightOverInfo{
|
||||
WinnerId: f.First.UserID,
|
||||
}) //广播逃跑原因
|
||||
})
|
||||
defer close(f.actionChan) //关闭战斗通道
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
f.Broadcast(func(ff *Input) {
|
||||
ret := info.AttackValueS{
|
||||
FAttack: *f.First.AttackValue,
|
||||
SAttack: *f.Second.AttackValue,
|
||||
}
|
||||
ret.FAttack.RemainHp = int32(f.First.CurrentPet.Info.Hp)
|
||||
|
||||
ret.FAttack.LostHp = uint32(f.First.Damage) //先手方造成血量
|
||||
ret.SAttack.RemainHp = int32(f.Second.CurrentPet.Info.Hp)
|
||||
ret.SAttack.LostHp = uint32(f.Second.Damage) //后手方造成血量
|
||||
ff.Player.SendAttackValue(ret)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -128,11 +128,7 @@ func (p *Player) GetAction() {
|
||||
|
||||
return
|
||||
}
|
||||
func (p *Player) End() {
|
||||
p.FightC = nil
|
||||
|
||||
return
|
||||
}
|
||||
func (p *Player) ID() uint32 {
|
||||
|
||||
return p.Info.UserID
|
||||
@@ -182,16 +178,15 @@ func (p *Player) SendFightEndInfo(b info.FightOverInfo) {
|
||||
t1 := NewTomeeHeader(2506, p.Info.UserID)
|
||||
|
||||
p.SendPack(t1.Pack(&b))
|
||||
p.FightC = nil
|
||||
}
|
||||
func (p *Player) GetPetInfo() []*model.PetInfo {
|
||||
var ret = make([]*model.PetInfo, 0)
|
||||
func (p *Player) GetPetInfo() []model.PetInfo {
|
||||
|
||||
for _, v := range p.Info.PetList {
|
||||
ret = append(ret, &v)
|
||||
return p.Info.PetList
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret
|
||||
func (f *Player) SetFightC(ff *FightC) {
|
||||
f.FightC = ff
|
||||
}
|
||||
|
||||
// Save 保存玩家数据
|
||||
|
||||
Reference in New Issue
Block a user