Files
bl/logic/controller/fight_塔.go
xinian c021b40fbe
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat: 增强踢人逻辑与BOSS脚本支持
优化踢人超时处理和僵尸连接清理,支持BOSS动作脚本并增加测试,修复事件匹配与战斗循环中的并发问题。
2026-04-05 21:59:22 +08:00

257 lines
7.5 KiB
Go

package controller
import (
"blazing/common/socket/errorcode"
"blazing/common/utils"
"blazing/logic/service/fight"
fightinfo "blazing/logic/service/fight/info"
"blazing/logic/service/player"
"blazing/logic/service/space/info"
configmodel "blazing/modules/config/model"
"blazing/modules/config/service"
"blazing/modules/player/model"
"sync/atomic"
"github.com/gogf/gf/v2/util/gconv"
"github.com/jinzhu/copier"
)
const (
towerCmdChoiceTrial uint32 = 2428
towerCmdChoiceBrave uint32 = 2414
towerCmdFightTrial uint32 = 2429
towerCmdFightBrave uint32 = 2415
towerCmdFightDark uint32 = 2425
towerTaskDark int = 110
towerTaskBrave int = 500
towerTaskTrial int = 600
)
type towerChoiceState struct {
currentLevel *uint32
maxLevel *uint32
service *service.TowerService
}
// 暗黑门进入boss
func (h Controller) FreshOpen(data *C2S_OPEN_DARKPORTAL, c *player.Player) (result *fight.S2C_OPEN_DARKPORTAL, err errorcode.ErrorCode) {
result = &fight.S2C_OPEN_DARKPORTAL{}
towerBosses := service.NewTower110Service().Boss(uint32(data.Level))
bossConfig, ok := firstTowerBossConfig(towerBosses)
if !ok {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
bosses := service.NewBossService().Get(bossConfig.BossIds[0])
if len(bosses) == 0 {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
result.CurBossID = uint32(bosses[0].MonID)
c.CurDark = uint32(data.Level)
defer c.GetSpace().LeaveMap(c)
return result, 0
}
// FreshChoiceFightLevel 处理玩家选择挑战模式(试炼之塔或勇者之塔)
func (h Controller) FreshChoiceFightLevel(data *C2S_FRESH_CHOICE_FIGHT_LEVEL, c *player.Player) (result *fight.S2C_FreshChoiceLevelRequestInfo, err errorcode.ErrorCode) {
result = &fight.S2C_FreshChoiceLevelRequestInfo{}
c.Info.CurrentFreshStage = utils.Max(c.Info.CurrentFreshStage, 1)
c.Info.CurrentStage = utils.Max(c.Info.CurrentStage, 1)
choiceState, ok := towerChoiceRuntime(c, data.Head.CMD)
if !ok {
return nil, errorcode.ErrorCodes.ErrSystemError
}
if data.Level > 0 {
if !canSelectTowerLevel(data.Level, *choiceState.maxLevel) {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
*choiceState.currentLevel = uint32(data.Level)
}
result.CurFightLevel = *choiceState.currentLevel
appendTowerBossPreview(&result.BossId, choiceState.service.Boss(*choiceState.currentLevel))
atomic.StoreUint32(&c.Canmon, 0)
defer c.GetSpace().LeaveMap(c)
return result, 0
}
func (h Controller) FreshLeaveFightLevel(data *FRESH_LEAVE_FIGHT_LEVEL, c *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
_ = data
defer c.GetSpace().EnterMap(c)
out := info.NewOutInfo()
copier.CopyWithOption(out, c.GetInfo(), copier.Option{DeepCopy: true})
return result, 0
}
func (h Controller) PetTawor(data *StartTwarInboundInfo, c *player.Player) (result *fight.S2C_ChoiceLevelRequestInfo, err errorcode.ErrorCode) {
if err = c.CanFight(); err != 0 {
return nil, err
}
bossList, currentLevel, taskID, ok := towerFightBosses(c, data.Head.CMD)
if !ok {
return nil, errorcode.ErrorCodes.ErrSystemError
}
currentBoss, ok := firstTowerBossConfig(bossList)
if !ok {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
result = &fight.S2C_ChoiceLevelRequestInfo{CurFightLevel: currentLevel}
appendTowerNextBossPreview(&result.BossID, bossList)
monsterInfo, bossScript, ok := buildTowerMonsterInfo(currentBoss)
if !ok {
return nil, errorcode.ErrorCodes.ErrPokemonNotExists
}
c.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE
c.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
ai := player.NewAI_player(monsterInfo)
ai.BossScript = bossScript
_, err = fight.NewFight(c, ai, c.GetPetInfo(100), ai.GetPetInfo(0), func(foi model.FightOverInfo) {
if foi.Reason != 0 || foi.WinnerId != c.Info.UserID {
return
}
handleTowerFightWin(c, data.Head.CMD, taskID, currentLevel)
})
return
}
func towerChoiceRuntime(c *player.Player, cmd uint32) (towerChoiceState, bool) {
switch cmd {
case towerCmdChoiceTrial:
return towerChoiceState{
currentLevel: &c.Info.CurrentFreshStage,
maxLevel: &c.Info.MaxFreshStage,
service: service.NewTower600Service(),
}, true
case towerCmdChoiceBrave:
return towerChoiceState{
currentLevel: &c.Info.CurrentStage,
maxLevel: &c.Info.MaxStage,
service: service.NewTower500Service(),
}, true
default:
return towerChoiceState{}, false
}
}
func towerFightBosses(c *player.Player, cmd uint32) ([]configmodel.BaseTowerConfig, uint32, int, bool) {
switch cmd {
case towerCmdFightTrial:
return service.NewTower600Service().Boss(c.Info.CurrentFreshStage, c.Info.CurrentFreshStage+1), c.Info.CurrentFreshStage, towerTaskTrial, true
case towerCmdFightBrave:
return service.NewTower500Service().Boss(c.Info.CurrentStage, c.Info.CurrentStage+1), c.Info.CurrentStage, towerTaskBrave, true
case towerCmdFightDark:
return service.NewTower110Service().Boss(c.CurDark), c.CurDark, towerTaskDark, true
default:
return nil, 0, 0, false
}
}
func canSelectTowerLevel(targetLevel uint, maxLevel uint32) bool {
return targetLevel == 1 || targetLevel <= uint(maxLevel)
}
func firstTowerBossConfig(bossList []configmodel.BaseTowerConfig) (configmodel.BaseTowerConfig, bool) {
if len(bossList) == 0 || len(bossList[0].BossIds) == 0 {
return configmodel.BaseTowerConfig{}, false
}
return bossList[0], true
}
func appendTowerBossPreview(dst *[]uint32, bossList []configmodel.BaseTowerConfig) {
bossConfig, ok := firstTowerBossConfig(bossList)
if !ok {
return
}
bosses := service.NewBossService().Get(bossConfig.BossIds[0])
for _, boss := range bosses {
*dst = append(*dst, uint32(boss.MonID))
}
}
func appendTowerNextBossPreview(dst *[]uint32, bossList []configmodel.BaseTowerConfig) {
if len(bossList) < 2 || len(bossList[1].BossIds) == 0 {
return
}
bosses := service.NewBossService().Get(bossList[1].BossIds[0])
for _, boss := range bosses {
*dst = append(*dst, uint32(boss.MonID))
}
}
func buildTowerMonsterInfo(towerBoss configmodel.BaseTowerConfig) (*model.PlayerInfo, string, bool) {
bosses := service.NewBossService().Get(towerBoss.BossIds[0])
if len(bosses) == 0 {
return nil, "", false
}
monsterInfo := &model.PlayerInfo{Nick: towerBoss.Name}
for i, boss := range bosses {
monster := model.GenPetInfo(int(boss.MonID), 24, int(boss.Nature), 0, int(boss.Lv), nil, 0)
if boss.Hp != 0 {
monster.Hp = uint32(boss.Hp)
monster.MaxHp = uint32(boss.Hp)
}
for statIdx, prop := range boss.Prop {
if prop != 0 {
monster.Prop[statIdx] = prop
}
}
for skillIdx := 0; skillIdx < len(monster.SkillList) && skillIdx < len(boss.SKill); skillIdx++ {
if boss.SKill[skillIdx] != 0 {
monster.SkillList[skillIdx].ID = boss.SKill[skillIdx]
}
}
effects := service.NewEffectService().Args(boss.Effect)
for _, effect := range effects {
monster.EffectInfo = append(monster.EffectInfo, model.PetEffectInfo{
Idx: uint16(effect.ID),
EID: gconv.Uint16(effect.Eid),
Args: gconv.Ints(effect.Args),
})
}
monster.CatchTime = uint32(i)
monsterInfo.PetList = append(monsterInfo.PetList, *monster)
}
return monsterInfo, bosses[0].Script, true
}
func handleTowerFightWin(c *player.Player, cmd uint32, taskID int, currentLevel uint32) {
c.TawerCompletedTask(taskID, int(currentLevel))
switch cmd {
case towerCmdFightTrial:
c.Info.CurrentFreshStage++
if c.Info.CurrentFreshStage >= c.Info.MaxFreshStage {
c.Info.MaxFreshStage = c.Info.CurrentFreshStage
}
case towerCmdFightBrave:
c.Info.CurrentStage++
if c.Info.CurrentStage >= c.Info.MaxStage {
c.Info.MaxStage = c.Info.CurrentStage
}
}
}