refactor: 重构战斗结构体以支持双打模式
This commit is contained in:
@@ -17,8 +17,9 @@
|
|||||||
### 1.2 本清单目标
|
### 1.2 本清单目标
|
||||||
|
|
||||||
- 在不破坏现有 `1v1` 的前提下,落地组队战斗可运行版本(MVP)。
|
- 在不破坏现有 `1v1` 的前提下,落地组队战斗可运行版本(MVP)。
|
||||||
- 对齐 `flash` 组队战斗关键协议与关键行为(开战、出招、切宠、道具、结算、战斗结束)。
|
- 对齐 `flash`/社区实现中的关键行为(开战、出招、切宠、道具、结算、战斗结束)。
|
||||||
- 留出兼容层,允许旧 `24xx/25xx` 流程继续使用。
|
- 协议层采用“一个统一结构体 + phase 字段”方案,单打/双打共用同一序列化模型。
|
||||||
|
- 保留旧 `24xx/25xx` 流程入口,通过服务端适配映射到统一结构体。
|
||||||
|
|
||||||
### 1.3 非目标
|
### 1.3 非目标
|
||||||
|
|
||||||
@@ -49,74 +50,58 @@
|
|||||||
- 回合主链仍以双动作兼容流程为中心
|
- 回合主链仍以双动作兼容流程为中心
|
||||||
- 组队相关特性存在 TODO(例如 `501/502/503`)
|
- 组队相关特性存在 TODO(例如 `501/502/503`)
|
||||||
|
|
||||||
|
### 2.3 外部实现参考(本次新增)
|
||||||
|
|
||||||
|
- `arcadia-star/seer2-fight-ui`
|
||||||
|
- 双打核心模型不是独立命令集,而是统一帧模型 + `uiStyle + side + position`。
|
||||||
|
- `uiStyle` 支持 `2v2/2v1`,战位通过 `position(main/sub)` 区分。
|
||||||
|
- `arcadia-star/seer2-next-message/src/entity/fight.rs`
|
||||||
|
- 采用统一战斗实体结构:`team/user/pet` + `side/position`。
|
||||||
|
- 行为包拆分为 `Load/Hurt/Change/Escape/...`,但底层字段模型统一。
|
||||||
|
- `ukuq/seer2-server/src/seer2/fight`
|
||||||
|
- `ArenaResourceLoadCMD -> TeamInfo -> FightUserInfo -> FighterInfo` 为层级化统一结构。
|
||||||
|
- `FighterInfo` 直接包含 `position/hp/maxHp/anger/skills`,适合直接映射为本项目统一结构体。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. 协议对齐清单(按优先级)
|
## 3. 协议对齐清单(按优先级)
|
||||||
|
|
||||||
> 说明:命令号来源于 `flash` 的 `4c07fa07:CommandID.as`。
|
> 说明:本清单改为“统一协议结构体”路线,不再强制先实现 `75xx` 独立命令族。
|
||||||
> 实施时可先做“服务端可跑+字段兼容”,逐步补全所有字段。
|
> 推荐做法:保留旧入口命令,服务端内部统一转为 `FightActionEnvelope/FightStateEnvelope`。
|
||||||
|
|
||||||
### 3.1 P0 必做(MVP 必须)
|
### 3.1 P0 必做(MVP 必须)
|
||||||
|
|
||||||
- [ ] `7555 GROUP_READY_TO_FIGHT`
|
- [ ] 统一入站动作结构 `FightActionEnvelope`
|
||||||
- 用途:组队成员准备
|
- 最少字段:`actionType/actorIndex/targetIndex/skillId/itemId/catchTime/escape/chat`
|
||||||
- 入参建议:`room/groupId + userId + actorSlotsReady`
|
- 兼容映射:
|
||||||
- 出参建议:广播 ready 状态变化
|
- `2405 -> actionType=skill`
|
||||||
|
- `2406 -> actionType=item`
|
||||||
- [ ] `7557 GROUP_START_FIGHT`
|
- `2407 -> actionType=change`
|
||||||
- 用途:下发组队开战结构
|
- `2410 -> actionType=escape`
|
||||||
- 参考客户端结构:
|
- [ ] 统一出站状态结构 `FightStateEnvelope`
|
||||||
- `isVsNPC`
|
- 最少字段:
|
||||||
- `groupOneMembCnt + groupOneList[]`
|
- `phase`(`start/skill_hurt/change/over/load/chat`)
|
||||||
- `groupTwoMembCnt + groupTwoList[]`
|
- `left[]/right[]`(元素为统一 `FighterState`)
|
||||||
- `group list` 每项至少要有:`side/pos/userID/petID/catchTime/hp/maxHP/level/catchType`
|
- `meta`(回合号、天气、胜负、结束原因)
|
||||||
|
- [ ] 统一战位子结构 `FighterState`
|
||||||
- [ ] `7558 GROUP_FT_USE_SKILL`
|
- 每项至少包含:`side/position(userSlot)/userId/petId(catchTime)/hp/maxHp/level/anger/status/prop/skills`
|
||||||
- 用途:组队技能指令
|
|
||||||
- 参考客户端 payload:`skillId(uint32) + side(byte) + pos(byte)`(6字节)
|
|
||||||
- 服务端内部映射:`actorIndex + targetIndex`
|
|
||||||
|
|
||||||
- [ ] `7563 GROUP_FT_CHG_PET`
|
|
||||||
- 用途:组队切宠请求
|
|
||||||
- 至少支持:按操作者位切宠
|
|
||||||
|
|
||||||
- [ ] `7562 GROUP_FT_USE_ITEM`
|
|
||||||
- 用途:组队用道具
|
|
||||||
- 至少支持:按 actor/target 生效
|
|
||||||
|
|
||||||
- [ ] `7560 GROUP_FT_OVER`
|
|
||||||
- 用途:组队战斗结束
|
|
||||||
- 至少下发:结束原因、胜方标识、结算主体
|
|
||||||
|
|
||||||
### 3.2 P1 强烈建议(提升一致性)
|
### 3.2 P1 强烈建议(提升一致性)
|
||||||
|
|
||||||
- [ ] `7559 GROUP_FT_SKILL_HURT`
|
- [ ] 完善 `phase=skill_hurt`
|
||||||
- 对齐技能伤害播报(攻击方+受击方快照)
|
- 至少带:施法方快照、受击方快照、技能、暴击、伤害、HP 变更
|
||||||
- 参考结构包含:状态数组、能力等级、技能、HP 变化、暴击、伤害值
|
- [ ] 完善 `phase=change`
|
||||||
|
- 至少带:切宠发起位、切入目标位、新精灵状态
|
||||||
- [ ] `7567 GROUP_FT_CHG_PET_SUC`
|
- [ ] 完善 `phase=over`
|
||||||
- 切宠成功广播,保障前端战位同步
|
- 至少带:结束原因、胜方、收益主体
|
||||||
|
- [ ] 完善 `phase=load/chat`
|
||||||
- [ ] `7568 GROUP_FT_ESCAPE_SUC`
|
- 组队加载进度、战斗内聊天统一走同一 envelope
|
||||||
- 逃跑成功广播(含退出战位)
|
|
||||||
|
|
||||||
- [ ] `7570 GROUP_CHAT_MSG`
|
|
||||||
- 组队战斗聊天转发
|
|
||||||
|
|
||||||
- [ ] `7571/7572 GROUP_LOAD_PERCENT(_NOTICE)`
|
|
||||||
- 组队加载进度同步
|
|
||||||
|
|
||||||
### 3.3 P2 视时间补齐
|
### 3.3 P2 视时间补齐
|
||||||
|
|
||||||
- [ ] `7561 GROUP_FT_SPRITE_DIE`
|
- [ ] `phase=sprite_die/sprite_notice/win_close`
|
||||||
- [ ] `7573 GROUP_FT_SPRITE_NOTICE`
|
- [ ] `phase=skill_wait/skill_wait_notice`
|
||||||
- [ ] `7574 GROUP_FT_WIN_CLOSE`
|
- [ ] `phase=overtime/timeout_exit/relation_notice`
|
||||||
- [ ] `7575 GP_SKILL_WAIT_PERT`
|
|
||||||
- [ ] `7576 GP_SKILL_WAIT_NOTICE`
|
|
||||||
- [ ] `7585 GROUP_FT_OVERTIME_NOTICE`
|
|
||||||
- [ ] `7586 GROUP_FT_SKILL_PLAY_OVER`
|
|
||||||
- [ ] `7587 GROUP_FT_TIME_OUT_EXIT`
|
|
||||||
- [ ] `7588 GROUP_FT_RELATION_NOTICE`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -124,13 +109,13 @@
|
|||||||
|
|
||||||
## 4.1 协议与结构层(Owner A)
|
## 4.1 协议与结构层(Owner A)
|
||||||
|
|
||||||
- [ ] 新增组队协议结构文件
|
- [ ] 新增统一协议结构文件
|
||||||
- 建议新建:`logic/service/fight/cmd_group.go`
|
- 建议新建:`logic/service/fight/cmd_unified.go`
|
||||||
- 要求:所有组队命令入站结构都有 `TomeeHeader cmd:"xxxx"`
|
- 要求:统一定义 `FightActionEnvelope` 和映射辅助结构
|
||||||
|
|
||||||
- [ ] 新增组队出站结构
|
- [ ] 新增统一出站结构
|
||||||
- 建议新建:`logic/service/fight/info/group_info.go`
|
- 建议新建:`logic/service/fight/info/unified_info.go`
|
||||||
- 要求:明确 `side/pos/user/pet/hp/maxHp/level` 等核心字段
|
- 要求:定义 `FightStateEnvelope/FighterState`,支持单打与双打
|
||||||
|
|
||||||
- [ ] 统一战位字段命名规范
|
- [ ] 统一战位字段命名规范
|
||||||
- `actorIndex`:我方执行位
|
- `actorIndex`:我方执行位
|
||||||
@@ -139,26 +124,20 @@
|
|||||||
|
|
||||||
验收:
|
验收:
|
||||||
|
|
||||||
- [ ] `controller.Init(true)` 能注册全部新命令,无重复 cmd panic。
|
- [ ] 旧 cmd(`2405/2406/2407/2410`)可无损映射到统一入站结构。
|
||||||
- [ ] 对每个新 cmd,可反序列化入参并进入控制器方法。
|
- [ ] 统一出站结构在 `start/skill_hurt/change/over` phase 均可序列化。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4.2 控制器与路由层(Owner B)
|
## 4.2 控制器与路由层(Owner B)
|
||||||
|
|
||||||
- [ ] 新增组队战斗控制器
|
- [ ] 新增统一动作入口(可单文件)
|
||||||
- 建议新建:`logic/controller/fight_group.go`
|
- 建议新建:`logic/controller/fight_unified.go`
|
||||||
- 包含:
|
- 用途:将旧包和未来扩展包统一落到 `FightActionEnvelope`
|
||||||
- 组队准备
|
|
||||||
- 组队出招
|
|
||||||
- 组队切宠
|
|
||||||
- 组队道具
|
|
||||||
- 组队逃跑
|
|
||||||
- 组队聊天
|
|
||||||
|
|
||||||
- [ ] 兼容旧协议入口
|
- [ ] 兼容旧协议入口
|
||||||
- `2405/2406/2407` 保持可用(默认 `actorIndex=0,targetIndex=0`)
|
- `2405/2406/2407` 保持可用(默认 `actorIndex=0,targetIndex=0`)
|
||||||
- 新 `75xx` 走组队专用入口
|
- 组队场景由 `actorIndex/targetIndex` 与战斗上下文决定,不再依赖独立 `75xx`
|
||||||
|
|
||||||
- [ ] 增加战前校验
|
- [ ] 增加战前校验
|
||||||
- 成员是否在同一组队房间
|
- 成员是否在同一组队房间
|
||||||
@@ -167,7 +146,7 @@
|
|||||||
|
|
||||||
验收:
|
验收:
|
||||||
|
|
||||||
- [ ] 双端同时发送 `7558` 能转化为 `UseSkillAt(...)`。
|
- [ ] 任意技能动作都能转化为 `UseSkillAt(...)`(含 `actorIndex/targetIndex`)。
|
||||||
- [ ] 非法战位命令被拒绝,不影响其他战位。
|
- [ ] 非法战位命令被拒绝,不影响其他战位。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -215,8 +194,8 @@
|
|||||||
- 战斗结束广播
|
- 战斗结束广播
|
||||||
|
|
||||||
- [ ] 保留旧包兼容(必要时双发)
|
- [ ] 保留旧包兼容(必要时双发)
|
||||||
- 组队战斗对新包
|
- 单打/双打统一走同一结构体
|
||||||
- 非组队仍可走旧 `2503/2505/2506`
|
- 如前端未升级,可按需保留旧 `2503/2505/2506` 过渡映射
|
||||||
|
|
||||||
验收:
|
验收:
|
||||||
|
|
||||||
@@ -276,14 +255,14 @@
|
|||||||
|
|
||||||
- 协议/结构:
|
- 协议/结构:
|
||||||
- `logic/service/fight/cmd.go`
|
- `logic/service/fight/cmd.go`
|
||||||
- `logic/service/fight/cmd_group.go`(新增)
|
- `logic/service/fight/cmd_unified.go`(新增)
|
||||||
- `logic/service/fight/info/info.go`
|
- `logic/service/fight/info/info.go`
|
||||||
- `logic/service/fight/info/group_info.go`(新增)
|
- `logic/service/fight/info/unified_info.go`(新增)
|
||||||
|
|
||||||
- 控制器:
|
- 控制器:
|
||||||
- `logic/controller/fight_base.go`
|
- `logic/controller/fight_base.go`
|
||||||
- `logic/controller/fight_pvp_withplayer.go`
|
- `logic/controller/fight_pvp_withplayer.go`
|
||||||
- `logic/controller/fight_group.go`(新增)
|
- `logic/controller/fight_unified.go`(新增)
|
||||||
|
|
||||||
- 核心流程:
|
- 核心流程:
|
||||||
- `logic/service/fight/new.go`
|
- `logic/service/fight/new.go`
|
||||||
@@ -309,8 +288,8 @@
|
|||||||
|
|
||||||
### M1(协议可通)
|
### M1(协议可通)
|
||||||
|
|
||||||
- [ ] 75xx 关键命令可收发
|
- [ ] 统一结构体可完成 `start/skill_hurt/change/over` 四类下发
|
||||||
- [ ] 控制器能转发到 `FightC` indexed 接口
|
- [ ] 旧命令入口均可映射到 `FightC` indexed 接口
|
||||||
|
|
||||||
### M2(核心可跑)
|
### M2(核心可跑)
|
||||||
|
|
||||||
@@ -339,7 +318,7 @@
|
|||||||
缓解:把“回合开始/结束”从 pair 执行中抽离,确保每回合只触发一次。
|
缓解:把“回合开始/结束”从 pair 执行中抽离,确保每回合只触发一次。
|
||||||
|
|
||||||
- 风险:协议切换导致旧客户端不可用。
|
- 风险:协议切换导致旧客户端不可用。
|
||||||
缓解:保留旧包并按战斗类型分流;必要时过渡期双发。
|
缓解:服务端保持旧入口不变,先做“旧包 -> 统一结构”映射;前端按版本切流。
|
||||||
|
|
||||||
- 风险:effect 批量改动引发回归。
|
- 风险:effect 批量改动引发回归。
|
||||||
缓解:先做组队关键 effect,其他 effect 分批迁移并每批回归。
|
缓解:先做组队关键 effect,其他 effect 分批迁移并每批回归。
|
||||||
@@ -363,3 +342,16 @@
|
|||||||
- 如改动协议字段,必须附抓包样例或字段注释,不允许只改代码不补说明。
|
- 如改动协议字段,必须附抓包样例或字段注释,不允许只改代码不补说明。
|
||||||
- 如发现与本清单冲突的历史逻辑,以“兼容线上行为优先”,并在文档记录偏差原因。
|
- 如发现与本清单冲突的历史逻辑,以“兼容线上行为优先”,并在文档记录偏差原因。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 可实现性结论(统一协议结构体)
|
||||||
|
|
||||||
|
- 结论:可实现,且风险可控。
|
||||||
|
- 依据:
|
||||||
|
- `seer2-fight-ui` 的双打模型本质是统一数据结构 + `uiStyle/side/position`,不是强依赖独立命令族。
|
||||||
|
- `seer2-next-message` 与 `seer2-server` 都采用统一 `team/user/pet` 层级结构,`position` 作为战位核心字段。
|
||||||
|
- 本仓库已具备 `actorIndex/targetIndex` 与 `UseSkillAt/ChangePetAt/UseItemAt` 能力,协议统一后只需补齐映射和广播。
|
||||||
|
- 实施建议:
|
||||||
|
- 先完成“旧入口 -> 统一入站结构”映射。
|
||||||
|
- 再完成“统一出站结构 + phase 广播”。
|
||||||
|
- 最后做前端切换与旧包退场(或长期双通道兼容)。
|
||||||
|
|||||||
@@ -280,15 +280,10 @@ func (f *FightC) buildFightStartInfo() info.FightStartOutboundInfo {
|
|||||||
startInfo := info.FightStartOutboundInfo{}
|
startInfo := info.FightStartOutboundInfo{}
|
||||||
ourInfos := f.collectFightPetInfos(f.Our)
|
ourInfos := f.collectFightPetInfos(f.Our)
|
||||||
oppInfos := f.collectFightPetInfos(f.Opp)
|
oppInfos := f.collectFightPetInfos(f.Opp)
|
||||||
startInfo.Infos = append(startInfo.Infos, ourInfos...)
|
startInfo.Info1 = append(startInfo.Info1, ourInfos...)
|
||||||
startInfo.Infos = append(startInfo.Infos, oppInfos...)
|
startInfo.Info2 = append(startInfo.Info2, oppInfos...)
|
||||||
startInfo.InfoLen = uint32(len(startInfo.Infos))
|
startInfo.Info1Len = uint32(len(startInfo.Info1))
|
||||||
if len(ourInfos) > 0 {
|
startInfo.Info2Len = uint32(len(startInfo.Info2))
|
||||||
startInfo.Info1 = ourInfos[0]
|
|
||||||
}
|
|
||||||
if len(oppInfos) > 0 {
|
|
||||||
startInfo.Info2 = oppInfos[0]
|
|
||||||
}
|
|
||||||
return startInfo
|
return startInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ func (e *Effect1263) Skill_Use() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
divisor := e.Args()[0]
|
divisor := e.Args()[0]
|
||||||
if e.CarrierInput().CurPet[0].GetHP().Cmp(e.OpponentInput().CurPet[0].GetHP()) < 0 {
|
if e.CarrierPet().GetHP().Cmp(e.OpponentPet().GetHP()) < 0 {
|
||||||
divisor = e.Args()[1]
|
divisor = e.Args()[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
damage := e.OpponentInput().CurPet[0].GetMaxHP().Div(divisor)
|
damage := e.OpponentPet().GetMaxHP().Div(divisor)
|
||||||
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ type Effect1268 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Effect1268) Skill_Use() bool {
|
func (e *Effect1268) Skill_Use() bool {
|
||||||
if len(e.Args()) == 0 || e.OpponentInput().CurPet[0].Info.Hp > 0 {
|
if len(e.Args()) == 0 || e.OpponentPet().Info.Hp > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
effect := e.CarrierInput().InitEffect(input.EffectType.Sub, 1268, int(e.Args()[0].IntPart()))
|
effect := e.CarrierInput().InitEffect(input.EffectType.Sub, 1268, int(e.Args()[0].IntPart()))
|
||||||
@@ -246,7 +246,7 @@ func (e *Effect1269) SkillHit_ex() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
damage := e.OpponentInput().CurPet[0].GetMaxHP().Div(e.Args()[1])
|
damage := e.OpponentPet().GetMaxHP().Div(e.Args()[1])
|
||||||
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -383,10 +383,10 @@ func (e *Effect1273) OnSkill() bool {
|
|||||||
if len(e.Args()) == 0 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 {
|
if len(e.Args()) == 0 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if e.CarrierInput().CurPet[0].GetHP().Cmp(e.CarrierInput().CurPet[0].GetMaxHP()) != 0 {
|
if e.CarrierPet().GetHP().Cmp(e.CarrierPet().GetMaxHP()) != 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
damage := e.CarrierInput().CurPet[0].GetMaxHP().Div(e.Args()[0])
|
damage := e.CarrierPet().GetMaxHP().Div(e.Args()[0])
|
||||||
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -423,7 +423,7 @@ func (e *Effect1275) Skill_Use() bool {
|
|||||||
if len(e.Args()) == 0 {
|
if len(e.Args()) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierInput().CurPet[0].GetHP()})
|
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierPet().GetHP()})
|
||||||
effect := e.CarrierInput().InitEffect(input.EffectType.Sub, 1275, int(e.Args()[0].IntPart()))
|
effect := e.CarrierInput().InitEffect(input.EffectType.Sub, 1275, int(e.Args()[0].IntPart()))
|
||||||
if effect != nil {
|
if effect != nil {
|
||||||
e.CarrierInput().AddEffect(e.CarrierInput(), effect)
|
e.CarrierInput().AddEffect(e.CarrierInput(), effect)
|
||||||
@@ -468,8 +468,8 @@ func (e *Effect1276) Skill_Use() bool {
|
|||||||
if len(e.Args()) == 0 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 {
|
if len(e.Args()) == 0 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
damage := e.CarrierInput().CurPet[0].GetHP().Div(e.Args()[0])
|
damage := e.CarrierPet().GetHP().Div(e.Args()[0])
|
||||||
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierInput().CurPet[0].GetHP()})
|
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierPet().GetHP()})
|
||||||
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
||||||
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: damage})
|
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: damage})
|
||||||
}
|
}
|
||||||
@@ -482,16 +482,16 @@ type Effect1277 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Effect1277) Skill_Use() bool {
|
func (e *Effect1277) Skill_Use() bool {
|
||||||
if e.CarrierInput().CurPet[0].GetHP().Cmp(e.OpponentInput().CurPet[0].GetHP()) > 0 {
|
if e.CarrierPet().GetHP().Cmp(e.OpponentPet().GetHP()) > 0 {
|
||||||
leave := alpacadecimal.NewFromInt(1)
|
leave := alpacadecimal.NewFromInt(1)
|
||||||
if e.CarrierInput().CurPet[0].GetHP().Cmp(leave) > 0 {
|
if e.CarrierPet().GetHP().Cmp(leave) > 0 {
|
||||||
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierInput().CurPet[0].GetHP().Sub(leave)})
|
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: e.CarrierPet().GetHP().Sub(leave)})
|
||||||
}
|
}
|
||||||
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: alpacadecimal.NewFromInt(int64(grand.N(350, 550)))})
|
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: alpacadecimal.NewFromInt(int64(grand.N(350, 550)))})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
lost := e.CarrierInput().CurPet[0].GetHP()
|
lost := e.CarrierPet().GetHP()
|
||||||
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: lost})
|
e.CarrierInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: lost})
|
||||||
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: lost})
|
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: lost})
|
||||||
applyStatusByID(e.CarrierInput(), e.OpponentInput(), int(info.PetStatus.Paralysis))
|
applyStatusByID(e.CarrierInput(), e.OpponentInput(), int(info.PetStatus.Paralysis))
|
||||||
@@ -614,7 +614,7 @@ func (e *Effect1280) OnSkill() bool {
|
|||||||
if percent > max {
|
if percent > max {
|
||||||
percent = max
|
percent = max
|
||||||
}
|
}
|
||||||
damage := e.CarrierInput().CurPet[0].GetHP().Mul(alpacadecimal.NewFromInt(int64(percent))).Div(alpacadecimal.NewFromInt(100))
|
damage := e.CarrierPet().GetHP().Mul(alpacadecimal.NewFromInt(int64(percent))).Div(alpacadecimal.NewFromInt(100))
|
||||||
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
||||||
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: damage})
|
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: damage})
|
||||||
}
|
}
|
||||||
@@ -634,12 +634,12 @@ func (e *Effect1281) Skill_Use() bool {
|
|||||||
}
|
}
|
||||||
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: shield})
|
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Fixed, Damage: shield})
|
||||||
if shield.Cmp(alpacadecimal.NewFromInt(300)) > 0 {
|
if shield.Cmp(alpacadecimal.NewFromInt(300)) > 0 {
|
||||||
dmg := e.CarrierInput().CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(3))
|
dmg := e.CarrierPet().GetMaxHP().Div(alpacadecimal.NewFromInt(3))
|
||||||
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: dmg})
|
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: dmg})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if shield.Cmp(alpacadecimal.NewFromInt(300)) < 0 {
|
if shield.Cmp(alpacadecimal.NewFromInt(300)) < 0 {
|
||||||
val := e.CarrierInput().CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(3))
|
val := e.CarrierPet().GetMaxHP().Div(alpacadecimal.NewFromInt(3))
|
||||||
e.CarrierInput().Heal(e.CarrierInput(), &action.SelectSkillAction{}, val)
|
e.CarrierInput().Heal(e.CarrierInput(), &action.SelectSkillAction{}, val)
|
||||||
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: val})
|
e.OpponentInput().Damage(e.CarrierInput(), &info.DamageZone{Type: info.DamageType.Percent, Damage: val})
|
||||||
}
|
}
|
||||||
@@ -729,7 +729,7 @@ type Effect1285 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Effect1285) Skill_Use() bool {
|
func (e *Effect1285) Skill_Use() bool {
|
||||||
if len(e.Args()) == 0 || e.OpponentInput().CurPet[0].Info.Hp == 0 {
|
if len(e.Args()) == 0 || e.OpponentPet().Info.Hp == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
zeroRandomSkillPP(e.OpponentInput(), int(e.Args()[0].IntPart()))
|
zeroRandomSkillPP(e.OpponentInput(), int(e.Args()[0].IntPart()))
|
||||||
|
|||||||
@@ -68,26 +68,33 @@ func (e *Effect201) OnSkill() bool {
|
|||||||
|
|
||||||
healAll := len(args) > 1 && args[0].IntPart() != 0
|
healAll := len(args) > 1 && args[0].IntPart() != 0
|
||||||
if !healAll {
|
if !healAll {
|
||||||
e.Ctx().Our.Heal(
|
target := e.TargetInput()
|
||||||
e.Ctx().Our,
|
if target == nil {
|
||||||
|
target = e.Ctx().Our
|
||||||
|
}
|
||||||
|
current := target.CurrentPet()
|
||||||
|
if current == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
target.Heal(
|
||||||
|
e.CarrierInput(),
|
||||||
&action.SelectSkillAction{},
|
&action.SelectSkillAction{},
|
||||||
e.Ctx().Our.CurPet[0].GetMaxHP().Div(divisor),
|
current.GetMaxHP().Div(divisor),
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pet := range e.Ctx().Our.AllPet {
|
team := e.Ctx().Our.Team
|
||||||
if pet == nil || !pet.Alive() {
|
if len(team) == 0 {
|
||||||
|
team = []*input.Input{e.Ctx().Our}
|
||||||
|
}
|
||||||
|
for _, ally := range team {
|
||||||
|
if ally == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if current := ally.CurrentPet(); current != nil && current.Alive() {
|
||||||
healAmount := pet.GetMaxHP().Div(divisor)
|
ally.Heal(e.CarrierInput(), &action.SelectSkillAction{}, current.GetMaxHP().Div(divisor))
|
||||||
if pet == e.Ctx().Our.CurPet[0] {
|
|
||||||
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, healAmount)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pet.Info.ModelHP(healAmount.IntPart())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -35,9 +35,14 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
|
|||||||
})
|
})
|
||||||
var originalProps [2][6]int8
|
var originalProps [2][6]int8
|
||||||
var originalPetInfo [2]model.PetInfo
|
var originalPetInfo [2]model.PetInfo
|
||||||
|
attackerPet := attacker.CurrentPet()
|
||||||
|
defenderPet := defender.CurrentPet()
|
||||||
|
if attackerPet == nil || defenderPet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
//复制属性
|
//复制属性
|
||||||
originalProps[0], originalProps[1] = attacker.Prop, defender.Prop
|
originalProps[0], originalProps[1] = attacker.Prop, defender.Prop
|
||||||
originalPetInfo[0], originalPetInfo[1] = attacker.CurPet[0].Info, defender.CurPet[0].Info
|
originalPetInfo[0], originalPetInfo[1] = attackerPet.Info, defenderPet.Info
|
||||||
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool {
|
||||||
//计算变威力
|
//计算变威力
|
||||||
effect.Ctx().SkillEntity = skill
|
effect.Ctx().SkillEntity = skill
|
||||||
@@ -59,7 +64,7 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
|
|||||||
|
|
||||||
//还原属性
|
//还原属性
|
||||||
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
|
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
|
||||||
attacker.CurPet[0].Info, defender.CurPet[0].Info = originalPetInfo[0], originalPetInfo[1]
|
attackerPet.Info, defenderPet.Info = originalPetInfo[0], originalPetInfo[1]
|
||||||
|
|
||||||
if attacker.IsCritical == 1 { //命中了才有暴击
|
if attacker.IsCritical == 1 { //命中了才有暴击
|
||||||
//暴击破防
|
//暴击破防
|
||||||
@@ -130,6 +135,28 @@ func (f *FightC) getSkillParticipants(skillAction *action.SelectSkillAction) (*i
|
|||||||
return f.GetInputByAction(skillAction, false), f.GetInputByAction(skillAction, true)
|
return f.GetInputByAction(skillAction, false), f.GetInputByAction(skillAction, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FightC) collectAttackValues(inputs []*input.Input) []model.AttackValue {
|
||||||
|
values := make([]model.AttackValue, 0, len(inputs))
|
||||||
|
for actorIndex, fighter := range inputs {
|
||||||
|
if fighter == nil || fighter.AttackValue == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
attackValue := *fighter.AttackValue
|
||||||
|
attackValue.ActorIndex = uint32(actorIndex)
|
||||||
|
values = append(values, attackValue)
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FightC) buildNoteUseSkillOutboundInfo() info.NoteUseSkillOutboundInfo {
|
||||||
|
result := info.NoteUseSkillOutboundInfo{}
|
||||||
|
result.FirstAttackInfo = append(result.FirstAttackInfo, f.collectAttackValues(f.Our)...)
|
||||||
|
result.SecondAttackInfo = append(result.SecondAttackInfo, f.collectAttackValues(f.Opp)...)
|
||||||
|
result.FirstAttackInfoLen = uint32(len(result.FirstAttackInfo))
|
||||||
|
result.SecondAttackInfoLen = uint32(len(result.SecondAttackInfo))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// enterturn 处理战斗回合逻辑
|
// enterturn 处理战斗回合逻辑
|
||||||
// 回合有先手方和后手方,同时有攻击方和被攻击方
|
// 回合有先手方和后手方,同时有攻击方和被攻击方
|
||||||
func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) {
|
func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) {
|
||||||
@@ -235,16 +262,18 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentSkill = originalSkill
|
currentSkill = originalSkill
|
||||||
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //这个是能否使用技能
|
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //这个是能否使用技能
|
||||||
effect.Ctx().SkillEntity = currentSkill
|
effect.Ctx().SkillEntity = currentSkill
|
||||||
return effect.ActionStartEx(firstAttack, secondAttack)
|
return effect.ActionStartEx(firstAttack, secondAttack)
|
||||||
})
|
})
|
||||||
canUseSkill := attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //这个是能否使用技能
|
canUseSkill := attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //这个是能否使用技能
|
||||||
effect.Ctx().SkillEntity = currentSkill
|
effect.Ctx().SkillEntity = currentSkill
|
||||||
return effect.ActionStart(firstAttack, secondAttack)
|
return effect.ActionStart(firstAttack, secondAttack)
|
||||||
})
|
})
|
||||||
|
|
||||||
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attacker.CurPet[0].Info.Hp > 0
|
attackerPet := attacker.CurrentPet()
|
||||||
|
defenderPet := defender.CurrentPet()
|
||||||
|
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attackerPet != nil && attackerPet.Info.Hp > 0
|
||||||
|
|
||||||
if !canUse {
|
if !canUse {
|
||||||
|
|
||||||
@@ -268,7 +297,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
f.processSkillAttack(attacker, defender, currentSkill)
|
f.processSkillAttack(attacker, defender, currentSkill)
|
||||||
currentSkill = originalSkill //还原技能
|
currentSkill = originalSkill //还原技能
|
||||||
|
|
||||||
_, skill, ok := utils.FindWithIndex(attacker.CurPet[0].Info.SkillList, func(item model.SkillInfo) bool {
|
_, skill, ok := utils.FindWithIndex(attackerPet.Info.SkillList, func(item model.SkillInfo) bool {
|
||||||
return item.ID == currentSkill.Info.ID
|
return item.ID == currentSkill.Info.ID
|
||||||
})
|
})
|
||||||
if ok {
|
if ok {
|
||||||
@@ -281,7 +310,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
skill.Use(usecount)
|
skill.Use(usecount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if defender.CurPet[0].Info.Hp > 0 {
|
if defenderPet != nil && defenderPet.Info.Hp > 0 {
|
||||||
//技能使用后
|
//技能使用后
|
||||||
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
|
defender.ExecWithOpponent(attacker, func(effect input.Effect) bool { //技能使用后的我方效果
|
||||||
effect.Ctx().SkillEntity = currentSkill
|
effect.Ctx().SkillEntity = currentSkill
|
||||||
@@ -290,7 +319,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if attacker.CurPet[0].Info.Hp > 0 {
|
if attackerPet != nil && attackerPet.Info.Hp > 0 {
|
||||||
//技能使用后
|
//技能使用后
|
||||||
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
|
attacker.ExecWithOpponent(defender, func(effect input.Effect) bool { //技能使用后的我方效果
|
||||||
effect.Ctx().SkillEntity = currentSkill
|
effect.Ctx().SkillEntity = currentSkill
|
||||||
@@ -311,16 +340,16 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
if defender.CurPet[0].Info.Hp <= 0 && attacker.CurPet[0].Info.Hp <= 0 { //先手方死亡,触发反同归于尽
|
if defenderPet != nil && attackerPet != nil && defenderPet.Info.Hp <= 0 && attackerPet.Info.Hp <= 0 { //先手方死亡,触发反同归于尽
|
||||||
attacker.CurPet[0].Info.Hp = 1
|
attackerPet.Info.Hp = 1
|
||||||
}
|
}
|
||||||
if defender.CurPet[0].Info.Hp <= 0 {
|
if defenderPet != nil && defenderPet.Info.Hp <= 0 {
|
||||||
|
|
||||||
f.TURNOVER(defender)
|
f.TURNOVER(defender)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if attacker.CurPet[0].Info.Hp <= 0 {
|
if attackerPet != nil && attackerPet.Info.Hp <= 0 {
|
||||||
f.TURNOVER(attacker)
|
f.TURNOVER(attacker)
|
||||||
|
|
||||||
break
|
break
|
||||||
@@ -343,10 +372,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attackValueResult := info.AttackValueS{
|
attackValueResult := f.buildNoteUseSkillOutboundInfo()
|
||||||
FAttack: *f.First.AttackValue,
|
|
||||||
SAttack: *f.Second.AttackValue,
|
|
||||||
}
|
|
||||||
//因为切完才能广播,所以必须和回合结束分开结算
|
//因为切完才能广播,所以必须和回合结束分开结算
|
||||||
f.Broadcast(func(fighter *input.Input) {
|
f.Broadcast(func(fighter *input.Input) {
|
||||||
for _, switchAction := range f.Switch {
|
for _, switchAction := range f.Switch {
|
||||||
|
|||||||
@@ -103,10 +103,6 @@ type FightPetInfo struct {
|
|||||||
//能力提升属性
|
//能力提升属性
|
||||||
Prop [6]int8
|
Prop [6]int8
|
||||||
}
|
}
|
||||||
type AttackValueS struct {
|
|
||||||
FAttack model.AttackValue
|
|
||||||
SAttack model.AttackValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAttackValue(userid uint32) *model.AttackValue {
|
func NewAttackValue(userid uint32) *model.AttackValue {
|
||||||
return &model.AttackValue{
|
return &model.AttackValue{
|
||||||
@@ -200,21 +196,22 @@ type PropDict struct {
|
|||||||
|
|
||||||
// NoteUseSkillOutboundInfo 战斗技能使用通知的出站信息结构体
|
// NoteUseSkillOutboundInfo 战斗技能使用通知的出站信息结构体
|
||||||
type NoteUseSkillOutboundInfo struct {
|
type NoteUseSkillOutboundInfo struct {
|
||||||
FirstAttackInfo model.AttackValue // 本轮先手的精灵在释放技能结束后的状态
|
FirstAttackInfoLen uint32 `struc:"sizeof=FirstAttackInfo"`
|
||||||
SecondAttackInfo model.AttackValue // 本轮后手的精灵在释放技能结束后的状态
|
FirstAttackInfo []model.AttackValue // 本轮先手方精灵在释放技能结束后的状态
|
||||||
|
SecondAttackInfoLen uint32 `struc:"sizeof=SecondAttackInfo"`
|
||||||
|
SecondAttackInfo []model.AttackValue // 本轮后手方精灵在释放技能结束后的状态
|
||||||
}
|
}
|
||||||
|
|
||||||
type FightStartOutboundInfo struct {
|
type FightStartOutboundInfo struct {
|
||||||
IsCanAuto uint32 `fieldDesc:"是否自动 默认给0 怀疑是自动战斗器使用的" `
|
IsCanAuto uint32 `fieldDesc:"是否自动 默认给0 怀疑是自动战斗器使用的" `
|
||||||
|
|
||||||
InfoLen uint32 `struc:"sizeof=Infos"`
|
Info1Len uint32 `struc:"sizeof=Info1"`
|
||||||
Infos []FightPetInfo
|
// 我方当前战斗精灵信息(前端通过userid判断是否为我方)
|
||||||
|
Info1 []FightPetInfo
|
||||||
|
|
||||||
// 当前战斗精灵信息1(前端通过userid判断是否为我方)
|
Info2Len uint32 `struc:"sizeof=Info2"`
|
||||||
Info1 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
|
// 对方当前战斗精灵信息(前端通过userid判断是否为我方)
|
||||||
|
Info2 []FightPetInfo
|
||||||
// 当前战斗精灵信息2(前端通过userid判断是否为我方)
|
|
||||||
Info2 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type S2C_2404 struct {
|
type S2C_2404 struct {
|
||||||
|
|||||||
@@ -162,6 +162,23 @@ func (f *FightC) linkOppInputs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FightC) linkTeamViews() {
|
||||||
|
for _, fighter := range f.Our {
|
||||||
|
if fighter == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fighter.Team = f.Our
|
||||||
|
fighter.OppTeam = f.Opp
|
||||||
|
}
|
||||||
|
for _, fighter := range f.Opp {
|
||||||
|
if fighter == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fighter.Team = f.Opp
|
||||||
|
fighter.OppTeam = f.Our
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input {
|
func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input {
|
||||||
isOur := f.isOurPlayerID(userID)
|
isOur := f.isOurPlayerID(userID)
|
||||||
if isOpposite {
|
if isOpposite {
|
||||||
@@ -177,36 +194,48 @@ func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) {
|
func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) {
|
||||||
isOur := f.isOurPlayerID(userID)
|
for _, in := range f.Our {
|
||||||
if isOur {
|
if in != nil && in.ControlledBy(userID) {
|
||||||
if in := f.selectInput(f.Our, 0); in != nil {
|
|
||||||
return in, true
|
return in, true
|
||||||
}
|
}
|
||||||
return nil, true
|
|
||||||
}
|
}
|
||||||
if in := f.selectInput(f.Opp, 0); in != nil {
|
for _, in := range f.Opp {
|
||||||
return in, false
|
if in != nil && in.ControlledBy(userID) {
|
||||||
|
return in, false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FightC) getInputByUserID(userID uint32, index int, isOpposite bool) *input.Input {
|
func (f *FightC) getInputByUserID(userID uint32, index int, isOpposite bool) *input.Input {
|
||||||
return f.selectInput(f.getSideInputs(userID, isOpposite), index)
|
selected := f.selectInput(f.getSideInputs(userID, isOpposite), index)
|
||||||
|
if selected == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 操作自身站位时,必须由该站位控制者发起。
|
||||||
|
if !isOpposite && !selected.ControlledBy(userID) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return selected
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FightC) getInputByController(userID uint32, isOpposite bool) *input.Input {
|
||||||
|
sideInputs := f.getSideInputs(userID, isOpposite)
|
||||||
|
for _, in := range sideInputs {
|
||||||
|
if in != nil && in.ControlledBy(userID) {
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f.selectInput(sideInputs, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FightC) expectedActionSlots() map[actionSlotKey]struct{} {
|
func (f *FightC) expectedActionSlots() map[actionSlotKey]struct{} {
|
||||||
slots := make(map[actionSlotKey]struct{}, len(f.Our)+len(f.Opp))
|
slots := make(map[actionSlotKey]struct{}, len(f.Our)+len(f.Opp))
|
||||||
for actorIndex, fighter := range f.Our {
|
for _, slot := range f.SideSlots(SideOur) {
|
||||||
if fighter == nil || fighter.Player == nil {
|
slots[newActionSlotKey(slot.ControllerUserID, slot.SlotIndex)] = struct{}{}
|
||||||
continue
|
|
||||||
}
|
|
||||||
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
|
|
||||||
}
|
}
|
||||||
for actorIndex, fighter := range f.Opp {
|
for _, slot := range f.SideSlots(SideOpp) {
|
||||||
if fighter == nil || fighter.Player == nil {
|
slots[newActionSlotKey(slot.ControllerUserID, slot.SlotIndex)] = struct{}{}
|
||||||
continue
|
|
||||||
}
|
|
||||||
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
|
|
||||||
}
|
}
|
||||||
return slots
|
return slots
|
||||||
}
|
}
|
||||||
@@ -234,7 +263,7 @@ func (f *FightC) GetInputByPlayer(c common.PlayerI, isOpposite bool) *input.Inpu
|
|||||||
}
|
}
|
||||||
return f.primaryOur()
|
return f.primaryOur()
|
||||||
}
|
}
|
||||||
return f.getInputByUserID(c.GetInfo().UserID, 0, isOpposite)
|
return f.getInputByController(c.GetInfo().UserID, isOpposite)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *input.Input {
|
func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *input.Input {
|
||||||
@@ -266,6 +295,14 @@ func (f *FightC) GetCurrPETAt(c common.PlayerI, actorIndex int) *info.BattlePetE
|
|||||||
}
|
}
|
||||||
return in.PrimaryCurPet()
|
return in.PrimaryCurPet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FightC) GetCurrPETByAction(c action.BattleActionI, isOpposite bool) *info.BattlePetEntity {
|
||||||
|
in := f.GetInputByAction(c, isOpposite)
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return in.CurrentPet()
|
||||||
|
}
|
||||||
func (f *FightC) GetOpp(c common.PlayerI) *input.Input {
|
func (f *FightC) GetOpp(c common.PlayerI) *input.Input {
|
||||||
return f.GetInputByPlayer(c, true)
|
return f.GetInputByPlayer(c, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,12 @@ func (our *Input) GetAction() {
|
|||||||
// 计算技能对对方的伤害(假设CalculatePower返回伤害值,或需从技能中获取)
|
// 计算技能对对方的伤害(假设CalculatePower返回伤害值,或需从技能中获取)
|
||||||
s.DamageValue = our.CalculatePower(our.Opp, s)
|
s.DamageValue = our.CalculatePower(our.Opp, s)
|
||||||
|
|
||||||
|
oppPet := our.Opp.CurrentPet()
|
||||||
|
if oppPet == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// 判断是否能秒杀(伤害 >= 对方当前生命值)
|
// 判断是否能秒杀(伤害 >= 对方当前生命值)
|
||||||
if s.DamageValue.Cmp(our.Opp.CurPet[0].GetHP()) != -1 { // 假设oppPet.HP为对方当前剩余生命值
|
if s.DamageValue.Cmp(oppPet.GetHP()) != -1 { // 假设oppPet.HP为对方当前剩余生命值
|
||||||
|
|
||||||
if usedskill != nil {
|
if usedskill != nil {
|
||||||
if s.DamageValue.Cmp(usedskill.DamageValue) != -1 {
|
if s.DamageValue.Cmp(usedskill.DamageValue) != -1 {
|
||||||
|
|||||||
@@ -91,9 +91,13 @@ func (our *Input) InitEffect(etype EnumEffectType, id int, a ...int) Effect {
|
|||||||
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
|
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
|
||||||
// 是否需要真实提升
|
// 是否需要真实提升
|
||||||
func (our *Input) GetProp(id int) alpacadecimal.Decimal {
|
func (our *Input) GetProp(id int) alpacadecimal.Decimal {
|
||||||
|
currentPet := our.CurrentPet()
|
||||||
|
if currentPet == nil {
|
||||||
|
return alpacadecimal.Zero
|
||||||
|
}
|
||||||
|
|
||||||
// 计算实际值(这里可以插入后续优化的函数调用)
|
// 计算实际值(这里可以插入后续优化的函数调用)
|
||||||
realValue := info.CalculateRealValue(alpacadecimal.NewFromInt(int64(our.CurPet[0].Info.Prop[id])), our.AttackValue.Prop[id])
|
realValue := info.CalculateRealValue(alpacadecimal.NewFromInt(int64(currentPet.Info.Prop[id])), our.AttackValue.Prop[id])
|
||||||
|
|
||||||
// todo: 插入获取后处理函数,例如:
|
// todo: 插入获取后处理函数,例如:
|
||||||
// realValue = postProcessValue(realValue, id, c)
|
// realValue = postProcessValue(realValue, id, c)
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ import (
|
|||||||
|
|
||||||
// 计算暴击
|
// 计算暴击
|
||||||
func (our *Input) CalculateCrit(opp *Input, skill *info.SkillEntity) {
|
func (our *Input) CalculateCrit(opp *Input, skill *info.SkillEntity) {
|
||||||
|
ourPet := our.CurrentPet()
|
||||||
|
oppPet := opp.CurrentPet()
|
||||||
|
if ourPet == nil || oppPet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
skill.Crit = 0
|
skill.Crit = 0
|
||||||
if skill.Category() == info.Category.STATUS { //属性技能不用算暴击
|
if skill.Category() == info.Category.STATUS { //属性技能不用算暴击
|
||||||
@@ -30,11 +35,11 @@ func (our *Input) CalculateCrit(opp *Input, skill *info.SkillEntity) {
|
|||||||
CritRate = 16
|
CritRate = 16
|
||||||
}
|
}
|
||||||
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0
|
// CritSelfHalfHp: 自身体力低于一半时必定致命一击; 默认: 0
|
||||||
if skill.XML.CritSelfHalfHp != 0 && (our.CurPet[0].HP < int(our.CurPet[0].Info.MaxHp)/2) {
|
if skill.XML.CritSelfHalfHp != 0 && (ourPet.HP < int(ourPet.Info.MaxHp)/2) {
|
||||||
CritRate = 16
|
CritRate = 16
|
||||||
}
|
}
|
||||||
// CritFoeHalfHp: 对方体力低于一半时必定致命一击; 默认: 0
|
// CritFoeHalfHp: 对方体力低于一半时必定致命一击; 默认: 0
|
||||||
if skill.XML.CritSelfHalfHp != 0 && (opp.CurPet[0].HP < int(opp.CurPet[0].Info.MaxHp)/2) {
|
if skill.XML.CritSelfHalfHp != 0 && (oppPet.HP < int(oppPet.Info.MaxHp)/2) {
|
||||||
CritRate = 16
|
CritRate = 16
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +52,10 @@ func (our *Input) CalculateCrit(opp *Input, skill *info.SkillEntity) {
|
|||||||
|
|
||||||
// 恢复血量
|
// 恢复血量
|
||||||
func (our *Input) Heal(in *Input, ac action.BattleActionI, value alpacadecimal.Decimal) {
|
func (our *Input) Heal(in *Input, ac action.BattleActionI, value alpacadecimal.Decimal) {
|
||||||
|
currentPet := our.CurrentPet()
|
||||||
|
if currentPet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
healValue := int(value.IntPart())
|
healValue := int(value.IntPart())
|
||||||
|
|
||||||
if ac != nil {
|
if ac != nil {
|
||||||
@@ -67,30 +76,38 @@ func (our *Input) Heal(in *Input, ac action.BattleActionI, value alpacadecimal.D
|
|||||||
}
|
}
|
||||||
|
|
||||||
if healValue >= 0 {
|
if healValue >= 0 {
|
||||||
our.CurPet[0].Info.ModelHP(int64(healValue))
|
currentPet.Info.ModelHP(int64(healValue))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
damage := uint32(-healValue)
|
damage := uint32(-healValue)
|
||||||
if damage >= our.CurPet[0].Info.Hp {
|
if damage >= currentPet.Info.Hp {
|
||||||
our.CurPet[0].Info.Hp = 0
|
currentPet.Info.Hp = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
our.CurPet[0].Info.Hp -= damage
|
currentPet.Info.Hp -= damage
|
||||||
|
|
||||||
}
|
}
|
||||||
func (our *Input) HealPP(value int) {
|
func (our *Input) HealPP(value int) {
|
||||||
|
currentPet := our.CurrentPet()
|
||||||
|
if currentPet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
our.CurPet[0].Info.HealPP(value)
|
currentPet.Info.HealPP(value)
|
||||||
|
|
||||||
}
|
}
|
||||||
func (our *Input) DelPP(value int) {
|
func (our *Input) DelPP(value int) {
|
||||||
|
currentPet := our.CurrentPet()
|
||||||
|
if currentPet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < len(our.CurPet[0].Info.SkillList); i++ {
|
for i := 0; i < len(currentPet.Info.SkillList); i++ {
|
||||||
if uint32(value) > our.CurPet[0].Info.SkillList[i].PP {
|
if uint32(value) > currentPet.Info.SkillList[i].PP {
|
||||||
our.CurPet[0].Info.SkillList[i].PP = 0
|
currentPet.Info.SkillList[i].PP = 0
|
||||||
} else {
|
} else {
|
||||||
our.CurPet[0].Info.SkillList[i].PP -= uint32(value)
|
currentPet.Info.SkillList[i].PP -= uint32(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -102,6 +119,10 @@ func (our *Input) DelPP(value int) {
|
|||||||
// 这个方法是对对方造成伤害
|
// 这个方法是对对方造成伤害
|
||||||
// 伤害落实 // 血量扣减节点比如触发回神,反弹也在这里实现
|
// 伤害落实 // 血量扣减节点比如触发回神,反弹也在这里实现
|
||||||
func (our *Input) Damage(in *Input, sub *info.DamageZone) {
|
func (our *Input) Damage(in *Input, sub *info.DamageZone) {
|
||||||
|
currentPet := our.CurrentPet()
|
||||||
|
if currentPet == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
attacker := in
|
attacker := in
|
||||||
if attacker == nil {
|
if attacker == nil {
|
||||||
attacker = our
|
attacker = our
|
||||||
@@ -200,11 +221,11 @@ func (our *Input) Damage(in *Input, sub *info.DamageZone) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if uint32(sub.Damage.IntPart()) > our.CurPet[0].Info.Hp {
|
if uint32(sub.Damage.IntPart()) > currentPet.Info.Hp {
|
||||||
|
|
||||||
our.CurPet[0].Info.Hp = 0
|
currentPet.Info.Hp = 0
|
||||||
} else {
|
} else {
|
||||||
our.CurPet[0].Info.Hp = our.CurPet[0].Info.Hp - uint32(sub.Damage.IntPart())
|
currentPet.Info.Hp = currentPet.Info.Hp - uint32(sub.Damage.IntPart())
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo 待实现死亡effet
|
//todo 待实现死亡effet
|
||||||
@@ -213,9 +234,17 @@ func (our *Input) Damage(in *Input, sub *info.DamageZone) {
|
|||||||
|
|
||||||
// 计算技能威力
|
// 计算技能威力
|
||||||
func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpacadecimal.Decimal {
|
func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpacadecimal.Decimal {
|
||||||
|
if deftype == nil {
|
||||||
|
return alpacadecimal.Zero
|
||||||
|
}
|
||||||
|
ourPet := our.CurrentPet()
|
||||||
|
defPet := deftype.CurrentPet()
|
||||||
|
if ourPet == nil || defPet == nil {
|
||||||
|
return alpacadecimal.Zero
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 计算等级因子 (level * 0.4 + 2)
|
// 1. 计算等级因子 (level * 0.4 + 2)
|
||||||
levelFactor := alpacadecimal.NewFromInt(int64(our.CurPet[0].Info.Level)).
|
levelFactor := alpacadecimal.NewFromInt(int64(ourPet.Info.Level)).
|
||||||
Mul(alpacadecimal.NewFromFloat(0.4)).Add(alpacadecimal.NewFromInt(2))
|
Mul(alpacadecimal.NewFromFloat(0.4)).Add(alpacadecimal.NewFromInt(2))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -243,17 +272,17 @@ func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpaca
|
|||||||
// 11. DmgBindHpDv: 造成的伤害等于自身剩余体力*1/2+潜力(个体值); 默认: 0
|
// 11. DmgBindHpDv: 造成的伤害等于自身剩余体力*1/2+潜力(个体值); 默认: 0
|
||||||
if skill.XML.DmgBindLv != 0 {
|
if skill.XML.DmgBindLv != 0 {
|
||||||
|
|
||||||
skill.XML.Power = int(deftype.CurPet[0].Info.Level)
|
skill.XML.Power = int(defPet.Info.Level)
|
||||||
|
|
||||||
}
|
}
|
||||||
if skill.XML.PwrBindDv != 0 {
|
if skill.XML.PwrBindDv != 0 {
|
||||||
if skill.XML.PwrBindDv == 1 {
|
if skill.XML.PwrBindDv == 1 {
|
||||||
|
|
||||||
skill.XML.Power = int(our.CurPet[0].Info.Dv * 5)
|
skill.XML.Power = int(ourPet.Info.Dv * 5)
|
||||||
|
|
||||||
}
|
}
|
||||||
if skill.XML.PwrBindDv == 2 {
|
if skill.XML.PwrBindDv == 2 {
|
||||||
skill.XML.Power = int(our.CurPet[0].Info.Hp/3 + our.CurPet[0].Info.Dv)
|
skill.XML.Power = int(ourPet.Info.Hp/3 + ourPet.Info.Dv)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -265,7 +294,7 @@ func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpaca
|
|||||||
|
|
||||||
}
|
}
|
||||||
if skill.XML.DmgBindHpDv != 0 {
|
if skill.XML.DmgBindHpDv != 0 {
|
||||||
skill.XML.Power = int(our.CurPet[0].Info.Hp/2 + our.CurPet[0].Info.Dv)
|
skill.XML.Power = int(ourPet.Info.Hp/2 + ourPet.Info.Dv)
|
||||||
|
|
||||||
}
|
}
|
||||||
// 5. 基础伤害公式:等级因子 * 威力因子 * 攻击 / 防御 / 50 + 2
|
// 5. 基础伤害公式:等级因子 * 威力因子 * 攻击 / 防御 / 50 + 2
|
||||||
@@ -276,8 +305,8 @@ func (our *Input) CalculatePower(deftype *Input, skill *info.SkillEntity) alpaca
|
|||||||
Add(alpacadecimal.NewFromInt(2))
|
Add(alpacadecimal.NewFromInt(2))
|
||||||
|
|
||||||
var typeRate alpacadecimal.Decimal
|
var typeRate alpacadecimal.Decimal
|
||||||
//fmt.Println(skill.Type().ID, deftype.CurPet[0].Type().ID)
|
//fmt.Println(skill.Type().ID, defPet.Type().ID)
|
||||||
t, _ := element.Calculator.GetOffensiveMultiplier(skill.GetType().ID, deftype.CurPet[0].GetType().ID)
|
t, _ := element.Calculator.GetOffensiveMultiplier(skill.GetType().ID, defPet.GetType().ID)
|
||||||
our.AttackValue.Offensive = gconv.Float32(t)
|
our.AttackValue.Offensive = gconv.Float32(t)
|
||||||
|
|
||||||
typeRate = alpacadecimal.NewFromFloat(t)
|
typeRate = alpacadecimal.NewFromFloat(t)
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ type Input struct {
|
|||||||
// CanAction bool //是否可以行动
|
// CanAction bool //是否可以行动
|
||||||
CurPet []*info.BattlePetEntity //当前上场精灵
|
CurPet []*info.BattlePetEntity //当前上场精灵
|
||||||
AllPet []*info.BattlePetEntity
|
AllPet []*info.BattlePetEntity
|
||||||
|
Team []*Input
|
||||||
|
OppTeam []*Input
|
||||||
Player common.PlayerI
|
Player common.PlayerI
|
||||||
Opp *Input
|
Opp *Input
|
||||||
CanCapture int
|
CanCapture int
|
||||||
@@ -30,8 +32,8 @@ type Input struct {
|
|||||||
EffectLost []Effect
|
EffectLost []Effect
|
||||||
// 删掉伤害记录,可以在回调中记录,而不是每次调用记录
|
// 删掉伤害记录,可以在回调中记录,而不是每次调用记录
|
||||||
*model.AttackValue
|
*model.AttackValue
|
||||||
FightC common.FightI
|
FightC common.FightI
|
||||||
SumDamage alpacadecimal.Decimal //伤害
|
SumDamage alpacadecimal.Decimal //伤害
|
||||||
ShieldDamageTaken alpacadecimal.Decimal
|
ShieldDamageTaken alpacadecimal.Decimal
|
||||||
// 记录上一回合结束时的能力等级,供效果727等回溯使用。
|
// 记录上一回合结束时的能力等级,供效果727等回溯使用。
|
||||||
LastTurnEndProp [6]int8
|
LastTurnEndProp [6]int8
|
||||||
@@ -76,6 +78,63 @@ func (our *Input) PrimaryCurPet() *info.BattlePetEntity {
|
|||||||
return our.CurPetAt(0)
|
return our.CurPetAt(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentPet 返回“当前输入槽位”的出战精灵。
|
||||||
|
// 注意:Input 本身已对应一个 actor 槽位,这里不是“整队的 0 号主位”。
|
||||||
|
func (our *Input) CurrentPet() *info.BattlePetEntity {
|
||||||
|
return our.PrimaryCurPet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (our *Input) IsCurrentPetCatchTime(catchTime uint32) bool {
|
||||||
|
current := our.CurrentPet()
|
||||||
|
if current == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return current.Info.CatchTime == catchTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (our *Input) ControlledBy(userID uint32) bool {
|
||||||
|
return our != nil && our.Player != nil && our.Player.GetInfo().UserID == userID
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchPets 返回当前站位后备精灵(不含当前出战精灵)。
|
||||||
|
func (our *Input) BenchPets() []*info.BattlePetEntity {
|
||||||
|
if our == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
current := our.CurrentPet()
|
||||||
|
if current == nil {
|
||||||
|
return append([]*info.BattlePetEntity(nil), our.AllPet...)
|
||||||
|
}
|
||||||
|
bench := make([]*info.BattlePetEntity, 0, len(our.AllPet))
|
||||||
|
for _, pet := range our.AllPet {
|
||||||
|
if pet == nil || pet.Info.CatchTime == current.Info.CatchTime {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bench = append(bench, pet)
|
||||||
|
}
|
||||||
|
return bench
|
||||||
|
}
|
||||||
|
|
||||||
|
func (our *Input) OpponentSlots() []*Input {
|
||||||
|
if our == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(our.OppTeam) == 0 {
|
||||||
|
if our.Opp != nil {
|
||||||
|
return []*Input{our.Opp}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
slots := make([]*Input, 0, len(our.OppTeam))
|
||||||
|
for _, in := range our.OppTeam {
|
||||||
|
if in == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
slots = append(slots, in)
|
||||||
|
}
|
||||||
|
return slots
|
||||||
|
}
|
||||||
|
|
||||||
func (our *Input) SetCurPetAt(index int, pet *info.BattlePetEntity) {
|
func (our *Input) SetCurPetAt(index int, pet *info.BattlePetEntity) {
|
||||||
if our == nil || index < 0 {
|
if our == nil || index < 0 {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ func (f *FightC) battleLoop() {
|
|||||||
if ff.Player.GetInfo().PetList[j].CatchTime == ff.AllPet[i].Info.CatchTime {
|
if ff.Player.GetInfo().PetList[j].CatchTime == ff.AllPet[i].Info.CatchTime {
|
||||||
|
|
||||||
if ff.UserID == f.WinnerId {
|
if ff.UserID == f.WinnerId {
|
||||||
if ff.CurPet[0].Info.CatchTime == ff.Player.GetInfo().PetList[j].CatchTime {
|
currentPet := ff.CurrentPet()
|
||||||
|
if currentPet != nil && currentPet.Info.CatchTime == ff.Player.GetInfo().PetList[j].CatchTime {
|
||||||
f.Winpet = &ff.Player.GetInfo().PetList[j]
|
f.Winpet = &ff.Player.GetInfo().PetList[j]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +101,14 @@ func (f *FightC) battleLoop() {
|
|||||||
addpet.EffectInfo = nil //清空特性信息
|
addpet.EffectInfo = nil //清空特性信息
|
||||||
f.Our[0].Player.(*player.Player).Service.Pet.PetAdd(&addpet, 0)
|
f.Our[0].Player.(*player.Player).Service.Pet.PetAdd(&addpet, 0)
|
||||||
|
|
||||||
|
oppPet := f.Opp[0].CurrentPet()
|
||||||
|
petID := uint32(0)
|
||||||
|
if oppPet != nil {
|
||||||
|
petID = uint32(oppPet.ID)
|
||||||
|
}
|
||||||
f.Our[0].Player.SendPackCmd(2409, &info.CatchMonsterOutboundInfo{
|
f.Our[0].Player.SendPackCmd(2409, &info.CatchMonsterOutboundInfo{
|
||||||
CatchTime: uint32(addpet.CatchTime),
|
CatchTime: uint32(addpet.CatchTime),
|
||||||
PetId: uint32(f.Opp[0].CurPet[0].ID),
|
PetId: petID,
|
||||||
})
|
})
|
||||||
|
|
||||||
defer f.Our[0].Player.(*player.Player).Service.Done.UpdatePet(addpet, 0, 1)
|
defer f.Our[0].Player.(*player.Player).Service.Done.UpdatePet(addpet, 0, 1)
|
||||||
@@ -323,11 +329,6 @@ func (f *FightC) handleTimeout(expectedSlots map[actionSlotKey]struct{}, actions
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type roundActionPair struct {
|
|
||||||
our action.BattleActionI
|
|
||||||
opp action.BattleActionI
|
|
||||||
}
|
|
||||||
|
|
||||||
func flattenActionMap(actions map[actionSlotKey]action.BattleActionI) []action.BattleActionI {
|
func flattenActionMap(actions map[actionSlotKey]action.BattleActionI) []action.BattleActionI {
|
||||||
flattened := make([]action.BattleActionI, 0, len(actions))
|
flattened := make([]action.BattleActionI, 0, len(actions))
|
||||||
for _, act := range actions {
|
for _, act := range actions {
|
||||||
@@ -371,44 +372,38 @@ func (f *FightC) sortActions(actions []action.BattleActionI) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FightC) buildRoundActionPairs(actions []action.BattleActionI) []roundActionPair {
|
func sortActionsByActor(actions []action.BattleActionI) {
|
||||||
remaining := append([]action.BattleActionI(nil), actions...)
|
sort.SliceStable(actions, func(i, j int) bool {
|
||||||
f.sortActions(remaining)
|
a, b := actions[i], actions[j]
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return a != nil
|
||||||
|
}
|
||||||
|
if a.GetActorIndex() != b.GetActorIndex() {
|
||||||
|
return a.GetActorIndex() < b.GetActorIndex()
|
||||||
|
}
|
||||||
|
if a.GetTargetIndex() != b.GetTargetIndex() {
|
||||||
|
return a.GetTargetIndex() < b.GetTargetIndex()
|
||||||
|
}
|
||||||
|
return a.GetPlayerID() < b.GetPlayerID()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
used := make([]bool, len(remaining))
|
func (f *FightC) splitRoundActions(actions []action.BattleActionI) ([]action.BattleActionI, []action.BattleActionI) {
|
||||||
pairs := make([]roundActionPair, 0, len(remaining))
|
our := make([]action.BattleActionI, 0, len(actions))
|
||||||
for i, act := range remaining {
|
opp := make([]action.BattleActionI, 0, len(actions))
|
||||||
if used[i] || act == nil {
|
for _, act := range actions {
|
||||||
|
if act == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
used[i] = true
|
|
||||||
|
|
||||||
pair := roundActionPair{}
|
|
||||||
if f.isOurPlayerID(act.GetPlayerID()) {
|
if f.isOurPlayerID(act.GetPlayerID()) {
|
||||||
pair.our = act
|
our = append(our, act)
|
||||||
} else {
|
} else {
|
||||||
pair.opp = act
|
opp = append(opp, act)
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := i + 1; j < len(remaining); j++ {
|
|
||||||
candidate := remaining[j]
|
|
||||||
if used[j] || candidate == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if f.isOurPlayerID(candidate.GetPlayerID()) == f.isOurPlayerID(act.GetPlayerID()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
used[j] = true
|
|
||||||
if pair.our == nil {
|
|
||||||
pair.our = candidate
|
|
||||||
} else {
|
|
||||||
pair.opp = candidate
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
pairs = append(pairs, pair)
|
|
||||||
}
|
}
|
||||||
return pairs
|
sortActionsByActor(our)
|
||||||
|
sortActionsByActor(opp)
|
||||||
|
return our, opp
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据动作类型执行一回合结算
|
// 根据动作类型执行一回合结算
|
||||||
@@ -418,11 +413,24 @@ func (f *FightC) resolveRound(actions []action.BattleActionI) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pair := range f.buildRoundActionPairs(actions) {
|
ourActions, oppActions := f.splitRoundActions(actions)
|
||||||
|
roundLen := len(ourActions)
|
||||||
|
if len(oppActions) > roundLen {
|
||||||
|
roundLen = len(oppActions)
|
||||||
|
}
|
||||||
|
for i := 0; i < roundLen; i++ {
|
||||||
if f.closefight {
|
if f.closefight {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.resolveActionPair(pair.our, pair.opp)
|
var ourAct action.BattleActionI
|
||||||
|
var oppAct action.BattleActionI
|
||||||
|
if i < len(ourActions) {
|
||||||
|
ourAct = ourActions[i]
|
||||||
|
}
|
||||||
|
if i < len(oppActions) {
|
||||||
|
oppAct = oppActions[i]
|
||||||
|
}
|
||||||
|
f.resolveActionPair(ourAct, oppAct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ func buildFight(opts *fightBuildOptions) (*FightC, errorcode.ErrorCode) {
|
|||||||
}
|
}
|
||||||
f.bindInputFightContext(f.Our)
|
f.bindInputFightContext(f.Our)
|
||||||
f.bindInputFightContext(f.Opp)
|
f.bindInputFightContext(f.Opp)
|
||||||
|
f.linkTeamViews()
|
||||||
f.linkOppInputs()
|
f.linkOppInputs()
|
||||||
|
|
||||||
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.primaryOur())
|
f.ReadyInfo.OurInfo, f.ReadyInfo.OurPetList = initfightready(f.primaryOur())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package node
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
element "blazing/common/data/Element"
|
element "blazing/common/data/Element"
|
||||||
|
"blazing/logic/service/fight/info"
|
||||||
"blazing/logic/service/fight/input"
|
"blazing/logic/service/fight/input"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -75,12 +76,90 @@ func (e *EffectNode) OpponentInput() *input.Input {
|
|||||||
return e.Ctx().Opp
|
return e.Ctx().Opp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EffectNode) SourcePet() *info.BattlePetEntity {
|
||||||
|
in := e.SourceInput()
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return in.CurrentPet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EffectNode) CarrierPet() *info.BattlePetEntity {
|
||||||
|
in := e.CarrierInput()
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return in.CurrentPet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EffectNode) TargetPet() *info.BattlePetEntity {
|
||||||
|
in := e.TargetInput()
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return in.CurrentPet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EffectNode) OpponentPet() *info.BattlePetEntity {
|
||||||
|
in := e.OpponentInput()
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return in.CurrentPet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EffectNode) ForEachOpponentSlot(fn func(*input.Input) bool) {
|
||||||
|
if fn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
carrier := e.CarrierInput()
|
||||||
|
if carrier == nil {
|
||||||
|
if opp := e.OpponentInput(); opp != nil {
|
||||||
|
fn(opp)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opponents := carrier.OpponentSlots()
|
||||||
|
if len(opponents) == 0 {
|
||||||
|
if opp := e.OpponentInput(); opp != nil {
|
||||||
|
fn(opp)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, opp := range opponents {
|
||||||
|
if opp == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !fn(opp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EffectNode) ForEachCarrierBenchPet(fn func(*info.BattlePetEntity) bool) {
|
||||||
|
if fn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
carrier := e.CarrierInput()
|
||||||
|
if carrier == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, pet := range carrier.BenchPets() {
|
||||||
|
if pet == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !fn(pet) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsOwner reports whether the current phase's Our side owns this effect.
|
// IsOwner reports whether the current phase's Our side owns this effect.
|
||||||
func (e *EffectNode) IsOwner() bool {
|
func (e *EffectNode) IsOwner() bool {
|
||||||
if e.Ctx().Our == nil || len(e.Ctx().Our.CurPet) == 0 || e.Ctx().Our.CurPet[0] == nil {
|
if e.Ctx().Our == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return e.ID().GetCatchTime() == e.Ctx().Our.CurPet[0].Info.CatchTime
|
return e.Ctx().Our.IsCurrentPetCatchTime(e.ID().GetCatchTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EffectNode) Ctx() *input.Ctx {
|
func (e *EffectNode) Ctx() *input.Ctx {
|
||||||
@@ -176,10 +255,15 @@ func (e *EffectNode) PropBefer(in *input.Input, prop int8, level int8) bool {
|
|||||||
func (e *EffectNode) ISNaturalEnemy() bool {
|
func (e *EffectNode) ISNaturalEnemy() bool {
|
||||||
source := e.SourceInput()
|
source := e.SourceInput()
|
||||||
opp := e.OpponentInput()
|
opp := e.OpponentInput()
|
||||||
if source == nil || opp == nil || source.CurPet[0] == nil || opp.CurPet[0] == nil {
|
if source == nil || opp == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
t, _ := element.Calculator.GetOffensiveMultiplier(opp.CurPet[0].Type, source.CurPet[0].Type)
|
sourcePet := source.CurrentPet()
|
||||||
|
oppPet := opp.CurrentPet()
|
||||||
|
if sourcePet == nil || oppPet == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
t, _ := element.Calculator.GetOffensiveMultiplier(oppPet.Type, sourcePet.Type)
|
||||||
|
|
||||||
if t <= 1 {
|
if t <= 1 {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -38,8 +38,11 @@ func (e *EffectNode) OnSkill() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *EffectNode) Skill_Can() bool {
|
func (e *EffectNode) Skill_Can() bool {
|
||||||
|
currentPet := e.Input.CurrentPet()
|
||||||
return e.Input.CurPet[0].HP != 0
|
if currentPet == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return currentPet.HP != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EffectNode) GenSub(t input.Effect, Duration int) input.Effect {
|
func (e *EffectNode) GenSub(t input.Effect, Duration int) input.Effect {
|
||||||
|
|||||||
51
logic/service/fight/slot_view.go
Normal file
51
logic/service/fight/slot_view.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package fight
|
||||||
|
|
||||||
|
import "blazing/logic/service/fight/input"
|
||||||
|
|
||||||
|
const (
|
||||||
|
SideOur = 1
|
||||||
|
SideOpp = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type SlotView struct {
|
||||||
|
Side int
|
||||||
|
SlotIndex int
|
||||||
|
ControllerUserID uint32
|
||||||
|
Input *input.Input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FightC) SlotInput(side int, slotIndex int) *input.Input {
|
||||||
|
switch side {
|
||||||
|
case SideOur:
|
||||||
|
return f.selectInput(f.Our, slotIndex)
|
||||||
|
case SideOpp:
|
||||||
|
return f.selectInput(f.Opp, slotIndex)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FightC) SideSlots(side int) []SlotView {
|
||||||
|
var sideInputs []*input.Input
|
||||||
|
switch side {
|
||||||
|
case SideOur:
|
||||||
|
sideInputs = f.Our
|
||||||
|
case SideOpp:
|
||||||
|
sideInputs = f.Opp
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
slots := make([]SlotView, 0, len(sideInputs))
|
||||||
|
for idx, in := range sideInputs {
|
||||||
|
if in == nil || in.Player == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
slots = append(slots, SlotView{
|
||||||
|
Side: side,
|
||||||
|
SlotIndex: idx,
|
||||||
|
ControllerUserID: in.Player.GetInfo().UserID,
|
||||||
|
Input: in,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return slots
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user