refactor: 重构战斗系统为统一动作包结构
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
This commit is contained in:
@@ -1,99 +0,0 @@
|
|||||||
# 屎山代码分析报告
|
|
||||||
|
|
||||||
## 总体评估
|
|
||||||
|
|
||||||
- **质量评分**: 31.03/100
|
|
||||||
- **质量等级**: 🌸 偶有异味 - 基本没事,但是有伤风化
|
|
||||||
- **分析文件数**: 203
|
|
||||||
- **代码总行数**: 20972
|
|
||||||
|
|
||||||
## 质量指标
|
|
||||||
|
|
||||||
| 指标 | 得分 | 权重 | 状态 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| 状态管理 | 4.84 | 0.15 | ✓✓ |
|
|
||||||
| 循环复杂度 | 6.28 | 0.25 | ✓✓ |
|
|
||||||
| 命名规范 | 25.00 | 0.10 | ✓ |
|
|
||||||
| 错误处理 | 35.00 | 0.15 | ○ |
|
|
||||||
| 代码结构 | 45.00 | 0.20 | ○ |
|
|
||||||
| 代码重复度 | 55.00 | 0.15 | • |
|
|
||||||
| 注释覆盖率 | 55.94 | 0.15 | • |
|
|
||||||
|
|
||||||
## 问题文件 (Top 5)
|
|
||||||
|
|
||||||
### 1. /workspace/blazing/common/utils/sturc/field.go (得分: 53.85)
|
|
||||||
**问题分类**: 🔄 复杂度问题:10, 📝 注释问题:1, ⚠️ 其他问题:5
|
|
||||||
|
|
||||||
**主要问题**:
|
|
||||||
- 函数 Size 的循环复杂度较高 (12),建议简化
|
|
||||||
- 函数 packVal 的循环复杂度过高 (23),考虑重构
|
|
||||||
- 函数 Pack 的循环复杂度较高 (14),建议简化
|
|
||||||
- 函数 unpackVal 的循环复杂度过高 (21),考虑重构
|
|
||||||
- 函数 Unpack 的循环复杂度较高 (12),建议简化
|
|
||||||
- 函数 'Size' () 较长 (33 行),可考虑重构
|
|
||||||
- 函数 'Size' () 复杂度过高 (12),建议简化
|
|
||||||
- 函数 'packVal' () 过长 (69 行),建议拆分
|
|
||||||
- 函数 'packVal' () 复杂度严重过高 (23),必须简化
|
|
||||||
- 函数 'Pack' () 较长 (48 行),可考虑重构
|
|
||||||
- 函数 'Pack' () 复杂度过高 (14),建议简化
|
|
||||||
- 函数 'unpackVal' () 过长 (57 行),建议拆分
|
|
||||||
- 函数 'unpackVal' () 复杂度严重过高 (21),必须简化
|
|
||||||
- 函数 'Unpack' () 较长 (33 行),可考虑重构
|
|
||||||
- 函数 'Unpack' () 复杂度过高 (12),建议简化
|
|
||||||
- 代码注释率极低 (1.38%),几乎没有注释
|
|
||||||
|
|
||||||
### 2. /workspace/blazing/common/utils/sturc/fields.go (得分: 46.83)
|
|
||||||
**问题分类**: 🔄 复杂度问题:4, 📝 注释问题:1, ⚠️ 其他问题:2
|
|
||||||
|
|
||||||
**主要问题**:
|
|
||||||
- 函数 Pack 的循环复杂度较高 (12),建议简化
|
|
||||||
- 函数 Unpack 的循环复杂度过高 (21),考虑重构
|
|
||||||
- 函数 'Pack' () 较长 (42 行),可考虑重构
|
|
||||||
- 函数 'Pack' () 复杂度过高 (12),建议简化
|
|
||||||
- 函数 'Unpack' () 过长 (73 行),建议拆分
|
|
||||||
- 函数 'Unpack' () 复杂度严重过高 (21),必须简化
|
|
||||||
- 代码注释率极低 (3.91%),几乎没有注释
|
|
||||||
|
|
||||||
### 3. /workspace/blazing/common/utils/sturc/parse.go (得分: 46.68)
|
|
||||||
**问题分类**: 🔄 复杂度问题:4, 📝 注释问题:1, ⚠️ 其他问题:3
|
|
||||||
|
|
||||||
**主要问题**:
|
|
||||||
- 代码注释率较低 (6.93%),建议增加注释
|
|
||||||
- 函数 parseField 的循环复杂度较高 (13),建议简化
|
|
||||||
- 函数 parseFieldsLocked 的循环复杂度过高 (18),考虑重构
|
|
||||||
- 函数 'parseField' () 过长 (64 行),建议拆分
|
|
||||||
- 函数 'parseField' () 复杂度过高 (13),建议简化
|
|
||||||
- 函数 'parseFieldsLocked' () 过长 (64 行),建议拆分
|
|
||||||
- 函数 'parseFieldsLocked' () 复杂度严重过高 (18),必须简化
|
|
||||||
- 函数 'parseFields' () 较长 (31 行),可考虑重构
|
|
||||||
|
|
||||||
### 4. /workspace/blazing/common/utils/xml/typeinfo.go (得分: 46.13)
|
|
||||||
**问题分类**: 🔄 复杂度问题:6, ⚠️ 其他问题:3
|
|
||||||
|
|
||||||
**主要问题**:
|
|
||||||
- 函数 getTypeInfo 的循环复杂度过高 (18),考虑重构
|
|
||||||
- 函数 structFieldInfo 的循环复杂度过高 (33),考虑重构
|
|
||||||
- 函数 addFieldInfo 的循环复杂度过高 (20),考虑重构
|
|
||||||
- 函数 'getTypeInfo' () 过长 (58 行),建议拆分
|
|
||||||
- 函数 'getTypeInfo' () 复杂度严重过高 (18),必须简化
|
|
||||||
- 函数 'structFieldInfo' () 极度过长 (114 行),必须拆分
|
|
||||||
- 函数 'structFieldInfo' () 复杂度严重过高 (33),必须简化
|
|
||||||
- 函数 'addFieldInfo' () 过长 (66 行),建议拆分
|
|
||||||
- 函数 'addFieldInfo' () 复杂度严重过高 (20),必须简化
|
|
||||||
|
|
||||||
### 5. /workspace/blazing/common/utils/go-jsonrpc/auth/handler.go (得分: 45.61)
|
|
||||||
**问题分类**: 📝 注释问题:1, ⚠️ 其他问题:1
|
|
||||||
|
|
||||||
**主要问题**:
|
|
||||||
- 函数 'ServeHTTP' () 较长 (31 行),可考虑重构
|
|
||||||
- 代码注释率极低 (0.00%),几乎没有注释
|
|
||||||
|
|
||||||
## 改进建议
|
|
||||||
|
|
||||||
### 高优先级
|
|
||||||
- 继续保持当前的代码质量标准
|
|
||||||
|
|
||||||
### 中优先级
|
|
||||||
- 可以考虑进一步优化性能和可读性
|
|
||||||
- 完善文档和注释,便于团队协作
|
|
||||||
|
|
||||||
@@ -2,7 +2,6 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/common/socket/errorcode"
|
"blazing/common/socket/errorcode"
|
||||||
"blazing/modules/player/model"
|
|
||||||
|
|
||||||
"blazing/logic/service/fight"
|
"blazing/logic/service/fight"
|
||||||
"blazing/logic/service/fight/info"
|
"blazing/logic/service/fight/info"
|
||||||
@@ -31,7 +30,7 @@ func (h Controller) UseSkill(data *UseSkillInInfo, c *player.Player) (result *fi
|
|||||||
if err := h.checkFightStatus(c); err != 0 {
|
if err := h.checkFightStatus(c); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
go c.FightC.UseSkill(c, data.SkillId)
|
h.dispatchFightActionEnvelope(c, buildLegacyUseSkillEnvelope(data))
|
||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,31 +40,7 @@ func (h Controller) UseSkillAt(data *UseSkillAtInboundInfo, c *player.Player) (r
|
|||||||
if err := h.checkFightStatus(c); err != 0 {
|
if err := h.checkFightStatus(c); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
actorIndex := int(data.ActorIndex)
|
h.dispatchFightActionEnvelope(c, buildIndexedUseSkillEnvelope(data))
|
||||||
targetIndex := int(data.TargetIndex)
|
|
||||||
targetRelation := data.TargetRelation
|
|
||||||
|
|
||||||
// 前端未显式给 relation 时,按 AtkType 兜底:3=自己,1=己方,其他按对方。
|
|
||||||
if targetRelation > fight.SkillTargetAlly {
|
|
||||||
switch data.AtkType {
|
|
||||||
case 3:
|
|
||||||
targetRelation = fight.SkillTargetSelf
|
|
||||||
case 1:
|
|
||||||
targetRelation = fight.SkillTargetAlly
|
|
||||||
default:
|
|
||||||
targetRelation = fight.SkillTargetOpponent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch targetRelation {
|
|
||||||
case fight.SkillTargetSelf:
|
|
||||||
targetIndex = actorIndex
|
|
||||||
go c.FightC.UseSkillAt(c, data.SkillId, actorIndex, fight.EncodeTargetIndex(targetIndex, false))
|
|
||||||
case fight.SkillTargetAlly:
|
|
||||||
go c.FightC.UseSkillAt(c, data.SkillId, actorIndex, fight.EncodeTargetIndex(targetIndex, false))
|
|
||||||
default:
|
|
||||||
go c.FightC.UseSkillAt(c, data.SkillId, actorIndex, fight.EncodeTargetIndex(targetIndex, true))
|
|
||||||
}
|
|
||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,8 +49,7 @@ func (h Controller) Escape(data *EscapeFightInboundInfo, c *player.Player) (resu
|
|||||||
if err := h.checkFightStatus(c); err != 0 {
|
if err := h.checkFightStatus(c); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
h.dispatchFightActionEnvelope(c, buildLegacyEscapeEnvelope())
|
||||||
go c.FightC.Over(c, model.BattleOverReason.PlayerEscape)
|
|
||||||
return nil, 0
|
return nil, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +58,7 @@ func (h Controller) ChangePet(data *ChangePetInboundInfo, c *player.Player) (res
|
|||||||
if err := h.checkFightStatus(c); err != 0 {
|
if err := h.checkFightStatus(c); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
go c.FightC.ChangePet(c, data.CatchTime)
|
h.dispatchFightActionEnvelope(c, buildLegacyChangeEnvelope(data))
|
||||||
return nil, -1
|
return nil, -1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +97,7 @@ func (h Controller) UsePetItemInboundInfo(data *UsePetItemInboundInfo, c *player
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go c.FightC.UseItem(c, data.CatchTime, data.ItemId)
|
h.dispatchFightActionEnvelope(c, buildLegacyUseItemEnvelope(data))
|
||||||
return nil, -1
|
return nil, -1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +106,6 @@ func (h Controller) FightChat(data *ChatInfo, c *player.Player) (result *fight.N
|
|||||||
if err := h.checkFightStatus(c); err != 0 {
|
if err := h.checkFightStatus(c); err != 0 {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
go c.FightC.Chat(c, data.Message)
|
h.dispatchFightActionEnvelope(c, buildChatEnvelope(data))
|
||||||
return nil, -1
|
return nil, -1
|
||||||
}
|
}
|
||||||
|
|||||||
79
logic/controller/fight_unified.go
Normal file
79
logic/controller/fight_unified.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/modules/player/model"
|
||||||
|
|
||||||
|
"blazing/logic/service/fight"
|
||||||
|
"blazing/logic/service/player"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dispatchFightActionEnvelope 把控制器层收到的统一动作结构分发回现有 FightI 接口。
|
||||||
|
func (h Controller) dispatchFightActionEnvelope(c *player.Player, envelope fight.FightActionEnvelope) {
|
||||||
|
if c == nil || c.FightC == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch envelope.ActionType {
|
||||||
|
case fight.FightActionTypeSkill:
|
||||||
|
go c.FightC.UseSkillAt(c, envelope.SkillID, envelope.ActorIndex, envelope.EncodedTargetIndex())
|
||||||
|
case fight.FightActionTypeItem:
|
||||||
|
go c.FightC.UseItemAt(c, envelope.CatchTime, envelope.ItemID, envelope.ActorIndex, envelope.EncodedTargetIndex())
|
||||||
|
case fight.FightActionTypeChange:
|
||||||
|
go c.FightC.ChangePetAt(c, envelope.CatchTime, envelope.ActorIndex)
|
||||||
|
case fight.FightActionTypeEscape:
|
||||||
|
go c.FightC.Over(c, model.BattleOverReason.PlayerEscape)
|
||||||
|
case fight.FightActionTypeChat:
|
||||||
|
go c.FightC.Chat(c, envelope.Chat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildLegacyUseSkillEnvelope 把旧 2405 技能包映射成统一动作结构。
|
||||||
|
func buildLegacyUseSkillEnvelope(data *UseSkillInInfo) fight.FightActionEnvelope {
|
||||||
|
if data == nil {
|
||||||
|
return fight.NewSkillActionEnvelope(0, 0, 0, fight.SkillTargetOpponent, 0)
|
||||||
|
}
|
||||||
|
return fight.NewSkillActionEnvelope(data.SkillId, 0, 0, fight.SkillTargetOpponent, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildIndexedUseSkillEnvelope 把 7505 多战位技能包映射成统一动作结构。
|
||||||
|
func buildIndexedUseSkillEnvelope(data *UseSkillAtInboundInfo) fight.FightActionEnvelope {
|
||||||
|
if data == nil {
|
||||||
|
return fight.NewSkillActionEnvelope(0, 0, 0, fight.SkillTargetOpponent, 0)
|
||||||
|
}
|
||||||
|
return fight.NewSkillActionEnvelope(
|
||||||
|
data.SkillId,
|
||||||
|
int(data.ActorIndex),
|
||||||
|
int(data.TargetIndex),
|
||||||
|
data.TargetRelation,
|
||||||
|
data.AtkType,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildLegacyUseItemEnvelope 把旧 2406 道具包映射成统一动作结构。
|
||||||
|
func buildLegacyUseItemEnvelope(data *UsePetItemInboundInfo) fight.FightActionEnvelope {
|
||||||
|
if data == nil {
|
||||||
|
return fight.NewItemActionEnvelope(0, 0, 0, 0, fight.SkillTargetOpponent)
|
||||||
|
}
|
||||||
|
return fight.NewItemActionEnvelope(data.CatchTime, data.ItemId, 0, 0, fight.SkillTargetOpponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildLegacyChangeEnvelope 把旧 2407 切宠包映射成统一动作结构。
|
||||||
|
func buildLegacyChangeEnvelope(data *ChangePetInboundInfo) fight.FightActionEnvelope {
|
||||||
|
if data == nil {
|
||||||
|
return fight.NewChangeActionEnvelope(0, 0)
|
||||||
|
}
|
||||||
|
return fight.NewChangeActionEnvelope(data.CatchTime, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildLegacyEscapeEnvelope 构造旧 2410 逃跑包对应的统一动作结构。
|
||||||
|
func buildLegacyEscapeEnvelope() fight.FightActionEnvelope {
|
||||||
|
return fight.NewEscapeActionEnvelope()
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildChatEnvelope 把战斗聊天包映射成统一动作结构。
|
||||||
|
func buildChatEnvelope(data *ChatInfo) fight.FightActionEnvelope {
|
||||||
|
if data == nil {
|
||||||
|
return fight.NewChatActionEnvelope("")
|
||||||
|
}
|
||||||
|
return fight.NewChatActionEnvelope(data.Message)
|
||||||
|
}
|
||||||
@@ -1,26 +1,25 @@
|
|||||||
package effect
|
package effect
|
||||||
|
|
||||||
import (
|
import "blazing/logic/service/fight/input"
|
||||||
"blazing/logic/service/fight/input"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 501. g1. 最后一个死 (只要有队友没死, 则自己又恢复hp和pp)
|
// 501. g1. 最后一个死 (只要有队友没死, 则自己又恢复hp和pp)
|
||||||
// TODO: 需要了解如何判断队友状态并恢复HP和PP
|
|
||||||
type NewSel501 struct {
|
type NewSel501 struct {
|
||||||
NewSel0
|
NewSel0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SwitchOut 在拥有者死亡离场时触发;若仍有存活队友,则把自己回满留作后续再上场。
|
||||||
func (e *NewSel501) SwitchOut(in *input.Input) bool {
|
func (e *NewSel501) SwitchOut(in *input.Input) bool {
|
||||||
//魂印特性有不在场的情况,绑定时候将精灵和特性绑定
|
owner := e.SourceInput()
|
||||||
if e.ID().GetCatchTime() != e.Ctx().Our.CurPet[0].Info.CatchTime {
|
if owner == nil || in != owner || !e.IsOwner() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
currentPet := owner.CurrentPet()
|
||||||
|
if currentPet == nil || currentPet.Info.Hp > 0 || !owner.HasLivingTeammate() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 检查是否有队友还活着
|
currentPet.Info.Hp = currentPet.Info.MaxHp
|
||||||
// 如果有队友活着,恢复自身HP和PP
|
owner.HealPP(-1)
|
||||||
// e.Ctx().Our.Heal(e.Ctx().Our, nil, e.Ctx().Our.CurPet[0].GetMaxHP())
|
|
||||||
// TODO: 恢复PP值的方法
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,31 @@
|
|||||||
package effect
|
package effect
|
||||||
|
|
||||||
import (
|
import "blazing/logic/service/fight/input"
|
||||||
"blazing/logic/service/fight/input"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 502. g2. 如果自身死亡, 恢复队友所有体力和PP值
|
// 502. g2. 如果自身死亡, 恢复队友所有体力和PP值
|
||||||
// TODO: 实现恢复队友所有体力和PP值的核心逻辑
|
|
||||||
type NewSel502 struct {
|
type NewSel502 struct {
|
||||||
NewSel0
|
NewSel0
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 需要找到精灵死亡时的回调接口
|
// SwitchOut 在拥有者死亡离场时触发;把仍在场的队友体力和 PP 恢复到满值。
|
||||||
func (e *NewSel502) SwitchOut(in *input.Input) bool {
|
func (e *NewSel502) SwitchOut(in *input.Input) bool {
|
||||||
//魂印特性有不在场的情况,绑定时候将精灵和特性绑定
|
owner := e.SourceInput()
|
||||||
if e.ID().GetCatchTime() != e.Ctx().Our.CurPet[0].Info.CatchTime {
|
if owner == nil || in != owner || !e.IsOwner() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
currentPet := owner.CurrentPet()
|
||||||
|
if currentPet == nil || currentPet.Info.Hp > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 检查精灵是否死亡(HP为0)
|
for _, teammate := range owner.LivingTeammates() {
|
||||||
// 如果死亡,恢复队友所有体力和PP值
|
pet := teammate.CurrentPet()
|
||||||
// 需要遍历队友的精灵并调用相应的方法
|
if pet == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pet.Info.Hp = pet.Info.MaxHp
|
||||||
|
teammate.HealPP(-1)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 503. g3. 群体攻击技能可额外增加一个目标(最多不超过5个目标)
|
// 503. g3. 群体攻击技能可额外增加一个目标(最多不超过5个目标)
|
||||||
// TODO: 需要了解如何修改群体攻击技能的目标数量
|
|
||||||
type NewSel503 struct {
|
type NewSel503 struct {
|
||||||
NewSel0
|
NewSel0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TurnStart 在拥有者本回合准备出手时触发;若本次技能是群体技能,则把目标数额外加 1。
|
||||||
func (e *NewSel503) TurnStart(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
|
func (e *NewSel503) TurnStart(fattack *action.SelectSkillAction, sattack *action.SelectSkillAction) {
|
||||||
//魂印特性有不在场的情况,绑定时候将精灵和特性绑定
|
owner := e.SourceInput()
|
||||||
if e.ID().GetCatchTime() != e.Ctx().Our.CurPet[0].Info.CatchTime {
|
if owner == nil || !e.IsOwner() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 检查技能是否是群体攻击技能
|
for _, act := range []*action.SelectSkillAction{fattack, sattack} {
|
||||||
// 如果是群体攻击,增加一个目标(最多不超过5个)
|
if act == nil || act.SkillEntity == nil || act.SkillEntity.Pet == nil {
|
||||||
// 需要了解技能的目标数量限制机制
|
continue
|
||||||
|
}
|
||||||
|
if act.SkillEntity.Pet.Info.CatchTime != e.ID().GetCatchTime() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if act.SkillEntity.XML.AtkType != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if act.SkillEntity.XML.AtkNum <= 0 {
|
||||||
|
act.SkillEntity.XML.AtkNum = 1
|
||||||
|
}
|
||||||
|
if act.SkillEntity.XML.AtkNum < 5 {
|
||||||
|
act.SkillEntity.XML.AtkNum++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
138
logic/service/fight/cmd_unified.go
Normal file
138
logic/service/fight/cmd_unified.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package fight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/logic/service/common"
|
||||||
|
"blazing/modules/player/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FightActionType 表示统一动作包里的动作类型。
|
||||||
|
type FightActionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FightActionTypeSkill 表示使用技能。
|
||||||
|
FightActionTypeSkill FightActionType = "skill"
|
||||||
|
// FightActionTypeItem 表示使用道具。
|
||||||
|
FightActionTypeItem FightActionType = "item"
|
||||||
|
// FightActionTypeChange 表示主动切宠。
|
||||||
|
FightActionTypeChange FightActionType = "change"
|
||||||
|
// FightActionTypeEscape 表示逃跑。
|
||||||
|
FightActionTypeEscape FightActionType = "escape"
|
||||||
|
// FightActionTypeChat 表示战斗内聊天。
|
||||||
|
FightActionTypeChat FightActionType = "chat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FightActionEnvelope 是统一入站动作结构。
|
||||||
|
// 约定:
|
||||||
|
// 1. actorIndex 始终表示发起方所在的我方槽位。
|
||||||
|
// 2. targetIndex 始终表示目标在所属阵营内的槽位。
|
||||||
|
// 3. targetRelation 用来区分 targetIndex 属于敌方、自己还是队友。
|
||||||
|
type FightActionEnvelope struct {
|
||||||
|
ActionType FightActionType `json:"actionType"`
|
||||||
|
ActorIndex int `json:"actorIndex"`
|
||||||
|
TargetIndex int `json:"targetIndex"`
|
||||||
|
TargetRelation uint8 `json:"targetRelation,omitempty"`
|
||||||
|
SkillID uint32 `json:"skillId,omitempty"`
|
||||||
|
ItemID uint32 `json:"itemId,omitempty"`
|
||||||
|
CatchTime uint32 `json:"catchTime,omitempty"`
|
||||||
|
Escape bool `json:"escape,omitempty"`
|
||||||
|
Chat string `json:"chat,omitempty"`
|
||||||
|
AtkType uint8 `json:"atkType,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSkillActionEnvelope 构造技能动作 envelope。
|
||||||
|
func NewSkillActionEnvelope(skillID uint32, actorIndex, targetIndex int, targetRelation, atkType uint8) FightActionEnvelope {
|
||||||
|
return FightActionEnvelope{
|
||||||
|
ActionType: FightActionTypeSkill,
|
||||||
|
ActorIndex: actorIndex,
|
||||||
|
TargetIndex: targetIndex,
|
||||||
|
TargetRelation: targetRelation,
|
||||||
|
SkillID: skillID,
|
||||||
|
AtkType: atkType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewItemActionEnvelope 构造道具动作 envelope。
|
||||||
|
func NewItemActionEnvelope(catchTime, itemID uint32, actorIndex, targetIndex int, targetRelation uint8) FightActionEnvelope {
|
||||||
|
return FightActionEnvelope{
|
||||||
|
ActionType: FightActionTypeItem,
|
||||||
|
ActorIndex: actorIndex,
|
||||||
|
TargetIndex: targetIndex,
|
||||||
|
TargetRelation: targetRelation,
|
||||||
|
ItemID: itemID,
|
||||||
|
CatchTime: catchTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChangeActionEnvelope 构造切宠动作 envelope。
|
||||||
|
func NewChangeActionEnvelope(catchTime uint32, actorIndex int) FightActionEnvelope {
|
||||||
|
return FightActionEnvelope{
|
||||||
|
ActionType: FightActionTypeChange,
|
||||||
|
ActorIndex: actorIndex,
|
||||||
|
CatchTime: catchTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEscapeActionEnvelope 构造逃跑动作 envelope。
|
||||||
|
func NewEscapeActionEnvelope() FightActionEnvelope {
|
||||||
|
return FightActionEnvelope{
|
||||||
|
ActionType: FightActionTypeEscape,
|
||||||
|
Escape: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChatActionEnvelope 构造聊天动作 envelope。
|
||||||
|
func NewChatActionEnvelope(chat string) FightActionEnvelope {
|
||||||
|
return FightActionEnvelope{
|
||||||
|
ActionType: FightActionTypeChat,
|
||||||
|
Chat: chat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizedTargetRelation 根据 TargetRelation 和 AtkType 兜底出最终目标关系。
|
||||||
|
func (e FightActionEnvelope) normalizedTargetRelation() uint8 {
|
||||||
|
if e.TargetRelation <= SkillTargetAlly {
|
||||||
|
return e.TargetRelation
|
||||||
|
}
|
||||||
|
switch e.AtkType {
|
||||||
|
case 3:
|
||||||
|
return SkillTargetSelf
|
||||||
|
case 1:
|
||||||
|
return SkillTargetAlly
|
||||||
|
default:
|
||||||
|
return SkillTargetOpponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodedTargetIndex 把统一结构里的目标信息编码成现有 FightC 内部使用的目标格式。
|
||||||
|
func (e FightActionEnvelope) EncodedTargetIndex() int {
|
||||||
|
targetIndex := e.TargetIndex
|
||||||
|
switch e.normalizedTargetRelation() {
|
||||||
|
case SkillTargetSelf:
|
||||||
|
targetIndex = e.ActorIndex
|
||||||
|
return EncodeTargetIndex(targetIndex, false)
|
||||||
|
case SkillTargetAlly:
|
||||||
|
return EncodeTargetIndex(targetIndex, false)
|
||||||
|
default:
|
||||||
|
return EncodeTargetIndex(targetIndex, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleActionEnvelope 把统一动作结构派发到现有 FightC 的 indexed 接口上。
|
||||||
|
func (f *FightC) HandleActionEnvelope(c common.PlayerI, envelope FightActionEnvelope) {
|
||||||
|
if f == nil || c == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch envelope.ActionType {
|
||||||
|
case FightActionTypeSkill:
|
||||||
|
f.UseSkillAt(c, envelope.SkillID, envelope.ActorIndex, envelope.EncodedTargetIndex())
|
||||||
|
case FightActionTypeItem:
|
||||||
|
f.UseItemAt(c, envelope.CatchTime, envelope.ItemID, envelope.ActorIndex, envelope.EncodedTargetIndex())
|
||||||
|
case FightActionTypeChange:
|
||||||
|
f.ChangePetAt(c, envelope.CatchTime, envelope.ActorIndex)
|
||||||
|
case FightActionTypeEscape:
|
||||||
|
f.Over(c, model.BattleOverReason.PlayerEscape)
|
||||||
|
case FightActionTypeChat:
|
||||||
|
f.Chat(c, envelope.Chat)
|
||||||
|
}
|
||||||
|
}
|
||||||
102
logic/service/fight/info/unified_info.go
Normal file
102
logic/service/fight/info/unified_info.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package info
|
||||||
|
|
||||||
|
import "blazing/modules/player/model"
|
||||||
|
|
||||||
|
// FightStatePhase 表示统一战斗状态包的阶段。
|
||||||
|
type FightStatePhase string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FightStatePhaseStart 表示开战快照。
|
||||||
|
FightStatePhaseStart FightStatePhase = "start"
|
||||||
|
// FightStatePhaseSkillHurt 表示技能结算后的伤害快照。
|
||||||
|
FightStatePhaseSkillHurt FightStatePhase = "skill_hurt"
|
||||||
|
// FightStatePhaseChange 表示切宠快照。
|
||||||
|
FightStatePhaseChange FightStatePhase = "change"
|
||||||
|
// FightStatePhaseOver 表示战斗结束快照。
|
||||||
|
FightStatePhaseOver FightStatePhase = "over"
|
||||||
|
// FightStatePhaseLoad 表示加载进度快照。
|
||||||
|
FightStatePhaseLoad FightStatePhase = "load"
|
||||||
|
// FightStatePhaseChat 表示战斗内聊天快照。
|
||||||
|
FightStatePhaseChat FightStatePhase = "chat"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FighterState 是统一的战位快照。
|
||||||
|
// position 表示该 side 下的槽位编号;actorIndex/targetIndex 与其一一对应。
|
||||||
|
type FighterState struct {
|
||||||
|
// Side 阵营标识:1=我方,2=敌方。
|
||||||
|
Side int `json:"side"`
|
||||||
|
// Position 战位下标;在各自 side 内部从 0 开始编号。
|
||||||
|
Position int `json:"position"`
|
||||||
|
// UserID 当前战位所属玩家 ID;野怪/NPC 通常为 0。
|
||||||
|
UserID uint32 `json:"userId"`
|
||||||
|
// ControllerUserID 当前上场精灵的实际操作者 ID;组队时可与 UserID 联合定位操作者和战位归属。
|
||||||
|
ControllerUserID uint32 `json:"controllerUserId"`
|
||||||
|
// PetID 当前上场精灵的物种/配置 ID。
|
||||||
|
PetID uint32 `json:"petId"`
|
||||||
|
// CatchTime 当前上场精灵的唯一实例 ID,可理解为这只精灵在玩家背包中的唯一标识。
|
||||||
|
CatchTime uint32 `json:"catchTime"`
|
||||||
|
// Name 当前上场精灵名字。
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
// HP 当前生命值。
|
||||||
|
HP uint32 `json:"hp"`
|
||||||
|
// MaxHP 最大生命值。
|
||||||
|
MaxHP uint32 `json:"maxHp"`
|
||||||
|
// Level 当前等级。
|
||||||
|
Level uint32 `json:"level"`
|
||||||
|
// Anger 怒气值;当前服务端主链路暂未实际填充时默认为 0,先为协议对齐预留。
|
||||||
|
Anger uint32 `json:"anger"`
|
||||||
|
// Status 当前异常/增益状态回合数组;下标语义沿用现有战斗状态定义。
|
||||||
|
Status [20]int8 `json:"status"`
|
||||||
|
// Prop 当前能力等级变化数组:攻击、防御、特攻、特防、速度、命中。
|
||||||
|
Prop [6]int8 `json:"prop"`
|
||||||
|
// Skills 当前可见技能列表,包含技能 ID 和当前 PP 等信息。
|
||||||
|
Skills []model.SkillInfo `json:"skills,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FightStateMeta 是统一状态包的公共元数据。
|
||||||
|
type FightStateMeta struct {
|
||||||
|
Round uint32 `json:"round"`
|
||||||
|
Weather uint32 `json:"weather,omitempty"`
|
||||||
|
WinnerID uint32 `json:"winnerId,omitempty"`
|
||||||
|
Reason model.EnumBattleOverReason `json:"reason,omitempty"`
|
||||||
|
LegacyCmd uint32 `json:"legacyCmd,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FightSkillHurtState 保存技能结算后的左右两侧战报快照。
|
||||||
|
type FightSkillHurtState struct {
|
||||||
|
Left []model.AttackValue `json:"left,omitempty"`
|
||||||
|
Right []model.AttackValue `json:"right,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FightLoadState 保存加载进度信息。
|
||||||
|
type FightLoadState struct {
|
||||||
|
UserID uint32 `json:"userId"`
|
||||||
|
Percent uint32 `json:"percent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FightChatState 保存战斗内聊天信息。
|
||||||
|
type FightChatState struct {
|
||||||
|
SenderID uint32 `json:"senderId"`
|
||||||
|
SenderNickname string `json:"senderNickname"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FightStateEnvelope 是统一出站状态结构。
|
||||||
|
type FightStateEnvelope struct {
|
||||||
|
// Phase 当前下发阶段,例如 start、skill_hurt、change、over、load、chat。
|
||||||
|
Phase FightStatePhase `json:"phase"`
|
||||||
|
// Left 我方阵营当前所有战位快照。
|
||||||
|
Left []FighterState `json:"left,omitempty"`
|
||||||
|
// Right 敌方阵营当前所有战位快照。
|
||||||
|
Right []FighterState `json:"right,omitempty"`
|
||||||
|
// Meta 当前阶段共用的元数据,如回合号、胜方、结束原因、旧协议命令号。
|
||||||
|
Meta FightStateMeta `json:"meta"`
|
||||||
|
// SkillHurt 技能结算阶段附带的详细战报;仅 phase=skill_hurt 时使用。
|
||||||
|
SkillHurt *FightSkillHurtState `json:"skillHurt,omitempty"`
|
||||||
|
// Change 切宠阶段附带的切宠详情;仅 phase=change 时使用。
|
||||||
|
Change *ChangePetInfo `json:"change,omitempty"`
|
||||||
|
// Load 加载阶段附带的进度信息;仅 phase=load 时使用。
|
||||||
|
Load *FightLoadState `json:"load,omitempty"`
|
||||||
|
// Chat 聊天阶段附带的聊天内容;仅 phase=chat 时使用。
|
||||||
|
Chat *FightChatState `json:"chat,omitempty"`
|
||||||
|
}
|
||||||
55
logic/service/fight/input/team.go
Normal file
55
logic/service/fight/input/team.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
// TeamSlots 返回当前输入所在阵营的全部有效战斗位视图。
|
||||||
|
func (our *Input) TeamSlots() []*Input {
|
||||||
|
if our == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(our.Team) == 0 {
|
||||||
|
return []*Input{our}
|
||||||
|
}
|
||||||
|
slots := make([]*Input, 0, len(our.Team))
|
||||||
|
for _, teammate := range our.Team {
|
||||||
|
if teammate == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
slots = append(slots, teammate)
|
||||||
|
}
|
||||||
|
return slots
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teammates 返回队友列表,不包含自己。
|
||||||
|
func (our *Input) Teammates() []*Input {
|
||||||
|
if our == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
teammates := make([]*Input, 0, len(our.Team))
|
||||||
|
for _, teammate := range our.TeamSlots() {
|
||||||
|
if teammate == nil || teammate == our {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
teammates = append(teammates, teammate)
|
||||||
|
}
|
||||||
|
return teammates
|
||||||
|
}
|
||||||
|
|
||||||
|
// LivingTeammates 返回当前仍有存活出战精灵的队友列表。
|
||||||
|
func (our *Input) LivingTeammates() []*Input {
|
||||||
|
if our == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
teammates := make([]*Input, 0, len(our.Team))
|
||||||
|
for _, teammate := range our.Teammates() {
|
||||||
|
currentPet := teammate.CurrentPet()
|
||||||
|
if currentPet == nil || currentPet.Info.Hp == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
teammates = append(teammates, teammate)
|
||||||
|
}
|
||||||
|
return teammates
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasLivingTeammate 用于快速判断当前战斗位是否还有存活队友。
|
||||||
|
func (our *Input) HasLivingTeammate() bool {
|
||||||
|
return len(our.LivingTeammates()) > 0
|
||||||
|
}
|
||||||
30
logic/service/fight/input/team_test.go
Normal file
30
logic/service/fight/input/team_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package input
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
fightinfo "blazing/logic/service/fight/info"
|
||||||
|
"blazing/modules/player/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLivingTeammatesFiltersSelfAndDeadSlots(t *testing.T) {
|
||||||
|
owner := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 10}}}}
|
||||||
|
aliveMate := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 5}}}}
|
||||||
|
deadMate := &Input{CurPet: []*fightinfo.BattlePetEntity{{Info: model.PetInfo{Hp: 0}}}}
|
||||||
|
|
||||||
|
team := []*Input{owner, aliveMate, deadMate}
|
||||||
|
owner.Team = team
|
||||||
|
aliveMate.Team = team
|
||||||
|
deadMate.Team = team
|
||||||
|
|
||||||
|
teammates := owner.LivingTeammates()
|
||||||
|
if len(teammates) != 1 {
|
||||||
|
t.Fatalf("expected 1 living teammate, got %d", len(teammates))
|
||||||
|
}
|
||||||
|
if teammates[0] != aliveMate {
|
||||||
|
t.Fatalf("expected alive teammate to be returned")
|
||||||
|
}
|
||||||
|
if owner.HasLivingTeammate() != true {
|
||||||
|
t.Fatalf("expected owner to detect living teammate")
|
||||||
|
}
|
||||||
|
}
|
||||||
113
logic/service/fight/unified_state.go
Normal file
113
logic/service/fight/unified_state.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package fight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/logic/service/fight/info"
|
||||||
|
"blazing/logic/service/fight/input"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildFightStateStartEnvelope 构造开战阶段的统一状态包。
|
||||||
|
func (f *FightC) BuildFightStateStartEnvelope() info.FightStateEnvelope {
|
||||||
|
return f.buildFightStateEnvelope(info.FightStatePhaseStart, 2504)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFightStateSkillHurtEnvelope 构造技能结算阶段的统一状态包。
|
||||||
|
func (f *FightC) BuildFightStateSkillHurtEnvelope() info.FightStateEnvelope {
|
||||||
|
envelope := f.buildFightStateEnvelope(info.FightStatePhaseSkillHurt, 2505)
|
||||||
|
envelope.SkillHurt = &info.FightSkillHurtState{
|
||||||
|
Left: f.collectAttackValues(f.Our),
|
||||||
|
Right: f.collectAttackValues(f.Opp),
|
||||||
|
}
|
||||||
|
return envelope
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFightStateChangeEnvelope 构造切宠阶段的统一状态包。
|
||||||
|
func (f *FightC) BuildFightStateChangeEnvelope(change info.ChangePetInfo) info.FightStateEnvelope {
|
||||||
|
envelope := f.buildFightStateEnvelope(info.FightStatePhaseChange, 2407)
|
||||||
|
envelope.Change = &change
|
||||||
|
return envelope
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFightStateOverEnvelope 构造结束阶段的统一状态包。
|
||||||
|
func (f *FightC) BuildFightStateOverEnvelope() info.FightStateEnvelope {
|
||||||
|
envelope := f.buildFightStateEnvelope(info.FightStatePhaseOver, 2506)
|
||||||
|
envelope.Meta.WinnerID = f.FightOverInfo.WinnerId
|
||||||
|
envelope.Meta.Reason = f.FightOverInfo.Reason
|
||||||
|
return envelope
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFightStateLoadEnvelope 构造加载阶段的统一状态包。
|
||||||
|
func (f *FightC) BuildFightStateLoadEnvelope(userID, percent uint32) info.FightStateEnvelope {
|
||||||
|
envelope := f.buildFightStateEnvelope(info.FightStatePhaseLoad, 2441)
|
||||||
|
envelope.Load = &info.FightLoadState{
|
||||||
|
UserID: userID,
|
||||||
|
Percent: percent,
|
||||||
|
}
|
||||||
|
return envelope
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildFightStateChatEnvelope 构造聊天阶段的统一状态包。
|
||||||
|
func (f *FightC) BuildFightStateChatEnvelope(senderID uint32, senderNickname, message string) info.FightStateEnvelope {
|
||||||
|
envelope := f.buildFightStateEnvelope(info.FightStatePhaseChat, 50002)
|
||||||
|
envelope.Chat = &info.FightChatState{
|
||||||
|
SenderID: senderID,
|
||||||
|
SenderNickname: senderNickname,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
return envelope
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildFightStateEnvelope 组装左右两侧通用快照和元数据。
|
||||||
|
func (f *FightC) buildFightStateEnvelope(phase info.FightStatePhase, legacyCmd uint32) info.FightStateEnvelope {
|
||||||
|
if f == nil {
|
||||||
|
return info.FightStateEnvelope{Phase: phase}
|
||||||
|
}
|
||||||
|
return info.FightStateEnvelope{
|
||||||
|
Phase: phase,
|
||||||
|
Left: snapshotFighterStates(SideOur, f.Our),
|
||||||
|
Right: snapshotFighterStates(SideOpp, f.Opp),
|
||||||
|
Meta: info.FightStateMeta{
|
||||||
|
Round: uint32(f.Round),
|
||||||
|
WinnerID: f.FightOverInfo.WinnerId,
|
||||||
|
Reason: f.FightOverInfo.Reason,
|
||||||
|
LegacyCmd: legacyCmd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// snapshotFighterStates 把指定侧的战斗位数组转成统一 fighter 快照。
|
||||||
|
func snapshotFighterStates(side int, fighters []*input.Input) []info.FighterState {
|
||||||
|
states := make([]info.FighterState, 0, len(fighters))
|
||||||
|
for position, fighter := range fighters {
|
||||||
|
if fighter == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
state := info.FighterState{
|
||||||
|
Side: side,
|
||||||
|
Position: position,
|
||||||
|
}
|
||||||
|
if fighter.Player != nil && fighter.Player.GetInfo() != nil {
|
||||||
|
state.UserID = fighter.Player.GetInfo().UserID
|
||||||
|
}
|
||||||
|
if fighter.AttackValue != nil {
|
||||||
|
state.Status = fighter.AttackValue.Status
|
||||||
|
state.Prop = fighter.AttackValue.Prop
|
||||||
|
}
|
||||||
|
currentPet := fighter.CurrentPet()
|
||||||
|
if currentPet == nil {
|
||||||
|
states = append(states, state)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
state.ControllerUserID = currentPet.ControllerUserID
|
||||||
|
state.PetID = currentPet.Info.ID
|
||||||
|
state.CatchTime = currentPet.Info.CatchTime
|
||||||
|
state.Name = currentPet.Info.Name
|
||||||
|
state.HP = currentPet.Info.Hp
|
||||||
|
state.MaxHP = currentPet.Info.MaxHp
|
||||||
|
state.Level = currentPet.Info.Level
|
||||||
|
if len(currentPet.Info.SkillList) > 0 {
|
||||||
|
state.Skills = append(state.Skills, currentPet.Info.SkillList...)
|
||||||
|
}
|
||||||
|
states = append(states, state)
|
||||||
|
}
|
||||||
|
return states
|
||||||
|
}
|
||||||
113
logic/service/fight/unified_test.go
Normal file
113
logic/service/fight/unified_test.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package fight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"blazing/common/socket/errorcode"
|
||||||
|
"blazing/logic/service/common"
|
||||||
|
fightinfo "blazing/logic/service/fight/info"
|
||||||
|
"blazing/logic/service/fight/input"
|
||||||
|
spaceinfo "blazing/logic/service/space/info"
|
||||||
|
"blazing/modules/player/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stubPlayer struct {
|
||||||
|
info model.PlayerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*stubPlayer) ApplyPetDisplayInfo(*spaceinfo.SimpleInfo) {}
|
||||||
|
func (*stubPlayer) GetPlayerCaptureContext() *fightinfo.PlayerCaptureContext { return nil }
|
||||||
|
func (*stubPlayer) Roll(int, int) (bool, float64, float64) { return false, 0, 0 }
|
||||||
|
func (*stubPlayer) Getfightinfo() fightinfo.Fightinfo { return fightinfo.Fightinfo{} }
|
||||||
|
func (*stubPlayer) ItemAdd(int64, int64) bool { return false }
|
||||||
|
func (p *stubPlayer) GetInfo() *model.PlayerInfo { return &p.info }
|
||||||
|
func (*stubPlayer) InvitePlayer(common.PlayerI) {}
|
||||||
|
func (*stubPlayer) SetFightC(common.FightI) {}
|
||||||
|
func (*stubPlayer) QuitFight() {}
|
||||||
|
func (*stubPlayer) MessWin(bool) {}
|
||||||
|
func (*stubPlayer) CanFight() errorcode.ErrorCode { return 0 }
|
||||||
|
func (*stubPlayer) SendPackCmd(uint32, any) {}
|
||||||
|
func (*stubPlayer) GetPetInfo(uint32) []model.PetInfo { return nil }
|
||||||
|
|
||||||
|
func TestFightActionEnvelopeEncodedTargetIndex(t *testing.T) {
|
||||||
|
self := NewSkillActionEnvelope(1, 2, 0, SkillTargetSelf, 0)
|
||||||
|
if got := self.EncodedTargetIndex(); got != EncodeTargetIndex(2, false) {
|
||||||
|
t.Fatalf("expected self target to encode actor slot, got %d", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
ally := NewSkillActionEnvelope(1, 0, 1, SkillTargetAlly, 0)
|
||||||
|
if got := ally.EncodedTargetIndex(); got != EncodeTargetIndex(1, false) {
|
||||||
|
t.Fatalf("expected ally target to keep friendly slot, got %d", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackSelf := NewSkillActionEnvelope(1, 3, 0, 9, 3)
|
||||||
|
if got := fallbackSelf.EncodedTargetIndex(); got != EncodeTargetIndex(3, false) {
|
||||||
|
t.Fatalf("expected atkType=3 to fall back to self target, got %d", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
opponent := NewSkillActionEnvelope(1, 0, 2, SkillTargetOpponent, 0)
|
||||||
|
if got := opponent.EncodedTargetIndex(); got != EncodeTargetIndex(2, true) {
|
||||||
|
t.Fatalf("expected opponent target to stay on opposite side, got %d", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildFightStateStartEnvelope(t *testing.T) {
|
||||||
|
ourPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 1001}}
|
||||||
|
oppPlayer := &stubPlayer{info: model.PlayerInfo{UserID: 2002}}
|
||||||
|
|
||||||
|
our := input.NewInput(nil, ourPlayer)
|
||||||
|
our.InitAttackValue()
|
||||||
|
our.AttackValue.Prop[0] = 2
|
||||||
|
our.AttackValue.Status[1] = 1
|
||||||
|
ourPet := fightinfo.CreateBattlePetEntity(model.PetInfo{
|
||||||
|
ID: 11,
|
||||||
|
Name: "Alpha",
|
||||||
|
Level: 20,
|
||||||
|
Hp: 88,
|
||||||
|
MaxHp: 100,
|
||||||
|
CatchTime: 101,
|
||||||
|
SkillList: []model.SkillInfo{{ID: 300, PP: 10}},
|
||||||
|
})
|
||||||
|
ourPet.BindController(ourPlayer.info.UserID)
|
||||||
|
our.SetCurPetAt(0, ourPet)
|
||||||
|
|
||||||
|
opp := input.NewInput(nil, oppPlayer)
|
||||||
|
opp.InitAttackValue()
|
||||||
|
oppPet := fightinfo.CreateBattlePetEntity(model.PetInfo{
|
||||||
|
ID: 22,
|
||||||
|
Name: "Beta",
|
||||||
|
Level: 21,
|
||||||
|
Hp: 77,
|
||||||
|
MaxHp: 110,
|
||||||
|
CatchTime: 202,
|
||||||
|
SkillList: []model.SkillInfo{{ID: 400, PP: 5}},
|
||||||
|
})
|
||||||
|
oppPet.BindController(oppPlayer.info.UserID)
|
||||||
|
opp.SetCurPetAt(0, oppPet)
|
||||||
|
|
||||||
|
fc := &FightC{
|
||||||
|
Our: []*input.Input{our},
|
||||||
|
Opp: []*input.Input{opp},
|
||||||
|
}
|
||||||
|
fc.Round = 7
|
||||||
|
|
||||||
|
envelope := fc.BuildFightStateStartEnvelope()
|
||||||
|
if envelope.Phase != fightinfo.FightStatePhaseStart {
|
||||||
|
t.Fatalf("expected start phase, got %s", envelope.Phase)
|
||||||
|
}
|
||||||
|
if envelope.Meta.Round != 7 {
|
||||||
|
t.Fatalf("expected round 7, got %d", envelope.Meta.Round)
|
||||||
|
}
|
||||||
|
if len(envelope.Left) != 1 || len(envelope.Right) != 1 {
|
||||||
|
t.Fatalf("expected one fighter on each side, got left=%d right=%d", len(envelope.Left), len(envelope.Right))
|
||||||
|
}
|
||||||
|
if envelope.Left[0].UserID != 1001 || envelope.Left[0].PetID != 11 {
|
||||||
|
t.Fatalf("unexpected left fighter snapshot: %+v", envelope.Left[0])
|
||||||
|
}
|
||||||
|
if envelope.Left[0].Prop[0] != 2 || envelope.Left[0].Status[1] != 1 {
|
||||||
|
t.Fatalf("expected prop/status snapshot to be copied, got %+v %+v", envelope.Left[0].Prop, envelope.Left[0].Status)
|
||||||
|
}
|
||||||
|
if envelope.Right[0].UserID != 2002 || envelope.Right[0].CatchTime != 202 {
|
||||||
|
t.Fatalf("unexpected right fighter snapshot: %+v", envelope.Right[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user