Merge branch 'main' of github.com:72wo/blazing

This commit is contained in:
2025-08-26 10:22:54 +08:00
20 changed files with 81 additions and 1270 deletions

View File

@@ -1,360 +0,0 @@
package entity
// import (
// "context"
// "database/sql"
// "errors"
// "fmt"
// "log"
// "sync"
// "time"
// )
// // Server 游戏服务器核心类,管理玩家、星球和游戏逻辑
// type Server struct {
// players map[int64]*entity.Player
// planets map[int64]*planet.Planet
// mutex sync.RWMutex
// gameRepo repo.GameResourceRepo
// serverRepo repo.ServerRepo
// accountRepo repo.AccountRepo
// playerInfoRepo repo.PlayerInfoRepo
// playerItemRepo repo.PlayerItemInfoRepo
// petRepo repo.PetRepo
// }
// // NewServer 创建新的游戏服务器实例
// func NewServer(
// gameRepo repo.GameResourceRepo,
// serverRepo repo.ServerRepo,
// accountRepo repo.AccountRepo,
// playerInfoRepo repo.PlayerInfoRepo,
// playerItemRepo repo.PlayerItemInfoRepo,
// petRepo repo.PetRepo,
// ) *Server {
// s := &Server{
// players: make(map[int64]*entity.Player),
// planets: make(map[int64]*planet.Planet),
// gameRepo: gameRepo,
// serverRepo: serverRepo,
// accountRepo: accountRepo,
// playerInfoRepo: playerInfoRepo,
// playerItemRepo: playerItemRepo,
// petRepo: petRepo,
// }
// s.initializePlanets()
// return s
// }
// // initializePlanets 初始化所有星球
// func (s *Server) initializePlanets() {
// maps := s.gameRepo.GetAllMaps()
// for _, config := range maps {
// planet := s.generatePlanet(config)
// s.planets[planet.GetId()] = planet
// }
// }
// // generatePlanet 根据地图配置生成星球
// func (s *Server) generatePlanet(config *mapInfo.MapXmlModel) *planet.Planet {
// entries := config.GetEntries()
// positions := make(map[int64]structs.Point)
// if len(entries) == 0 {
// positions = make(map[int64]structs.Point)
// } else {
// for _, entry := range entries {
// positions[entry.GetFromMap()] = s.generatePoint(entry)
// }
// }
// return planet.NewPlanet(
// s,
// int64(config.GetId()),
// config.GetName(),
// structs.Point{X: config.GetX(), Y: config.GetY()},
// positions,
// s.gameRepo.CanMapRefresh(config.GetId()),
// )
// }
// // generatePoint 从入口配置生成点坐标
// func (s *Server) generatePoint(xml *mapInfo.EntryXmlModel) structs.Point {
// return structs.Point{X: xml.GetPosX(), Y: xml.GetPosY()}
// }
// // GetPlayer 获取玩家信息
// func (s *Server) GetPlayer(accountID int64) (*entity.Player, error) {
// s.mutex.RLock()
// defer s.mutex.RUnlock()
// player, exists := s.players[accountID]
// if !exists {
// return nil, errors.New("玩家不存在")
// }
// return player, nil
// }
// // GetPlanet 获取星球信息
// func (s *Server) GetPlanet(planetID int64) (*planet.Planet, error) {
// s.mutex.RLock()
// defer s.mutex.RUnlock()
// planet, exists := s.planets[planetID]
// if !exists {
// return nil, errors.New("星球不存在")
// }
// return planet, nil
// }
// // GetDefaultPlanet 获取默认星球
// func (s *Server) GetDefaultPlanet() (*planet.Planet, error) {
// s.mutex.RLock()
// defer s.mutex.RUnlock()
// planet, exists := s.planets[1] // 假设1是默认星球ID
// if !exists {
// return nil, errors.New("未找到默认星球")
// }
// return planet, nil
// }
// // PlayerEnter 玩家加入服务器
// func (s *Server) PlayerEnter(player *entity.Player) {
// s.mutex.Lock()
// defer s.mutex.Unlock()
// s.players[player.GetAccountID()] = player
// }
// // PlayerOffline 玩家离线处理
// func (s *Server) PlayerOffline(player *entity.Player) {
// s.mutex.Lock()
// defer s.mutex.Unlock()
// // 清理缓存会话数据
// if err := s.accountRepo.RemoveSessionID(player.GetSessionID()); err != nil {
// log.Printf("清除会话ID失败: %v", err)
// }
// // 清理登录绑定服务器
// if err := s.serverRepo.CancelRecordAccount(player.GetAccountID()); err != nil {
// log.Printf("取消账号绑定失败: %v", err)
// }
// // 从玩家列表中移除
// delete(s.players, player.GetAccountID())
// }
// // BroadcastMessage 广播消息给所有在线玩家
// func (s *Server) BroadcastMessage(message *net.OutboundMessage) {
// s.mutex.RLock()
// defer s.mutex.RUnlock()
// for _, player := range s.players {
// if player.IsOnline() {
// player.SendMessage(message)
// }
// }
// }
// // BroadcastMessageWithFilter 按条件广播消息给在线玩家
// func (s *Server) BroadcastMessageWithFilter(message *net.OutboundMessage, filter func(*entity.Player) bool) {
// s.mutex.RLock()
// defer s.mutex.RUnlock()
// for _, player := range s.players {
// if player.IsOnline() && filter(player) {
// player.SendMessage(message)
// }
// }
// }
// // GeneratePetEntity 生成宠物实体
// func (s *Server) GeneratePetEntity(
// playerID int64,
// petTypeID int,
// individualValue int16,
// nature int,
// abilityTypeEnum int,
// isShiny bool,
// level int,
// ) (*pet.PetEntity, error) {
// if level < 1 || level > 100 {
// return nil, fmt.Errorf("精灵等级必须在1到100之间, level: %d", level)
// }
// petInfo, err := s.gameRepo.GetMonsterByID(petTypeID)
// if err != nil {
// return nil, fmt.Errorf("无效的精灵ID, pet_id: %d, 错误: %v", petTypeID, err)
// }
// firstSkillInfo, err := s.gameRepo.GetPetFirstSkillID(petTypeID, level)
// if err != nil {
// return nil, fmt.Errorf("精灵没有初始技能, pet_id: %d, 错误: %v", petTypeID, err)
// }
// zero := int16(0)
// pet := pet.NewPetEntityBuilder()
// .WithAsset(petInfo)
// .WithLevelToExp(s.gameRepo.GetLevelToExp())
// .WithPlayerID(playerID)
// .WithCapturePlayerID(playerID)
// .WithCaptureTime(time.Now().Unix())
// .WithCaptureMap(0) // 假设0是默认地图ID
// .WithCaptureRect(0) // 假设0是默认区域
// .WithCaptureLevel(level)
// .WithIndividualValue(individualValue)
// .WithNature(nature)
// .WithAbilityTypeEnum(abilityTypeEnum)
// .WithIsShiny(isShiny)
// .WithLevel(level)
// .WithCurrentExp(0)
// .WithEvHp(zero)
// .WithEvAttack(zero)
// .WithEvDefense(zero)
// .WithEvSpecialAttack(zero)
// .WithEvSpecialDefense(zero)
// .WithEvSpeed(zero)
// .WithSkill1ID(firstSkillInfo[0].GetId())
// .WithSkill1Pp(firstSkillInfo[0].GetMaxPp())
// .WithSkill2ID(firstSkillInfo[1].GetId())
// .WithSkill2Pp(firstSkillInfo[1].GetMaxPp())
// .WithSkill3ID(firstSkillInfo[2].GetId())
// .WithSkill3Pp(firstSkillInfo[2].GetMaxPp())
// .WithSkill4ID(firstSkillInfo[3].GetId())
// .WithSkill4Pp(firstSkillInfo[3].GetMaxPp())
// .WithIndividualGuarantee(0)
// .WithNatureGuarantee(0)
// .Build()
// if err := s.CalculatePetPanel(pet); err != nil {
// return nil, err
// }
// return pet, nil
// }
// // CalculatePetPanel 计算宠物面板属性
// func (s *Server) CalculatePetPanel(petEntity *pet.PetEntity) error {
// natureInfo, err := s.gameRepo.GetNatureInfoByID(petEntity.GetNature())
// if err != nil {
// return fmt.Errorf("无效的性格ID, nature: %d, 错误: %v", petEntity.GetNature(), err)
// }
// hp := util.CalculatePetHPPanelSize(
// petEntity.GetAsset().GetHp(),
// petEntity.GetIndividualValue(),
// petEntity.GetLevel(),
// petEntity.GetEvHp(),
// )
// attack := util.CalculatePetPanelSize(
// petEntity.GetAsset().GetAtk(),
// petEntity.GetIndividualValue(),
// petEntity.GetLevel(),
// petEntity.GetEvHp(),
// natureInfo.GetAttackCorrect(),
// )
// defense := util.CalculatePetPanelSize(
// petEntity.GetAsset().GetDef(),
// petEntity.GetIndividualValue(),
// petEntity.GetLevel(),
// petEntity.GetEvHp(),
// natureInfo.GetDefenseCorrect(),
// )
// specialAttack := util.CalculatePetPanelSize(
// petEntity.GetAsset().GetSpAtk(),
// petEntity.GetIndividualValue(),
// petEntity.GetLevel(),
// petEntity.GetEvHp(),
// natureInfo.GetSaCorrect(),
// )
// specialDefense := util.CalculatePetPanelSize(
// petEntity.GetAsset().GetSpDef(),
// petEntity.GetIndividualValue(),
// petEntity.GetLevel(),
// petEntity.GetEvHp(),
// natureInfo.GetSdCorrect(),
// )
// speed := util.CalculatePetPanelSize(
// petEntity.GetAsset().GetSpd(),
// petEntity.GetIndividualValue(),
// petEntity.GetLevel(),
// petEntity.GetEvHp(),
// natureInfo.GetSpeedCorrect(),
// )
// petEntity.SetMaxHp(hp)
// petEntity.SetCurrentHp(hp)
// petEntity.SetAttack(attack)
// petEntity.SetDefense(defense)
// petEntity.SetSpecialAttack(specialAttack)
// petEntity.SetSpecialDefense(specialDefense)
// petEntity.SetSpeed(speed)
// return nil
// }
// // SavePlayer 保存玩家信息到数据库
// func (s *Server) SavePlayer(player *entity.Player) (bool, error) {
// ctx := context.Background()
// tx, err := s.playerInfoRepo.BeginTransaction(ctx)
// if err != nil {
// return false, fmt.Errorf("开始事务失败: %v", err)
// }
// defer func() {
// if r := recover(); r != nil {
// tx.Rollback()
// log.Printf("保存玩家时发生panic: %v", r)
// } else if err != nil {
// tx.Rollback()
// } else {
// tx.Commit()
// }
// }()
// playerID := player.GetGameID()
// pets := player.GetPetBag().GetUsedPets()
// items := player.GetItemBag().GetItems()
// // 保存玩家信息
// if err := s.playerInfoRepo.Save(ctx, tx, player); err != nil {
// return false, fmt.Errorf("保存玩家信息失败: %v", err)
// }
// // 保存精灵信息
// if err := s.petRepo.SaveAll(ctx, tx, playerID, pets); err != nil {
// return false, fmt.Errorf("保存精灵信息失败: %v", err)
// }
// // 保存玩家背包信息
// if err := s.playerItemRepo.SaveAll(ctx, tx, playerID, items); err != nil {
// return false, fmt.Errorf("保存背包信息失败: %v", err)
// }
// return true, nil
// }
// // Destroy 销毁服务器,清理资源
// func (s *Server) Destroy() error {
// // 保存所有玩家数据
// s.mutex.RLock()
// for _, player := range s.players {
// go func(p *entity.Player) {
// if _, err := s.SavePlayer(p); err != nil {
// log.Printf("保存玩家 %d 数据失败: %v", p.GetAccountID(), err)
// }
// }(player)
// }
// s.mutex.RUnlock()
// log.Println("Destroying server ...")
// return nil
// }

View File

@@ -50,6 +50,7 @@ func (s *Server) OnClose(c gnet.Conn, _ error) (action gnet.Action) {
t := v.GetPlayer()
if t != nil {
glog.Debug(context.Background(), t.UserID, "断开连接")
t.IsLogin = false
cool.Mainplayer.Delete(t.UserID)
share.ShareManager.DeleteUserOnline(t.UserID) //设置用户登录服务器
}

View File

@@ -42,7 +42,7 @@ func (h *Controller) MapEnter(data *maps.InInfo, c *entity.Player) (result *maps
case <-ticker.C:
// 刷新当前地图的怪物
if !c.IsFighting && c.MapId != 0 {
spawnMonsters(currentMap, c)
spawnMonsters(c)
}
}
@@ -85,28 +85,58 @@ func (h *Controller) MapList(data *maps.ListMapPlayerInboundInfo, c *entity.Play
}
// 刷怪具体实现
func spawnMonsters(mapID int, c *entity.Player) {
func spawnMonsters(c *entity.Player) {
// 获取当前地图的怪物配置
if mservice.NewMonsterService().GetId(c.MapId) == 0 {
if c == nil || mservice.NewMonsterService().GetId(c.MapId) == 0 { //用户离线
return
}
if !c.IsLogin {
defer func() {
if c.StopChan != nil {
close(c.StopChan)
c.StopChan = nil
}
}()
}
// 创建数据包
tt := handler.NewTomeeHeader(2004, c.UserID)
t1 := genMonster(c.MapId)
c.SendPack(tt.Pack(&t1))
}
func genMonster(id uint32) maps.OgreInfo {
// 设置怪物信息
t1 := maps.OgreInfo{}
//copy(t1.Data[:], monsterConfig) // 使用地图对应的怪物配置
var localMenu = make([]int, 0)
for i := 7; i <= 448; i++ {
if bitsCount(i) == 3 {
localMenu = append(localMenu, i)
}
}
for i := 0; i < 3; i++ {
ttt := maps.OgrePetInfo{}
ttt.Id = mservice.NewMonsterService().GetId(c.MapId)
ttt.Id = mservice.NewMonsterService().GetId(id)
ttt.Shiny = uint32(i + 1)
//t1.Data[i] = mservice.NewMonsterService().GetId(c.MapId)
t1.Data[i] = ttt
}
// 发送数据包
c.SendPack(tt.Pack(&t1))
return t1
}
// 计算整数的二进制1的个数对应Java的Integer.bitCount
func bitsCount(n int) int {
count := 0
for n > 0 {
count += n & 1
n >>= 1
}
return count
}

View File

@@ -1,140 +0,0 @@
package node
import "fmt"
// 基础上下文
type EffectContext struct {
Turn int // 当前回合数
Actor string // 当前行动者(谁在出手)
Target string // 当前目标(技能攻击对象)
Skill string // 使用的技能
Extra map[string]interface{} // 临时附加信息(状态、标记、计算中间值等)
}
// 节点类型
type NodeFunc func(ctx *EffectContext, next func())
type EffectNode struct {
Name string
Exec NodeFunc
}
// 节点管理器
type NodeRunner struct {
nodes []*EffectNode
index int
ctx *EffectContext
}
func NewNodeRunner(ctx *EffectContext, nodes []*EffectNode) *NodeRunner {
return &NodeRunner{
nodes: nodes,
index: 0,
ctx: ctx,
}
}
// 执行下一个节点
func (r *NodeRunner) Run() {
if r.index >= len(r.nodes) {
return
}
current := r.nodes[r.index]
r.index++
current.Exec(r.ctx, r.Run)
}
// 跳过当前节点
func (r *NodeRunner) Skip() {
r.index++
r.Run()
}
// ------------------------
// 定义战斗流程节点
// ------------------------
func BuildBattleNodes() []*EffectNode {
return []*EffectNode{
{Name: "战斗开始", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("OnBattleStart")
next()
}},
{Name: "登场/切换", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("OnSwitchIn / OnSwitchOut / OnTransform")
next()
}},
{Name: "回合开始", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("TurnStart")
next()
}},
{Name: "操作阶段", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("选择技能 / 使用道具 / 切换")
next()
}},
{Name: "先手判定", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("先手权判定")
next()
}},
{Name: "先手出手-前置", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("BeforeUseSkillCheck / BeforeHit / OnMiss")
next()
}},
{Name: "先手出手-技能命中", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("技能命中判定 / 初始伤害公式 / 红伤数值计算")
next()
}},
{Name: "先手出手-技能效果结算", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("技能效果结算 / 魂印 / 套装 / 回合类效果")
next()
}},
{Name: "先手出手-伤害结算", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("最终伤害生效 / 体力扣除")
next()
}},
{Name: "先手出手-行动结束", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("行动后效果 / 额外行动 / 机盖弹伤 / 出手结束效果")
next()
}},
{Name: "后手出手", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("后手节点同先手节点")
next()
}},
{Name: "回合结束后①", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("回合结束后通用时点① / 桃园回血 / 回合扣减①")
next()
}},
{Name: "回合结束后②", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("回合结束后通用时点② / 战争猎魔 / 16年魂印续航")
next()
}},
{Name: "死亡判定", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("死亡结算 / 保护机制")
next()
}},
{Name: "击败/未击败判定", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("击败触发效果 / 未击败触发效果")
next()
}},
{Name: "进入下回合", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("进入下回合,流程重新开始")
next()
}},
}
}
// ------------------------
// 测试运行
// ------------------------
func TestBattleFlow() {
ctx := &EffectContext{
Turn: 1,
Actor: "PlayerA",
Target: "PlayerB",
Skill: "火球",
Extra: map[string]interface{}{},
}
nodes := BuildBattleNodes()
runner := NewNodeRunner(ctx, nodes)
runner.Run()
}

View File

@@ -1,40 +0,0 @@
package node
import "github.com/tnnmigga/enum"
// ================= BaseEvent =================
// EnumBaseEvent 定义了战斗中可能触发的基础事件类型。
// 这些事件通常作为 Effect 的触发点,例如攻击前、受到伤害后等。
type EnumBaseEvent string
var BaseEvent = enum.New[struct {
Attack EnumBaseEvent `enum:"attack"` // 攻击事件
Damage EnumBaseEvent `enum:"damage"` // 伤害事件
Heal EnumBaseEvent `enum:"heal"` // 治疗事件
Buff EnumBaseEvent `enum:"buff"` // Buff/状态效果事件
Turn EnumBaseEvent `enum:"turn"` // 回合开始或结束事件
Death EnumBaseEvent `enum:"death"` // 死亡事件
Skill EnumBaseEvent `enum:"skill"` // 技能释放事件
}]()
// ================= HookPhase =================
// EnumHookPhase 定义了事件触发的阶段。
// 在同一个事件中某些效果可能需要在事件发生前Before触发
// 也可能需要在事件发生后After触发。
type EnumHookPhase string
var HookPhase = enum.New[struct {
Before EnumHookPhase `enum:"before"` // 事件发生前触发
After EnumHookPhase `enum:"after"` // 事件发生后触发
}]()
// ================= LifeType =================
// EnumLifeType 定义了效果Effect的生命周期。
// 用来决定效果是持续多个回合、按次数消耗,还是永久存在。
type EnumLifeType string
var LifeType = enum.New[struct {
TurnBased EnumLifeType `enum:"turn_based"` // 按回合数存续如中毒3回合
CountBased EnumLifeType `enum:"count_based"` // 按次数存续如可抵挡2次攻击
Permanent EnumLifeType `enum:"permanent"` // 永久存在(如种族特性/被动)
}]()

View File

@@ -1,233 +0,0 @@
package battle
import "fmt"
// ==================== 基础类型 ====================
type BaseEvent string
type HookPhase string
type LifeType string
const (
Before HookPhase = "before"
After HookPhase = "after"
TurnBased LifeType = "turn_based"
CountBased LifeType = "count_based"
Permanent LifeType = "permanent"
)
type EffectContext struct {
Actor string
Target string
Skill string
Extra map[string]interface{}
Success bool
}
type Effect struct {
Name string
Trigger string
LifeType LifeType
Duration int
Phase HookPhase
Handler func(ctx *EffectContext) bool
}
type EffectNode struct {
Name string
Exec func(ctx *EffectContext, next func())
}
type EffectManager struct {
nodes []*EffectNode
effects map[string][]*Effect
}
func NewEffectManager(nodes []*EffectNode) *EffectManager {
return &EffectManager{
nodes: nodes,
effects: make(map[string][]*Effect),
}
}
func (m *EffectManager) RegisterEffect(node string, eff *Effect) {
m.effects[node] = append(m.effects[node], eff)
}
func (m *EffectManager) ExecuteNode(nodeName string, ctx *EffectContext) {
if effs, ok := m.effects[nodeName]; ok {
for _, eff := range effs {
if eff.Handler != nil {
eff.Handler(ctx)
}
}
}
}
func (m *EffectManager) RunBattle(ctx *EffectContext) {
var i int
var execNext func()
execNext = func() {
if i >= len(m.nodes) {
return
}
node := m.nodes[i]
i++
node.Exec(ctx, execNext)
}
execNext()
}
// ==================== 节点定义 ====================
func BuildBattleNodes() []*EffectNode {
return []*EffectNode{
{Name: "战斗开始", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("OnBattleStart")
next()
}},
{Name: "登场/切换", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("OnSwitchIn / OnSwitchOut / OnTransform")
next()
}},
{Name: "回合开始", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("TurnStart")
next()
}},
{Name: "操作阶段", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("选择技能 / 使用道具 / 切换")
next()
}},
{Name: "先手判定", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("先手权判定")
next()
}},
{Name: "先手出手-前置", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("BeforeUseSkillCheck / BeforeHit / OnMiss")
next()
}},
{Name: "先手出手-技能命中", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("技能命中判定 / 初始伤害公式 / 红伤数值计算")
next()
}},
{Name: "先手出手-技能效果结算", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("技能效果结算 / 魂印 / 套装 / 回合类效果")
next()
}},
{Name: "先手出手-伤害结算", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("最终伤害生效 / 体力扣除")
next()
}},
{Name: "先手出手-行动结束", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("行动后效果 / 额外行动 / 机盖弹伤 / 出手结束效果")
next()
}},
{Name: "后手出手", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("后手节点同先手节点")
next()
}},
{Name: "回合结束后①", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("回合结束后通用时点① / 桃园回血 / 回合扣减①")
next()
}},
{Name: "回合结束后②", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("回合结束后通用时点② / 战争猎魔 / 16年魂印续航")
next()
}},
{Name: "死亡判定", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("死亡结算 / 保护机制")
next()
}},
{Name: "击败/未击败判定", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("击败触发效果 / 未击败触发效果")
next()
}},
{Name: "进入下回合", Exec: func(ctx *EffectContext, next func()) {
fmt.Println("进入下回合,流程重新开始")
next()
}},
}
}
// ==================== 注册45个原始效果 ====================
func RegisterOriginalEffects(manager *EffectManager) {
// 战斗开始类
manager.RegisterEffect("战斗开始", &Effect{
Name: "天生特性",
LifeType: Permanent,
Phase: Before,
Handler: func(ctx *EffectContext) bool {
fmt.Println(ctx.Actor, "天生特性触发")
return true
},
})
manager.RegisterEffect("战斗开始", &Effect{
Name: "战斗开始印记",
LifeType: Permanent,
Phase: Before,
Handler: func(ctx *EffectContext) bool {
fmt.Println(ctx.Actor, "战斗开始印记触发")
return true
},
})
// 登场/切换类
manager.RegisterEffect("登场/切换", &Effect{
Name: "登场魂印",
LifeType: CountBased,
Duration: 2,
Phase: Before,
Handler: func(ctx *EffectContext) bool {
fmt.Println(ctx.Actor, "登场魂印触发")
return true
},
})
// 回合开始
manager.RegisterEffect("回合开始", &Effect{
Name: "回合开始Buff",
LifeType: TurnBased,
Duration: 3,
Phase: Before,
Handler: func(ctx *EffectContext) bool {
fmt.Println(ctx.Actor, "回合开始Buff生效")
return true
},
})
// 先手出手节点示例
manager.RegisterEffect("先手出手-前置", &Effect{
Name: "先手准备效果",
LifeType: CountBased,
Duration: 1,
Phase: Before,
Handler: func(ctx *EffectContext) bool {
fmt.Println(ctx.Actor, "先手前置效果触发")
return true
},
})
manager.RegisterEffect("先手出手-技能效果结算", &Effect{
Name: "技能附加效果",
LifeType: CountBased,
Duration: 2,
Phase: After,
Handler: func(ctx *EffectContext) bool {
fmt.Println(ctx.Actor, "技能附加效果生效")
return true
},
})
// 回合结束后①
manager.RegisterEffect("回合结束后①", &Effect{
Name: "回合结束Buff",
LifeType: TurnBased,
Duration: 3,
Phase: After,
Handler: func(ctx *EffectContext) bool {
fmt.Println(ctx.Actor, "回合结束后Buff触发")
return true
},
})
// 你可以继续按此格式将原45个效果依次映射到16节点
// 节点选择 Before/AfterLifeType选择 TurnBased/CountBased/Permanent
}

View File

@@ -1,87 +0,0 @@
package over
import "sync"
// 战斗结束原因池,用于管理战斗结束的各种原因数据
type BattleOverPool struct {
dataMap map[EnumBattleOverReason]BattleOverData
size int
value BattleOverData
mutex sync.RWMutex // 用于并发安全
}
// 初始化战斗结束原因池
func NewBattleOverPool() *BattleOverPool {
return &BattleOverPool{
dataMap: make(map[EnumBattleOverReason]BattleOverData),
}
}
// 添加战斗结束原因
func (p *BattleOverPool) PushReason(reason EnumBattleOverReason, data BattleOverData) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.dataMap[reason] = data
p.value = data
p.size++
}
// 获取并移除指定的战斗结束原因
func (p *BattleOverPool) PopReason(reason EnumBattleOverReason) BattleOverData {
p.mutex.Lock()
defer p.mutex.Unlock()
data, exists := p.dataMap[reason]
if exists {
delete(p.dataMap, reason)
p.size--
}
return data
}
// 获取并移除所有战斗结束原因
func (p *BattleOverPool) PopReasons() []BattleOverData {
p.mutex.Lock()
defer p.mutex.Unlock()
reasons := make([]BattleOverData, 0, p.size)
for _, data := range p.dataMap {
reasons = append(reasons, data)
}
p.dataMap = make(map[EnumBattleOverReason]BattleOverData)
p.size = 0
p.value = nil
return reasons
}
// 获取最后添加的战斗结束原因
func (p *BattleOverPool) PopReasonLast() BattleOverData {
p.mutex.Lock()
defer p.mutex.Unlock()
data := p.value
p.value = nil
if p.size > 0 {
p.size--
}
// 清空map
if p.size == 0 {
p.dataMap = make(map[EnumBattleOverReason]BattleOverData)
}
return data
}
// 判断战斗是否应该结束
func (p *BattleOverPool) IsShouldOver() bool {
p.mutex.RLock()
defer p.mutex.RUnlock()
return p.size > 0
}
// 获取当前战斗结束原因的数量
func (p *BattleOverPool) Size() int {
p.mutex.RLock()
defer p.mutex.RUnlock()
return p.size
}

View File

@@ -1,60 +0,0 @@
package over
import (
"github.com/tnnmigga/enum"
)
// 战斗结束原因数据接口
type BattleOverData interface{}
// 玩家离线数据
type PlayerOfflineData struct {
// 可以根据需要添加字段
PlayerID int64
}
// 玩家逃脱数据
type PlayerEscapeData struct {
// 可以根据需要添加字段
PlayerID int64
}
// 玩家捕获成功数据
type PlayerCaptureSuccessData struct {
// 可以根据需要添加字段
CaptorID int64
TargetID int64
CaptureTime int64
}
// 默认结束数据
type DefaultEndData struct {
// 可以根据需要添加字段
Reason string
}
// 战斗结束原因枚举
type EnumBattleOverReason int
var BattleOverReason = enum.New[struct {
PlayerOffline EnumBattleOverReason `enum:"1"` //掉线
PlayerEscape EnumBattleOverReason `enum:"2"` //逃跑
PlayerCaptureSuccess EnumBattleOverReason `enum:"3"` //捕捉成功
DefaultEnd EnumBattleOverReason `enum:"4"` //默认结束
}]()
// 获取对应的类型
func (e EnumBattleOverReason) DataType() interface{} {
switch e {
case BattleOverReason.PlayerOffline:
return PlayerOfflineData{}
case BattleOverReason.PlayerEscape:
return PlayerEscapeData{}
case BattleOverReason.PlayerCaptureSuccess:
return PlayerCaptureSuccessData{}
case BattleOverReason.DefaultEnd:
return DefaultEndData{}
default:
return nil
}
}

View File

@@ -1,148 +0,0 @@
package effect
import (
"blazing/logic/service/fight/battle/node"
"sort"
"github.com/tnnmigga/enum"
)
// ========================
// 上下文:一次效果执行环境
// ========================
type EffectContext struct {
Parent string // 上下文来源(比如 "Skill"、"Buff"、"Passive"
Trigger node.EnumEffectTrigger // 当前触发的节点
Container *EffectContainer // 效果容器(通常挂在 Actor 身上)
Effect *Effect // 当前正在执行的 Effect
Available bool // 是否可用
Success bool // 是否执行成功
Done bool // 是否中止后续执行
}
// ========================
// Effect: 单个效果
// ========================
type Effect struct {
ID uint32 // 唯一标识
Trigger node.EnumEffectTrigger // 触发节点
Priority int // 执行优先级,数值越大越先执行
LifeType EnumLifeType //回合效果 是否可持续 继承到下一直精灵
Duration int // 持续回合/次0 = 即时生效,>0 = 回合数 ,负数是永久)
Stacks int // 当前层数
MaxStack int // 最大叠加层数
Handler func(ctx *EffectContext, next func()) bool // 执行逻辑,返回 true 表示继续保留 //TODO 内部判断是否切换了精灵,是否满足条件
}
// ================= LifeType =================
type EnumLifeType int
var LifeType = enum.New[struct {
TurnBased EnumLifeType `enum:"1"` // 回合数限制
CountBased EnumLifeType `enum:"2"` // 次数限制
}]()
// ========================
// 容器:存放多个效果
// ========================
type EffectContainer struct {
//GlobalEffects []*Effect // 全局常驻/回合/次数效果
Effects []*Effect //effects 实际上全局就是effect无限回合
}
// 添加效果
func (c *EffectContainer) AddEffect(e *Effect) {
// 如果已有同 ID 的效果,尝试叠加
for _, eff := range c.Effects {
if eff.ID == e.ID {
if eff.Stacks < eff.MaxStack {
eff.Stacks++
}
return
}
}
// 否则新加入
c.Effects = append(c.Effects, e)
}
// 添加全局效果
func (c *EffectContainer) AddGlobalEffects(e *Effect) {
// 如果已有同 ID 的效果,尝试叠加
for _, eff := range c.GlobalEffects {
if eff.ID == e.ID {
if eff.Stacks < eff.MaxStack {
eff.Stacks++
}
return
}
}
// 否则新加入
c.GlobalEffects = append(c.GlobalEffects, e)
}
// 触发执行
func (c *EffectContainer) Trigger(trigger node.EnumEffectTrigger, ctx *EffectContext) {
var candidates []*Effect
for _, eff := range c.Effects {
if eff.Trigger == trigger {
candidates = append(candidates, eff)
}
}
// 按优先级排序
sort.SliceStable(candidates, func(i, j int) bool {
return candidates[i].Priority > candidates[j].Priority
})
// 执行
for _, eff := range candidates {
ctx.Effect = eff
keep := eff.Apply(ctx)
if !keep {
// 持续回合结束 / 返回 false 的 effect 删除
c.removeEffect(eff)
}
if ctx.Done {
break // 被拦截
}
}
}
// 每回合结束时调用,用于处理持续时间
func (c *EffectContainer) Tick() {
var remain []*Effect
for _, eff := range c.Effects {
if eff.Duration > 0 {
eff.Duration--
}
if eff.Duration != 0 { // 保留 (负数表示永久)
remain = append(remain, eff)
}
}
c.Effects = remain
}
// 删除
func (c *EffectContainer) removeEffect(e *Effect) {
var remain []*Effect
for _, eff := range c.Effects {
if eff != e {
remain = append(remain, eff)
}
}
c.Effects = remain
}
// 清理过期效果
func (c *EffectContainer) Cleanup() {
var alive []*Effect
for _, e := range c.Effects {
if e.Duration != 0 {
alive = append(alive, e)
}
}
c.Effects = alive //挂载到普通effect
}

View File

@@ -1,32 +0,0 @@
package base
import (
"blazing/logic/service/fight/battle/node"
"fmt"
)
type BurnEffect struct {
life LifeCycle
}
func NewBurnEffect(turns int) *BurnEffect {
return &BurnEffect{life: NewTurnLife(turns)}
}
func (b *BurnEffect) Trigger() node.EnumEffectTrigger {
return node.EffectTrigger.TurnEnd // 每回合结束触发掉血
}
func (b *BurnEffect) Apply(ctx *node.EffectContext, next func()) {
fmt.Printf("[%s] 在回合结束时受灼烧伤害!\n", ctx.Target)
b.life.Use() // 消耗一次触发次数(如果你定义为按次数存活)
next()
}
func (b *BurnEffect) Tick() {
b.life.Tick()
}
func (b *BurnEffect) Alive() bool {
return b.life.Alive()
}

View File

@@ -1,21 +0,0 @@
package impl
import (
"blazing/logic/service/fight/battle/node"
"blazing/logic/service/fight/battle/skill/effect"
"fmt"
)
var burn = &effect.Effect{
Name: "Burn",
Trigger: node.EnumEffectTrigger{
Event: BaseEvent.Damage, // ✅ 类型安全
Phase: HookPhase.After,
},
LifeType: LifeType.TurnBased,
Duration: 3,
Handler: func(ctx *EffectContext, next func()) {
fmt.Printf("[%s] 灼烧 %s\n", ctx.Actor, ctx.Target)
next()
},
}

View File

@@ -1,76 +0,0 @@
package effect
import (
"blazing/logic/service/fight/battle/node"
"github.com/tnnmigga/enum"
)
type EffectManager struct {
containers map[string]*EffectContainer
}
func NewEffectManager() *EffectManager {
return &EffectManager{
containers: make(map[string]*EffectContainer),
}
}
// 添加效果
func (m *EffectManager) AddEffect(owner string, e *Effect) {
if _, ok := m.containers[owner]; !ok {
m.containers[owner] = &EffectContainer{}
}
m.containers[owner].AddEffect(e)
}
// 触发事件,按顺序链式执行效果
func (m *EffectManager) Trigger(owner string, trigger node.EnumEffectTrigger, ctx *EffectContext) {
container, ok := m.containers[owner]
if !ok {
return
}
var idx int
var next func()
next = func() {
if idx >= len(container.Effects) {
return
}
e := container.Effects[idx]
idx++
// 判断触发器匹配
if e.Trigger == trigger {
e.Handler(ctx, next)
if e.LifeType == e.LifeType {
e.Duration--
}
} else {
// 不匹配直接跳过
next()
}
}
next()
container.Cleanup()
}
type EnumBaseEvent string
var BaseEvent = enum.New[struct {
Attack EnumBaseEvent `enum:"attack"`
Damage EnumBaseEvent `enum:"damage"`
Heal EnumBaseEvent `enum:"heal"`
Buff EnumBaseEvent `enum:"buff"`
Turn EnumBaseEvent `enum:"turn"`
Death EnumBaseEvent `enum:"death"`
Skill EnumBaseEvent `enum:"skill"`
}]()
// ================= HookPhase =================
type EnumHookPhase string
var HookPhase = enum.New[struct {
Before EnumHookPhase `enum:"before"`
After EnumHookPhase `enum:"after"`
}]()

View File

@@ -23,7 +23,6 @@ var PlayerOperations = enum.New[struct {
type BattleAction struct {
Priority EnumPlayerOperation //优先级本质上是action的itoa
//ramdom *random.RandomXS128
ctx context.Context
}

View File

@@ -4,7 +4,7 @@ import "context"
type BattleInputSourceEntity struct {
FightUserInfo
ctx context.Context
ctx context.Context //输入源的上下文
}
// 新建一个宠物

View File

@@ -58,30 +58,6 @@ type BattlePetEntity struct {
skills [4]*BattleSkillEntity // 技能槽最多4个技能
// // 特殊属性
// Perseverance int // 毅力值:抵消致命伤
// Stubborn bool // 顽强特性
// StubbornProbability int // 顽强触发概率
// Revival bool // 回神特性
// RevivalProbability int // 回神触发概率
// Definitely int // 必定命中概率
// Dodge int // 闪避概率
// // 护盾相关
// MaxShield int64 // 最大护盾值
// Shield int64 // 当前护盾值
// CountShield int // 次数盾
// Level int64
// // 属性变化回合
// //petAttributeRound map[BattleRoundStarEffect]int // 属性变化类型 -> 剩余回合
// petImmunityEffectIds map[int]int // 免疫效果ID -> 剩余回合 (-1表示永久)
// petImmunityBuffs map[string]int // 免疫buff效果ID -> 剩余回合 (-1表示永久)
// IsDead bool // 是否死亡
// otherRates map[DamageMultiplierZone]decimal.Decimal // 各伤害乘算区倍率使用高精度decimal
// 战斗开始时拥有的特殊buff
//battleStartHavingBuffs []buff.BattleBuffInterface
}
// calculateRealValue 计算实际属性值根据状态变化计算实际值

View File

@@ -3,7 +3,8 @@ package info
import (
element "blazing/common/data/Element"
"blazing/common/data/xml/skill"
"blazing/logic/service/fight/battle/random"
"blazing/common/utils/random"
"context"
"fmt"
"strconv"
@@ -104,41 +105,6 @@ func (s *BattleSkillEntity) Type() element.ElementType {
return element.ElementType(s.Move.Type)
}
// UseOnce 使用一次技能消耗1点PP
func (s *BattleSkillEntity) UseOnce() *BattleSkillEntity {
if !s.InfinityPP {
s.PP--
s.correctPP()
}
return s
}
// AddPP 增加PP值
func (s *BattleSkillEntity) AddPP(value int) *BattleSkillEntity {
if !s.InfinityPP {
s.PP += value
s.correctPP()
}
return s
}
// MinusPP 减少PP值
func (s *BattleSkillEntity) MinusPP(value int) *BattleSkillEntity {
if !s.InfinityPP {
s.PP -= value
s.correctPP()
}
return s
}
// 修正PP值确保在0到MaxPP之间
func (s *BattleSkillEntity) correctPP() {
if s.PP < 0 {
s.PP = 0
} else if s.PP > s.MaxPP {
s.PP = s.MaxPP
}
}
func (u *BattleSkillEntity) NewBattleAction(ctx context.Context) {
ret := BattleAction{}

View File

@@ -1,7 +1,7 @@
package info
import (
"blazing/logic/service/fight/battle/random"
"blazing/common/utils/random"
"github.com/tnnmigga/enum"
)
@@ -21,3 +21,39 @@ var BattleMode = enum.New[struct {
PVE EnumBattleMode `enum:"3"`
PVP EnumBattleMode `enum:"1"`
}]()
// 玩家离线数据
type PlayerOfflineData struct {
// 可以根据需要添加字段
PlayerID int64
}
// 玩家逃脱数据
type PlayerEscapeData struct {
// 可以根据需要添加字段
PlayerID int64
}
// 玩家捕获成功数据
type PlayerCaptureSuccessData struct {
// 可以根据需要添加字段
CaptorID int64
TargetID int64
CaptureTime int64
}
// 默认结束数据
type DefaultEndData struct {
// 可以根据需要添加字段
Reason string
}
// 战斗结束原因枚举
type EnumBattleOverReason int
var BattleOverReason = enum.New[struct {
PlayerOffline EnumBattleOverReason `enum:"1"` //掉线
PlayerEscape EnumBattleOverReason `enum:"2"` //逃跑
PlayerCaptureSuccess EnumBattleOverReason `enum:"3"` //捕捉成功
DefaultEnd EnumBattleOverReason `enum:"4"` //默认结束
}]()

View File

@@ -1,7 +1,7 @@
package info
import (
"blazing/logic/service/fight/battle/random"
"blazing/common/utils/random"
"context"
"time"
@@ -9,7 +9,7 @@ import (
type BattleContainer1V1 struct {
BattleContext
ownerid uint32
ownerid uint32 // 房主ID
}
// 返回房主信息

View File

@@ -1,4 +1,4 @@
package start
package fight
import (
"fmt"