369 lines
8.3 KiB
Go
369 lines
8.3 KiB
Go
package service
|
||
|
||
import (
|
||
"blazing/common/data/xmlres"
|
||
"blazing/logic/service/fight/info"
|
||
"blazing/modules/blazing/model"
|
||
"fmt"
|
||
"math"
|
||
"math/rand"
|
||
"time"
|
||
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
"github.com/jinzhu/copier"
|
||
"github.com/mohae/deepcopy"
|
||
"github.com/panjf2000/ants/v2"
|
||
)
|
||
|
||
type PlayerI interface {
|
||
ID() uint32
|
||
MapID() uint32
|
||
GetAction()
|
||
GetPetInfo() []*model.PetInfo
|
||
End() //结束战斗
|
||
SendPack(b []byte) error
|
||
SendReadyToFightInfo(info.FightStartOutboundInfo)
|
||
SendNoteReadyToFightInfo(info.NoteReadyToFightInfo)
|
||
SendFightEndInfo(info.FightOverInfo)
|
||
SendAttackValue(info.AttackValueS)
|
||
SendChangePet(info.ChangePetInfo)
|
||
}
|
||
|
||
type FightC struct {
|
||
Info *info.NoteReadyToFightInfo
|
||
Our PlayerI
|
||
OurCurrentPet *info.BattlePetEntity //我方精灵
|
||
Opp PlayerI
|
||
OppCurrentPet *info.BattlePetEntity //对方当前精灵
|
||
MAXPET uint32 // 最大精灵数
|
||
OwnerID uint32 // 战斗发起者ID
|
||
AFinished bool
|
||
BFinished bool
|
||
//random *rand.Rand //随机数种子
|
||
StartTime time.Time
|
||
actionChan chan info.BattleActionI // 所有操作统一从这里进入
|
||
Round int //回合数
|
||
EffectS info.NodeManager //effects容器
|
||
}
|
||
|
||
// 玩家逃跑
|
||
func (f *FightC) Escape(c PlayerI) {
|
||
ret := &info.EscapeAction{
|
||
PlayerID: c.ID(),
|
||
Reason: info.FightOverInfo{
|
||
|
||
Reason: uint32(info.BattleOverReason.PlayerEscape),
|
||
},
|
||
}
|
||
|
||
f.actionChan <- ret
|
||
}
|
||
|
||
// 切换精灵 主动和被驱逐
|
||
func (f *FightC) ChangePet(c PlayerI, id int32) {
|
||
ret := &info.ActiveSwitchAction{
|
||
PlayerID: c.ID(),
|
||
}
|
||
|
||
rett := func(t PlayerI) {
|
||
for _, v := range t.GetPetInfo() {
|
||
if v.CatchTime == uint32(id) {
|
||
f.OurCurrentPet = info.CreateBattlePetEntity(v, f.Random()) //存入自己的精灵信息
|
||
copier.Copy(&ret.Reason, &v)
|
||
ret.Reason.UserId = c.ID()
|
||
}
|
||
|
||
}
|
||
}
|
||
if c == f.Our {
|
||
|
||
rett(f.Our)
|
||
|
||
} else {
|
||
|
||
rett(f.Opp)
|
||
}
|
||
f.actionChan <- ret
|
||
}
|
||
|
||
// 玩家使用技能
|
||
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
|
||
}
|
||
|
||
for _, v := range ret.PetInfo.Skills {
|
||
|
||
if v != nil && v.ID == int(id) {
|
||
ret.Skill = v
|
||
}
|
||
|
||
}
|
||
f.actionChan <- ret
|
||
}
|
||
|
||
// 战斗准备
|
||
func (f *FightC) ReadyFight(c PlayerI) {
|
||
rett := info.FightStartOutboundInfo{}
|
||
|
||
copier.Copy(&rett.Info1, &f.Info.OurPetList[0]) // 复制自己的信息
|
||
copier.Copy(&rett.Info2, &f.Info.OpponentPetList[0])
|
||
rett.Info1.UserID = f.Info.OurInfo.UserID //
|
||
rett.Info2.UserID = f.Info.OpponentInfo.UserID
|
||
|
||
rrsult := func() { //传回函数
|
||
|
||
f.Our.SendReadyToFightInfo(rett)
|
||
f.Opp.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()
|
||
}
|
||
}
|
||
case 3: // 野怪战斗
|
||
|
||
//判断捕捉率大于0
|
||
if gconv.Int(xmlres.PetMAP[int(f.Info.OpponentPetList[0].ID)].CatchRate) > 0 {
|
||
rett.Info2.Catchable = 1
|
||
}
|
||
|
||
rrsult()
|
||
}
|
||
}
|
||
func (f *FightC) Random() *rand.Source {
|
||
//先产生战斗的随机数
|
||
// 组合「时间戳(纳秒精度)+ 双方ID + 回合数」生成种子
|
||
|
||
seed := f.StartTime.UnixNano() ^ int64(f.OwnerID) ^ int64(f.Our.ID()) ^ int64(f.Round) // 用异或运算混合多维度信息
|
||
ret := rand.NewSource(seed)
|
||
return &ret
|
||
|
||
}
|
||
|
||
var Fightpool *ants.Pool
|
||
|
||
func init() {
|
||
Fightpool, _ = ants.NewPool(math.MaxInt32)
|
||
//defer p.Release()
|
||
}
|
||
|
||
// 创建新战斗
|
||
func (f *FightC) NewFight(i *info.NoteReadyToFightInfo, plays PlayerI, mo *model.PetInfo) {
|
||
|
||
f.Our = plays
|
||
f.Info = i
|
||
f.StartTime = time.Now()
|
||
f.actionChan = make(chan info.BattleActionI, 2) // 初始化全局操作通道
|
||
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: // 野怪战斗
|
||
|
||
f.Opp = &AI_player{fightinfo: *i, FightC: f} // 创建虚拟对手
|
||
plays.SendNoteReadyToFightInfo(*i)
|
||
}
|
||
|
||
rr := Fightpool.Submit(f.battleLoop)
|
||
if rr != nil {
|
||
panic(rr)
|
||
}
|
||
//go f.battleLoop() // 起战斗循环
|
||
}
|
||
|
||
// 广播,并是否结束回合
|
||
func (f *FightC) Broadcast(t info.BattleActionI) bool {
|
||
switch ff := t.(type) {
|
||
|
||
case *info.EscapeAction:
|
||
|
||
f.Our.SendFightEndInfo(ff.Reason)
|
||
f.Opp.SendFightEndInfo(ff.Reason)
|
||
return true
|
||
|
||
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) battleLoop() {
|
||
for {
|
||
f.Round++ //回合数自增
|
||
// f.AFinished = false
|
||
// f.BFinished = false
|
||
if f.Round > 250 { //回合数超过250,战斗平局结束
|
||
|
||
}
|
||
actions := make(map[uint32]info.BattleActionI) // 每个玩家一条记录
|
||
timeout := time.After(60 * time.Second)
|
||
|
||
for len(actions) < 2 {
|
||
select {
|
||
case action := <-f.actionChan:
|
||
// 只接受有效玩家 ID
|
||
if action.GetPlayerID() != f.Our.ID() && action.GetPlayerID() != f.Opp.ID() {
|
||
continue
|
||
}
|
||
if a, isExpelled := action.(*info.ActiveSwitchAction); isExpelled {
|
||
fmt.Println("对方死亡切换")
|
||
f.Broadcast(a)
|
||
if !a.Type {
|
||
continue
|
||
}
|
||
|
||
}
|
||
if action.GetPlayerID() == f.Our.ID() && f.Info.FightId == 3 {
|
||
|
||
go f.Opp.GetAction() //获取AI的动作
|
||
|
||
}
|
||
// 如果该玩家已经提交过,就忽略重复动作
|
||
if _, exists := actions[uint32(action.GetPlayerID())]; exists {
|
||
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", action.GetPlayerID())
|
||
continue
|
||
}
|
||
|
||
actions[uint32(action.GetPlayerID())] = action
|
||
fmt.Println("玩家%d 执行动作", action.GetPlayerID())
|
||
|
||
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.Opp.ID()]; !exists {
|
||
actions[f.Opp.ID()] = &info.SystemGiveUpAction{PlayerID: f.Opp.ID()} //系统选择出手
|
||
}
|
||
}
|
||
}
|
||
|
||
// 双方动作齐了,取出来结算
|
||
p1Action := actions[f.Our.ID()]
|
||
p2Action := actions[f.Opp.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 {
|
||
|
||
break
|
||
}
|
||
}
|
||
|
||
if BattleActionI[0].Priority() < 0 && BattleActionI[1].Priority() < 0 { //双方都空过
|
||
break
|
||
|
||
}
|
||
|
||
var p_skill [2]*info.SelectSkillAction
|
||
|
||
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))
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//开始计算技能效果battleLoop
|
||
|
||
p_skill[i].Attack = info.AttackValue{
|
||
p_skill[i].PlayerID,
|
||
uint32(p_skill[i].Skill.ID),
|
||
1,
|
||
200,
|
||
0,
|
||
int32(f.OurCurrentPet.Info.MaxHp),
|
||
f.OurCurrentPet.Info.MaxHp,
|
||
0,
|
||
0,
|
||
[]model.SkillInfo{},
|
||
1,
|
||
info.StatusDict{},
|
||
info.PropDict{},
|
||
}
|
||
|
||
}
|
||
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{},
|
||
}
|
||
f.BroadcastSkill(info.AttackValueS{
|
||
p_skill[0].Attack, p_skill[1].Attack,
|
||
})
|
||
|
||
}
|
||
close(f.actionChan) //关闭战斗通道
|
||
//取消战斗信息
|
||
f.Our.End()
|
||
f.Opp.End()
|
||
}
|