From 46f2bdcd9ad0945eedd1fd67e0c9ca91459acc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=94=E5=BF=B5?= <12574910+72wo@users.noreply.github.com> Date: Tue, 31 Mar 2026 00:54:29 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(effect):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=88=98=E6=96=97=E6=95=88=E6=9E=9C1785=E3=80=812195=E3=80=812?= =?UTF-8?q?215=E3=80=812219=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增战斗效果1785:N回合内每回合使用技能吸取对手最大体力的1/M, 满体力时额外恢复己方所有不在场精灵O点体力 修复战斗效果2195:当对方回合存在效果时才降低技能优先级, --- docs/fight-effect-impl-skill.md | 57 +++++++++++++++++++++++++ logic/service/fight/effect/1770_1794.go | 48 +++++++++++++++++++++ logic/service/fight/effect/2195_2219.go | 54 +++++++++++++++++++---- 3 files changed, 151 insertions(+), 8 deletions(-) diff --git a/docs/fight-effect-impl-skill.md b/docs/fight-effect-impl-skill.md index c1ef1cb97..23d92f1cb 100644 --- a/docs/fight-effect-impl-skill.md +++ b/docs/fight-effect-impl-skill.md @@ -113,6 +113,58 @@ Task documents may flag effects as missing even when they already exist in group - 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. +## Batch Work Rules + +When continuing `docs/effect-unimplemented-tasks/` in batches: + +- Split work by grouped file and assign disjoint write ranges. +- Avoid touching `logic/service/fight/effect/effect_info_map.go` during parallel effect implementation unless the user explicitly asks for description-map updates in the same pass. +- Treat task docs as a backlog, not ground truth. A task file may still exist even when some IDs in the slice were already implemented or partially repaired. +- Prefer finishing the easiest grounded IDs in a partial slice instead of repeatedly rescanning the entire slice. +- Keep a clear list of: + - newly implemented IDs, + - already-existing IDs, + - still-partial IDs, + - task docs safe to delete. + +## Frequent Compile Pitfalls + +Before considering a slice done, check these repo-specific issues: + +- `input.InitEffect(...)` always needs all three args: effect type, numeric ID, effect instance. +- If a new sub-effect type is added, also register the sub-effect explicitly in `init()`. +- `SkillEntity.XML.Power` and `Priority` are `int`-based in current generated structs; avoid mixing with `int8`, `int32`, or `int64` arithmetic. +- Skill PP on runtime battle state is commonly `uint32`; cast carefully when subtracting or restoring PP. +- `model.SkillInfo` does not expose fields like `MaxPP` or `Category`; look them up through `xmlres.SkillMap[int(skill.ID)]`. +- `xmlres.Move` does not expose every runtime field. Use runtime state such as `SkillEntity.AttackTime` when the XML struct lacks a field. +- `input.Input` does not always expose nested objects assumed by task text, such as `Opp.SkillEntity`; verify available runtime fields before coding. +- Decimal math must use `alpacadecimal` values, not raw integer literals. +- Large grouped files can accidentally keep stale duplicate `init()` registration blocks after manual merges or batch patches. Check the file tail before closing out a slice. + +## Partial Slice Strategy + +When a grouped file is only partially grounded: + +- Do not delete the task docs for that slice yet. +- Keep the implemented IDs in place and make the remaining gaps explicit. +- Prefer conservative, repo-shaped implementations over speculative full feature work for heavy system effects. +- Good candidates to finish in partial slices are: + - simple priority modifiers, + - PP-based power changes, + - round-based sub-effects using existing helper bases, + - status immunity or status application patterns that already exist nearby. +- Leave highly coupled systems partial if they appear to depend on larger mechanics not yet modeled in nearby code, such as custom resource tracks or complex transformation states. + +## Task Doc Deletion + +Delete a task file only when one of these is true: + +- every effect ID in the task range is now implemented in repo code, +- the IDs already existed and were verified as not missing, +- or the user explicitly accepts documented non-implementation for reserved placeholders. + +Do not delete the task doc when the slice is still mixed between implemented and partial IDs. + ## Common Tasks ### Random power or conditional power effects @@ -150,6 +202,11 @@ Run, at minimum: - `cd /workspace/logic && go test ./service/fight/effect` - `cd /workspace/logic && go build ./...` +If the user explicitly says tests are not required for the current pass, downgrade validation to: +- `gofmt -w` on every edited Go file, +- fix current editor/compiler diagnostics for touched files, +- and report that package tests/build were intentionally skipped by user request. + If the task came from `docs/effect-unimplemented-tasks/`, remove the completed task file when the user asked for it. ## Output diff --git a/logic/service/fight/effect/1770_1794.go b/logic/service/fight/effect/1770_1794.go index 0c3cbce90..d23a76b9e 100644 --- a/logic/service/fight/effect/1770_1794.go +++ b/logic/service/fight/effect/1770_1794.go @@ -466,6 +466,52 @@ func (e *Effect1784Sub) DamageLockEx(zone *info.DamageZone) bool { } // Effect 1791: 消除双方护盾并附加护盾值{0}%的百分比伤害,若护盾值总和超过{1}点则转变为附加护盾值{2}%的百分比伤害 +// Effect 1785: {0}回合内每回合使用技能吸取对手最大体力的1/{1},满体力时额外恢复己方所有不在场精灵{2}点体力 +type Effect1785 struct { + node.EffectNode +} + +func (e *Effect1785) Skill_Use() bool { + if len(e.Args()) < 3 { + return true + } + + sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 1785, 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 Effect1785Sub struct { + RoundEffectArg0Base +} + +func (e *Effect1785Sub) OnSkill() bool { + if len(e.Args()) < 3 || e.Ctx().Our == nil || e.Ctx().Our.CurrentPet == nil || e.Ctx().Opp == nil || e.Ctx().Opp.CurrentPet == nil { + return true + } + if e.Ctx().SkillEntity == nil || e.Args()[1].Cmp(alpacadecimal.Zero) <= 0 { + return true + } + + fullHP := e.Ctx().Our.CurrentPet.GetHP().Cmp(e.Ctx().Our.CurrentPet.GetMaxHP()) >= 0 + amount := e.Ctx().Opp.CurrentPet.GetMaxHP().Div(e.Args()[1]) + if amount.Cmp(alpacadecimal.Zero) <= 0 { + return true + } + + e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{ + Type: info.DamageType.Percent, + Damage: amount, + }) + e.Ctx().Our.Heal(e.Ctx().Our, &action.SelectSkillAction{}, amount) + if fullHP { + healBench(e.Ctx().Our, e.Args()[2]) + } + return true +} + type Effect1791 struct { node.EffectNode } @@ -608,6 +654,8 @@ func init() { input.InitEffect(input.EffectType.Sub, 1775, &Effect1775Sub{}) input.InitEffect(input.EffectType.Skill, 1776, &Effect1776{}) input.InitEffect(input.EffectType.Sub, 1776, &Effect1776Sub{}) + input.InitEffect(input.EffectType.Skill, 1785, &Effect1785{}) + input.InitEffect(input.EffectType.Sub, 1785, &Effect1785Sub{}) input.InitEffect(input.EffectType.Skill, 1781, &Effect1781{}) input.InitEffect(input.EffectType.Sub, 1781, &Effect1781Sub{}) input.InitEffect(input.EffectType.Skill, 1782, &Effect1782{}) diff --git a/logic/service/fight/effect/2195_2219.go b/logic/service/fight/effect/2195_2219.go index 75a1a6b3d..d51eaa235 100644 --- a/logic/service/fight/effect/2195_2219.go +++ b/logic/service/fight/effect/2195_2219.go @@ -1,6 +1,7 @@ package effect import ( + "blazing/common/data/xmlres" "blazing/logic/service/fight/action" "blazing/logic/service/fight/info" "blazing/logic/service/fight/input" @@ -22,8 +23,30 @@ func (e *Effect2195) Skill_Use() bool { if e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS { return true } + + before := activeTurnEffectCount(e.Ctx().Opp) e.Ctx().Opp.CancelTurn(e.Ctx().Our) - e.Ctx().SkillEntity.XML.Priority -= int(e.Args()[1].IntPart()) + if before <= 0 { + return true + } + + sub := e.Ctx().Opp.InitEffect(input.EffectType.Sub, 2195, int(e.Args()[0].IntPart()), int(e.Args()[1].IntPart())) + if sub != nil { + e.Ctx().Opp.AddEffect(e.Ctx().Our, sub) + } + return true +} + +type Effect2195Sub struct { + RoundEffectArg0Base +} + +func (e *Effect2195Sub) ComparePre(fattack, sattack *action.SelectSkillAction) bool { + current := actionByPlayer(fattack, sattack, e.Ctx().Our.UserID) + if current == nil || current.SkillEntity == nil { + return true + } + current.SkillEntity.XML.Priority -= int(e.Args()[1].IntPart()) return true } @@ -319,12 +342,24 @@ type Effect2215 struct { } func (e *Effect2215) Damage_Mul(zone *info.DamageZone) bool { - if zone == nil || zone.Type != info.DamageType.Red || len(e.Args()) == 0 { + if zone == nil || zone.Type != info.DamageType.Red || len(e.Args()) == 0 || e.Ctx().SkillEntity == nil { return true } - if e.Ctx().SkillEntity != nil { - zone.Damage = zone.Damage.Mul(hundred.Add(e.Args()[0])).Div(hundred) + skill, ok := xmlres.SkillMap[int(e.Ctx().SkillEntity.Info.ID)] + if !ok || skill.MaxPP <= 0 || e.Args()[0].Cmp(alpacadecimal.Zero) <= 0 { + return true } + lostPercent := int64(skill.MaxPP-int(e.Ctx().SkillEntity.Info.PP)) * 100 / int64(skill.MaxPP) + step := int64(e.Args()[0].IntPart()) + if step <= 0 { + return true + } + tiers := lostPercent / step + if tiers <= 0 { + return true + } + bonus := alpacadecimal.NewFromInt(100 + tiers*step) + zone.Damage = zone.Damage.Mul(bonus).Div(hundred) return true } @@ -376,19 +411,22 @@ type Effect2219 struct { node.EffectNode } -func (e *Effect2219) Damage_Mul(zone *info.DamageZone) bool { - if zone == nil || zone.Type != info.DamageType.Red || len(e.Args()) == 0 { +func (e *Effect2219) Skill_Use() bool { + if len(e.Args()) == 0 || e.Ctx().Our == nil || e.Ctx().Our.CurrentPet == nil { return true } - if e.Ctx().SkillEntity == nil { + target := e.Ctx().Our.CurrentPet.Info.MaxHp * int(e.Args()[0].IntPart()) / 100 + current := e.Ctx().Our.CurrentDivineEnergy() + if target <= current { return true } - zone.Damage = zone.Damage.Mul(hundred.Add(e.Args()[0])).Div(hundred) + e.Ctx().Our.AddDivineEnergy(target - current) return true } func init() { input.InitEffect(input.EffectType.Skill, 2195, &Effect2195{}) + input.InitEffect(input.EffectType.Sub, 2195, &Effect2195Sub{}) input.InitEffect(input.EffectType.Skill, 2196, &Effect2196{}) input.InitEffect(input.EffectType.Skill, 2197, &Effect2197{}) input.InitEffect(input.EffectType.Skill, 2198, &Effect2198{})