refactor: 删除战斗模块冗余代码,增加克洛斯地心入口

- 移除了 fight 包下多个冗余的结构体和接口定义
- 删除了未使用的战斗状态机和技能限制效果相关代码
- 移除了多余的 XML 注释内容
This commit is contained in:
2025-08-24 20:35:08 +08:00
parent 081f990110
commit cf74930e6e
10 changed files with 556 additions and 2 deletions

View File

@@ -0,0 +1,54 @@
package effect
import "github.com/tnnmigga/enum"
type EnumEffectTrigger string
var EffectTrigger = enum.New[struct {
OnBattleStart EnumEffectTrigger `enum:"OnBattleStart"` // 战斗开始时触发
BeforeSort EnumEffectTrigger `enum:"BeforeSort"` // 先手顺序判定前触发
BeforeUseSkillCheck EnumEffectTrigger `enum:"BeforeUseSkillCheck"` // 使用技能前检查PP、状态等
AfterUseSkillCheck EnumEffectTrigger `enum:"AfterUseSkillCheck"` // 使用技能检查后触发
BeforeMultiHit EnumEffectTrigger `enum:"BeforeMultiHit"` // 多段攻击开始前触发
BeforeHit EnumEffectTrigger `enum:"BeforeHit"` // 攻击命中前触发
OnCritPreDamage EnumEffectTrigger `enum:"OnCritPreDamage"` // 暴击判定成功且伤害计算前触发
PreDamage EnumEffectTrigger `enum:"PreDamage"` // 技能伤害计算前触发(增伤/减伤等)
OnHit EnumEffectTrigger `enum:"OnHit"` // 技能命中时触发
OnMiss EnumEffectTrigger `enum:"OnMiss"` // 技能未命中时触发
AfterAttacked EnumEffectTrigger `enum:"AfterAttacked"` // 被攻击后触发(受击判定)
OnDefeat EnumEffectTrigger `enum:"OnDefeat"` // 精灵被击败时触发
SkillUseEnd EnumEffectTrigger `enum:"SkillUseEnd"` // 技能使用结束后触发
OnBeforeCalculateDamage EnumEffectTrigger `enum:"OnBeforeCalculateDamage"` // 最终伤害计算前触发
OnDamage EnumEffectTrigger `enum:"OnDamage"` // 造成伤害时触发
Shield EnumEffectTrigger `enum:"Shield"` // 护盾值变化时触发
PostDamage EnumEffectTrigger `enum:"PostDamage"` // 伤害结算后触发(血量扣除后)
OnCritPostDamage EnumEffectTrigger `enum:"OnCritPostDamage"` // 暴击伤害结算后触发
OnTransform EnumEffectTrigger `enum:"OnTransform"` // 精灵变形/进化时触发
OnTransformEnd EnumEffectTrigger `enum:"OnTransformEnd"` // 变形/进化结束时触发
BeforeTransform EnumEffectTrigger `enum:"BeforeTransform"` // 变形/进化前触发
AfterTransform EnumEffectTrigger `enum:"AfterTransform"` // 变形/进化后触发
TurnStart EnumEffectTrigger `enum:"TurnStart"` // 回合开始时触发
TurnEnd EnumEffectTrigger `enum:"TurnEnd"` // 回合结束时触发
OnBeforeAddMark EnumEffectTrigger `enum:"OnBeforeAddMark"` // 添加印记前触发
OnAnyMarkAdded EnumEffectTrigger `enum:"OnAnyMarkAdded"` // 任何印记添加时触发
OnRemoveMark EnumEffectTrigger `enum:"OnRemoveMark"` // 移除印记时触发
OnMarkCreated EnumEffectTrigger `enum:"OnMarkCreated"` // 印记创建时触发
OnMarkDestroy EnumEffectTrigger `enum:"OnMarkDestroy"` // 印记销毁时触发
OnMarkDurationEnd EnumEffectTrigger `enum:"OnMarkDurationEnd"` // 印记持续回合结束时触发
OnStackBefore EnumEffectTrigger `enum:"OnStackBefore"` // 堆叠效果前触发
OnStack EnumEffectTrigger `enum:"OnStack"` // 堆叠效果触发
OnBeforeConsumeStack EnumEffectTrigger `enum:"OnBeforeConsumeStack"` // 消耗堆叠前触发
OnConsumeStack EnumEffectTrigger `enum:"OnConsumeStack"` // 消耗堆叠时触发
OnBeforeHeal EnumEffectTrigger `enum:"OnBeforeHeal"` // 治疗前触发
OnHeal EnumEffectTrigger `enum:"OnHeal"` // 治疗生效时触发
BeforeRageGain EnumEffectTrigger `enum:"BeforeRageGain"` // 增怒前触发
BeforeRageLoss EnumEffectTrigger `enum:"BeforeRageLoss"` // 减怒前触发
OnRageGain EnumEffectTrigger `enum:"OnRageGain"` // 增怒时触发
OnRageLoss EnumEffectTrigger `enum:"OnRageLoss"` // 减怒时触发
OnSwitchIn EnumEffectTrigger `enum:"OnSwitchIn"` // 精灵出战/上场时触发
OnSwitchOut EnumEffectTrigger `enum:"OnSwitchOut"` // 精灵下场时触发
OnOwnerSwitchIn EnumEffectTrigger `enum:"OnOwnerSwitchIn"` // 所属玩家精灵出战时触发
OnOwnerSwitchOut EnumEffectTrigger `enum:"OnOwnerSwitchOut"` // 所属玩家精灵下场时触发
BeforeEffect EnumEffectTrigger `enum:"BeforeEffect"` // 效果生效前触发
AfterEffect EnumEffectTrigger `enum:"AfterEffect"` // 效果生效后触发
}]()

View File

@@ -0,0 +1,215 @@
package effect
import (
"fmt"
"sort"
"github.com/badu/bus"
"github.com/tnnmigga/enum"
)
// ========================
// BattleMode 枚举
// ========================
type EnumBattleMode string
var BattleMode = enum.New[struct {
PVE EnumBattleMode `enum:"3"`
PVP EnumBattleMode `enum:"1"`
}]()
// ========================
// Effect 框架
// ========================
type ContextType interface {
SourceID() string
}
type EffectContext struct {
Parent ContextType
Trigger EnumEffectTrigger
Container *EffectContainer
Effect *Effect
Available bool
Success bool
Done bool
}
func (c *EffectContext) Stop() { c.Done = true }
func (c *EffectContext) SourceID() string {
if c.Container != nil {
return c.Container.ID
}
return "unknown"
}
type EffectFunc func(ctx *EffectContext, next func())
type Effect struct {
ID string
Priority int
Triggers []EnumEffectTrigger
Condition func(*EffectContext) bool
Apply EffectFunc
}
type EffectContainer struct {
ID string
Effects []*Effect
Subs []*bus.Listener[*EffectContext]
Battle *Battle
Parent ContextType
}
func NewEffectContainer(id string, effects []*Effect, battle *Battle, parent ContextType) *EffectContainer {
c := &EffectContainer{
ID: id,
Effects: effects,
Battle: battle,
Parent: parent,
}
// 自动订阅 triggers
for _, e := range effects {
for _, trig := range e.Triggers {
sub := battle.GetTopic(trig).Sub(func(ctx *EffectContext) {
if ctx.Available && e.Apply != nil {
ctx.Effect = e
c.executeWithPriority([]*EffectContext{ctx})
}
})
c.Subs = append(c.Subs, sub)
}
}
return c
}
// done/next 执行队列,按优先级
func (c *EffectContainer) executeWithPriority(queue []*EffectContext) {
sort.SliceStable(queue, func(i, j int) bool {
return queue[i].Effect.Priority > queue[j].Effect.Priority
})
var runNext func(idx int)
runNext = func(idx int) {
if idx >= len(queue) {
return
}
ctx := queue[idx]
if ctx.Available {
ctx.Effect.Apply(ctx, func() {
runNext(idx + 1)
})
} else {
runNext(idx + 1)
}
}
runNext(0)
}
// ========================
// Battle
// ========================
type Battle struct {
Turn int
topics map[EnumEffectTrigger]*bus.Topic[*EffectContext]
}
func NewBattle() *Battle {
allTriggers := []EnumEffectTrigger{
EffectTrigger.OnBattleStart,
EffectTrigger.BeforeSort,
EffectTrigger.BeforeUseSkillCheck,
EffectTrigger.AfterUseSkillCheck,
EffectTrigger.BeforeMultiHit,
EffectTrigger.BeforeHit,
EffectTrigger.OnCritPreDamage,
EffectTrigger.PreDamage,
EffectTrigger.OnHit,
EffectTrigger.OnMiss,
EffectTrigger.AfterAttacked,
EffectTrigger.OnDefeat,
EffectTrigger.SkillUseEnd,
EffectTrigger.OnBeforeCalculateDamage,
EffectTrigger.OnDamage,
EffectTrigger.Shield,
EffectTrigger.PostDamage,
EffectTrigger.OnCritPostDamage,
EffectTrigger.OnTransform,
EffectTrigger.OnTransformEnd,
EffectTrigger.BeforeTransform,
EffectTrigger.AfterTransform,
EffectTrigger.TurnStart,
EffectTrigger.TurnEnd,
EffectTrigger.OnBeforeAddMark,
EffectTrigger.OnAnyMarkAdded,
EffectTrigger.OnRemoveMark,
EffectTrigger.OnMarkCreated,
EffectTrigger.OnMarkDestroy,
EffectTrigger.OnMarkDurationEnd,
EffectTrigger.OnStackBefore,
EffectTrigger.OnStack,
EffectTrigger.OnBeforeConsumeStack,
EffectTrigger.OnConsumeStack,
EffectTrigger.OnBeforeHeal,
EffectTrigger.OnHeal,
EffectTrigger.BeforeRageGain,
EffectTrigger.BeforeRageLoss,
EffectTrigger.OnRageGain,
EffectTrigger.OnRageLoss,
EffectTrigger.OnSwitchIn,
EffectTrigger.OnSwitchOut,
EffectTrigger.OnOwnerSwitchIn,
EffectTrigger.OnOwnerSwitchOut,
EffectTrigger.BeforeEffect,
EffectTrigger.AfterEffect,
}
topics := make(map[EnumEffectTrigger]*bus.Topic[*EffectContext])
for _, trig := range allTriggers {
topics[trig] = bus.NewTopic[*EffectContext]()
}
return &Battle{Turn: 0, topics: topics}
}
func (b *Battle) GetTopic(trig EnumEffectTrigger) *bus.Topic[*EffectContext] {
return b.topics[trig]
}
func (b *Battle) NextTurn(containers []*EffectContainer) {
b.Turn++
fmt.Printf("=== 回合 %d 开始 ===\n", b.Turn)
b.PublishTrigger(EffectTrigger.TurnStart, containers)
fmt.Println("=== 玩家操作阶段 ===")
b.PublishTrigger(EffectTrigger.TurnEnd, containers)
fmt.Printf("=== 回合 %d 结束 ===\n\n", b.Turn)
}
func (b *Battle) PublishTrigger(trigger EnumEffectTrigger, containers []*EffectContainer) {
for _, c := range containers {
for _, e := range c.Effects {
for _, t := range e.Triggers {
if t == trigger {
ctx := &EffectContext{
Parent: c.Parent,
Trigger: trigger,
Container: c,
Effect: e,
Available: true,
}
b.GetTopic(trigger).Pub(ctx)
}
}
}
}
}
// ========================
// 示例
// ========================
type Player struct {
ID string
}
func (p *Player) SourceID() string { return p.ID }

View File

@@ -0,0 +1,40 @@
package effect
import (
"fmt"
"testing"
"time"
)
func Test_mainTT(t *testing.T) {
battle := NewBattle()
player := &Player{ID: "player1"}
container := NewEffectContainer("container1", []*Effect{
{
ID: "startEffect",
Priority: 10,
Triggers: []EnumEffectTrigger{EffectTrigger.TurnStart},
Apply: func(ctx *EffectContext, next func()) {
fmt.Println("回合开始效果: 增加护盾")
next()
},
},
{
ID: "endEffect",
Priority: 5,
Triggers: []EnumEffectTrigger{EffectTrigger.TurnEnd},
Apply: func(ctx *EffectContext, next func()) {
fmt.Println("回合结束效果: 恢复怒气")
next()
},
},
}, battle, player)
for i := 0; i < 2; i++ {
battle.NextTurn([]*EffectContainer{container})
time.Sleep(500 * time.Millisecond)
}
fmt.Println("测试 BattleMode 枚举:", BattleMode.PVE, BattleMode.PVP)
}

View File

@@ -6,6 +6,6 @@ import "github.com/tnnmigga/enum"
type EnumBattleMode int
var BattleMode = enum.New[struct {
PVE EnumBattleMode // 玩家 vs AI
PVP EnumBattleMode // 玩家 vs 玩家
PVE EnumBattleMode `enum:"3"`
PVP EnumBattleMode `enum:"1"`
}]()

View File

@@ -0,0 +1,244 @@
# ELO评级系统
## 概述
为匹配框架引入了ELO评级系统为每个规则集单独维护ELO评级提供更精确的玩家技能评估和匹配质量
## 核心特性
### 🎯 规则集分离
- 每个规则集如休闲模式竞技模式独立维护ELO评级
- 玩家在不同规则集下有不同的ELO分数
- 支持无限扩展新的规则集
### 📊 标准ELO算法
- 基于经典ELO评级系统
- 期望得分计算`1 / (1 + 10^((对手ELO - 自己ELO) / 400))`
- 新ELO = 旧ELO + K * (实际得分 - 期望得分)
- 支持胜利(1)失败(0)平局(0.5)
### 动态K因子
- **新手** (< 30): K = 32 (快速调整)
- **普通** (30-100): K = 24 (中等调整)
- **老手** (> 100): K = 16 (稳定调整)
### 🛡 安全边界
- ELO范围100 - 3000
- 初始ELO1200
- 自动记录历史最高ELO
## 数据库设计
### player_elo_ratings
```sql
CREATE TABLE player_elo_ratings (
player_id TEXT NOT NULL, -- 玩家ID
rule_set_id TEXT NOT NULL, -- 规则集ID
elo_rating INTEGER DEFAULT 1200, -- 当前ELO评级
games_played INTEGER DEFAULT 0, -- 游戏场次
wins INTEGER DEFAULT 0, -- 胜利次数
losses INTEGER DEFAULT 0, -- 失败次数
draws INTEGER DEFAULT 0, -- 平局次数
highest_elo INTEGER DEFAULT 1200, -- 历史最高ELO
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (player_id, rule_set_id)
);
```
### 核心函数
- `get_or_create_player_elo()` - 获取或创建ELO记录
- `update_player_elo()` - 更新单个玩家ELO
- `batch_update_player_elos()` - 批量更新ELO战斗结束
- `get_elo_leaderboard()` - 获取排行榜
## API端点
### 排行榜
```
GET /api/v1/elo/leaderboard/:ruleSetId?limit=50&offset=0
```
### 玩家ELO信息
```
GET /api/v1/elo/player/:playerId/:ruleSetId
GET /api/v1/elo/player/:playerId # 所有规则集
```
### 统计信息
```
GET /api/v1/elo/statistics/:ruleSetId
```
### 战斗预测
```
GET /api/v1/elo/predict/:playerAId/:playerBId/:ruleSetId
```
### 配置信息
```
GET /api/v1/elo/config
GET /api/v1/elo/win-rate/:eloDifference
```
## 系统集成
### 战斗结束自动更新
战斗结束时`battleReportService` 会自动
1. 获取两个玩家的当前ELO
2. 根据战斗结果计算新ELO
3. 原子性批量更新数据库
4. 记录详细日志
```typescript
// 在 battleReportService.ts 中
if (battleData.ruleSetId && battleResult !== 'abandoned') {
await eloService.processBattleEloUpdate(
battleData.playerAId,
battleData.playerBId,
winnerId,
battleData.ruleSetId
)
}
```
### 匹配系统集成
匹配系统已支持规则集分离ELO系统可以
- 基于ELO进行更精确的匹配
- 预测战斗结果概率
- 提供匹配质量评估
## 部署步骤
### 1. 数据库迁移
在Supabase SQL编辑器中执行
```sql
-- 执行ELO系统迁移
\i packages/database/sql/06_add_elo_system.sql
```
### 2. 服务器更新
ELO系统已集成到现有服务器中无需额外配置
### 3. 测试验证
```bash
# 测试ELO计算逻辑
cd packages/server && npx tsx src/test-elo.ts
# 启动服务器测试API
node dist/cli.js server --port 8102
```
## 使用示例
### 获取排行榜
```bash
curl "http://localhost:8102/api/v1/elo/leaderboard/casual_standard_ruleset?limit=10"
```
### 查看玩家ELO
```bash
curl "http://localhost:8102/api/v1/elo/player/player123/casual_standard_ruleset"
```
### 预测战斗结果
```bash
curl "http://localhost:8102/api/v1/elo/predict/player1/player2/competitive_ruleset"
```
## 配置选项
ELO系统支持运行时配置调整
```typescript
const eloService = new EloService(repository, calculationService)
// 更新配置
eloService.updateEloConfig({
initialElo: 1500,
kFactor: {
newbie: 40,
normal: 20,
veteran: 10
},
minElo: 200,
maxElo: 2800
})
```
## 监控和日志
### 日志记录
- ELO更新详细日志
- 计算过程追踪
- 错误处理和恢复
### 性能监控
- 数据库查询优化
- 批量更新性能
- API响应时间
## 未来扩展
### 可能的增强功能
1. **季度重置** - 定期重置ELO评级
2. **衰减机制** - 长期不活跃玩家ELO衰减
3. **匹配优化** - 基于ELO的智能匹配
4. **成就系统** - ELO里程碑奖励
5. **数据分析** - ELO分布统计和趋势分析
### 扩展新规则集
添加新规则集只需
1. 在规则系统中定义新规则集
2. ELO系统自动支持无需代码修改
3. 玩家首次游戏时自动创建ELO记录
## 故障排除
### 常见问题
1. **ELO记录不存在**
- 系统会自动创建初始记录
- 检查规则集ID是否正确
2. **ELO更新失败**
- 检查数据库连接
- 查看服务器日志
- 验证战斗数据完整性
3. **API响应错误**
- 确认数据库迁移已执行
- 检查API路由注册
- 验证请求参数格式
### 调试工具
```bash
# 测试ELO计算
npx tsx packages/server/src/test-elo.ts
# 检查数据库表
SELECT * FROM player_elo_ratings LIMIT 10;
# 查看API状态
curl "http://localhost:8102/api/v1/elo/config"
```

View File

@@ -8,6 +8,7 @@ des 鼠标移上去的提示
<!-- 从场景FromMap进入本场景时玩家将出现在 (PosX, PosY)
<Entries>节点下的表示几个进出点玩家出现的坐标
<changeMapComp>节点下表示切换场景组件
<funComp>节点下表示点击然后走近触发某个动作
<autoComp>节点下表示不需要点击走到碰撞区域就触发的动作
-->