refactor(nono): 优化非诺相关结构体字段命名与初始化方式

将 nono 包中多个结构体的字段初始化方式改为键值对形式,提升可读性。
同时统一字段命名风格,增强代码一致性。

feat(fight): 移除冗余战斗循环逻辑并调整部分依赖引用

删除 fightc.go 中的 battleLoop 方法及相关逻辑,简化战斗流程控制。
移除未使用的包引用,并调整结构体成员顺序以提高清晰度。

refactor(player): 修改数据库字段类型为 jsonb 提升灵活性

将 Player 结构体中的 Data 字段由 text 类型修改为 jsonb 类型,
便于后续进行更灵活的数据存储与查询操作。
```
This commit is contained in:
2025-11-01 18:36:21 +08:00
parent ea4ca98e49
commit 2b3c4b6ccd
8 changed files with 210 additions and 186 deletions

1
.gitignore vendored
View File

@@ -37,3 +37,4 @@ public/logic
logic/logic2
login/login
login/login
logic/logic1

View File

@@ -13,12 +13,12 @@ func (h *Controller) NonoFollowOrHome(data *nono.NonoFollowOrHomeInInfo, c *play
c.Info.NONO.Flag = data.Flag
result = &nono.NonoFollowOutInfo{
data.Head.UserID,
data.Flag,
data.Flag,
"",
0,
0,
UserID: data.Head.UserID,
SuperStage: data.Flag,
Flag: data.Flag,
Nick: "",
Color: 0,
Power: 0,
}
return
@@ -43,8 +43,8 @@ func (h *Controller) GetNonoInfo(data *nono.NonoInboundInfo, c *player.Player) (
func (h *Controller) SwitchFlying(data *nono.SwitchFlyingInboundInfo, c *player.Player) (result *nono.SwitchFlyingOutboundInfo, err errorcode.ErrorCode) { //这个时候player应该是空的
result = &nono.SwitchFlyingOutboundInfo{
data.Head.UserID,
data.Type,
UserId: data.Head.UserID,
Type: data.Type,
}
return

Binary file not shown.

View File

@@ -3,7 +3,6 @@ package action
import (
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"time"
"github.com/tnnmigga/enum"
)
@@ -120,15 +119,3 @@ func (e *OverTimeAction) GetInfo() info.FightOverInfo {
func (e *OverTimeAction) Priority() int {
return int(PlayerOperations.PlayerOffline)
}
// PlayerOfflineAction 玩家掉线的战斗动作
type PlayerOfflineAction struct {
BaseAction
OfflineTime time.Time // 掉线时间
Reason info.FightOverInfo
}
// Priority 返回动作优先级
func (p *PlayerOfflineAction) Priority() int {
return int(PlayerOperations.PlayerOffline)
}

View File

@@ -6,7 +6,6 @@ import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/player"
"blazing/modules/blazing/model"
"fmt"
"math/rand"
@@ -20,8 +19,8 @@ import (
)
type FightC struct {
Info info.NoteReadyToFightInfo
Info info.NoteReadyToFightInfo
IsReady bool
ownerID uint32 // 战斗发起者ID
Our *input.Input //始终等于房主ID
Opp *input.Input //对手ID
@@ -194,6 +193,7 @@ func NewFight(mode, status info.EnumBattleMode, p1 common.PlayerI, p2 common.Pla
f.Broadcast(func(ff *input.Input) {
ff.Player.SendNoteReadyToFightInfo(f.Info)
})
}()
@@ -234,167 +234,6 @@ func (f *FightC) Broadcast(t func(ff *input.Input)) {
}
// 战斗回合循环
func (f *FightC) battleLoop() {
f.actionChan = make(chan action.BattleActionI, 2) // 初始化全局操作通道
fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime)
//战斗开始前操作
for {
if f.closefight { //回合数超过250,战斗平局结束f.Round > 250 ||
close(f.actionChan)
break
}
f.Round++ //回合数自增
actions := make(map[uint32]action.BattleActionI) // 每个玩家一条记录
timeout := time.After(60 * time.Second)
for len(actions) < 2 {
select {
case paction, ok := <-f.actionChan:
// 只接受有效玩家 ID\
if paction == nil {
continue
}
if !ok {
fmt.Println("战斗结束")
break
}
if f.closefight { //回合数超过250,战斗平局结束f.Round > 250 ||
break
}
if paction.GetPlayerID() != f.Our.Player.GetInfo().UserID && paction.GetPlayerID() != f.Opp.Player.GetInfo().UserID {
continue
}
if _, isExpelled := paction.(*action.ActiveSwitchAction); isExpelled {
if f.GetInputByAction(paction, false).CanChange {
//如果是被动切换,不计入回合结算
f.GetInputByAction(paction, false).CanChange = false
continue
}
}
if paction.GetPlayerID() != 0 {
if f.Info.Status == info.BattleStatus.FIGHT_WITH_BOSS || f.Info.Status == info.BattleStatus.FIGHT_WITH_NPC {
//AI的action实质上就是放技能如果阻止掉比如中毒那就也不能逃跑
f.GetInputByAction(paction, true).GetAction(f.Our)
}
}
// 如果该玩家已经提交过,就忽略重复动作
if _, exists := actions[uint32(paction.GetPlayerID())]; exists {
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", paction.GetPlayerID())
continue
}
actions[uint32(paction.GetPlayerID())] = paction
fmt.Println("玩家 执行动作", paction.GetPlayerID(), paction.Priority())
case <-timeout:
if _, exists := actions[f.Our.Player.GetInfo().UserID]; !exists {
f.Over(f.Opp.Player, info.BattleOverReason.PlayerOVerTime)
}
if _, exists := actions[f.Opp.Player.GetInfo().UserID]; !exists {
f.Over(f.Our.Player, info.BattleOverReason.PlayerOVerTime)
}
}
}
// 双方动作齐了,取出来结算
//todo 如果一方没有选择,实际上就是后端判断PP是否还有,前端是直接发的
p1Action := actions[f.Our.Player.GetInfo().UserID]
p2Action := actions[f.Opp.Player.GetInfo().UserID]
fmt.Println("开始结算回合")
// 统一赋值,减少重复代码
var BattleActionI [2]action.BattleActionI
BattleActionI[0], BattleActionI[1] = f.Compare(p1Action, p2Action)
switch faction := BattleActionI[0].(type) {
case *action.PlayerOfflineAction: // 单方掉线
f.Broadcast(func(ff *input.Input) {
ff.Player.SendFightEndInfo(faction.Reason) // 广播逃跑原因
})
f.closefight = true
case *action.EscapeAction: // 优先逃跑
f.Broadcast(func(ff *input.Input) {
ff.Player.SendFightEndInfo(faction.Reason) // 广播逃跑原因
})
f.closefight = true
case *action.ActiveSwitchAction: // 切换上场的, 切换方放弃出手
// 如果后手不是技能,替换成空技能(放弃出手)
if _, ok := BattleActionI[1].(*action.SelectSkillAction); !ok {
f.enterturn(nil, nil) //双方都不出手
} else {
//后手方先手,先手方放弃出手
f.enterturn(BattleActionI[1].(*action.SelectSkillAction), nil)
}
case *action.UseItemAction: // 使用道具
switch {
case faction.ItemID >= 30001 && faction.ItemID <= 300010: // 胶囊
tt, ok := f.Our.Player.(*player.Player)
mo, ism := f.Opp.Player.(*player.AI_player)
if ok && ism && mo.CanCapture {
ok, res := f.Our.Capture(f.Opp.CurrentPet, faction.ItemID, -1)
if ok {
fmt.Println(res)
tt.Service.PetAdd(*f.Opp.CurrentPet.Info)
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{
CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
PetId: uint32(f.Opp.CurrentPet.ID),
})
tt.SendFightEndInfo(info.FightOverInfo{
WinnerId: f.ownerID,
})
f.closefight = true
} else {
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
}
} else {
tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
}
fmt.Println("ItemID 在范围内")
case faction.ItemID == 300001:
fmt.Println("ItemID 是 300001")
default:
fmt.Println("ItemID 不在指定范围内")
}
if _, ok := BattleActionI[1].(*action.SelectSkillAction); !ok {
f.enterturn(nil, nil)
} else {
f.enterturn(BattleActionI[1].(*action.SelectSkillAction), nil)
}
default: // 双方都是技能或者默认情况
f.enterturn(BattleActionI[0].(*action.SelectSkillAction), BattleActionI[1].(*action.SelectSkillAction))
}
}
}
// 处理技能攻击逻辑
func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *action.SelectSkillAction) {

196
logic/service/fight/loop.go Normal file
View File

@@ -0,0 +1,196 @@
package fight
import (
"blazing/logic/service/common"
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"blazing/logic/service/player"
"fmt"
"time"
)
func (f *FightC) battleLoop() {
f.actionChan = make(chan action.BattleActionI, 2)
fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime)
ourID := f.Our.Player.GetInfo().UserID
oppID := f.Opp.Player.GetInfo().UserID
for {
if f.closefight {
close(f.actionChan)
break
}
f.Round++
fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round)
actions := f.collectPlayerActions(ourID, oppID)
if f.closefight {
break
}
f.resolveRound(actions[ourID], actions[oppID])
}
fmt.Println("战斗循环结束")
}
// 收集玩家动作(含超时判定)
func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.BattleActionI {
actions := make(map[uint32]action.BattleActionI)
timeout := time.After(60 * time.Second)
for len(actions) < 2 {
select {
case paction, ok := <-f.actionChan:
if !ok || f.closefight {
return actions
}
if paction == nil {
continue
}
pid := paction.GetPlayerID()
if pid != ourID && pid != oppID {
continue
}
// 避免重复提交
if _, exists := actions[pid]; exists {
fmt.Printf("玩家%d 已经提交过动作,忽略重复\n", pid)
continue
}
// 被动切换处理(不计入本回合)
if as, ok := paction.(*action.ActiveSwitchAction); ok {
if f.GetInputByAction(as, false).CanChange {
f.GetInputByAction(as, false).CanChange = false
continue
}
}
// AI自动技能
if pid != 0 && (f.Info.Status == info.BattleStatus.FIGHT_WITH_BOSS ||
f.Info.Status == info.BattleStatus.FIGHT_WITH_NPC) {
f.GetInputByAction(paction, true).GetAction(f.Our)
}
actions[pid] = paction
fmt.Println("玩家执行动作:", pid, paction.Priority())
case <-timeout:
f.handleTimeout(ourID, oppID, actions)
return actions
}
}
return actions
}
// 超时处理逻辑
func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.BattleActionI) {
for _, pid := range []uint32{ourID, oppID} {
if _, exists := actions[pid]; exists {
continue
}
fmt.Printf("玩家%d 超时\n", pid)
player := f.GetInputByPlayer(f.getPlayerByID(pid), true).Player
if !f.GetInputByPlayer(f.getPlayerByID(pid), false).Finished {
f.UseSkill(player, 0) //卡加载,给对方也一个action
}
f.Over(f.getPlayerByID(pid), info.BattleOverReason.PlayerOVerTime)
}
}
// 根据动作类型执行一回合结算
func (f *FightC) resolveRound(p1Action, p2Action action.BattleActionI) {
if p1Action == nil || p2Action == nil {
fmt.Println("某方未选择动作,自动跳过结算")
return
}
fmt.Println("开始结算回合")
// 动作优先级排序
b1, b2 := f.Compare(p1Action, p2Action)
switch a := b1.(type) {
case *action.EscapeAction:
f.Broadcast(func(ff *input.Input) {
ff.Player.SendFightEndInfo(a.Reason)
})
f.closefight = true
case *action.ActiveSwitchAction:
if _, ok := b2.(*action.SelectSkillAction); ok {
f.enterturn(b2.(*action.SelectSkillAction), nil)
} else {
f.enterturn(nil, nil)
}
case *action.UseItemAction:
f.handleItemAction(a, b2)
default:
f.handleSkillActions(b1, b2)
}
}
// 使用道具的逻辑封装
func (f *FightC) handleItemAction(a *action.UseItemAction, other action.BattleActionI) {
switch {
case a.ItemID >= 30001 && a.ItemID <= 300010:
our, ok1 := f.Our.Player.(*player.Player)
opp, ok2 := f.Opp.Player.(*player.AI_player)
if ok1 && ok2 && opp.CanCapture {
ok, res := f.Our.Capture(f.Opp.CurrentPet, a.ItemID, -1)
if ok {
fmt.Println(res)
our.Service.PetAdd(*f.Opp.CurrentPet.Info)
our.CatchPetInfo(info.CatchMonsterOutboundInfo{
CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
PetId: uint32(f.Opp.CurrentPet.ID),
})
our.SendFightEndInfo(info.FightOverInfo{WinnerId: f.ownerID})
f.closefight = true
} else {
our.CatchPetInfo(info.CatchMonsterOutboundInfo{})
}
}
case a.ItemID == 300001:
fmt.Println("ItemID 是 300001")
default:
fmt.Println("ItemID 不在指定范围内")
}
if _, ok := other.(*action.SelectSkillAction); ok {
f.enterturn(other.(*action.SelectSkillAction), nil)
} else {
f.enterturn(nil, nil)
}
}
// 双方都是技能时的结算逻辑
func (f *FightC) handleSkillActions(a1, a2 action.BattleActionI) {
s1, _ := a1.(*action.SelectSkillAction)
s2, _ := a2.(*action.SelectSkillAction)
switch {
case s1 == nil || s1.SkillEntity == nil:
f.enterturn(s2, nil)
case s2 == nil || s2.SkillEntity == nil:
f.enterturn(s1, nil)
default:
f.enterturn(s1, s2)
}
}
// 根据玩家ID返回对应对象
func (f *FightC) getPlayerByID(id uint32) common.PlayerI {
if id == f.Our.Player.GetInfo().UserID {
return f.Our.Player
}
return f.Opp.Player
}

View File

@@ -113,6 +113,7 @@ func (f *FightC) Capture(c common.PlayerI, id uint32) {
// 战斗准备
func (f *FightC) ReadyFight(c common.PlayerI) {
rett := info.FightStartOutboundInfo{}
copier.Copy(&rett.Info1, &f.Info.OurPetList[0]) // 复制自己的信息

View File

@@ -12,7 +12,7 @@ const TableNamePlayerInfo = "player_info"
type Player struct {
*cool.Model
PlayerID uint64 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
Data string `gorm:"type:text;not null;comment:'全部数据'" json:"data"`
Data string `gorm:"type:jsonb;not null;comment:'全部数据'" json:"data"`
}
type PlayerEX struct {
Player