From c40430aaa42256f73f419ea0a10e00462cd94fbc Mon Sep 17 00:00:00 2001 From: xinian Date: Sun, 29 Mar 2026 16:38:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=88=98=E6=96=97?= =?UTF-8?q?=E6=8A=80=E8=83=BD=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task-001-effects-17-600.md | 36 ---- .../task-002-effects-601-605.md | 39 ---- .../task-003-effects-606-614.md | 38 ---- .../task-020-effects-709-713.md | 36 ---- .../task-023-effects-724-728.md | 35 ---- docs/fight-effect-impl-skill.md | 161 ++++++++++++++ logic/service/fight/effect/17.go | 142 +++++++++++++ logic/service/fight/effect/600_605.go | 95 +++++++++ logic/service/fight/effect/606_608_612_614.go | 50 ++++- logic/service/fight/effect/709_713.go | 198 ++++++++++++++++++ logic/service/fight/effect/effect_724_728.go | 114 ++++++++++ logic/service/fight/effect/effect_info_map.go | 14 ++ logic/service/fight/effect/none.go | 95 +++++++-- logic/service/fight/fightc.go | 1 + logic/service/fight/input/input.go | 7 + 15 files changed, 851 insertions(+), 210 deletions(-) delete mode 100644 docs/effect-unimplemented-tasks/task-001-effects-17-600.md delete mode 100644 docs/effect-unimplemented-tasks/task-002-effects-601-605.md delete mode 100644 docs/effect-unimplemented-tasks/task-003-effects-606-614.md delete mode 100644 docs/effect-unimplemented-tasks/task-020-effects-709-713.md delete mode 100644 docs/effect-unimplemented-tasks/task-023-effects-724-728.md create mode 100644 docs/fight-effect-impl-skill.md create mode 100644 logic/service/fight/effect/17.go create mode 100644 logic/service/fight/effect/709_713.go create mode 100644 logic/service/fight/effect/effect_724_728.go diff --git a/docs/effect-unimplemented-tasks/task-001-effects-17-600.md b/docs/effect-unimplemented-tasks/task-001-effects-17-600.md deleted file mode 100644 index 471f1e9c0..000000000 --- a/docs/effect-unimplemented-tasks/task-001-effects-17-600.md +++ /dev/null @@ -1,36 +0,0 @@ -# Task 001: Effects 17-600 - -## 目标 - -- 补齐以下 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 17 -- `argsNum`: `0` -- `info`: `1回合等待,次回合攻击` - -### Effect 139 -- `argsNum`: `0` -- `info`: `50%威力301-350、30%威力101-300,20%威力5-100` - -### Effect 201 -- `argsNum`: `2` -- `info`: `对选中对象或本方全体恢复1/{1}的体力` - -### Effect 445 -- `argsNum`: `0` -- `info`: `使用后在战斗结束时可以获得500赛尔豆,每日上限5000` - -### Effect 600 -- `argsNum`: `4` -- `info`: `若对手是{0}则造成伤害提升{1}%,若对手不是{0},则有{2}%概率使对手{3}` -- `param`: `5,0,0|1,3,3` - -## 备注 - -- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。 -- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。 diff --git a/docs/effect-unimplemented-tasks/task-002-effects-601-605.md b/docs/effect-unimplemented-tasks/task-002-effects-601-605.md deleted file mode 100644 index ba3e5202d..000000000 --- a/docs/effect-unimplemented-tasks/task-002-effects-601-605.md +++ /dev/null @@ -1,39 +0,0 @@ -# Task 002: Effects 601-605 - -## 目标 - -- 补齐以下 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 601 -- `argsNum`: `6` -- `info`: `若本回合击败对手,下回合对方出场精灵{0}` -- `param`: `24,0,0` - -### Effect 602 -- `argsNum`: `9` -- `info`: `{0}回合内若{1}使用攻击技能则{2}的{3}` -- `param`: `9,1,1|9,2,2|0,3,3` - -### Effect 603 -- `argsNum`: `3` -- `info`: `{0}%概率使对手陷入{1}状态{2}回合` -- `param`: `1,1,1` - -### Effect 604 -- `argsNum`: `4` -- `info`: `威力随机,随机范围{0}-{1},连续使用每次威力提升{2},最高提升至{3}` - -### Effect 605 -- `argsNum`: `3` -- `info`: `自身体力{0}对手时附加{1}值{2}%的百分比伤害` -- `param`: `7,0,0|8,1,1` - -## 备注 - -- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。 -- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。 diff --git a/docs/effect-unimplemented-tasks/task-003-effects-606-614.md b/docs/effect-unimplemented-tasks/task-003-effects-606-614.md deleted file mode 100644 index 71481082a..000000000 --- a/docs/effect-unimplemented-tasks/task-003-effects-606-614.md +++ /dev/null @@ -1,38 +0,0 @@ -# Task 003: Effects 606-614 - -## 目标 - -- 补齐以下 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 606 -- `argsNum`: `1` -- `info`: `若体力{0}对手,技能威力翻倍` -- `param`: `7,0,0` - -### Effect 607 -- `argsNum`: `9` -- `info`: `{0}回合内若{1}使用属性技能则{2}的{3}` -- `param`: `9,1,1|9,2,2|0,3,3` - -### Effect 608 -- `argsNum`: `3` -- `info`: `若对手体力低于1/2,该技能先制额外+1` - -### Effect 612 -- `argsNum`: `2` -- `info`: `{0}%概率攻击{1}次` - -### Effect 614 -- `argsNum`: `2` -- `info`: `{0}回合内若对手使用攻击技能则对手{1}` -- `param`: `1,1,1` - -## 备注 - -- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。 -- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。 diff --git a/docs/effect-unimplemented-tasks/task-020-effects-709-713.md b/docs/effect-unimplemented-tasks/task-020-effects-709-713.md deleted file mode 100644 index 96f24f3ce..000000000 --- a/docs/effect-unimplemented-tasks/task-020-effects-709-713.md +++ /dev/null @@ -1,36 +0,0 @@ -# Task 020: Effects 709-713 - -## 目标 - -- 补齐以下 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 709 -- `argsNum`: `0` -- `info`: `击败对手时若自身处于能力提升状态,则将所处的能力提升状态翻倍` - -### Effect 710 -- `argsNum`: `1` -- `info`: `解除自身能力下降状态,若解除成功则{0}回合内免疫能力下降状态` - -### Effect 711 -- `argsNum`: `1` -- `info`: `下{0}回合若自身能力提升状态被消除则必定致命一击` - -### Effect 712 -- `argsNum`: `7` -- `info`: `{0}回合内将对手的{1}能力降为0点` -- `param`: `16,1,1` - -### Effect 713 -- `argsNum`: `1` -- `info`: `附加自身能力提升等级总和X{0}的固定伤害` - -## 备注 - -- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。 -- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。 diff --git a/docs/effect-unimplemented-tasks/task-023-effects-724-728.md b/docs/effect-unimplemented-tasks/task-023-effects-724-728.md deleted file mode 100644 index d9378d5f2..000000000 --- a/docs/effect-unimplemented-tasks/task-023-effects-724-728.md +++ /dev/null @@ -1,35 +0,0 @@ -# Task 023: Effects 724-728 - -## 目标 - -- 补齐以下 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 724 -- `argsNum`: `3` -- `info`: `{0}回合内受到攻击{1}%恢复1/{2}体力` - -### Effect 725 -- `argsNum`: `3` -- `info`: `{0}回合内每回合有{1}%的概率降低对手最大体力的1/{2}` - -### Effect 726 -- `argsNum`: `1` -- `info`: `下{0}回合若对手先出手,则令对手当回合使用的攻击技能无效` - -### Effect 727 -- `argsNum`: `0` -- `info`: `后出手时将自身能力等级返回至上一回合结束时` - -### Effect 728 -- `argsNum`: `1` -- `info`: `自身处于能力提升状态时,回合结束时直接减少对手1/{0}最大体力` - -## 备注 - -- 该清单按当前仓库静态注册结果生成;如果某个 effect 实际通过其他模块或运行时路径实现,需要先复核后再落代码。 -- 对 `201`、`445` 这类占位 effect,优先补核心逻辑或补充明确的不可实现说明。 diff --git a/docs/fight-effect-impl-skill.md b/docs/fight-effect-impl-skill.md new file mode 100644 index 000000000..c1ef1cb97 --- /dev/null +++ b/docs/fight-effect-impl-skill.md @@ -0,0 +1,161 @@ +--- +name: fight-effect-impl +description: Implement or repair Go fight effects in the Blazing battle system. Use when working in logic/service/fight/effect or nearby boss hooks, especially for missing effect tasks, effect registration, hook selection, delayed/round effects, status application, effect_info_map updates, and package-level validation. +--- + +# Fight Effect Impl + +Implement effect work in the existing battle framework instead of inventing a parallel pattern. + +## Workflow + +1. Read the task source first. +If the request comes from `docs/effect-unimplemented-tasks/`, open the task file and extract effect IDs, arg counts, and description text. + +2. Confirm whether each effect is actually missing. +Search for both type names and registrations. Do not rely only on direct `InitEffect(...)` grep results. +Also inspect shared registration files such as: +- `logic/service/fight/effect/sterStatusEffects.go` +- `logic/service/fight/effect/effect_power_doblue.go` +- `logic/service/fight/effect/EffectAttackMiss.go` +- `logic/service/fight/effect/EffectPhysicalAttackAddStatus.go` +- `logic/service/fight/effect/EffectDefeatTrigger.go` +- `logic/service/fight/effect/effect_attr.go` + +3. Reuse the nearest local pattern. +Open effects with similar timing or semantics before writing code. Prefer matching existing hooks, helper bases, registration style, and comments over building a generic abstraction. + +4. Choose the hook from battle flow, not from description text alone. +Read `logic/service/fight/input/interface.go`, `logic/service/fight/fightc.go`, and `logic/service/fight/loop.go` when timing is unclear. + +## Effect Hooks + +Use this section when effect timing is unclear. + +### Core call order + +The main references are: +- `logic/service/fight/input/interface.go` +- `logic/service/fight/fightc.go` +- `logic/service/fight/loop.go` + +Typical attack flow inside `processSkillAttack` and `enterturn` is: +1. defender `SkillHit_ex()` +2. attacker `SkillHit()` +3. attacker `CalculatePre()` +4. attacker `OnSkill()` +5. defender `Damage(...)` settles red/fixed/true damage +6. defender `Skill_Use_ex()` +7. attacker `Skill_Use()` +8. defender `Action_end_ex()` +9. attacker `Action_end()` +10. both sides `TurnEnd()` at round end +11. all live effects `OnBattleEnd()` at fight end + +### Hook selection cheatsheet + +- `SkillHit_ex()` +Use for defender-side pre-hit interception, miss forcing, and hit-rate disruption. + +- `SkillHit()` +Use for attacker-side power, crit, or skill-property changes before damage is computed. + +- `CalculatePre()` +Use for temporary state rewrites that must exist during power calculation and then be restored. + +- `OnSkill()` +Use for on-hit side effects, extra fixed damage setup, healing, status attach, or delayed-effect spawning. + +- `ActionStartEx()` +Use for defender-side pre-action gates. + +- `ActionStart()` +Use for attacker-side action gating, forced no-action behavior, and same-turn priority-sensitive logic. + +- `Skill_Use_ex()` +Use for defender-side after-being-targeted behavior. + +- `Skill_Use()` +Use for attacker-side after-using-skill behavior. + +- `ComparePre()` +Use for priority changes before turn order is finalized. + +- `TurnStart()` +Use for per-round setup or replacing the current round's selected action before execution. + +- `TurnEnd()` +Use for countdown or expiry. The default node decrements positive durations and clears zero-duration effects. + +- `OnBattleEnd()` +Use only when the effect truly settles at battle end. Confirm any reward path can be persisted from this hook. + +### Repo-specific cautions + +- `EffectCache` matters. +Parsed skill effects are stored in `EffectCache` before execution. If a first-turn charge effect must suppress the rest of the skill's side effects, explicitly disable sibling cached effects for that turn. + +- `addSubEffect(...)` is lightweight. +Read `logic/service/fight/effect/sub_effect_helper.go` before assuming helper behavior. The current helper forwards IDs and args, but does not automatically apply the `duration` argument to the sub-effect instance. + +- Team-wide healing is limited by current model. +There is no generic battle-target abstraction for friendly bench targets. If the effect heals all owned pets, iterate `AllPet` and mutate non-active pets carefully. + +- Static task scans can be false positives. +Task documents may flag effects as missing even when they already exist in grouped files or shared registration files. Verify before editing. + +## Implementation Rules + +- Prefer existing base structs in `logic/service/fight/effect/sub_effect_helper.go` when they already match duration behavior. +- Verify helper semantics before using them. In this repo, some helpers are thinner than their names suggest. +- For status effects, create them through `InitEffect(input.EffectType.Status, ...)` and add them through `AddEffect(...)` on the target input. +- For healing, use `Input.Heal(...)` for the active battler and mutate non-active owned pets only when the current model already stores them in `AllPet`. +- For battle-end rewards or delayed settlement, confirm the hook is actually executed in `loop.go` before coding against it. +- Keep comments short and effect-focused. + +## Common Tasks + +### Random power or conditional power effects +Use `SkillHit()` when the effect changes `SkillEntity.XML.Power` before damage is calculated. +Examples in repo: `139.go`, `effect_power_doblue.go`, `600_605.go`. + +### Hit-time status or side effects +Use `OnSkill()` when the effect should fire after hit/damage calculation setup and before final damage application is settled. +For direct status application, initialize the status effect from the source input and add it to the opponent. + +### Round or delayed effects +For multi-turn logic, confirm whether the effect should: +- modify this turn only, +- start next turn, +- trigger when attacked, +- or replace the next selected skill. + +For next-turn logic, inspect nearby effects such as `62`, `407`, `440`, `499`, `551`, `560`, and any adjacent ID patterns. + +### Two-turn charge effects +Preserve the repo's existing battle loop assumptions. +A practical pattern is: +- cache the release skill on the first turn, +- suppress first-turn damage/effect output, +- inject the cached skill into the next turn's selected action in `TurnStart`, +- avoid double PP consumption in `HookPP`. + +### Reward-on-battle-end effects +Check `OnBattleEnd()` execution in `logic/service/fight/loop.go`. +If a reward has a daily cap, prefer the shared counter utilities under `common/data/share/` over inventing new state. + +## Validation + +Run, at minimum: +- `cd /workspace/logic && go test ./service/fight/effect` +- `cd /workspace/logic && go build ./...` + +If the task came from `docs/effect-unimplemented-tasks/`, remove the completed task file when the user asked for it. + +## Output + +When finishing a task, report: +- which effect IDs were truly implemented, +- which IDs already existed and were left untouched, +- validation commands actually run, +- any remaining model limitations or behavior assumptions. diff --git a/logic/service/fight/effect/17.go b/logic/service/fight/effect/17.go new file mode 100644 index 000000000..414cfa28a --- /dev/null +++ b/logic/service/fight/effect/17.go @@ -0,0 +1,142 @@ +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" +) + +// Effect 17: 1回合等待,次回合攻击 +type Effect17 struct { + node.EffectNode + releaseSkill *info.SkillEntity + releasing bool +} + +func (e *Effect17) SetArgs(t *input.Input, a ...int) { + e.EffectNode.SetArgs(t, a...) + e.Duration(1) +} + +func (e *Effect17) TurnStart(fattack, sattack *action.SelectSkillAction) { + if e.Duration() != 0 || e.releaseSkill == nil { + return + } + + nextAction := e.findActionByPlayer(fattack, sattack) + if nextAction == nil { + return + } + + releaseSkill := cloneSkillEntity(e.releaseSkill) + if releaseSkill == nil { + return + } + if releaseSkill.Info != nil && releaseSkill.Info.PP == 0 { + releaseSkill.Info.PP = 1 + } + + nextAction.SkillEntity = releaseSkill + e.releasing = true +} + +func (e *Effect17) ActionStart(fattack, sattack *action.SelectSkillAction) bool { + if e.Ctx().SkillEntity == nil { + return true + } + + if e.Duration() == 0 && e.releasing { + return true + } + + if e.releaseSkill == nil { + e.releaseSkill = e.buildReleaseSkill(e.Ctx().SkillEntity) + } + + // 蓄力回合仅保留 Effect17 自身,避免同技能其他副作用提前触发。 + for _, effect := range e.Ctx().Our.EffectCache { + if effect == e { + continue + } + effect.Alive(false) + } + + e.Ctx().SkillEntity.XML.Power = 0 + return true +} + +func (e *Effect17) DamageLock(zone *info.DamageZone) bool { + if e.Duration() > 0 && zone.Type == info.DamageType.Red { + zone.Damage = alpacadecimal.Zero + } + return true +} + +func (e *Effect17) HookPP(count *int) bool { + if e.Duration() == 0 && e.releasing { + *count = 0 + e.releasing = false + } + return true +} + +func (e *Effect17) SwitchOut(in *input.Input) bool { + if in == e.Ctx().Our { + e.Alive(false) + } + return true +} + +func (e *Effect17) findActionByPlayer(fattack, sattack *action.SelectSkillAction) *action.SelectSkillAction { + playerID := e.Input.Player.GetInfo().UserID + if fattack != nil && fattack.GetPlayerID() == playerID { + return fattack + } + if sattack != nil && sattack.GetPlayerID() == playerID { + return sattack + } + return nil +} + +func (e *Effect17) buildReleaseSkill(skill *info.SkillEntity) *info.SkillEntity { + releaseSkill := cloneSkillEntity(skill) + if releaseSkill == nil { + return nil + } + + filteredEffects := make([]int, 0, len(releaseSkill.XML.SideEffectS)) + for _, effectID := range releaseSkill.XML.SideEffectS { + if effectID == 17 { + continue + } + filteredEffects = append(filteredEffects, effectID) + } + releaseSkill.XML.SideEffectS = filteredEffects + return releaseSkill +} + +func cloneSkillEntity(skill *info.SkillEntity) *info.SkillEntity { + if skill == nil { + return nil + } + + cloned := *skill + if skill.Info != nil { + infoCopy := *skill.Info + cloned.Info = &infoCopy + } + + xmlCopy := skill.XML + xmlCopy.SideEffectS = append([]int(nil), skill.XML.SideEffectS...) + xmlCopy.SideEffectArgS = append([]int(nil), skill.XML.SideEffectArgS...) + cloned.XML = xmlCopy + + return &cloned +} + +func init() { + input.InitEffect(input.EffectType.Skill, 17, &Effect17{}) +} diff --git a/logic/service/fight/effect/600_605.go b/logic/service/fight/effect/600_605.go index b1c98b725..ce0d6e380 100644 --- a/logic/service/fight/effect/600_605.go +++ b/logic/service/fight/effect/600_605.go @@ -9,6 +9,29 @@ import ( "github.com/gogf/gf/v2/util/grand" ) +func effectRelativeInput(our, opp *input.Input, code int) *input.Input { + if code == 1 { + return our + } + return opp +} + +func applyEffectPropChanges(target *input.Input, source *input.Input, changes []int, normalizePositiveAsDebuff bool) { + if target == nil { + return + } + + for i, delta := range changes { + if delta == 0 { + continue + } + if normalizePositiveAsDebuff && delta > 0 { + delta = -delta + } + target.SetProp(source, int8(i), int8(delta)) + } +} + // Effect 600: 若对手是{0}则造成伤害提升{1}%,若对手不是{0},则有{2}%概率使对手{3} type Effect600 struct { node.EffectNode @@ -37,6 +60,76 @@ func (e *Effect600) SkillHit() bool { return true } +// Effect 601: 若本回合击败对手,下回合对方出场精灵{0} +type Effect601 struct { + FixedDuration1Base + pending bool +} + +func (e *Effect601) SwitchOut(in *input.Input) bool { + if in == e.Input || in == e.Ctx().Our { + e.Alive(false) + return true + } + if in != e.Ctx().Opp { + return true + } + if e.Ctx().Opp.CurrentPet.Alive() { + return true + } + + e.pending = true + return true +} + +func (e *Effect601) SwitchIn(in *input.Input) bool { + if !e.pending || in != e.Ctx().Opp { + return true + } + + changes := make([]int, len(e.SideEffectArgs)) + for i, delta := range e.SideEffectArgs { + changes[i] = delta + } + // 601 的实际技能数据以正数记录削弱幅度,这里归一化为对登场精灵的能力下降。 + applyEffectPropChanges(e.Ctx().Opp, e.Ctx().Our, changes, true) + e.pending = false + e.Alive(false) + return true +} + +// Effect 602: {0}回合内若{1}使用攻击技能则{2}的{3} +type Effect602 struct { + RoundEffectArg0Base +} + +func (e *Effect602) Skill_Use() bool { + return e.tryApplyOnSkillUse(true) +} + +func (e *Effect602) Skill_Use_ex() bool { + return e.tryApplyOnSkillUse(false) +} + +func (e *Effect602) tryApplyOnSkillUse(ownerUsedSkill bool) bool { + if len(e.SideEffectArgs) < 9 || e.Ctx().SkillEntity == nil { + return true + } + if e.Ctx().SkillEntity.Category() == info.Category.STATUS { + return true + } + + triggerTarget := effectRelativeInput(e.Ctx().Our, e.Ctx().Opp, e.SideEffectArgs[1]) + expectedOwnerUsedSkill := triggerTarget == e.Ctx().Our + if ownerUsedSkill != expectedOwnerUsedSkill { + return true + } + + affected := effectRelativeInput(e.Ctx().Our, e.Ctx().Opp, e.SideEffectArgs[2]) + applyEffectPropChanges(affected, e.Ctx().Our, e.SideEffectArgs[3:], false) + return true +} + // Effect 603: {0}%概率使对手陷入{1}状态{2}回合 type Effect603 struct { node.EffectNode @@ -104,6 +197,8 @@ func (e *Effect605) OnSkill() bool { func init() { input.InitEffect(input.EffectType.Skill, 600, &Effect600{}) + input.InitEffect(input.EffectType.Skill, 601, &Effect601{}) + input.InitEffect(input.EffectType.Skill, 602, &Effect602{}) input.InitEffect(input.EffectType.Skill, 603, &Effect603{}) input.InitEffect(input.EffectType.Skill, 604, &Effect604{}) input.InitEffect(input.EffectType.Skill, 605, &Effect605{}) diff --git a/logic/service/fight/effect/606_608_612_614.go b/logic/service/fight/effect/606_608_612_614.go index 585760deb..a137c55e3 100644 --- a/logic/service/fight/effect/606_608_612_614.go +++ b/logic/service/fight/effect/606_608_612_614.go @@ -19,7 +19,7 @@ func (e *Effect606) SkillHit() bool { ourHP := e.Ctx().Our.CurrentPet.GetHP() oppHP := e.Ctx().Opp.CurrentPet.GetHP() - mode := int(e.Args()[0].IntPart()) + mode := e.SideEffectArgs[0] if (mode == 0 && ourHP.Cmp(oppHP) > 0) || (mode == 1 && ourHP.Cmp(oppHP) < 0) { e.Ctx().SkillEntity.XML.Power *= 2 @@ -28,6 +28,44 @@ func (e *Effect606) SkillHit() bool { return true } +// Effect 607: {0}回合内若{1}使用属性技能则{2}的{3} +type Effect607 struct { + RoundEffectArg0Base +} + +func (e *Effect607) applyStatusSkillPropChange(triggerSelf bool) bool { + if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS { + return true + } + + wantTriggerSelf := len(e.SideEffectArgs) > 1 && e.SideEffectArgs[1] == 1 + if wantTriggerSelf != triggerSelf { + return true + } + + target := e.Ctx().Opp + if len(e.SideEffectArgs) > 2 && e.SideEffectArgs[2] == 1 { + target = e.Ctx().Our + } + + for i, level := range e.SideEffectArgs[3:] { + if level == 0 { + continue + } + target.SetProp(e.Ctx().Our, int8(i), int8(level)) + } + + return true +} + +func (e *Effect607) OnSkill() bool { + return e.applyStatusSkillPropChange(true) +} + +func (e *Effect607) Skill_Use_ex() bool { + return e.applyStatusSkillPropChange(false) +} + // Effect 608: 若对手体力低于1/2,该技能先制额外+1 type Effect608 struct { node.EffectNode @@ -40,8 +78,13 @@ func (e *Effect608) ComparePre(fattack, sattack *action.SelectSkillAction) bool oppHP := e.Ctx().Opp.CurrentPet.GetHP() oppMaxHP := e.Ctx().Opp.CurrentPet.GetMaxHP() - if oppHP.Mul(e.Args()[0]).Cmp(oppMaxHP) < 0 { - sattack.SkillEntity.XML.Priority += int(e.Args()[1].IntPart()) + mode := e.SideEffectArgs[0] + threshold := e.Args()[1] + priorityAdd := e.SideEffectArgs[2] + hpCmp := oppHP.Mul(threshold).Cmp(oppMaxHP) + + if (mode == 0 && hpCmp > 0) || (mode == 1 && hpCmp < 0) { + sattack.SkillEntity.XML.Priority += priorityAdd } return true @@ -88,6 +131,7 @@ func (e *Effect614) Skill_Use_ex() bool { func init() { input.InitEffect(input.EffectType.Skill, 606, &Effect606{}) + input.InitEffect(input.EffectType.Skill, 607, &Effect607{}) input.InitEffect(input.EffectType.Skill, 608, &Effect608{}) input.InitEffect(input.EffectType.Skill, 612, &Effect612{}) input.InitEffect(input.EffectType.Skill, 614, &Effect614{}) diff --git a/logic/service/fight/effect/709_713.go b/logic/service/fight/effect/709_713.go new file mode 100644 index 000000000..535e67174 --- /dev/null +++ b/logic/service/fight/effect/709_713.go @@ -0,0 +1,198 @@ +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" +) + +// Effect 709: 击败对手时若自身处于能力提升状态,则将所处的能力提升状态翻倍 +type Effect709 struct { + FixedDuration1Base +} + +func (e *Effect709) SwitchOut(in *input.Input) bool { + if in == e.Ctx().Our { + e.Alive(false) + return true + } + if in != e.Ctx().Opp || e.Ctx().Opp.CurrentPet.Info.Hp > 0 { + return true + } + + for i, v := range e.Ctx().Our.Prop[:] { + if v > 0 { + e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), v) + } + } + + e.Alive(false) + return true +} + +// Effect 710: 解除自身能力下降状态,若解除成功则{0}回合内免疫能力下降状态 +type Effect710 struct { + node.EffectNode +} + +func (e *Effect710) OnSkill() bool { + cleared := false + for i, v := range e.Ctx().Our.Prop[:] { + if v < 0 && e.Ctx().Our.SetProp(e.Ctx().Our, int8(i), 0) { + cleared = true + } + } + if !cleared { + return true + } + + addSubEffect(e.Ctx().Our, e.Ctx().Our, &e.EffectNode, &Effect47{}, -1) + return true +} + +// Effect 711: 下{0}回合若自身能力提升状态被消除则必定致命一击 +type Effect711 struct { + node.EffectNode +} + +func (e *Effect711) SetArgs(t *input.Input, a ...int) { + setArgsWithFixedDuration(&e.EffectNode, t, -1, a...) +} + +func (e *Effect711) PropBefer(source *input.Input, prop int8, level int8) bool { + if source != e.Ctx().Opp { + return true + } + if prop < 0 || int(prop) >= len(e.Ctx().Our.Prop) { + return true + } + if level != 0 || e.Ctx().Our.Prop[prop] <= 0 { + return true + } + + addSubEffect(e.Ctx().Our, e.Ctx().Our, &e.EffectNode, &Effect711Sub{}, -1) + e.Alive(false) + return true +} + +type Effect711Sub struct { + RoundEffectArg0Base +} + +func (e *Effect711Sub) ActionStart(a, b *action.SelectSkillAction) bool { + if e.Ctx().SkillEntity == nil { + return true + } + if e.Ctx().SkillEntity.Category() == info.Category.STATUS { + return true + } + + e.Ctx().SkillEntity.XML.CritRate = 16 + return true +} + +// Effect 712: {0}回合内将对手的{1}能力降为0点 +type Effect712 struct { + node.EffectNode +} + +func (e *Effect712) OnSkill() bool { + sub := &Effect712Sub{} + addSubEffect(e.Ctx().Our, e.Ctx().Opp, &e.EffectNode, sub, -1) + sub.applyToTarget(e.Ctx().Opp.CurrentPet) + return true +} + +type Effect712Sub struct { + node.EffectNode + target *info.BattlePetEntity + oldProp [5]uint32 + oldHP uint32 + applied bool + hpZeroed bool +} + +func (e *Effect712Sub) SetArgs(t *input.Input, a ...int) { + setArgsWithDuration0(&e.EffectNode, t, a...) +} + +func (e *Effect712Sub) applyToTarget(target *info.BattlePetEntity) { + if e.applied || target == nil { + return + } + + e.target = target + e.oldProp = target.Info.Prop + e.oldHP = target.Info.Hp + for i := 0; i < len(target.Info.Prop) && i+1 < len(e.SideEffectArgs); i++ { + if e.SideEffectArgs[i+1] != 0 { + target.Info.Prop[i] = 0 + } + } + if len(e.SideEffectArgs) > 6 && e.SideEffectArgs[6] != 0 && target.Info.Hp > 0 { + target.Info.Hp = 0 + e.hpZeroed = true + } + e.applied = true +} + +func (e *Effect712Sub) restore() { + if !e.applied || e.target == nil { + return + } + + e.target.Info.Prop = e.oldProp + if e.hpZeroed && e.target.Info.Hp > 0 { + e.target.Info.Hp = e.oldHP + } + e.applied = false +} + +func (e *Effect712Sub) Alive(t ...bool) bool { + if len(t) > 0 && !t[0] { + e.restore() + } + return e.EffectNode.Alive(t...) +} + +func (e *Effect712Sub) TurnEnd() { + if e.Duration() <= 1 { + e.Alive(false) + return + } + e.EffectNode.TurnEnd() +} + +// Effect 713: 附加自身能力提升等级总和X{0}的固定伤害 +type Effect713 struct { + node.EffectNode +} + +func (e *Effect713) OnSkill() bool { + total := int64(0) + for _, v := range e.Ctx().Our.Prop[:] { + if v > 0 { + total += int64(v) + } + } + if total <= 0 { + return true + } + + e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{ + Type: info.DamageType.Fixed, + Damage: e.Args()[0].Mul(alpacadecimal.NewFromInt(total)), + }) + return true +} + +func init() { + input.InitEffect(input.EffectType.Skill, 709, &Effect709{}) + input.InitEffect(input.EffectType.Skill, 710, &Effect710{}) + input.InitEffect(input.EffectType.Skill, 711, &Effect711{}) + input.InitEffect(input.EffectType.Skill, 712, &Effect712{}) + input.InitEffect(input.EffectType.Skill, 713, &Effect713{}) +} diff --git a/logic/service/fight/effect/effect_724_728.go b/logic/service/fight/effect/effect_724_728.go new file mode 100644 index 000000000..ff731e4ba --- /dev/null +++ b/logic/service/fight/effect/effect_724_728.go @@ -0,0 +1,114 @@ +package effect + +import ( + "blazing/logic/service/fight/action" + "blazing/logic/service/fight/info" + "blazing/logic/service/fight/input" + "blazing/logic/service/fight/node" +) + +// Effect 724: {0}回合内受到攻击{1}%恢复1/{2}体力 +type Effect724 struct { + RoundEffectArg0Base +} + +func (e *Effect724) Skill_Use_ex() bool { + skill := e.Ctx().SkillEntity + if skill == nil || skill.Category() == info.Category.STATUS || skill.AttackTime == 0 { + return true + } + + success, _, _ := e.Input.Player.Roll(int(e.Args()[1].IntPart()), 100) + if !success { + return true + } + + healAmount := e.Ctx().Our.CurrentPet.GetMaxHP().Div(e.Args()[2]) + e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, healAmount) + return true +} + +// Effect 725: {0}回合内每回合有{1}%的概率降低对手最大体力的1/{2} +type Effect725 struct { + RoundEffectArg0Base +} + +func (e *Effect725) TurnEnd() { + success, _, _ := e.Input.Player.Roll(int(e.Args()[1].IntPart()), 100) + if success { + damage := e.Ctx().Opp.CurrentPet.GetMaxHP().Div(e.Args()[2]) + e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{ + Type: info.DamageType.Percent, + Damage: damage, + }) + } + e.EffectNode.TurnEnd() +} + +// Effect 726: 下{0}回合若对手先出手,则令对手当回合使用的攻击技能无效 +type Effect726 struct { + RoundEffectArg0Base + armed bool +} + +func (e *Effect726) SkillHit_ex() bool { + if !e.armed { + return true + } + + skill := e.Ctx().SkillEntity + if skill == nil || skill.Category() == info.Category.STATUS { + return true + } + if !e.Ctx().Opp.FightC.IsFirst(e.Ctx().Opp.Player) { + return true + } + + skill.SetMiss() + return true +} + +func (e *Effect726) TurnEnd() { + if !e.armed { + e.armed = true + } + e.EffectNode.TurnEnd() +} + +// Effect 727: 后出手时将自身能力等级返回至上一回合结束时 +type Effect727 struct { + node.EffectNode +} + +func (e *Effect727) Action_end() bool { + if e.IsFirst() || e.Ctx().SkillEntity == nil { + return true + } + + e.Ctx().Our.AttackValue.Prop = e.Ctx().Our.LastTurnEndProp + return true +} + +// Effect 728: 自身处于能力提升状态时,回合结束时直接减少对手1/{0}最大体力 +type Effect728 struct { + RoundEffectArg0Base +} + +func (e *Effect728) TurnEnd() { + if e.Ctx().Our.HasPropADD() { + damage := e.Ctx().Opp.CurrentPet.GetMaxHP().Div(e.Args()[0]) + e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{ + Type: info.DamageType.Percent, + Damage: damage, + }) + } + e.EffectNode.TurnEnd() +} + +func init() { + input.InitEffect(input.EffectType.Skill, 724, &Effect724{}) + input.InitEffect(input.EffectType.Skill, 725, &Effect725{}) + input.InitEffect(input.EffectType.Skill, 726, &Effect726{}) + input.InitEffect(input.EffectType.Skill, 727, &Effect727{}) + input.InitEffect(input.EffectType.Skill, 728, &Effect728{}) +} diff --git a/logic/service/fight/effect/effect_info_map.go b/logic/service/fight/effect/effect_info_map.go index fd51d6e39..6edfdd42d 100644 --- a/logic/service/fight/effect/effect_info_map.go +++ b/logic/service/fight/effect/effect_info_map.go @@ -12,6 +12,7 @@ var effectInfoByID = map[int]string{ 8: "伤害大于对方体力时,对方会余下1体力", 9: "连续使用每次威力增加{0},最高威力{1}", 13: "{0}回合吸取对方最大体力的1/8(对草系无效)", + 17: "1回合等待,次回合攻击", 20: "{0}%令本方疲惫,{1}回合无法攻击", 21: "", 28: "降低对方1/{0}的hp", @@ -347,10 +348,13 @@ var effectInfoByID = map[int]string{ 613: "{0}回合内自身令对手使用的{1}系攻击技能无效", 609: "若对手{0},技能威力翻倍", 600: "若对手是{0}则造成伤害提升{1}%,若对手不是{0},则有{2}%概率使对手{3}", + 601: "若本回合击败对手,下回合对方出场精灵{0}", + 602: "{0}回合内若{1}使用攻击技能则{2}的{3}", 603: "{0}%概率使对手陷入{1}状态{2}回合", 604: "威力随机,随机范围{0}-{1},连续使用每次威力提升{2},最高提升至{3}", 605: "自身体力{0}对手时附加{1}值{2}%的百分比伤害", 606: "若体力{0}对手,技能威力翻倍", + 607: "{0}回合内若{1}使用属性技能则{2}的{3}", 608: "若对手体力低于1/2,该技能先制额外+1", 612: "{0}%概率攻击{1}次", 614: "{0}回合内若对手使用攻击技能则对手{1}", @@ -367,6 +371,16 @@ var effectInfoByID = map[int]string{ 687: "若对手{0},则对对方造成伤害的{1}%恢复自身体力", 688: "{0}回合内抵挡受到的攻击", 689: "若造成的伤害高于{0},则恢复自身1/{1}最大体力", + 709: "击败对手时若自身处于能力提升状态,则将所处的能力提升状态翻倍", + 710: "解除自身能力下降状态,若解除成功则{0}回合内免疫能力下降状态", + 711: "下{0}回合若自身能力提升状态被消除则必定致命一击", + 712: "{0}回合内将对手的{1}能力降为0点", + 713: "附加自身能力提升等级总和X{0}的固定伤害", + 724: "{0}回合内受到攻击{1}%恢复1/{2}体力", + 725: "{0}回合内每回合有{1}%的概率降低对手最大体力的1/{2}", + 726: "下{0}回合若对手先出手,则令对手当回合使用的攻击技能无效", + 727: "后出手时将自身能力等级返回至上一回合结束时", + 728: "自身处于能力提升状态时,回合结束时直接减少对手1/{0}最大体力", 851: "使对手随机进入害怕、失明、烧伤、冻伤、中毒其中{0}种异常状态", 852: "附加自身最大体力{0}%的百分比伤害并恢复等量体力,恢复体力时若自身体力低于最大体力的1/{1}则恢复效果和百分比伤害翻倍", 853: "附加自身最大体力值与速度值总和{0}%的百分比伤害,每次使用增加{1}%,最高{2}%", diff --git a/logic/service/fight/effect/none.go b/logic/service/fight/effect/none.go index 3ee834181..5723e94aa 100644 --- a/logic/service/fight/effect/none.go +++ b/logic/service/fight/effect/none.go @@ -1,6 +1,8 @@ package effect import ( + "blazing/common/data/share" + "blazing/logic/service/fight/action" "blazing/logic/service/fight/input" "blazing/logic/service/fight/node" ) @@ -10,14 +12,38 @@ type Effect445 struct { node.EffectNode } -// func (e *Effect445) OnSkill() bool { -// // 这个效果需要在战斗结束后执行,暂时记录奖励 -// e.Ctx().Our.EndReward = 500 -// return true -// } +func (e *Effect445) SetArgs(t *input.Input, a ...int) { + e.EffectNode.SetArgs(t, a...) + e.Duration(-1) +} + +func (e *Effect445) OnBattleEnd() bool { + player := e.Ctx().Our.Player + if player == nil || player.GetInfo().UserID == 0 { + return true + } + + count, err := share.GlobalCounterManager.GetCount(&share.DailyPeriod, player.GetInfo().UserID, 445) + if err != nil && err != share.ErrCacheMiss { + return true + } + if err == share.ErrCacheMiss { + count = 0 + } + if count >= 10 { + return true + } + + if !player.ItemAdd(1, 500) { + return true + } + + _, _ = share.GlobalCounterManager.IncrCount(&share.DailyPeriod, player.GetInfo().UserID, 445) + return true +} + func init() { input.InitEffect(input.EffectType.Skill, 445, &Effect445{}) - } // Effect 201: 对选中对象或本方全体恢复1/{1}的体力 @@ -25,25 +51,48 @@ type Effect201 struct { node.EffectNode } -// func (e *Effect201) OnSkill() bool { -// // 检查是否在组队战斗中 -// if e.Ctx().IsTeamBattle { -// // 计算恢复量 -// team := e.Ctx().Our.TeamPets // 假设有队伍宠物列表 -// for _, pet := range team { -// if pet.Info.Hp > 0 { // 只恢复还活着的宠物 -// maxHp := pet.GetMaxHP() -// healAmount := maxHp.Div(e.Args()[0]) // 1/n +func (e *Effect201) OnSkill() bool { + args := e.Args() + if len(args) == 0 { + return true + } -// // 恢复体力 -// pet.Heal(pet, &action.SelectSkillAction{}, healAmount) -// } -// } -// } + divisorIndex := len(args) - 1 + if len(args) > 1 { + divisorIndex = 1 + } + divisor := args[divisorIndex] + if divisor.IntPart() <= 0 { + return true + } + + healAll := len(args) > 1 && args[0].IntPart() != 0 + if !healAll { + e.Ctx().Our.Heal( + e.Ctx().Our, + &action.SelectSkillAction{}, + e.Ctx().Our.CurrentPet.GetMaxHP().Div(divisor), + ) + return true + } + + for _, pet := range e.Ctx().Our.AllPet { + if pet == nil || !pet.Alive() { + continue + } + + healAmount := pet.GetMaxHP().Div(divisor) + if pet == e.Ctx().Our.CurrentPet { + e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, healAmount) + continue + } + + pet.Info.ModelHP(healAmount.IntPart()) + } + + return true +} -// return true -// } func init() { input.InitEffect(input.EffectType.Skill, 201, &Effect201{}) - } diff --git a/logic/service/fight/fightc.go b/logic/service/fight/fightc.go index 9cbab90e1..fdbdd5ddf 100644 --- a/logic/service/fight/fightc.go +++ b/logic/service/fight/fightc.go @@ -308,6 +308,7 @@ func (f *FightC) enterturn(firstAttack, secondAttack *action.SelectSkillAction) return true }) ff.GenInfo() + ff.SnapshotTurnProp() }) if f.TrueFirst != f.First { f.First, f.Second = f.Second, f.First diff --git a/logic/service/fight/input/input.go b/logic/service/fight/input/input.go index 9bea1c476..0df629bf3 100644 --- a/logic/service/fight/input/input.go +++ b/logic/service/fight/input/input.go @@ -32,6 +32,8 @@ type Input struct { EffectLost []Effect // 删掉伤害记录,可以在回调中记录,而不是每次调用记录 SumDamage alpacadecimal.Decimal //伤害 + // 记录上一回合结束时的能力等级,供效果727等回溯使用。 + LastTurnEndProp [6]int8 // DamageZone struct { // Damage decimal.Decimal //伤害 // BeforeADD decimal.Decimal //攻击伤害 @@ -172,6 +174,10 @@ func (our *Input) GenInfo() { // ret.SAttack = *f.Second.AttackValue } +func (our *Input) SnapshotTurnProp() { + our.LastTurnEndProp = our.AttackValue.Prop +} + func (our *Input) ResetAttackValue() { our.AttackValue.SkillID = 0 our.AttackValue.IsCritical = 0 @@ -184,6 +190,7 @@ func (our *Input) ResetAttackValue() { // 这个每回合都会调用 func (our *Input) InitAttackValue() { our.AttackValue = info.NewAttackValue(our.Player.GetInfo().UserID) + our.LastTurnEndProp = [6]int8{} } func (our *Input) GetPet(id uint32) (ii *info.BattlePetEntity, Reason info.ChangePetInfo) {