refactor: 重构战斗系统支持多单位多动作
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
# Task 202: Effects 1625-1629
|
||||
|
||||
## 目标
|
||||
|
||||
- 补齐以下 5 个(或最后一组不足 5 个)当前判定未实现的 skill effect。
|
||||
- 实现位置优先放在 `logic/service/fight/effect/`。
|
||||
- 如 effect 需要展示说明,同步更新 `logic/service/fight/effect/effect_info_map.go`。
|
||||
- 完成后至少执行:`cd /workspace/logic && go test ./service/fight/effect`。
|
||||
|
||||
## Effect 列表
|
||||
|
||||
### Effect 1625
|
||||
- `argsNum`: `3`
|
||||
- `info`: `造成的伤害高于{0}则{1}%令自身全属性+{2}`
|
||||
|
||||
### Effect 1626
|
||||
- `argsNum`: `1`
|
||||
- `info`: `后出手时将当回合护盾所承受的伤害值以百分比伤害的形式{0}%反弹给对手`
|
||||
|
||||
### Effect 1627
|
||||
- `argsNum`: `3`
|
||||
- `info`: `{0}回合做{1}-{2}次攻击,若本回合攻击次数达到最大则必定秒杀对手`
|
||||
|
||||
### Effect 1628
|
||||
- `argsNum`: `2`
|
||||
- `info`: `每次使用该技能击败对手则恢复自身全部体力,同时重置该技能使用次数并使该技能攻击威力提升{0}点,未击败对手时令自身下回合攻击技能先制+{1}`
|
||||
|
||||
### Effect 1629
|
||||
- `argsNum`: `4`
|
||||
- `info`: `{0}基础速度值{1}{2}则自身下回合先制+{3}`
|
||||
- `param`: `4,0,0|7,1,1`
|
||||
|
||||
## 备注
|
||||
|
||||
- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。
|
||||
- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。
|
||||
@@ -1,35 +0,0 @@
|
||||
# Task 204: Effects 1635-1639
|
||||
|
||||
## 目标
|
||||
|
||||
- 补齐以下 5 个(或最后一组不足 5 个)当前判定未实现的 skill effect。
|
||||
- 实现位置优先放在 `logic/service/fight/effect/`。
|
||||
- 如 effect 需要展示说明,同步更新 `logic/service/fight/effect/effect_info_map.go`。
|
||||
- 完成后至少执行:`cd /workspace/logic && go test ./service/fight/effect`。
|
||||
|
||||
## Effect 列表
|
||||
|
||||
### Effect 1635
|
||||
- `argsNum`: `2`
|
||||
- `info`: `立刻恢复自身{0}点体力,{1}回合后恢复自身全部体力`
|
||||
|
||||
### Effect 1636
|
||||
- `argsNum`: `0`
|
||||
- `info`: `涵双1回合释放4-8张玫瑰卡牌进行攻击,每张卡牌额外附加50点固定伤害,自身体力低于最大体力的1/3时效果翻倍`
|
||||
|
||||
### Effect 1637
|
||||
- `argsNum`: `2`
|
||||
- `info`: `{0}回合内若对手使用属性技能,则使用属性技能后的下{1}回合属性技能命中效果失效`
|
||||
|
||||
### Effect 1638
|
||||
- `argsNum`: `2`
|
||||
- `info`: `{0}回合内若自身未受到攻击伤害则令对手全属性-{1}`
|
||||
|
||||
### Effect 1639
|
||||
- `argsNum`: `0`
|
||||
- `info`: `自身处于能力提升状态时100%打出致命一击`
|
||||
|
||||
## 备注
|
||||
|
||||
- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。
|
||||
- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。
|
||||
@@ -1,35 +0,0 @@
|
||||
# Task 206: Effects 1645-1649
|
||||
|
||||
## 目标
|
||||
|
||||
- 补齐以下 5 个(或最后一组不足 5 个)当前判定未实现的 skill effect。
|
||||
- 实现位置优先放在 `logic/service/fight/effect/`。
|
||||
- 如 effect 需要展示说明,同步更新 `logic/service/fight/effect/effect_info_map.go`。
|
||||
- 完成后至少执行:`cd /workspace/logic && go test ./service/fight/effect`。
|
||||
|
||||
## Effect 列表
|
||||
|
||||
### Effect 1645
|
||||
- `argsNum`: `3`
|
||||
- `info`: `{0}回合内对手使用属性技能则自身下{1}次受到的攻击伤害减少{2}%`
|
||||
|
||||
### Effect 1646
|
||||
- `argsNum`: `1`
|
||||
- `info`: `全属性+{0},对手存在致命裂痕时强化效果翻倍`
|
||||
|
||||
### Effect 1647
|
||||
- `argsNum`: `4`
|
||||
- `info`: `{0}回合内每回合使用技能吸取对手最大体力的1/{1},吸取体力时若自身体力低于最大体力的1/{2}则吸取效果翻倍,对手免疫百分比伤害时额外附加{3}点真实伤害`
|
||||
|
||||
### Effect 1648
|
||||
- `argsNum`: `1`
|
||||
- `info`: `附加自身最大体力{0}%的百分比伤害并恢复等量体力值,对手存在致命裂痕时转变为等量的真实伤害`
|
||||
|
||||
### Effect 1649
|
||||
- `argsNum`: `4`
|
||||
- `info`: `{0}%概率造成的攻击伤害为{1}倍,对手每存在1层致命裂痕则概率提升{2}%,未触发则{3}回合内令对手使用的属性技能无效`
|
||||
|
||||
## 备注
|
||||
|
||||
- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。
|
||||
- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。
|
||||
@@ -1,35 +0,0 @@
|
||||
# Task 207: Effects 1650-1654
|
||||
|
||||
## 目标
|
||||
|
||||
- 补齐以下 5 个(或最后一组不足 5 个)当前判定未实现的 skill effect。
|
||||
- 实现位置优先放在 `logic/service/fight/effect/`。
|
||||
- 如 effect 需要展示说明,同步更新 `logic/service/fight/effect/effect_info_map.go`。
|
||||
- 完成后至少执行:`cd /workspace/logic && go test ./service/fight/effect`。
|
||||
|
||||
## Effect 列表
|
||||
|
||||
### Effect 1650
|
||||
- `argsNum`: `4`
|
||||
- `info`: `命中后{0}%随机为对手任意技能散布{1}枚致命印记,若对手当前精灵致命裂痕≥{2}层则额外散布{3}枚致命印记`
|
||||
|
||||
### Effect 1651
|
||||
- `argsNum`: `2`
|
||||
- `info`: `当回合击败对手则令对手下{0}次触发致命印记真实伤害效果转变为1/{1}`
|
||||
|
||||
### Effect 1652
|
||||
- `argsNum`: `2`
|
||||
- `info`: `释放技能时自身每损失{0}%的体力值则此技能威力提升{1}点`
|
||||
|
||||
### Effect 1653
|
||||
- `argsNum`: `2`
|
||||
- `info`: `释放技能时对手每残留{0}%的体力值则此技能附加{1}点固定伤害`
|
||||
|
||||
### Effect 1654
|
||||
- `argsNum`: `1`
|
||||
- `info`: `当回合击败对手则令对手下只登场精灵首次使用的技能所附加的效果失效`
|
||||
|
||||
## 备注
|
||||
|
||||
- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。
|
||||
- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。
|
||||
@@ -1,36 +0,0 @@
|
||||
# Task 208: Effects 1655-1659
|
||||
|
||||
## 目标
|
||||
|
||||
- 补齐以下 5 个(或最后一组不足 5 个)当前判定未实现的 skill effect。
|
||||
- 实现位置优先放在 `logic/service/fight/effect/`。
|
||||
- 如 effect 需要展示说明,同步更新 `logic/service/fight/effect/effect_info_map.go`。
|
||||
- 完成后至少执行:`cd /workspace/logic && go test ./service/fight/effect`。
|
||||
|
||||
## Effect 列表
|
||||
|
||||
### Effect 1655
|
||||
- `argsNum`: `3`
|
||||
- `info`: `{0}回合内每回合结束后{1}恢复自身所有技能{2}点PP值`
|
||||
- `param`: `25,1,1`
|
||||
|
||||
### Effect 1656
|
||||
- `argsNum`: `0`
|
||||
- `info`: `100%复制对手当回合释放的技能,若对手当回合使用的技能为攻击技能则令对手随机1个技能PP值归零,若对手当回合使用的技能为属性技能则令对手下回合先制-2`
|
||||
|
||||
### Effect 1657
|
||||
- `argsNum`: `3`
|
||||
- `info`: `己方每有一只精灵死亡则附加{0}点固定伤害,对手体力高于最大体力的1/{1}时转变为{2}点`
|
||||
|
||||
### Effect 1658
|
||||
- `argsNum`: `0`
|
||||
- `info`: `3回合内每回合80%闪避对手攻击,未触发时自身处于圣念状态则使对手随机1项技能PP值归零,自身处于邪念状态则使对手失明`
|
||||
|
||||
### Effect 1659
|
||||
- `argsNum`: `4`
|
||||
- `info`: `随机附加给对手{0}-{1}点固定伤害,若打出致命一击则效果转变为吸取对手{2}-{3}点体力`
|
||||
|
||||
## 备注
|
||||
|
||||
- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。
|
||||
- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。
|
||||
@@ -1,5 +1,23 @@
|
||||
# 战斗多单位模式改造交接文档(2026-04-04)
|
||||
|
||||
## 0. 2026-04-04 本轮完成情况
|
||||
|
||||
本轮已完成以下落地项:
|
||||
|
||||
- 动作提交改为按 `playerID + actorIndex` 去重,同一玩家同回合的多个上场位动作不会再互相覆盖
|
||||
- 主循环已从“双动作入口”改为“动作列表入口”,`resolveRound` 现接收并处理 `[]action.BattleActionI`
|
||||
- 回合结算增加了基于优先级和速度的统一排序,并按跨阵营动作对子顺序执行,保留现有 `enterturn(first, second)` 兼容层
|
||||
- 技能和道具的目标选择已接入 `targetIndex`,不再固定打对面 `0` 号位
|
||||
- 切宠同步改为携带 `actorIndex`,同一玩家多上场位的切宠播报不再冲突
|
||||
- 开战同步结构新增当前战斗位数组,同时保留 `Info1/Info2` 兼容旧结构
|
||||
- `FightI` 已补充 `UseSkillAt/ChangePetAt/UseItemAt/GetCurrPETAt`
|
||||
- 增加了动作队列的基础单测,覆盖“同玩家不同槽位保留”和“同槽位动作替换”
|
||||
|
||||
本轮仍保留的限制:
|
||||
|
||||
- `enterturn` 和大量 `effect/node` 逻辑仍是双动作上下文,因此当前实现采用“动作列表排序 + 跨阵营配对兼容执行”的过渡方案,而不是一次性重写所有效果系统
|
||||
- `NewFight` 仍按现有建房流程创建双方 1 个战斗位;本轮打通的是多战斗位结算骨架和接口,不是外部建房入口的全量切换
|
||||
|
||||
## 1. 任务目标
|
||||
|
||||
将当前战斗系统从“每回合双方各 1 个动作”的模型,改造成支持多上场位、多操作者的统一回合模型,最终支持以下 3 种战斗模式:
|
||||
@@ -280,4 +298,3 @@
|
||||
- 当前仓库工作区可能是脏的,不要回滚无关修改。
|
||||
- 这次改造的真正核心不是结构字段改数组,而是把回合系统从“双动作模型”改成“动作列表模型”。
|
||||
- 已有 `ActorIndex/TargetIndex` 只是入口铺垫,不代表多单位模式已经完成。
|
||||
|
||||
|
||||
@@ -8,15 +8,19 @@ import (
|
||||
type FightI interface {
|
||||
Over(c PlayerI, id model.EnumBattleOverReason) //逃跑
|
||||
UseSkill(c PlayerI, id uint32) //使用技能
|
||||
GetCurrPET(c PlayerI) *info.BattlePetEntity //当前精灵
|
||||
UseSkillAt(c PlayerI, id uint32, actorIndex, targetIndex int)
|
||||
GetCurrPET(c PlayerI) *info.BattlePetEntity //当前精灵
|
||||
GetCurrPETAt(c PlayerI, actorIndex int) *info.BattlePetEntity
|
||||
GetOverInfo() model.FightOverInfo
|
||||
Ownerid() uint32
|
||||
ReadyFight(c PlayerI) //是否准备战斗
|
||||
ChangePet(c PlayerI, id uint32)
|
||||
ChangePetAt(c PlayerI, id uint32, actorIndex int)
|
||||
Capture(c PlayerI, id uint32)
|
||||
|
||||
LoadPercent(c PlayerI, percent int32)
|
||||
UseItem(c PlayerI, cacthid, itemid uint32)
|
||||
UseItemAt(c PlayerI, cacthid, itemid uint32, actorIndex, targetIndex int)
|
||||
Chat(c PlayerI, msg string)
|
||||
IsFirst(c PlayerI) bool
|
||||
GetOverChan() chan struct{}
|
||||
|
||||
@@ -6,12 +6,16 @@ import (
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/modules/player/model"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
// Compare 比较两个1v1战斗动作的执行优先级(核心逻辑)
|
||||
func (*FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.BattleActionI) {
|
||||
// Compare 比较两个战斗动作的执行优先级
|
||||
func (f *FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.BattleActionI) {
|
||||
if a == nil {
|
||||
return b, a
|
||||
}
|
||||
if b == nil {
|
||||
return a, b
|
||||
}
|
||||
// 动作本身的优先级比较
|
||||
p1 := b.Priority() - a.Priority()
|
||||
if p1 > 0 { // 对手优先级更高
|
||||
@@ -20,11 +24,22 @@ func (*FightC) Compare(a, b action.BattleActionI) (action.BattleActionI, action.
|
||||
return a, b
|
||||
}
|
||||
|
||||
if speedA, speedB := f.actionSpeed(a), f.actionSpeed(b); speedA.Cmp(speedB) != 0 {
|
||||
if speedA.Cmp(speedB) > 0 {
|
||||
return a, b
|
||||
}
|
||||
return b, a
|
||||
}
|
||||
if a.GetActorIndex() != b.GetActorIndex() {
|
||||
if a.GetActorIndex() < b.GetActorIndex() {
|
||||
return a, b
|
||||
}
|
||||
return b, a
|
||||
}
|
||||
|
||||
return a, b // 速度相同时,发起方优先
|
||||
}
|
||||
|
||||
const maxPendingActionsPerPlayer = 2
|
||||
|
||||
func (f *FightC) openActionWindow() {
|
||||
f.actionMu.Lock()
|
||||
f.acceptActions = true
|
||||
@@ -58,16 +73,15 @@ func (f *FightC) submitAction(act action.BattleActionI) {
|
||||
f.actionMu.Unlock()
|
||||
return
|
||||
}
|
||||
count := 0
|
||||
replaceIndex := -1
|
||||
for i, pending := range f.pendingActions {
|
||||
if pending == nil || pending.GetPlayerID() != act.GetPlayerID() {
|
||||
if pending == nil || actionSlotKeyFromAction(pending) != actionSlotKeyFromAction(act) {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
replaceIndex = i
|
||||
break
|
||||
}
|
||||
if count >= maxPendingActionsPerPlayer && replaceIndex >= 0 {
|
||||
if replaceIndex >= 0 {
|
||||
f.pendingActions[replaceIndex] = act
|
||||
} else {
|
||||
f.pendingActions = append(f.pendingActions, act)
|
||||
@@ -263,21 +277,51 @@ func (f *FightC) ReadyFight(c common.PlayerI) {
|
||||
|
||||
// buildFightStartInfo 构建战斗开始时需要发送给双方的信息
|
||||
func (f *FightC) buildFightStartInfo() info.FightStartOutboundInfo {
|
||||
var startInfo info.FightStartOutboundInfo
|
||||
|
||||
// 复制双方初始宠物信息(取列表第一个宠物)
|
||||
if len(f.ReadyInfo.OurPetList) > 0 {
|
||||
_ = copier.Copy(&startInfo.Info1, &f.ReadyInfo.OurPetList[0])
|
||||
startInfo.Info1.UserID = f.ReadyInfo.OurInfo.UserID
|
||||
startInfo := info.FightStartOutboundInfo{}
|
||||
ourInfos := f.collectFightPetInfos(f.Our)
|
||||
oppInfos := f.collectFightPetInfos(f.Opp)
|
||||
startInfo.Infos = append(startInfo.Infos, ourInfos...)
|
||||
startInfo.Infos = append(startInfo.Infos, oppInfos...)
|
||||
startInfo.InfoLen = uint32(len(startInfo.Infos))
|
||||
if len(ourInfos) > 0 {
|
||||
startInfo.Info1 = ourInfos[0]
|
||||
}
|
||||
if len(f.ReadyInfo.OpponentPetList) > 0 {
|
||||
_ = copier.Copy(&startInfo.Info2, &f.ReadyInfo.OpponentPetList[0])
|
||||
startInfo.Info2.UserID = f.ReadyInfo.OpponentInfo.UserID
|
||||
if len(oppInfos) > 0 {
|
||||
startInfo.Info2 = oppInfos[0]
|
||||
}
|
||||
|
||||
return startInfo
|
||||
}
|
||||
|
||||
func (f *FightC) collectFightPetInfos(inputs []*input.Input) []info.FightPetInfo {
|
||||
infos := make([]info.FightPetInfo, 0, len(inputs))
|
||||
for actorIndex, fighter := range inputs {
|
||||
if fighter == nil || fighter.Player == nil {
|
||||
continue
|
||||
}
|
||||
currentPet := fighter.PrimaryCurPet()
|
||||
if currentPet == nil {
|
||||
continue
|
||||
}
|
||||
fightInfo := info.FightPetInfo{
|
||||
UserID: fighter.Player.GetInfo().UserID,
|
||||
ActorIndex: uint32(actorIndex),
|
||||
ControllerUserID: currentPet.ControllerUserID,
|
||||
ID: currentPet.Info.ID,
|
||||
Name: currentPet.Info.Name,
|
||||
CatchTime: currentPet.Info.CatchTime,
|
||||
Hp: currentPet.Info.Hp,
|
||||
MaxHp: currentPet.Info.MaxHp,
|
||||
Level: currentPet.Info.Level,
|
||||
Catchable: uint32(fighter.CanCapture),
|
||||
}
|
||||
if fighter.AttackValue != nil {
|
||||
fightInfo.Prop = fighter.AttackValue.Prop
|
||||
}
|
||||
infos = append(infos, fightInfo)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// checkBothPlayersReady 检查PVP战斗中双方是否都已准备完成
|
||||
// 参数c为当前准备的玩家,返回true表示双方均准备完成
|
||||
func (f *FightC) checkBothPlayersReady(currentPlayer common.PlayerI) bool {
|
||||
|
||||
45
logic/service/fight/action_test.go
Normal file
45
logic/service/fight/action_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package fight
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/action"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSubmitActionKeepsDifferentActorSlots(t *testing.T) {
|
||||
f := &FightC{
|
||||
acceptActions: true,
|
||||
pendingActions: make([]action.BattleActionI, 0, 4),
|
||||
}
|
||||
f.actionRound.Store(1)
|
||||
|
||||
first := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 0}}
|
||||
second := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 1}}
|
||||
|
||||
f.submitAction(first)
|
||||
f.submitAction(second)
|
||||
|
||||
if got := len(f.pendingActions); got != 2 {
|
||||
t.Fatalf("expected 2 pending actions, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitActionReplacesSameActorSlot(t *testing.T) {
|
||||
f := &FightC{
|
||||
acceptActions: true,
|
||||
pendingActions: make([]action.BattleActionI, 0, 4),
|
||||
}
|
||||
f.actionRound.Store(1)
|
||||
|
||||
first := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 0}}
|
||||
replacement := &action.SelectSkillAction{BaseAction: action.BaseAction{PlayerID: 1001, ActorIndex: 0, TargetIndex: 1}}
|
||||
|
||||
f.submitAction(first)
|
||||
f.submitAction(replacement)
|
||||
|
||||
if got := len(f.pendingActions); got != 1 {
|
||||
t.Fatalf("expected 1 pending action, got %d", got)
|
||||
}
|
||||
if f.pendingActions[0] != replacement {
|
||||
t.Fatalf("expected replacement action to be kept")
|
||||
}
|
||||
}
|
||||
199
logic/service/fight/effect/1635_1639.go
Normal file
199
logic/service/fight/effect/1635_1639.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/action"
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/logic/service/fight/node"
|
||||
|
||||
"github.com/alpacahq/alpacadecimal"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// Effect 1635: 立刻恢复自身{0}点体力,{1}回合后恢复自身全部体力
|
||||
type Effect1635 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect1635) Skill_Use() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Our == nil {
|
||||
return true
|
||||
}
|
||||
if e.Args()[0].Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, e.Args()[0])
|
||||
}
|
||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1635, int(e.Args()[1].IntPart()))
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1635Sub struct {
|
||||
RoundEffectArg0Base
|
||||
}
|
||||
|
||||
func (e *Effect1635Sub) TurnEnd() {
|
||||
if len(e.Args()) == 0 {
|
||||
return
|
||||
}
|
||||
if e.Duration() == 1 && e.Ctx().Our != nil && e.Ctx().Our.CurPet[0] != nil {
|
||||
e.Ctx().Our.Heal(
|
||||
e.Ctx().Our,
|
||||
&action.SelectSkillAction{},
|
||||
e.Ctx().Our.CurPet[0].GetMaxHP().Sub(e.Ctx().Our.CurPet[0].GetHP()),
|
||||
)
|
||||
}
|
||||
e.EffectNode.TurnEnd()
|
||||
}
|
||||
|
||||
// Effect 1636: 涵双1回合释放4-8张玫瑰卡牌进行攻击,每张卡牌额外附加50点固定伤害,自身体力低于最大体力的1/3时效果翻倍
|
||||
type Effect1636 struct {
|
||||
node.EffectNode
|
||||
hits int
|
||||
bonusHP alpacadecimal.Decimal
|
||||
multipl int
|
||||
}
|
||||
|
||||
func (e *Effect1636) SkillHit() bool {
|
||||
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 {
|
||||
return true
|
||||
}
|
||||
base := 4 + grand.Intn(5)
|
||||
extra := base
|
||||
if e.Ctx().Our != nil && e.Ctx().Our.CurPet[0] != nil {
|
||||
threshold := e.Ctx().Our.CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(3))
|
||||
if e.Ctx().Our.CurPet[0].GetHP().Cmp(threshold) < 0 {
|
||||
extra *= 2
|
||||
}
|
||||
}
|
||||
e.hits = extra
|
||||
if e.hits > 1 {
|
||||
e.Ctx().SkillEntity.AttackTime += uint32(e.hits - 1)
|
||||
}
|
||||
e.bonusHP = alpacadecimal.NewFromInt(int64(extra * 50))
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Effect1636) DamageDivEx(zone *info.DamageZone) bool {
|
||||
if e.hits <= 0 || zone == nil || zone.Type != info.DamageType.Red {
|
||||
return true
|
||||
}
|
||||
if e.bonusHP.Cmp(alpacadecimal.Zero) > 0 {
|
||||
zone.Damage = zone.Damage.Add(e.bonusHP)
|
||||
}
|
||||
e.hits = 0
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1637: {0}回合内若对手使用属性技能,则使用属性技能后的下{1}回合属性技能命中效果失效
|
||||
type Effect1637 struct {
|
||||
node.EffectNode
|
||||
triggered bool
|
||||
}
|
||||
|
||||
func (e *Effect1637) Skill_Use() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Opp == nil {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1637, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()))
|
||||
if sub != nil {
|
||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1637Sub struct {
|
||||
RoundEffectArg0Base
|
||||
applied bool
|
||||
}
|
||||
|
||||
func (e *Effect1637Sub) ActionStart(fattack, sattack *action.SelectSkillAction) bool {
|
||||
if e.applied || len(e.Args()) < 2 {
|
||||
return true
|
||||
}
|
||||
oppAction := actionByPlayer(fattack, sattack, e.Ctx().Opp.UserID)
|
||||
if oppAction == nil || oppAction.SkillEntity == nil || oppAction.SkillEntity.Category() != info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 16371, int(e.Args()[1].IntPart()))
|
||||
if sub != nil {
|
||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
e.applied = true
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect16371Sub struct {
|
||||
RoundEffectArg0Base
|
||||
}
|
||||
|
||||
func (e *Effect16371Sub) SkillHit_ex() bool {
|
||||
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
e.Ctx().SkillEntity.SetNoSide()
|
||||
e.Ctx().SkillEntity.AttackTime = 0
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1638: {0}回合内若自身未受到攻击伤害则令对手全属性-{1}
|
||||
type Effect1638 struct {
|
||||
RoundEffectArg0Base
|
||||
touched bool
|
||||
}
|
||||
|
||||
func (e *Effect1638) Skill_Use() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Our == nil {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Our.InitEffect(
|
||||
input.EffectType.Sub,
|
||||
1638,
|
||||
int(e.Args()[0].IntPart()),
|
||||
int(e.Args()[1].IntPart()),
|
||||
)
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Effect1638) DamageSubEx(zone *info.DamageZone) bool {
|
||||
if zone != nil && zone.Type == info.DamageType.Red && zone.Damage.Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.touched = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Effect1638) TurnEnd() {
|
||||
if len(e.Args()) >= 2 && !e.touched {
|
||||
applyAllPropDown(e.Ctx().Our, e.Ctx().Opp, int8(e.Args()[1].IntPart()))
|
||||
}
|
||||
e.touched = false
|
||||
e.EffectNode.TurnEnd()
|
||||
}
|
||||
|
||||
// Effect 1639: 自身处于能力提升状态时100%打出致命一击
|
||||
type Effect1639 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect1639) SkillHit() bool {
|
||||
if e.Ctx().SkillEntity == nil || e.Ctx().Our == nil || !e.Ctx().Our.HasPropADD() || e.Ctx().SkillEntity.Category() == info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
e.Ctx().SkillEntity.XML.CritRate = 16
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.Skill, 1635, &Effect1635{})
|
||||
input.InitEffect(input.EffectType.Sub, 1635, &Effect1635Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1636, &Effect1636{})
|
||||
input.InitEffect(input.EffectType.Skill, 1637, &Effect1637{})
|
||||
input.InitEffect(input.EffectType.Sub, 1637, &Effect1637Sub{})
|
||||
input.InitEffect(input.EffectType.Sub, 16371, &Effect16371Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1638, &Effect1638{})
|
||||
input.InitEffect(input.EffectType.Skill, 1639, &Effect1639{})
|
||||
}
|
||||
160
logic/service/fight/effect/1640_1644.go
Normal file
160
logic/service/fight/effect/1640_1644.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/action"
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/logic/service/fight/node"
|
||||
|
||||
"github.com/alpacahq/alpacadecimal"
|
||||
)
|
||||
|
||||
func addFatalMarks(target *input.Input, count int) {
|
||||
if target == nil || count <= 0 {
|
||||
return
|
||||
}
|
||||
pet := target.CurPet[0]
|
||||
if pet == nil {
|
||||
return
|
||||
}
|
||||
pet.FatalCrackLayers += count
|
||||
}
|
||||
|
||||
// Effect 1640: 出手时若自身满体力则100%打出致命一击
|
||||
type Effect1640 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1640) Skill_Use() bool {
|
||||
if e.Ctx().Our == nil || e.Ctx().Opp == nil || e.Ctx().Our.CurPet[0] == nil {
|
||||
return true
|
||||
}
|
||||
if e.Ctx().Our.CurPet[0].GetHP().Cmp(e.Ctx().Our.CurPet[0].GetMaxHP()) < 0 {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1640)
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1640Sub struct{ FixedDuration1Base }
|
||||
|
||||
func (e *Effect1640Sub) ActionStart(fattack, sattack *action.SelectSkillAction) bool {
|
||||
current := actionByPlayer(fattack, sattack, e.Ctx().Our.UserID)
|
||||
if current == nil || current.SkillEntity == nil || current.SkillEntity.Category() == info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
current.SkillEntity.XML.CritRate = 16
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1641: 自身处于能力提升状态时造成伤害的{0}%恢复自身体力值,当前体力低于最大体力的1/{1}时附加等量百分比伤害
|
||||
type Effect1641 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1641) Skill_Use() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Our == nil || e.Ctx().Opp == nil || e.Ctx().Our.CurPet[0] == nil || e.Ctx().Opp.CurPet[0] == nil {
|
||||
return true
|
||||
}
|
||||
if !e.Ctx().Our.HasPropADD() {
|
||||
return true
|
||||
}
|
||||
|
||||
damage := e.Ctx().Our.SumDamage
|
||||
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
heal := damage.Mul(e.Args()[0]).Div(hundred)
|
||||
if heal.Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, heal)
|
||||
}
|
||||
|
||||
threshold := e.Ctx().Our.CurPet[0].GetMaxHP().Div(e.Args()[1])
|
||||
if e.Ctx().Our.CurPet[0].GetHP().Cmp(threshold) < 0 && heal.Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: heal})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1642: 消除对手能力提升状态,消除成功则{0}%随机为对手任意技能散布{1}枚致命印记
|
||||
type Effect1642 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1642) Skill_Use() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Opp == nil {
|
||||
return true
|
||||
}
|
||||
cleared := clearPositiveProps(e.Ctx().Opp, e.Ctx().Our)
|
||||
if !cleared {
|
||||
return true
|
||||
}
|
||||
if ok, _, _ := e.Input.Player.Roll(int(e.Args()[0].IntPart()), 100); ok {
|
||||
addFatalMarks(e.Ctx().Opp, int(e.Args()[1].IntPart()))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1643: 对手每存在1层致命裂痕则附加{0}点真实伤害
|
||||
type Effect1643 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1643) Skill_Use() bool {
|
||||
if len(e.Args()) == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil {
|
||||
return true
|
||||
}
|
||||
layers := e.Ctx().Opp.CurPet[0].FatalCrackLayers
|
||||
if layers <= 0 {
|
||||
return true
|
||||
}
|
||||
damage := alpacadecimal.NewFromInt(int64(layers)).Mul(e.Args()[0])
|
||||
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: damage})
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1644: {0}回合内对手使用攻击技能则随机进入{1}种异常状态,未触发则随机为对手任意技能散布{2}枚致命印记
|
||||
type Effect1644 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1644) Skill_Use() bool {
|
||||
if len(e.Args()) < 3 || e.Ctx().Opp == nil {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1644, e.SideEffectArgs...)
|
||||
if sub != nil {
|
||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1644Sub struct {
|
||||
RoundEffectArg0Base
|
||||
triggered bool
|
||||
}
|
||||
|
||||
func (e *Effect1644Sub) Action_end() bool {
|
||||
if len(e.Args()) < 3 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 {
|
||||
return true
|
||||
}
|
||||
if e.Args()[1].IntPart() > 0 {
|
||||
applyRandomStatuses1507(e.Ctx().Our, e.Ctx().Opp, int(e.Args()[1].IntPart()))
|
||||
}
|
||||
e.triggered = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Effect1644Sub) TurnEnd() {
|
||||
if !e.triggered && len(e.Args()) >= 3 {
|
||||
addFatalMarks(e.Ctx().Opp, int(e.Args()[2].IntPart()))
|
||||
}
|
||||
e.triggered = false
|
||||
e.EffectNode.TurnEnd()
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.Skill, 1640, &Effect1640{})
|
||||
input.InitEffect(input.EffectType.Sub, 1640, &Effect1640Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1641, &Effect1641{})
|
||||
input.InitEffect(input.EffectType.Skill, 1642, &Effect1642{})
|
||||
input.InitEffect(input.EffectType.Skill, 1643, &Effect1643{})
|
||||
input.InitEffect(input.EffectType.Skill, 1644, &Effect1644{})
|
||||
input.InitEffect(input.EffectType.Sub, 1644, &Effect1644Sub{})
|
||||
}
|
||||
272
logic/service/fight/effect/1645_1649.go
Normal file
272
logic/service/fight/effect/1645_1649.go
Normal file
@@ -0,0 +1,272 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/action"
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/logic/service/fight/node"
|
||||
|
||||
"github.com/alpacahq/alpacadecimal"
|
||||
)
|
||||
|
||||
const fatalCrackEffectID = 17001
|
||||
|
||||
func fatalCrackLayers(target *input.Input) int {
|
||||
if target == nil {
|
||||
return 0
|
||||
}
|
||||
total := 0
|
||||
for _, eff := range target.Effects {
|
||||
if eff == nil || !eff.Alive() || eff.ID().GetEffectType() != input.EffectType.Sub {
|
||||
continue
|
||||
}
|
||||
if int(eff.ID().Suffix()) != fatalCrackEffectID {
|
||||
continue
|
||||
}
|
||||
if sub, ok := eff.(*FatalCrackSub); ok {
|
||||
total += sub.layers
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func addFatalCrack(owner, target *input.Input, layers int) {
|
||||
if target == nil || owner == nil || layers <= 0 {
|
||||
return
|
||||
}
|
||||
eff := owner.InitEffect(input.EffectType.Sub, fatalCrackEffectID, layers)
|
||||
if eff != nil {
|
||||
target.AddEffect(owner, eff)
|
||||
}
|
||||
}
|
||||
|
||||
type FatalCrackSub struct {
|
||||
node.EffectNode
|
||||
layers int
|
||||
}
|
||||
|
||||
func (f *FatalCrackSub) SetArgs(t *input.Input, a ...int) {
|
||||
f.EffectNode.SetArgs(t, a...)
|
||||
f.CanStack(true)
|
||||
f.Duration(-1)
|
||||
if len(a) > 0 {
|
||||
f.layers = a[0]
|
||||
}
|
||||
if f.layers <= 0 {
|
||||
f.layers = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Effect 1645: {0}回合内对手使用属性技能则自身下{1}次受到的攻击伤害减少{2}%
|
||||
type Effect1645 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1645) Skill_Use() bool {
|
||||
if len(e.Args()) < 3 {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1645, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()), int(e.Args()[2].IntPart()))
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1645Sub struct {
|
||||
RoundEffectArg0Base
|
||||
active bool
|
||||
hits int
|
||||
reduce alpacadecimal.Decimal
|
||||
}
|
||||
|
||||
func (e *Effect1645Sub) SetArgs(t *input.Input, a ...int) {
|
||||
e.RoundEffectArg0Base.SetArgs(t, a...)
|
||||
if len(a) > 1 {
|
||||
e.hits = a[1]
|
||||
}
|
||||
if len(a) > 2 {
|
||||
e.reduce = alpacadecimal.NewFromInt(int64(a[2]))
|
||||
}
|
||||
if e.hits < 0 {
|
||||
e.hits = 0
|
||||
}
|
||||
if e.reduce.Cmp(alpacadecimal.Zero) < 0 {
|
||||
e.reduce = alpacadecimal.Zero
|
||||
}
|
||||
e.active = false
|
||||
}
|
||||
|
||||
func (e *Effect1645Sub) SkillHit_ex() bool {
|
||||
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
if e.hits <= 0 {
|
||||
return true
|
||||
}
|
||||
e.active = true
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Effect1645Sub) DamageDivEx(zone *info.DamageZone) bool {
|
||||
if !e.active || e.hits <= 0 || zone == nil || zone.Type != info.DamageType.Red || e.reduce.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
zone.Damage = zone.Damage.Mul(hundred.Sub(e.reduce)).Div(hundred)
|
||||
e.hits--
|
||||
if e.hits <= 0 {
|
||||
e.active = false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1646: 全属性+{0},对手存在致命裂痕时强化效果翻倍
|
||||
type Effect1646 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1646) Skill_Use() bool {
|
||||
if len(e.Args()) == 0 {
|
||||
return true
|
||||
}
|
||||
boost := int8(e.Args()[0].IntPart())
|
||||
if fatalCrackLayers(e.Ctx().Opp) > 0 {
|
||||
boost *= 2
|
||||
}
|
||||
for i := 0; i < 6; i++ {
|
||||
e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), boost)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1647: {0}回合内每回合使用技能吸取对手最大体力的1/{1},吸取体力时若自身体力低于最大体力的1/{2}则吸取效果翻倍,对手免疫百分比伤害时额外附加{3}点真实伤害
|
||||
type Effect1647 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1647) Skill_Use() bool {
|
||||
if len(e.Args()) < 4 {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1647, e.SideEffectArgs...)
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1647Sub struct{ RoundEffectArg0Base }
|
||||
|
||||
func (e *Effect1647Sub) OnSkill() bool {
|
||||
if len(e.Args()) < 4 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().Our == nil || e.Ctx().Our.CurPet[0] == nil {
|
||||
return true
|
||||
}
|
||||
drain := e.Ctx().Opp.CurPet[0].GetMaxHP().Div(e.Args()[1])
|
||||
threshold := e.Ctx().Our.CurPet[0].GetMaxHP().Div(alpacadecimal.NewFromInt(2))
|
||||
if e.Ctx().Our.CurPet[0].GetHP().Cmp(threshold) < 0 {
|
||||
drain = drain.Mul(alpacadecimal.NewFromInt(2))
|
||||
}
|
||||
if drain.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
before := e.Ctx().Opp.CurPet[0].GetHP()
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Percent, Damage: drain})
|
||||
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, drain)
|
||||
if e.Ctx().Opp.CurPet[0].GetHP().Cmp(before) == 0 && e.Args()[3].Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: e.Args()[3]})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1648: 附加自身最大体力{0}%的百分比伤害并恢复等量体力值,对手存在致命裂痕时转变为等量的真实伤害
|
||||
type Effect1648 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1648) OnSkill() bool {
|
||||
if len(e.Args()) == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil {
|
||||
return true
|
||||
}
|
||||
percent := e.Args()[0]
|
||||
damage := e.Ctx().Our.CurPet[0].GetMaxHP().Mul(percent).Div(alpacadecimal.NewFromInt(100))
|
||||
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
target := &info.DamageZone{Type: info.DamageType.Percent, Damage: damage}
|
||||
if fatalCrackLayers(e.Ctx().Opp) > 0 {
|
||||
target.Type = info.DamageType.True
|
||||
}
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, target)
|
||||
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, damage)
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1649: {0}%概率造成的攻击伤害为{1}倍,对手每存在1层致命裂痕则概率提升{2}%,未触发则{3}回合内令对手使用的属性技能无效
|
||||
type Effect1649 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1649) Skill_Use() bool {
|
||||
if len(e.Args()) < 4 {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1649, e.SideEffectArgs...)
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1649Sub struct {
|
||||
RoundEffectArg0Base
|
||||
fired bool
|
||||
}
|
||||
|
||||
func (e *Effect1649Sub) SkillHit() bool {
|
||||
e.fired = false
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Effect1649Sub) Damage_Mul(zone *info.DamageZone) bool {
|
||||
if e.fired || zone == nil || zone.Type != info.DamageType.Red || len(e.Args()) < 4 {
|
||||
return true
|
||||
}
|
||||
chance := int(e.Args()[0].IntPart())
|
||||
chance += fatalCrackLayers(e.Ctx().Opp) * int(e.Args()[2].IntPart())
|
||||
if chance > 100 {
|
||||
chance = 100
|
||||
}
|
||||
if chance > 0 {
|
||||
if ok, _, _ := e.Input.Player.Roll(chance, 100); ok {
|
||||
mul := int(e.Args()[1].IntPart())
|
||||
if mul <= 1 {
|
||||
mul = 1
|
||||
}
|
||||
e.fired = true
|
||||
zone.Damage = zone.Damage.Mul(alpacadecimal.NewFromInt(int64(mul)))
|
||||
return true
|
||||
}
|
||||
}
|
||||
duration := int(e.Args()[3].IntPart())
|
||||
if duration > 0 {
|
||||
block := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 16494, duration)
|
||||
if block != nil {
|
||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, block)
|
||||
}
|
||||
}
|
||||
e.fired = true
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1649BlockSub struct{ RoundEffectArg0Base }
|
||||
|
||||
func (e *Effect1649BlockSub) SkillHit_ex() bool {
|
||||
if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
e.Ctx().SkillEntity.SetNoSide()
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.Sub, fatalCrackEffectID, &FatalCrackSub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1645, &Effect1645{})
|
||||
input.InitEffect(input.EffectType.Sub, 1645, &Effect1645Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1646, &Effect1646{})
|
||||
input.InitEffect(input.EffectType.Skill, 1647, &Effect1647{})
|
||||
input.InitEffect(input.EffectType.Sub, 1647, &Effect1647Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1648, &Effect1648{})
|
||||
input.InitEffect(input.EffectType.Skill, 1649, &Effect1649{})
|
||||
input.InitEffect(input.EffectType.Sub, 1649, &Effect1649Sub{})
|
||||
input.InitEffect(input.EffectType.Sub, 16494, &Effect1649BlockSub{})
|
||||
}
|
||||
270
logic/service/fight/effect/1650_1654.go
Normal file
270
logic/service/fight/effect/1650_1654.go
Normal file
@@ -0,0 +1,270 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/logic/service/fight/node"
|
||||
|
||||
"github.com/alpacahq/alpacadecimal"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
const fatalMarkDamagePercent = 10
|
||||
|
||||
func randomSkillIndexes(count int, skills []*info.SkillEntity) []int {
|
||||
if count <= 0 || len(skills) == 0 {
|
||||
return nil
|
||||
}
|
||||
if count > len(skills) {
|
||||
count = len(skills)
|
||||
}
|
||||
indexes := grand.Perm(len(skills))
|
||||
return indexes[:count]
|
||||
}
|
||||
|
||||
func addFatalMark(owner, target *input.Input, skillID int) bool {
|
||||
if owner == nil || target == nil {
|
||||
return false
|
||||
}
|
||||
sub := target.InitEffect(input.EffectType.Sub, 1650, skillID)
|
||||
if sub == nil {
|
||||
return false
|
||||
}
|
||||
target.AddEffect(owner, sub)
|
||||
return true
|
||||
}
|
||||
|
||||
func fatalMarkCount(target *input.Input) int {
|
||||
if target == nil {
|
||||
return 0
|
||||
}
|
||||
count := 0
|
||||
for _, eff := range target.Effects {
|
||||
if eff == nil || !eff.Alive() {
|
||||
continue
|
||||
}
|
||||
if eff.ID().GetEffectType() != input.EffectType.Sub || int(eff.ID().Suffix()) != 1650 {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func fatalMarkReduction(target *input.Input) (int, *Effect1651Sub) {
|
||||
if target == nil {
|
||||
return 1, nil
|
||||
}
|
||||
eff := target.GetEffect(input.EffectType.Sub, 1651)
|
||||
if eff == nil {
|
||||
return 1, nil
|
||||
}
|
||||
sub, ok := eff.(*Effect1651Sub)
|
||||
if !ok || sub.remaining <= 0 {
|
||||
return 1, nil
|
||||
}
|
||||
divisor := sub.divisor
|
||||
if divisor <= 0 {
|
||||
divisor = 1
|
||||
}
|
||||
sub.remaining--
|
||||
if sub.remaining <= 0 {
|
||||
sub.Alive(false)
|
||||
}
|
||||
return divisor, sub
|
||||
}
|
||||
|
||||
// Effect 1650: 命中后{0}%随机为对手任意技能散布{1}枚致命印记,若对手当前精灵致命裂痕≥{2}层则额外散布{3}枚致命印记
|
||||
type Effect1650 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect1650) SkillHit() bool {
|
||||
if len(e.Args()) < 4 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil {
|
||||
return true
|
||||
}
|
||||
if ok, _, _ := e.Input.Player.Roll(int(e.Args()[0].IntPart()), 100); !ok {
|
||||
return true
|
||||
}
|
||||
count := int(e.Args()[1].IntPart())
|
||||
if count <= 0 {
|
||||
return true
|
||||
}
|
||||
skillList := e.Ctx().Opp.CurPet[0].Skills
|
||||
selected := randomSkillIndexes(count, skillList)
|
||||
if fatalMarkCount(e.Ctx().Opp) >= int(e.Args()[2].IntPart()) {
|
||||
extra := randomSkillIndexes(int(e.Args()[3].IntPart()), skillList)
|
||||
selected = append(selected, extra...)
|
||||
}
|
||||
for _, idx := range selected {
|
||||
if idx < 0 || idx >= len(skillList) {
|
||||
continue
|
||||
}
|
||||
addFatalMark(e.Ctx().Our, e.Ctx().Opp, skillList[idx].XML.ID)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1650MarkSub struct {
|
||||
node.EffectNode
|
||||
skillID int
|
||||
}
|
||||
|
||||
func (e *Effect1650MarkSub) SetArgs(t *input.Input, a ...int) {
|
||||
e.EffectNode.SetArgs(t, a...)
|
||||
e.Duration(-1)
|
||||
if len(a) > 0 {
|
||||
e.skillID = a[0]
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Effect1650MarkSub) SkillHit() bool {
|
||||
if e.skillID == 0 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.AttackTime == 0 || e.Ctx().SkillEntity.Category() == info.Category.STATUS {
|
||||
return true
|
||||
}
|
||||
if e.Ctx().SkillEntity.XML.ID != e.skillID {
|
||||
return true
|
||||
}
|
||||
damage := e.Ctx().Our.CurPet[0].GetMaxHP().Mul(alpacadecimal.NewFromInt(fatalMarkDamagePercent)).Div(alpacadecimal.NewFromInt(100))
|
||||
divisor, _ := fatalMarkReduction(e.Ctx().Opp)
|
||||
damage = damage.Div(alpacadecimal.NewFromInt(int64(divisor)))
|
||||
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.True, Damage: damage})
|
||||
}
|
||||
e.Alive(false)
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1651: 当回合击败对手则令对手下{0}次触发致命印记真实伤害效果转变为1/{1}
|
||||
type Effect1651 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect1651) Skill_Use() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().Opp.CurPet[0].Info.Hp > 0 {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1651, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()))
|
||||
if sub != nil {
|
||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1651Sub struct {
|
||||
node.EffectNode
|
||||
remaining int
|
||||
divisor int
|
||||
}
|
||||
|
||||
func (e *Effect1651Sub) SetArgs(t *input.Input, a ...int) {
|
||||
e.EffectNode.SetArgs(t, a...)
|
||||
e.Duration(-1)
|
||||
if len(a) > 0 {
|
||||
e.remaining = a[0]
|
||||
}
|
||||
if len(a) > 1 {
|
||||
e.divisor = a[1]
|
||||
}
|
||||
}
|
||||
|
||||
// Effect 1652: 释放技能时自身每损失{0}%的体力值则此技能威力提升{1}点
|
||||
type Effect1652 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect1652) SkillHit() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().Our == nil || e.Ctx().Our.CurPet[0] == nil {
|
||||
return true
|
||||
}
|
||||
maxHP := e.Ctx().Our.CurPet[0].GetMaxHP()
|
||||
curHP := e.Ctx().Our.CurPet[0].GetHP()
|
||||
if maxHP.Cmp(alpacadecimal.Zero) == 0 {
|
||||
return true
|
||||
}
|
||||
missing := maxHP.Sub(curHP)
|
||||
percent := missing.Mul(alpacadecimal.NewFromInt(100)).Div(maxHP)
|
||||
unit := e.Args()[0]
|
||||
if unit.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
steps := int(percent.Div(unit).IntPart())
|
||||
if steps <= 0 {
|
||||
return true
|
||||
}
|
||||
e.Ctx().SkillEntity.XML.Power += steps * int(e.Args()[1].IntPart())
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1653: 释放技能时对手每残留{0}%的体力值则此技能附加{1}点固定伤害
|
||||
type Effect1653 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect1653) Skill_Use() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.AttackTime == 0 {
|
||||
return true
|
||||
}
|
||||
maxHP := e.Ctx().Opp.CurPet[0].GetMaxHP()
|
||||
curHP := e.Ctx().Opp.CurPet[0].GetHP()
|
||||
if maxHP.Cmp(alpacadecimal.Zero) == 0 {
|
||||
return true
|
||||
}
|
||||
percent := curHP.Mul(alpacadecimal.NewFromInt(100)).Div(maxHP)
|
||||
unit := e.Args()[0]
|
||||
if unit.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
count := int(percent.Div(unit).IntPart())
|
||||
if count <= 0 {
|
||||
return true
|
||||
}
|
||||
damage := alpacadecimal.NewFromInt(int64(count) * int64(e.Args()[1].IntPart()))
|
||||
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: damage})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1654: 当回合击败对手则令对手下只登场精灵首次使用的技能所附加的效果失效
|
||||
type Effect1654 struct {
|
||||
node.EffectNode
|
||||
}
|
||||
|
||||
func (e *Effect1654) Skill_Use() bool {
|
||||
if len(e.Args()) == 0 || e.Ctx().Opp == nil || e.Ctx().Opp.CurPet[0] == nil || e.Ctx().Opp.CurPet[0].Info.Hp > 0 {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1654)
|
||||
if sub != nil {
|
||||
e.Ctx().Opp.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1654Sub struct {
|
||||
node.EffectNode
|
||||
deployed bool
|
||||
}
|
||||
|
||||
func (e *Effect1654Sub) OnSkill() bool {
|
||||
if e.deployed || e.Ctx().SkillEntity == nil {
|
||||
return true
|
||||
}
|
||||
e.Ctx().SkillEntity.XML.SideEffectS = nil
|
||||
e.Ctx().SkillEntity.XML.SideEffectArgS = nil
|
||||
e.deployed = true
|
||||
e.Alive(false)
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.Skill, 1650, &Effect1650{})
|
||||
input.InitEffect(input.EffectType.Sub, 1650, &Effect1650MarkSub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1651, &Effect1651{})
|
||||
input.InitEffect(input.EffectType.Sub, 1651, &Effect1651Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1652, &Effect1652{})
|
||||
input.InitEffect(input.EffectType.Skill, 1653, &Effect1653{})
|
||||
input.InitEffect(input.EffectType.Skill, 1654, &Effect1654{})
|
||||
input.InitEffect(input.EffectType.Sub, 1654, &Effect1654Sub{})
|
||||
}
|
||||
233
logic/service/fight/effect/1655_1659.go
Normal file
233
logic/service/fight/effect/1655_1659.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package effect
|
||||
|
||||
import (
|
||||
"blazing/logic/service/fight/action"
|
||||
"blazing/logic/service/fight/info"
|
||||
"blazing/logic/service/fight/input"
|
||||
"blazing/logic/service/fight/node"
|
||||
|
||||
"github.com/alpacahq/alpacadecimal"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// Effect 1655: {0}回合内每回合结束后{1}%恢复自身所有技能{2}点PP值
|
||||
type Effect1655 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1655) Skill_Use() bool {
|
||||
if len(e.Args()) < 3 {
|
||||
return true
|
||||
}
|
||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1655, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart()), int(e.Args()[2].IntPart()))
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1655Sub struct{ RoundEffectArg0Base }
|
||||
|
||||
func (e *Effect1655Sub) TurnEnd() {
|
||||
if len(e.Args()) < 3 || e.Ctx().Our == nil || e.Args()[2].Cmp(alpacadecimal.Zero) <= 0 {
|
||||
e.EffectNode.TurnEnd()
|
||||
return
|
||||
}
|
||||
chance := int(e.Args()[1].IntPart())
|
||||
if chance <= 0 {
|
||||
e.EffectNode.TurnEnd()
|
||||
return
|
||||
}
|
||||
if ok, _, _ := e.Input.Player.Roll(chance, 100); ok {
|
||||
amount := int(e.Args()[2].IntPart())
|
||||
if amount > 0 && e.Ctx().Our.CurPet[0] != nil {
|
||||
for i := range e.Ctx().Our.CurPet[0].Info.SkillList {
|
||||
e.Ctx().Our.CurPet[0].Info.SkillList[i].PP += uint32(amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
e.EffectNode.TurnEnd()
|
||||
}
|
||||
|
||||
// Effect 1656: 100%复制对手当回合释放的技能,若对手当回合使用的技能为攻击技能则令对手随机1个技能PP值归零,若对手当回合使用的技能为属性技能则令对手下回合先制-2
|
||||
type Effect1656 struct {
|
||||
node.EffectNode
|
||||
applied bool
|
||||
}
|
||||
|
||||
func (e *Effect1656) ComparePre(fattack, sattack *action.SelectSkillAction) bool {
|
||||
if e.applied {
|
||||
return true
|
||||
}
|
||||
current := actionByPlayer(fattack, sattack, e.Ctx().Our.UserID)
|
||||
opponent := actionByPlayer(fattack, sattack, e.Ctx().Opp.UserID)
|
||||
if current == nil || opponent == nil || opponent.SkillEntity == nil {
|
||||
return true
|
||||
}
|
||||
clone := cloneSkillEntity(opponent.SkillEntity)
|
||||
if clone == nil {
|
||||
return true
|
||||
}
|
||||
current.SkillEntity = clone
|
||||
e.applied = true
|
||||
if opponent.SkillEntity.Category() != info.Category.STATUS {
|
||||
zeroRandomSkillPP(e.Ctx().Opp, 1)
|
||||
} else {
|
||||
sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 1656, 1, 2)
|
||||
if sub != nil {
|
||||
e.Ctx().Opp.AddEffect(e.Ctx().Opp, sub)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Effect1656) Action_end() bool {
|
||||
e.applied = false
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1656Sub struct {
|
||||
FixedDuration1Base
|
||||
priority int
|
||||
}
|
||||
|
||||
func (e *Effect1656Sub) SetArgs(t *input.Input, a ...int) {
|
||||
e.FixedDuration1Base.SetArgs(t, a...)
|
||||
e.CanStack(false)
|
||||
if len(a) > 1 {
|
||||
e.priority = a[1]
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Effect1656Sub) ComparePre(fattack, sattack *action.SelectSkillAction) bool {
|
||||
if e.priority == 0 {
|
||||
return true
|
||||
}
|
||||
current := actionByPlayer(fattack, sattack, e.Ctx().Opp.UserID)
|
||||
if current == nil || current.SkillEntity == nil {
|
||||
return true
|
||||
}
|
||||
current.SkillEntity.XML.Priority -= e.priority
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1657: 己方每有一只精灵死亡则附加{0}点固定伤害,对手体力高于最大体力的1/{1}时转变为{2}点
|
||||
type Effect1657 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1657) Skill_Use() bool {
|
||||
if len(e.Args()) < 3 || e.Ctx().Opp == nil || e.Ctx().Our == nil {
|
||||
return true
|
||||
}
|
||||
dead := 0
|
||||
for _, pet := range e.Ctx().Our.AllPet {
|
||||
if pet == nil {
|
||||
continue
|
||||
}
|
||||
if !pet.Alive() {
|
||||
dead++
|
||||
}
|
||||
}
|
||||
if dead == 0 {
|
||||
return true
|
||||
}
|
||||
damage := e.Args()[0].Mul(alpacadecimal.NewFromInt(int64(dead)))
|
||||
if e.Args()[1].Cmp(alpacadecimal.Zero) > 0 && e.Ctx().Opp.CurPet[0] != nil {
|
||||
threshold := e.Ctx().Opp.CurPet[0].GetMaxHP().Div(e.Args()[1])
|
||||
if threshold.Cmp(alpacadecimal.Zero) > 0 && e.Ctx().Opp.CurPet[0].GetHP().Cmp(threshold) > 0 {
|
||||
damage = e.Args()[2]
|
||||
}
|
||||
}
|
||||
if damage.Cmp(alpacadecimal.Zero) <= 0 {
|
||||
return true
|
||||
}
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: damage})
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1658: 3回合内每回合80%闪避对手攻击,未触发时自身处于圣念状态则使对手随机1项技能PP值归零,自身处于邪念状态则使对手失明
|
||||
type Effect1658 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1658) Skill_Use() bool {
|
||||
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1658, 3, 80)
|
||||
if sub != nil {
|
||||
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type Effect1658Sub struct{ RoundEffectArg0Base }
|
||||
|
||||
func (e *Effect1658Sub) SkillHit_ex() bool {
|
||||
if len(e.Args()) < 2 || e.Ctx().Opp == nil || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS || e.Ctx().SkillEntity.AttackTime == 0 {
|
||||
return true
|
||||
}
|
||||
if e.Ctx().SkillEntity.AttackTime == 2 {
|
||||
return true
|
||||
}
|
||||
chance := int(e.Args()[1].IntPart())
|
||||
if chance <= 0 {
|
||||
return true
|
||||
}
|
||||
if ok, _, _ := e.Input.Player.Roll(chance, 100); ok {
|
||||
e.Ctx().SkillEntity.SetMiss()
|
||||
return true
|
||||
}
|
||||
switch {
|
||||
case e.Ctx().Our.StatEffect_Exist(petStatus2077Holy):
|
||||
zeroRandomSkillPP(e.Ctx().Opp, 1)
|
||||
case e.Ctx().Our.StatEffect_Exist(petStatus2077Evil):
|
||||
addStatusByID(e.Ctx().Our, e.Ctx().Opp, int(info.PetStatus.Blind))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Effect 1659: 随机附加给对手{0}-{1}点固定伤害,若打出致命一击则效果转变为吸取对手{2}-{3}点体力
|
||||
type Effect1659 struct{ node.EffectNode }
|
||||
|
||||
func (e *Effect1659) Skill_Use() bool {
|
||||
if len(e.Args()) < 4 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.AttackTime == 0 || e.Ctx().Opp == nil {
|
||||
return true
|
||||
}
|
||||
minDamage := int(e.Args()[0].IntPart())
|
||||
maxDamage := int(e.Args()[1].IntPart())
|
||||
if maxDamage < minDamage {
|
||||
maxDamage = minDamage
|
||||
}
|
||||
damage := alpacadecimal.NewFromInt(int64(minDamage))
|
||||
if maxDamage > minDamage {
|
||||
damage = alpacadecimal.NewFromInt(int64(minDamage + grand.Intn(maxDamage-minDamage+1)))
|
||||
}
|
||||
if e.Ctx().SkillEntity.Crit != 0 {
|
||||
minDrain := int(e.Args()[2].IntPart())
|
||||
maxDrain := int(e.Args()[3].IntPart())
|
||||
if maxDrain < minDrain {
|
||||
maxDrain = minDrain
|
||||
}
|
||||
if minDrain < 0 {
|
||||
minDrain = 0
|
||||
}
|
||||
drain := minDrain
|
||||
if maxDrain > minDrain {
|
||||
drain += grand.Intn(maxDrain - minDrain + 1)
|
||||
}
|
||||
if drain > 0 {
|
||||
amount := alpacadecimal.NewFromInt(int64(drain))
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: amount})
|
||||
e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, amount)
|
||||
}
|
||||
return true
|
||||
}
|
||||
if damage.Cmp(alpacadecimal.Zero) > 0 {
|
||||
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: damage})
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
input.InitEffect(input.EffectType.Skill, 1655, &Effect1655{})
|
||||
input.InitEffect(input.EffectType.Sub, 1655, &Effect1655Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1656, &Effect1656{})
|
||||
input.InitEffect(input.EffectType.Sub, 1656, &Effect1656Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1657, &Effect1657{})
|
||||
input.InitEffect(input.EffectType.Skill, 1658, &Effect1658{})
|
||||
input.InitEffect(input.EffectType.Sub, 1658, &Effect1658Sub{})
|
||||
input.InitEffect(input.EffectType.Skill, 1659, &Effect1659{})
|
||||
}
|
||||
@@ -18,6 +18,7 @@ func (e *Effect63) OnSkill() bool {
|
||||
for i, v := range e.Ctx().Our.Prop[:] {
|
||||
if v < 0 {
|
||||
e.Ctx().Opp.SetProp(e.Ctx().Our, int8(i), v)
|
||||
e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), 0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1060,6 +1060,26 @@ var effectInfoByID = map[int]string{
|
||||
1632: "吸取对手{0}点体力,若对手任意1项技能PP值小于{1}点则额外吸取{2}点体力",
|
||||
1633: "使自身体力百分比与对手体力百分比对调,自身体力百分比高于对手时{0}%令对手{1}",
|
||||
1634: "自身体力低于250时必定先手",
|
||||
1640: "出手时若自身满体力则100%打出致命一击",
|
||||
1641: "自身处于能力提升状态时造成伤害的{0}%恢复自身体力值,当前体力低于最大体力的1/{1}时附加等量百分比伤害",
|
||||
1642: "消除对手能力提升状态,消除成功则{0}%随机在对手身上散布{1}枚致命印记",
|
||||
1643: "对手每存在1层致命裂痕则附加{0}点真实伤害",
|
||||
1644: "{0}回合内对手使用攻击技能则随机进入{1}种异常状态,未触发则随机散布{2}枚致命印记",
|
||||
1645: "{0}回合内对手使用属性技能则自身下{1}次受到的攻击伤害减少{2}%",
|
||||
1646: "全属性+{0},对手存在致命裂痕时强化效果翻倍",
|
||||
1647: "{0}回合内每回合使用技能吸取对手最大体力的1/{1},吸取体力时若自身体力低于最大体力的1/{2}则吸取效果翻倍,对手免疫百分比伤害时额外附加{3}点真实伤害",
|
||||
1648: "附加自身最大体力{0}%的百分比伤害并恢复等量体力值,对手存在致命裂痕时转变为等量的真实伤害",
|
||||
1649: "{0}%概率造成的攻击伤害为{1}倍,对手每存在1层致命裂痕则概率提升{2}%,未触发则{3}回合内令对手使用的属性技能无效",
|
||||
1650: "命中后{0}%随机为对手任意技能散布{1}枚致命印记,若对手当前精灵致命裂痕≥{2}层则额外散布{3}枚致命印记",
|
||||
1651: "当回合击败对手则令对手下{0}次触发致命印记真实伤害效果转变为1/{1}",
|
||||
1652: "释放技能时自身每损失{0}%的体力值则此技能威力提升{1}点",
|
||||
1653: "释放技能时对手每残留{0}%的体力值则此技能附加{1}点固定伤害",
|
||||
1654: "当回合击败对手则令对手下只登场精灵首次使用的技能所附加的效果失效",
|
||||
1655: "{0}回合内每回合结束后{1}%恢复自身所有技能{2}点PP值",
|
||||
1656: "100%复制对手当回合释放的技能,若对手当回合使用的技能为攻击技能则令对手随机1个技能PP值归零,若对手当回合使用的技能为属性技能则令对手下回合先制-2",
|
||||
1657: "己方每有一只精灵死亡则附加{0}点固定伤害,对手体力高于最大体力的1/{1}时转变为{2}点",
|
||||
1658: "3回合内每回合80%闪避对手攻击,未触发时自身处于圣念状态则使对手随机1项技能PP值归零,自身处于邪念状态则使对手失明",
|
||||
1659: "随机附加给对手{0}-{1}点固定伤害,若打出致命一击则效果转变为吸取对手{2}-{3}点体力",
|
||||
1601: "命中后附加自身最大体力{0}%的百分比伤害,若打出的攻击伤害为奇数则额外恢复等量体力值",
|
||||
1602: "{0}回合内每回合使用技能恢复自身最大体力的1/{1},恢复体力时若自身为满体力则恢复己方所有不在场精灵{2}点体力",
|
||||
1603: "{0}%降低对手所有PP值{1}点",
|
||||
@@ -1088,6 +1108,11 @@ var effectInfoByID = map[int]string{
|
||||
1627: "{0}回合做{1}-{2}次攻击,若本回合攻击次数达到最大则必定秒杀对手",
|
||||
1628: "每次使用该技能击败对手则恢复自身全部体力,同时重置该技能使用次数并使该技能攻击威力提升{0}点,未击败对手时令自身下回合攻击技能先制+{1}",
|
||||
1629: "{0}基础速度值{1}{2}则自身下回合先制+{3}",
|
||||
1635: "立刻恢复自身{0}点体力,{1}回合后恢复自身全部体力",
|
||||
1636: "涵双1回合释放4-8张玫瑰卡牌进行攻击,每张卡牌额外附加50点固定伤害,自身体力低于最大体力的1/3时效果翻倍",
|
||||
1637: "{0}回合内若对手使用属性技能,则使用属性技能后的下{1}回合属性技能命中效果失效",
|
||||
1638: "{0}回合内若自身未受到攻击伤害则令对手全属性-{1}",
|
||||
1639: "自身处于能力提升状态时100%打出致命一击",
|
||||
1670: "{0}%令对手{1},对手为自身天敌时概率提升{2}%,未触发则消除对手回合类效果",
|
||||
1671: "造成的攻击伤害不低于{0},若对手处于能力提升状态则造成的攻击伤害不低于{1}",
|
||||
1672: "出手时若自身未满体力则吸取对手{0}点体力",
|
||||
|
||||
@@ -16,6 +16,9 @@ import (
|
||||
|
||||
// processSkillAttack 处理技能攻击逻辑
|
||||
func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info.SkillEntity) {
|
||||
if attacker == nil || defender == nil || skill == nil {
|
||||
return
|
||||
}
|
||||
skill.AttackTimeC(attacker.Prop[5]) //计算命中
|
||||
|
||||
defender.Exec(func(effect input.Effect) bool { //计算闪避,然后修改对方命中),同时相当于计算属性无效这种
|
||||
@@ -33,8 +36,8 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
|
||||
var originalProps [2][6]int8
|
||||
var originalPetInfo [2]model.PetInfo
|
||||
//复制属性
|
||||
originalProps[0], originalProps[1] = f.Our[0].Prop, f.Opp[0].Prop //先复制能力提升
|
||||
originalPetInfo[0], originalPetInfo[1] = f.Our[0].CurPet[0].Info, f.Opp[0].CurPet[0].Info //先复制宠物信息
|
||||
originalProps[0], originalProps[1] = attacker.Prop, defender.Prop
|
||||
originalPetInfo[0], originalPetInfo[1] = attacker.CurPet[0].Info, defender.CurPet[0].Info
|
||||
attacker.Exec(func(effect input.Effect) bool {
|
||||
//计算变威力
|
||||
effect.Ctx().SkillEntity = skill
|
||||
@@ -55,8 +58,8 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, skill *info
|
||||
}
|
||||
|
||||
//还原属性
|
||||
f.Our[0].Prop, f.Opp[0].Prop = originalProps[0], originalProps[1]
|
||||
f.Our[0].CurPet[0].Info, f.Opp[0].CurPet[0].Info = originalPetInfo[0], originalPetInfo[1]
|
||||
attacker.Prop, defender.Prop = originalProps[0], originalProps[1]
|
||||
attacker.CurPet[0].Info, defender.CurPet[0].Info = originalPetInfo[0], originalPetInfo[1]
|
||||
|
||||
if attacker.IsCritical == 1 { //命中了才有暴击
|
||||
//暴击破防
|
||||
@@ -149,15 +152,13 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
||||
})
|
||||
})
|
||||
|
||||
if firstAttack != nil { //如果首技能是空的,说明都空过了
|
||||
firstAttacker, _ := f.getSkillParticipants(firstAttack)
|
||||
if firstAttacker != nil {
|
||||
firstAttacker.Parseskill(firstAttack)
|
||||
}
|
||||
secondAttacker, _ := f.getSkillParticipants(secondAttack)
|
||||
if secondAttacker != nil {
|
||||
secondAttacker.Parseskill(secondAttack)
|
||||
for _, skillAction := range []*action.SelectSkillAction{firstAttack, secondAttack} {
|
||||
attackerInput, _ := f.getSkillParticipants(skillAction)
|
||||
if attackerInput == nil {
|
||||
continue
|
||||
}
|
||||
f.setActionAttackValue(skillAction)
|
||||
attackerInput.Parseskill(skillAction)
|
||||
}
|
||||
f.Broadcast(func(fighter *input.Input) {
|
||||
fighter.Exec(func(effect input.Effect) bool { //回合开始前
|
||||
@@ -212,17 +213,26 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
||||
for i := 0; i < 2; i++ {
|
||||
var originalSkill *info.SkillEntity //原始技能
|
||||
var currentSkill *info.SkillEntity //当前技能
|
||||
var currentAction *action.SelectSkillAction
|
||||
if i == 0 {
|
||||
attacker, defender = f.First, f.Second
|
||||
currentAction = firstAttack
|
||||
attacker, defender = f.getSkillParticipants(firstAttack)
|
||||
originalSkill = f.copySkill(firstAttack)
|
||||
//先手阶段,先修复后手效果
|
||||
f.Second.RecoverEffect()
|
||||
} else {
|
||||
attacker, defender = f.Second, f.First
|
||||
currentAction = secondAttack
|
||||
attacker, defender = f.getSkillParticipants(secondAttack)
|
||||
originalSkill = f.copySkill(secondAttack)
|
||||
//取消后手历史效果
|
||||
f.Second.ReactvieEffect()
|
||||
}
|
||||
if attacker == nil {
|
||||
attacker = f.First
|
||||
}
|
||||
if defender == nil {
|
||||
defender = f.Second
|
||||
}
|
||||
|
||||
currentSkill = originalSkill
|
||||
defender.Exec(func(effect input.Effect) bool { //这个是能否使用技能
|
||||
@@ -234,7 +244,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
||||
return effect.ActionStart(firstAttack, secondAttack)
|
||||
})
|
||||
|
||||
canUse := canUseSkill && action.CanUse(currentSkill) && attacker.CurPet[0].Info.Hp > 0
|
||||
canUse := canUseSkill && action.CanUse(currentSkill) && attacker != nil && attacker.CurPet[0].Info.Hp > 0
|
||||
|
||||
if !canUse {
|
||||
|
||||
@@ -250,6 +260,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
||||
//先手权不一定出手
|
||||
|
||||
} else {
|
||||
f.setActionAttackValue(currentAction)
|
||||
|
||||
for _, effect := range attacker.EffectCache {
|
||||
effect.IsFirst(true)
|
||||
@@ -345,7 +356,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction)
|
||||
}
|
||||
}
|
||||
})
|
||||
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
|
||||
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
|
||||
if f.closefight && f.Info.Mode == info.BattleMode.PET_MELEE {
|
||||
// f.Broadcast(func(fighter *input.Input) {
|
||||
// if fighter.UserID != f.WinnerId {
|
||||
|
||||
@@ -17,6 +17,7 @@ type BattlePetEntity struct {
|
||||
Shield alpacadecimal.Decimal
|
||||
DivineEnergy int
|
||||
ControllerUserID uint32
|
||||
FatalCrackLayers int
|
||||
//*input.Input
|
||||
//PType int
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
type ChangePetInfo struct {
|
||||
// UserId 米米号,野怪为0
|
||||
UserId uint32 `json:"userId"`
|
||||
// ActorIndex 当前发生切宠的战斗位
|
||||
ActorIndex uint32 `json:"actorIndex"`
|
||||
// PetId 切换上场的精灵编号
|
||||
ID uint32 `fieldDesc:"当前对战精灵ID" `
|
||||
// PetName 精灵名字,固定16字节长度
|
||||
@@ -75,6 +77,10 @@ type RPCFightStartinfo struct {
|
||||
type FightPetInfo struct {
|
||||
// 用户ID(野怪为0)
|
||||
UserID uint32 `fieldDesc:"用户ID 野怪为0" `
|
||||
// ActorIndex 当前战斗位下标
|
||||
ActorIndex uint32 `json:"actorIndex"`
|
||||
// ControllerUserID 当前战斗位绑定的操作者
|
||||
ControllerUserID uint32 `json:"controllerUserId"`
|
||||
|
||||
// 当前对战精灵ID
|
||||
ID uint32 `fieldDesc:"当前对战精灵ID" `
|
||||
@@ -104,20 +110,21 @@ type AttackValueS struct {
|
||||
|
||||
func NewAttackValue(userid uint32) *model.AttackValue {
|
||||
return &model.AttackValue{
|
||||
userid,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
[]model.SkillInfo{},
|
||||
0,
|
||||
[20]int8{},
|
||||
[6]int8{},
|
||||
0,
|
||||
UserID: userid,
|
||||
ActorIndex: 0,
|
||||
TargetIndex: 0,
|
||||
SkillID: 0,
|
||||
AttackTime: 0,
|
||||
LostHp: 0,
|
||||
GainHp: 0,
|
||||
RemainHp: 0,
|
||||
MaxHp: 0,
|
||||
State: 0,
|
||||
SkillList: []model.SkillInfo{},
|
||||
IsCritical: 0,
|
||||
Status: [20]int8{},
|
||||
Prop: [6]int8{},
|
||||
Offensive: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +207,9 @@ type NoteUseSkillOutboundInfo struct {
|
||||
type FightStartOutboundInfo struct {
|
||||
IsCanAuto uint32 `fieldDesc:"是否自动 默认给0 怀疑是自动战斗器使用的" `
|
||||
|
||||
InfoLen uint32 `struc:"sizeof=Infos"`
|
||||
Infos []FightPetInfo
|
||||
|
||||
// 当前战斗精灵信息1(前端通过userid判断是否为我方)
|
||||
Info1 FightPetInfo `fieldDesc:"当前战斗精灵的信息 可能不准.看前端代码是以userid来判断哪个结构体是我方的" serialize:"struct"`
|
||||
|
||||
|
||||
@@ -30,14 +30,14 @@ type FightC struct {
|
||||
Opp []*input.Input // 敌方战斗位
|
||||
OurPlayers []common.PlayerI // 我方操作者
|
||||
OppPlayers []common.PlayerI // 敌方操作者
|
||||
Switch map[uint32]*action.ActiveSwitchAction
|
||||
Switch map[actionSlotKey]*action.ActiveSwitchAction
|
||||
|
||||
startl sync.Once
|
||||
StartTime time.Time
|
||||
actionMu sync.Mutex
|
||||
actionNotify chan struct{}
|
||||
acceptActions bool
|
||||
pendingActions []action.BattleActionI // 待处理动作队列,同一玩家最多保留两段动作
|
||||
pendingActions []action.BattleActionI // 待处理动作队列,同一战斗位最多保留一个动作
|
||||
actionRound atomic.Uint32
|
||||
|
||||
quit chan struct{}
|
||||
@@ -53,6 +53,25 @@ type FightC struct {
|
||||
callback func(model.FightOverInfo)
|
||||
}
|
||||
|
||||
type actionSlotKey struct {
|
||||
PlayerID uint32
|
||||
ActorIndex int
|
||||
}
|
||||
|
||||
func newActionSlotKey(playerID uint32, actorIndex int) actionSlotKey {
|
||||
return actionSlotKey{
|
||||
PlayerID: playerID,
|
||||
ActorIndex: actorIndex,
|
||||
}
|
||||
}
|
||||
|
||||
func actionSlotKeyFromAction(act action.BattleActionI) actionSlotKey {
|
||||
if act == nil {
|
||||
return actionSlotKey{}
|
||||
}
|
||||
return newActionSlotKey(act.GetPlayerID(), act.GetActorIndex())
|
||||
}
|
||||
|
||||
func (f *FightC) primaryOur() *input.Input {
|
||||
if len(f.Our) == 0 {
|
||||
return nil
|
||||
@@ -116,6 +135,20 @@ func (f *FightC) isOurPlayerID(userID uint32) bool {
|
||||
return userID == f.ownerID
|
||||
}
|
||||
|
||||
func (f *FightC) getSideInputs(userID uint32, isOpposite bool) []*input.Input {
|
||||
isOur := f.isOurPlayerID(userID)
|
||||
if isOpposite {
|
||||
if isOur {
|
||||
return f.Opp
|
||||
}
|
||||
return f.Our
|
||||
}
|
||||
if isOur {
|
||||
return f.Our
|
||||
}
|
||||
return f.Opp
|
||||
}
|
||||
|
||||
func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) {
|
||||
isOur := f.isOurPlayerID(userID)
|
||||
if isOur {
|
||||
@@ -131,17 +164,36 @@ func (f *FightC) findInputByUserID(userID uint32) (*input.Input, bool) {
|
||||
}
|
||||
|
||||
func (f *FightC) getInputByUserID(userID uint32, index int, isOpposite bool) *input.Input {
|
||||
isOur := f.isOurPlayerID(userID)
|
||||
if isOpposite {
|
||||
if isOur {
|
||||
return f.selectInput(f.Opp, index)
|
||||
return f.selectInput(f.getSideInputs(userID, isOpposite), index)
|
||||
}
|
||||
|
||||
func (f *FightC) expectedActionSlots() map[actionSlotKey]struct{} {
|
||||
slots := make(map[actionSlotKey]struct{}, len(f.Our)+len(f.Opp))
|
||||
for actorIndex, fighter := range f.Our {
|
||||
if fighter == nil || fighter.Player == nil {
|
||||
continue
|
||||
}
|
||||
return f.selectInput(f.Our, index)
|
||||
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
|
||||
}
|
||||
if isOur {
|
||||
return f.selectInput(f.Our, index)
|
||||
for actorIndex, fighter := range f.Opp {
|
||||
if fighter == nil || fighter.Player == nil {
|
||||
continue
|
||||
}
|
||||
slots[newActionSlotKey(fighter.Player.GetInfo().UserID, actorIndex)] = struct{}{}
|
||||
}
|
||||
return f.selectInput(f.Opp, index)
|
||||
return slots
|
||||
}
|
||||
|
||||
func (f *FightC) setActionAttackValue(act action.BattleActionI) {
|
||||
if act == nil {
|
||||
return
|
||||
}
|
||||
attacker := f.GetInputByAction(act, false)
|
||||
if attacker == nil || attacker.AttackValue == nil {
|
||||
return
|
||||
}
|
||||
attacker.AttackValue.ActorIndex = uint32(act.GetActorIndex())
|
||||
attacker.AttackValue.TargetIndex = uint32(act.GetTargetIndex())
|
||||
}
|
||||
|
||||
func (f *FightC) Ownerid() uint32 {
|
||||
@@ -174,7 +226,14 @@ func (f *FightC) GetInputByAction(c action.BattleActionI, isOpposite bool) *inpu
|
||||
|
||||
// 玩家使用技能
|
||||
func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity {
|
||||
in := f.GetInputByPlayer(c, false)
|
||||
return f.GetCurrPETAt(c, 0)
|
||||
}
|
||||
|
||||
func (f *FightC) GetCurrPETAt(c common.PlayerI, actorIndex int) *info.BattlePetEntity {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
in := f.getInputByUserID(c.GetInfo().UserID, actorIndex, false)
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -293,10 +352,17 @@ func initfightready(in *input.Input) (model.FightUserInfo, []model.ReadyFightPet
|
||||
|
||||
// 被击败的ID
|
||||
func (f *FightC) IsWin(c *input.Input) bool {
|
||||
|
||||
for _, v := range f.GetInputByPlayer(c.Player, true).AllPet {
|
||||
if v.Alive() { //如果存活
|
||||
return false
|
||||
if c == nil || c.Player == nil {
|
||||
return false
|
||||
}
|
||||
for _, sideInput := range f.getSideInputs(c.Player.GetInfo().UserID, true) {
|
||||
if sideInput == nil {
|
||||
continue
|
||||
}
|
||||
for _, v := range sideInput.AllPet {
|
||||
if v.Alive() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"blazing/cool"
|
||||
"blazing/modules/player/model"
|
||||
"context"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"blazing/logic/service/common"
|
||||
@@ -45,20 +46,19 @@ func (f *FightC) battleLoop() {
|
||||
}()
|
||||
//fmt.Println("战斗开始精灵", f.Our[0].Player.GetInfo().PetList[0].CatchTime)
|
||||
|
||||
ourID := f.Our[0].Player.GetInfo().UserID
|
||||
oppID := f.Opp[0].Player.GetInfo().UserID
|
||||
//fmt.Println("开始收集玩家动作", waitr)
|
||||
for !f.closefight {
|
||||
|
||||
f.Round++
|
||||
|
||||
actions := f.collectPlayerActions(ourID, oppID)
|
||||
expectedSlots := f.expectedActionSlots()
|
||||
actions := f.collectPlayerActions(expectedSlots)
|
||||
if f.closefight {
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
f.resolveRound(actions[ourID], actions[oppID])
|
||||
f.resolveRound(actions)
|
||||
|
||||
}
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
@@ -145,8 +145,8 @@ func (f *FightC) battleLoop() {
|
||||
}
|
||||
|
||||
// 收集玩家动作(含超时判定)
|
||||
func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.BattleActionI {
|
||||
actions := make(map[uint32]action.BattleActionI)
|
||||
func (f *FightC) collectPlayerActions(expectedSlots map[actionSlotKey]struct{}) []action.BattleActionI {
|
||||
actions := make(map[actionSlotKey]action.BattleActionI, len(expectedSlots))
|
||||
f.openActionWindow()
|
||||
defer f.closeActionWindow()
|
||||
|
||||
@@ -157,12 +157,12 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
|
||||
waitr := time.Duration(f.waittime)*time.Millisecond*10 + 30*time.Second
|
||||
|
||||
timeout := time.After(waitr)
|
||||
for len(actions) < 2 {
|
||||
for len(actions) < len(expectedSlots) {
|
||||
|
||||
select {
|
||||
case <-f.quit:
|
||||
f.closefight = true
|
||||
return actions
|
||||
return flattenActionMap(actions)
|
||||
|
||||
case <-f.actionNotify:
|
||||
paction := f.nextAction()
|
||||
@@ -170,18 +170,21 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
|
||||
continue
|
||||
}
|
||||
|
||||
pid := paction.GetPlayerID()
|
||||
|
||||
if pid != ourID && pid != oppID {
|
||||
key := actionSlotKeyFromAction(paction)
|
||||
if _, ok := expectedSlots[key]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
selfinput := f.GetInputByAction(paction, false)
|
||||
if selfinput == nil {
|
||||
continue
|
||||
}
|
||||
if ret, ok := paction.(*action.ActiveSwitchAction); ok {
|
||||
|
||||
//正常结束可以切换,以及死切后还能再切一次
|
||||
if selfinput.CanChange == 0 {
|
||||
if selfinput.CurPet[0].Info.Hp > 0 { //非死亡切换
|
||||
currentPet := selfinput.PrimaryCurPet()
|
||||
if currentPet != nil && currentPet.Info.Hp > 0 { //非死亡切换
|
||||
selfinput.CanChange = 1
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
|
||||
@@ -199,11 +202,17 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
|
||||
}
|
||||
// oldpet := selfinput.CurPet[0]
|
||||
// InitAttackValue := *selfinput.AttackValue
|
||||
selfinput.CurPet[0], ret.Reason = selfinput.GetPet(ret.Cid)
|
||||
nextPet, reason := selfinput.GetPet(ret.Cid)
|
||||
if nextPet == nil {
|
||||
continue
|
||||
}
|
||||
selfinput.SetCurPetAt(0, nextPet)
|
||||
ret.Reason = reason
|
||||
ret.Reason.ActorIndex = uint32(ret.ActorIndex)
|
||||
|
||||
selfinput.Player.SendPackCmd(2407, &ret.Reason)
|
||||
|
||||
f.Switch[selfinput.UserID] = ret
|
||||
f.Switch[key] = ret
|
||||
|
||||
selfinput.InitAttackValue() //切换精灵消除能力提升
|
||||
//这时候精灵已经切换过了,可以直接给新精灵加效果
|
||||
@@ -220,8 +229,8 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
|
||||
if selfinput.CanChange == 2 {
|
||||
|
||||
selfinput.CanChange = 0
|
||||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && pid == 0 {
|
||||
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
|
||||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC && paction.GetPlayerID() == 0 {
|
||||
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
|
||||
f.Our[0].Player.SendPackCmd(2407, &ret.Reason)
|
||||
//println("AI出手死切")
|
||||
go f.Opp[0].GetAction() //boss出手后获取出招
|
||||
@@ -235,51 +244,78 @@ func (f *FightC) collectPlayerActions(ourID, oppID uint32) map[uint32]action.Bat
|
||||
|
||||
} else {
|
||||
// println("玩家执行释放技能动作:", pid, paction.(*action.SelectSkillAction).Info.ID)
|
||||
if selfinput.CurPet[0].Info.Hp <= 0 { //0血执行非切换动作
|
||||
currentPet := selfinput.PrimaryCurPet()
|
||||
if currentPet == nil || currentPet.Info.Hp <= 0 { //0血执行非切换动作
|
||||
//todo 记录异常操作
|
||||
cool.Logger.Print(context.TODO(), "玩家执行了异常操作,当前精灵血量为0,不能执行非切换动作", pid)
|
||||
cool.Logger.Print(context.TODO(), "玩家执行了异常操作,当前精灵血量为0,不能执行非切换动作", paction.GetPlayerID())
|
||||
continue
|
||||
}
|
||||
if selfinput.CanChange == 1 { //非被动死亡情况下,不能执行额外动作,0允许切,2是死亡,可以额外动作
|
||||
cool.Logger.Print(context.TODO(), "玩家执行了异常操作,切换后二次释放技能,不能执行非切换动作", pid)
|
||||
cool.Logger.Print(context.TODO(), "玩家执行了异常操作,切换后二次释放技能,不能执行非切换动作", paction.GetPlayerID())
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
actions[pid] = paction
|
||||
actions[key] = paction
|
||||
//fmt.Println("玩家执行动作:", pid, paction.Priority())
|
||||
|
||||
case <-timeout:
|
||||
r := f.handleTimeout(ourID, oppID, actions)
|
||||
r := f.handleTimeout(expectedSlots, actions)
|
||||
if r {
|
||||
return actions
|
||||
return flattenActionMap(actions)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return actions
|
||||
return flattenActionMap(actions)
|
||||
}
|
||||
|
||||
// 超时处理逻辑
|
||||
func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.BattleActionI) bool {
|
||||
func (f *FightC) handleTimeout(expectedSlots map[actionSlotKey]struct{}, actions map[actionSlotKey]action.BattleActionI) bool {
|
||||
|
||||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
|
||||
// f.WinnerId = 0
|
||||
|
||||
go f.UseSkill(f.Our[0].Player, 0) //boss出手后获取出招
|
||||
for key := range expectedSlots {
|
||||
if _, exists := actions[key]; exists || !f.isOurPlayerID(key.PlayerID) {
|
||||
continue
|
||||
}
|
||||
player := f.getPlayerByID(key.PlayerID)
|
||||
if player != nil {
|
||||
go f.UseSkillAt(player, 0, key.ActorIndex, 0)
|
||||
}
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
var pid uint32
|
||||
for _, pid = range []uint32{ourID, oppID} {
|
||||
|
||||
if _, exists := actions[pid]; exists {
|
||||
missingOur := false
|
||||
missingOpp := false
|
||||
for key := range expectedSlots {
|
||||
if _, exists := actions[key]; exists {
|
||||
continue
|
||||
}
|
||||
if f.isOurPlayerID(key.PlayerID) {
|
||||
missingOur = true
|
||||
continue
|
||||
}
|
||||
missingOpp = true
|
||||
}
|
||||
switch {
|
||||
case missingOur && !missingOpp:
|
||||
if player := f.primaryOppPlayer(); player != nil {
|
||||
f.WinnerId = player.GetInfo().UserID
|
||||
}
|
||||
case missingOpp && !missingOur:
|
||||
if player := f.primaryOurPlayer(); player != nil {
|
||||
f.WinnerId = player.GetInfo().UserID
|
||||
}
|
||||
default:
|
||||
for _, act := range actions {
|
||||
f.WinnerId = act.GetPlayerID()
|
||||
break
|
||||
}
|
||||
|
||||
if f.WinnerId == 0 {
|
||||
f.WinnerId = f.ownerID
|
||||
}
|
||||
}
|
||||
//获胜方是已经出招的
|
||||
f.WinnerId = f.GetInputByPlayer(f.getPlayerByID(pid), false).Player.GetInfo().UserID
|
||||
f.Reason = model.BattleOverReason.PlayerOVerTime
|
||||
f.closefight = true
|
||||
return true
|
||||
@@ -287,10 +323,111 @@ func (f *FightC) handleTimeout(ourID, oppID uint32, actions map[uint32]action.Ba
|
||||
|
||||
}
|
||||
|
||||
type roundActionPair struct {
|
||||
our action.BattleActionI
|
||||
opp action.BattleActionI
|
||||
}
|
||||
|
||||
func flattenActionMap(actions map[actionSlotKey]action.BattleActionI) []action.BattleActionI {
|
||||
flattened := make([]action.BattleActionI, 0, len(actions))
|
||||
for _, act := range actions {
|
||||
if act != nil {
|
||||
flattened = append(flattened, act)
|
||||
}
|
||||
}
|
||||
return flattened
|
||||
}
|
||||
|
||||
func (f *FightC) actionSpeed(act action.BattleActionI) alpacadecimal.Decimal {
|
||||
if act == nil {
|
||||
return alpacadecimal.Zero
|
||||
}
|
||||
attacker := f.GetInputByAction(act, false)
|
||||
if attacker == nil {
|
||||
return alpacadecimal.Zero
|
||||
}
|
||||
return attacker.GetProp(4)
|
||||
}
|
||||
|
||||
func (f *FightC) sortActions(actions []action.BattleActionI) {
|
||||
sort.SliceStable(actions, func(i, j int) bool {
|
||||
a, b := actions[i], actions[j]
|
||||
if a == nil || b == nil {
|
||||
return a != nil
|
||||
}
|
||||
if a.Priority() != b.Priority() {
|
||||
return a.Priority() > b.Priority()
|
||||
}
|
||||
if speedA, speedB := f.actionSpeed(a), f.actionSpeed(b); speedA.Cmp(speedB) != 0 {
|
||||
return speedA.Cmp(speedB) > 0
|
||||
}
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
func (f *FightC) buildRoundActionPairs(actions []action.BattleActionI) []roundActionPair {
|
||||
remaining := append([]action.BattleActionI(nil), actions...)
|
||||
f.sortActions(remaining)
|
||||
|
||||
used := make([]bool, len(remaining))
|
||||
pairs := make([]roundActionPair, 0, len(remaining))
|
||||
for i, act := range remaining {
|
||||
if used[i] || act == nil {
|
||||
continue
|
||||
}
|
||||
used[i] = true
|
||||
|
||||
pair := roundActionPair{}
|
||||
if f.isOurPlayerID(act.GetPlayerID()) {
|
||||
pair.our = act
|
||||
} else {
|
||||
pair.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
|
||||
}
|
||||
|
||||
// 根据动作类型执行一回合结算
|
||||
func (f *FightC) resolveRound(p1Action, p2Action action.BattleActionI) {
|
||||
if p1Action == nil || p2Action == nil {
|
||||
cool.Logger.Debug(context.Background(), "某方未选择动作,自动跳过结算")
|
||||
func (f *FightC) resolveRound(actions []action.BattleActionI) {
|
||||
if len(actions) == 0 {
|
||||
cool.Logger.Debug(context.Background(), "当前回合没有可执行动作,自动跳过结算")
|
||||
return
|
||||
}
|
||||
|
||||
for _, pair := range f.buildRoundActionPairs(actions) {
|
||||
if f.closefight {
|
||||
return
|
||||
}
|
||||
f.resolveActionPair(pair.our, pair.opp)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FightC) resolveActionPair(p1Action, p2Action action.BattleActionI) {
|
||||
if p1Action == nil && p2Action == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -324,11 +461,15 @@ func (f *FightC) handleActiveSwitchAction(_ *action.ActiveSwitchAction, otherAct
|
||||
|
||||
// handleUseItemAction 处理使用道具动作
|
||||
func (f *FightC) handleUseItemAction(itemAction *action.UseItemAction, otherAction action.BattleActionI) {
|
||||
f.setActionAttackValue(itemAction)
|
||||
f.handleItemAction(itemAction)
|
||||
|
||||
input := f.GetInputByAction(itemAction, false)
|
||||
if input.CurPet[0].Info.Hp <= 0 {
|
||||
input.CurPet[0].Info.Hp = 1
|
||||
if input == nil {
|
||||
return
|
||||
}
|
||||
if currentPet := input.PrimaryCurPet(); currentPet != nil && currentPet.Info.Hp <= 0 {
|
||||
currentPet.Info.Hp = 1
|
||||
}
|
||||
|
||||
if skillAction, ok := otherAction.(*action.SelectSkillAction); ok {
|
||||
@@ -346,28 +487,32 @@ func (f *FightC) handleUseItemAction(itemAction *action.UseItemAction, otherActi
|
||||
|
||||
// 使用道具的逻辑封装
|
||||
func (f *FightC) handleItemAction(a *action.UseItemAction) {
|
||||
source := f.GetInputByAction(a, false)
|
||||
target := f.GetInputByAction(a, true)
|
||||
if source == nil {
|
||||
return
|
||||
}
|
||||
|
||||
item, ok := xmlres.ItemsMAP[int(a.ItemID)]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
r := f.GetInputByAction(a, false).Player.(*player.Player).Service.Item.CheakItem(uint32(a.ItemID))
|
||||
r := source.Player.(*player.Player).Service.Item.CheakItem(uint32(a.ItemID))
|
||||
if r <= 0 {
|
||||
return
|
||||
}
|
||||
f.GetInputByAction(a, false).Player.(*player.Player).Service.Item.UPDATE(a.ItemID, -1)
|
||||
source.Player.(*player.Player).Service.Item.UPDATE(a.ItemID, -1)
|
||||
|
||||
switch {
|
||||
case gconv.Int(item.Bonus) != 0:
|
||||
|
||||
if f.Opp[0].CanCapture > 0 { //可以捕捉
|
||||
f.Opp[0].CurPet[0].CatchRate = f.Opp[0].CanCapture
|
||||
ok, _ := f.Our[0].Capture(f.Opp[0].CurPet[0], a.ItemID, -1)
|
||||
our := f.Our[0].Player.(*player.Player)
|
||||
if target != nil && target.CanCapture > 0 && target.PrimaryCurPet() != nil { //可以捕捉
|
||||
target.PrimaryCurPet().CatchRate = target.CanCapture
|
||||
ok, _ := source.Capture(target.PrimaryCurPet(), a.ItemID, -1)
|
||||
our := source.Player.(*player.Player)
|
||||
if ok {
|
||||
r := input.GetFunc(int64(item.ID))
|
||||
if r != nil {
|
||||
r.Exec(f, &f.Opp[0].Player.GetInfo().PetList[0])
|
||||
r.Exec(f, &target.Player.GetInfo().PetList[0])
|
||||
}
|
||||
f.Reason = model.BattleOverReason.Cacthok
|
||||
f.closefight = true
|
||||
@@ -378,24 +523,32 @@ func (f *FightC) handleItemAction(a *action.UseItemAction) {
|
||||
}
|
||||
case gconv.Int(item.HP) != 0:
|
||||
addhp := item.HP
|
||||
f.GetInputByAction(a, false).Heal(f.GetInputByAction(a, false), a, alpacadecimal.NewFromInt(int64(addhp)))
|
||||
source.Heal(source, a, alpacadecimal.NewFromInt(int64(addhp)))
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
currentPet := source.PrimaryCurPet()
|
||||
if currentPet == nil {
|
||||
return
|
||||
}
|
||||
ff.Player.SendPackCmd(2406, &info.UsePetIteminfo{
|
||||
UserID: f.GetInputByAction(a, false).UserID,
|
||||
UserID: source.UserID,
|
||||
ChangeHp: int32(addhp),
|
||||
ItemID: uint32(item.ID),
|
||||
UserHp: uint32(f.GetInputByAction(a, false).CurPet[0].Info.Hp),
|
||||
UserHp: uint32(currentPet.Info.Hp),
|
||||
})
|
||||
|
||||
})
|
||||
case gconv.Int(item.PP) != 0:
|
||||
f.GetInputByAction(a, false).HealPP(item.PP)
|
||||
source.HealPP(item.PP)
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
currentPet := source.PrimaryCurPet()
|
||||
if currentPet == nil {
|
||||
return
|
||||
}
|
||||
ff.Player.SendPackCmd(2406, &info.UsePetIteminfo{
|
||||
UserID: f.GetInputByAction(a, false).UserID,
|
||||
UserID: source.UserID,
|
||||
|
||||
ItemID: uint32(item.ID),
|
||||
UserHp: uint32(f.GetInputByAction(a, false).CurPet[0].Info.Hp),
|
||||
UserHp: uint32(currentPet.Info.Hp),
|
||||
})
|
||||
|
||||
})
|
||||
@@ -445,8 +598,15 @@ func (f *FightC) handleSkillActions(a1, a2 action.BattleActionI) {
|
||||
|
||||
// 根据玩家ID返回对应对象
|
||||
func (f *FightC) getPlayerByID(id uint32) common.PlayerI {
|
||||
if id == f.Our[0].Player.GetInfo().UserID {
|
||||
return f.Our[0].Player
|
||||
for _, player := range f.OurPlayers {
|
||||
if player != nil && player.GetInfo().UserID == id {
|
||||
return player
|
||||
}
|
||||
}
|
||||
return f.Opp[0].Player
|
||||
for _, player := range f.OppPlayers {
|
||||
if player != nil && player.GetInfo().UserID == id {
|
||||
return player
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.Fight
|
||||
f.OurPlayers = []common.PlayerI{p1}
|
||||
f.OppPlayers = []common.PlayerI{p2}
|
||||
|
||||
f.Switch = make(map[uint32]*action.ActiveSwitchAction)
|
||||
f.Switch = make(map[actionSlotKey]*action.ActiveSwitchAction)
|
||||
f.callback = fn //战斗结束的回调
|
||||
f.quit = make(chan struct{})
|
||||
f.over = make(chan struct{})
|
||||
@@ -58,7 +58,6 @@ func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.Fight
|
||||
ff.SetOPP(f.GetInputByPlayer(ff.Player, true))
|
||||
|
||||
})
|
||||
f.FightStartOutboundInfo = f.buildFightStartInfo()
|
||||
if f.Info.Status == info.BattleMode.FIGHT_WITH_NPC {
|
||||
|
||||
f.Opp[0].Finished = true //PVE 默认boss数据直接加载完成
|
||||
@@ -67,11 +66,10 @@ func NewFight(p1, p2 common.PlayerI, b1, b2 []model.PetInfo, fn func(model.Fight
|
||||
|
||||
if f.Opp[0].Player.(*player.AI_player).CanCapture > 0 {
|
||||
f.Opp[0].CanCapture = f.Opp[0].Player.(*player.AI_player).CanCapture
|
||||
f.FightStartOutboundInfo.Info2.Catchable = 1 //可以捕捉就置1
|
||||
}
|
||||
f.Opp[0].AttackValue.Prop = f.Opp[0].Player.(*player.AI_player).Prop
|
||||
f.FightStartOutboundInfo.Info2.Prop = f.Opp[0].AttackValue.Prop
|
||||
}
|
||||
f.FightStartOutboundInfo = f.buildFightStartInfo()
|
||||
|
||||
f.Broadcast(func(ff *input.Input) {
|
||||
|
||||
|
||||
@@ -155,13 +155,15 @@ var BattleOverReason = enum.New[struct {
|
||||
|
||||
// AttackValue 战斗中的攻击数值信息
|
||||
type AttackValue struct {
|
||||
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
|
||||
SkillID uint32 `json:"skillId" fieldDescription:"使用技能的id"`
|
||||
AttackTime uint32 `json:"attackTime" fieldDescription:"是否击中 如果为0 则miss 如果为1 则击中,2为必中"`
|
||||
LostHp uint32 `json:"lostHp" fieldDescription:"我方造成的伤害"`
|
||||
GainHp int32 `json:"gainHp" fieldDescription:"我方获得血量"`
|
||||
RemainHp int32 `json:"remainHp" fieldDescription:"我方剩余血量"`
|
||||
MaxHp uint32 `json:"maxHp" fieldDescription:"我方最大血量"`
|
||||
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
|
||||
ActorIndex uint32 `json:"actorIndex" fieldDescription:"当前动作所属战斗位"`
|
||||
TargetIndex uint32 `json:"targetIndex" fieldDescription:"当前动作目标战斗位"`
|
||||
SkillID uint32 `json:"skillId" fieldDescription:"使用技能的id"`
|
||||
AttackTime uint32 `json:"attackTime" fieldDescription:"是否击中 如果为0 则miss 如果为1 则击中,2为必中"`
|
||||
LostHp uint32 `json:"lostHp" fieldDescription:"我方造成的伤害"`
|
||||
GainHp int32 `json:"gainHp" fieldDescription:"我方获得血量"`
|
||||
RemainHp int32 `json:"remainHp" fieldDescription:"我方剩余血量"`
|
||||
MaxHp uint32 `json:"maxHp" fieldDescription:"我方最大血量"`
|
||||
//颜色
|
||||
State uint32 `json:"state" `
|
||||
SkillListLen uint32 `struc:"sizeof=SkillList"`
|
||||
|
||||
Reference in New Issue
Block a user