Files
bl/logic/service/fight/input.go

383 lines
9.1 KiB
Go
Raw Normal View History

2025-11-11 05:54:24 +00:00
package fight
import (
"blazing/common/data"
"blazing/common/socket/errorcode"
"blazing/common/utils"
2025-11-11 05:54:24 +00:00
"blazing/cool"
"blazing/modules/config/service"
"blazing/modules/player/model"
"encoding/json"
"fmt"
2025-11-15 22:17:43 +00:00
2025-11-11 05:54:24 +00:00
"blazing/logic/service/common"
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/player"
"blazing/logic/service/user"
2025-11-11 05:54:24 +00:00
"math/rand"
"sync"
2025-11-11 05:54:24 +00:00
"time"
2025-11-23 23:38:03 +00:00
"github.com/gogf/gf/v2/util/grand"
2025-11-11 05:54:24 +00:00
"github.com/jinzhu/copier"
)
type FightC struct {
//准备战斗信息
ReadyInfo info.NoteReadyToFightInfo
//开始战斗信息
info.FightStartOutboundInfo
Info info.Fightinfo
IsReady bool
ownerID uint32 // 战斗发起者ID
Our *input.Input //始终等于房主ID
Opp *input.Input //对手ID
Switch map[uint32]*action.ActiveSwitchAction
startl sync.Once
2025-11-11 05:54:24 +00:00
rand *rand.Rand
StartTime time.Time
actionChan chan action.BattleActionI // 所有操作统一从这里进入
quit chan struct{}
over chan struct{}
2025-11-11 05:54:24 +00:00
First *input.Input
TrueFirst *input.Input
2025-11-11 05:54:24 +00:00
Second *input.Input
closefight bool
overl sync.Once
waittime int
2025-11-11 05:54:24 +00:00
info.FightOverInfo
2025-11-15 23:02:46 +00:00
//战斗结束的插装
callback func(info.FightOverInfo)
2025-11-11 05:54:24 +00:00
}
func (f *FightC) Ownerid() uint32 {
return f.ownerID
}
func (f *FightC) GetInputByPlayer(c common.PlayerI, isOpposite bool) *input.Input {
// 判断当前玩家是否为我方玩家
isOurPlayer := c.GetInfo().UserID == f.ownerID
// 当isOurPlayer与isOpposite值不同时返回我方相同时返回对方
if isOurPlayer != isOpposite {
return f.Our
}
return f.Opp
}
func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *input.Input {
// 判断动作所属玩家是否为我方
isOurAction := c.GetPlayerID() == f.Our.Player.GetInfo().UserID
// 根据isOpposite决定是否返回相反方向的输入
if isOurAction == !isOpposite {
return f.Our
}
return f.Opp
}
// 玩家使用技能
func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity {
if f.Our.Player.GetInfo().UserID == c.GetInfo().UserID {
return f.Our.CurrentPet
} else {
return f.Opp.CurrentPet
}
}
func (f *FightC) GetOpp(c common.PlayerI) *input.Input {
return f.GetInputByPlayer(c, true)
}
// 获取随机数
func (f *FightC) GetRand() *rand.Rand {
return f.rand
}
// 获取随机数
func (f *FightC) IsFirst(play common.PlayerI) bool {
return f.TrueFirst.Player == play
2025-11-11 05:54:24 +00:00
}
func (f *FightC) Chat(c common.PlayerI, msg string) {
f.GetInputByPlayer(c, true).Player.SendPackCmd(50002, &user.ChatOutboundInfo{
SenderId: c.GetInfo().UserID,
SenderNickname: c.GetInfo().Nick,
Message: utils.RemoveLast(msg),
})
}
2025-11-11 05:54:24 +00:00
// 加载进度
func (f *FightC) LoadPercent(c common.PlayerI, percent int32) {
if f.Info.Mode == info.BattleMode.PET_MELEE {
return
}
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
return
}
f.GetInputByPlayer(c, true).Player.SendPackCmd(2441, &info.LoadPercentOutboundInfo{
2025-11-11 05:54:24 +00:00
Id: c.GetInfo().UserID,
Percent: uint32(percent),
})
2025-11-18 20:52:04 +00:00
2025-11-11 05:54:24 +00:00
}
var meetpet = make(map[int]model.PetInfo)
func (f *FightC) initplayer(c common.PlayerI) (*input.Input, errorcode.ErrorCode) {
2025-11-11 05:54:24 +00:00
if !c.CanFight() {
return nil, errorcode.ErrorCodes.ErrNoEligiblePokemon
2025-11-11 05:54:24 +00:00
}
in := input.NewInput(f, c)
in.AllPet = make([]*info.BattlePetEntity, 0)
in.InitAttackValue()
2025-11-11 05:54:24 +00:00
for i := 0; i < len(c.GetInfo().PetList); i++ {
2025-11-15 23:02:46 +00:00
in.AllPet = append(in.AllPet, info.CreateBattlePetEntity(c.GetInfo().PetList[i], f.rand))
2025-11-11 05:54:24 +00:00
}
in.SortPet()
if len(in.AllPet) == 0 {
return nil, errorcode.ErrorCodes.ErrNoEligiblePokemon
}
2025-11-11 05:54:24 +00:00
switch f.Info.Mode {
case info.BattleMode.SINGLE_MODE:
in.AllPet = in.AllPet[:1]
case info.BattleMode.PET_MELEE:
in.AllPet = make([]*info.BattlePetEntity, 0)
// if len(meetpet) == 0 {
// initmeetpet()
// }
r := service.NewMELEEService().Def()
if len(r) == 0 {
return nil, errorcode.ErrorCodes.ErrNoEligiblePokemon
}
for i, v := range r {
if v.Lv == 0 {
v.Lv = 100
}
// var effect int
// if len(v.Effect) > 0 {
// effect = int(v.Effect[0])
// }
pet := model.GenPetInfo(int(v.MonID), 24, int(v.Nature), int(v.Effect[0]), int(v.Lv), nil)
var color data.GlowFilter
err := json.Unmarshal([]byte(v.Color), &color)
if err == nil {
pet.ShinyInfo = append(pet.ShinyInfo, color)
}
if len(v.Prop) == 5 {
pet.Prop = [5]uint32(v.Prop)
}
if v.Hp != 0 {
pet.MaxHp = uint32(v.Hp)
}
if len(v.SKill) != 0 {
for i := 0; i < 4; i++ {
if v.SKill[i] != 0 {
pet.SkillList[i].ID = v.SKill[i]
}
}
}
pet.CatchTime = c.GetInfo().UserID + uint32(i)*1000000
pet.Cure()
in.AllPet = append(in.AllPet, info.CreateBattlePetEntity(*pet, f.rand))
}
// for i, v := range meetpet {
// v1 := v
// if len(in.AllPet) > 2 {
// break
// }
// v1.CatchTime = c.GetInfo().UserID + uint32(i)*1000000
// v1.Cure()
// in.AllPet = append(in.AllPet, info.CreateBattlePetEntity(v1, f.rand))
// }
//in.AllPet = in.AllPet[:3]
2025-11-11 05:54:24 +00:00
default:
}
in.CurrentPet = in.AllPet[0]
return in, 0
}
2025-11-11 05:54:24 +00:00
// RandomElfIDs 从1-2000中随机抽取n个不重复的精灵ID
func RandomElfIDs(n int) []int {
if n <= 0 || n > 2000 {
return nil
}
2025-11-11 05:54:24 +00:00
// 用map记录已抽取的ID避免重复
used := make(map[int]struct{}, n)
ids := make([]int, 0, n)
2025-11-11 05:54:24 +00:00
for len(ids) < n {
// 生成1-2000的随机数
2025-11-23 23:38:03 +00:00
id := grand.Intn(2000) + 1 // rand.Intn(2000)生成0-1999+1后为1-2000
2025-11-11 05:54:24 +00:00
// 检查是否已抽取
if _, exists := used[id]; !exists {
used[id] = struct{}{}
ids = append(ids, id)
2025-11-11 05:54:24 +00:00
}
}
return ids
}
func initfightready(in *input.Input) (info.FightUserInfo, []info.ReadyFightPetInfo) {
t := make([]info.ReadyFightPetInfo, len(in.AllPet))
userindo := info.FightUserInfo{
UserID: in.UserID,
Nick: in.Player.GetInfo().Nick,
}
2025-11-11 05:54:24 +00:00
for i := 0; i < len(in.AllPet); i++ {
2025-11-11 05:54:24 +00:00
err := copier.CopyWithOption(&t[i], &in.AllPet[i].Info, copier.Option{IgnoreEmpty: true, DeepCopy: true})
if err != nil {
panic(err)
}
2025-11-11 05:54:24 +00:00
}
return userindo, t
2025-11-11 05:54:24 +00:00
}
// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
func NewFight(p1, p2 common.PlayerI, fn func(info.FightOverInfo)) (*FightC, errorcode.ErrorCode) {
fmt.Println("NewFight", p1.GetInfo().UserID)
2025-11-11 05:54:24 +00:00
f := &FightC{}
f.ownerID = p1.GetInfo().UserID
2026-01-22 16:01:52 +00:00
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
2025-11-18 20:52:04 +00:00
f.callback = fn //战斗结束的回调
f.quit = make(chan struct{})
f.over = make(chan struct{})
2025-11-11 05:54:24 +00:00
f.StartTime = time.Now()
seed := f.StartTime.UnixNano() ^ int64(p1.GetInfo().UserID) ^ int64(p2.GetInfo().UserID) // ^ int64(f.Round) // 用异或运算混合多维度信息
f.rand = rand.New(rand.NewSource(seed))
2025-11-16 20:30:17 +00:00
f.Info = p1.Getfightinfo()
2025-11-18 20:52:04 +00:00
//这里应该挪到玩家初始化执行
2025-11-16 20:30:17 +00:00
f.ReadyInfo.Status = f.Info.Status
var err errorcode.ErrorCode
f.Our, err = f.initplayer(p1)
if err > 0 {
return nil, err
2025-11-15 23:02:46 +00:00
}
f.Opp, err = f.initplayer(p2)
if err > 0 {
return nil, err
}
2025-11-11 05:54:24 +00:00
2025-11-16 20:30:17 +00:00
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.Our)
f.ReadyInfo.OpponentInfo, f.ReadyInfo.OpponentPetList = initfightready(f.Opp)
var loadtime time.Duration = 120 * time.Second
//说明是PVE
f.Broadcast(func(ff *input.Input) {
ff.SetOPP(f.GetInputByPlayer(ff.Player, true))
})
f.FightStartOutboundInfo = f.buildFightStartInfo()
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
2025-11-11 05:54:24 +00:00
f.Opp.Finished = true //PVE 默认boss数据直接加载完成
loadtime = 60 * time.Second
//f.handleNPCFightSpecial(&fightStartInfo)
2025-11-11 05:54:24 +00:00
if f.Opp.Player.(*player.AI_player).CanCapture > 0 {
f.Opp.CanCapture = f.Opp.Player.(*player.AI_player).CanCapture
f.FightStartOutboundInfo.Info2.Catchable = 1 //可以捕捉就置1
}
f.Opp.AttackValue.Prop = f.Opp.Player.(*player.AI_player).Prop
f.FightStartOutboundInfo.Info2.Prop = f.Opp.AttackValue.Prop
}
f.Broadcast(func(ff *input.Input) {
2026-01-22 16:01:52 +00:00
ff.Player.SendPackCmd(2503, &f.ReadyInfo)
2026-01-27 06:40:49 +00:00
if p, ok := ff.Player.(*player.Player); ok {
p.Service.Info.Save(*p.Info)
}
})
cool.Cron.AfterFunc(loadtime, func() {
fmt.Println(f.Our.UserID, "战斗超时结算")
if !f.Our.Finished || !f.Opp.Finished { //如果有任一没有加载完成
2025-11-18 20:52:04 +00:00
f.closefight = true //阻止继续添加action
f.Reason = info.BattleOverReason.PlayerOffline
switch {
case !f.Opp.Finished: //邀请方没加载完成 先判断邀请方,如果都没加载完成,就算做房主胜利
f.WinnerId = f.Our.Player.GetInfo().UserID
case !f.Our.Finished: //被邀请方没加载完成
f.WinnerId = f.Opp.Player.GetInfo().UserID
}
f.Broadcast(func(ff *input.Input) {
//todo 将血量和技能pp传回enterturn
ff.Player.SendPackCmd(2506, &f.FightOverInfo)
2025-11-18 22:16:55 +00:00
ff.Player.QuitFight()
})
}
2025-11-11 05:54:24 +00:00
})
return f, 0
2025-11-11 05:54:24 +00:00
}
// 被击败的ID
func (f *FightC) IsWin(c *input.Input) bool {
2025-11-11 05:54:24 +00:00
for _, v := range f.GetInputByPlayer(c.Player, true).AllPet {
2025-11-11 05:54:24 +00:00
if !v.NotAlive { //如果存活
return false
}
}
return true
}
// 广播,并是否结束回合
func (f *FightC) Broadcast(t func(ff *input.Input)) {
t(f.Our)
t(f.Opp)
}
func (f *FightC) GetOverChan() chan struct{} {
return f.over
}
func (f *FightC) GetOverInfo() info.FightOverInfo {
return f.FightOverInfo
}