fix: 修正技能效果索引与实现逻辑
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

This commit is contained in:
xinian
2026-03-31 03:42:46 +08:00
committed by cnb
parent 7e3f31e267
commit 79c014f9cd
18 changed files with 2062 additions and 334 deletions

View File

@@ -1,35 +0,0 @@
# Task 325: Effects 2240-2244
## 目标
- 补齐以下 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 2240
- `argsNum`: `1`
- `info`: `解除自身所处的异常状态,解除成功则本次攻击威力提升{0}%`
### Effect 2241
- `argsNum`: `2`
- `info`: `消除对手能力提升状态,消除成功则{0}回合内对手体力恢复效果减少{1}%`
### Effect 2242
- `argsNum`: `0`
- `info`: `此技能PP值不为满时先制+1`
### Effect 2243
- `argsNum`: `0`
- `info`: `若先出手则自身下次受到致死伤害时重生`
### Effect 2244
- `argsNum`: `1`
- `info`: `{0}回合内免疫受到的非限制类异常状态`
## 备注
- 该清单按当前仓库静态注册结果生成如果某个 effect 实际通过其他模块或运行时路径实现需要先复核后再落代码
- `201``445` 这类占位 effect优先补核心逻辑或补充明确的不可实现说明

View File

@@ -1,38 +0,0 @@
# Task 331: Effects 2270-2274
## 目标
- 补齐以下 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 2270
- `argsNum`: `3`
- `info`: `免疫下{0}次受到的攻击,触发成功则{1}%令对手{2}`
- `param`: `1,2,2`
### Effect 2271
- `argsNum`: `4`
- `info`: `{0}回合内若自身能力提升状态被消除或吸取则{1}%令对手{2},未触发则自身下{3}次攻击必定致命一击`
- `param`: `1,2,2`
### Effect 2272
- `argsNum`: `1`
- `info`: `己方有其他精灵存活时造成的攻击伤害提升{0}%`
### Effect 2273
- `argsNum`: `2`
- `info`: `对手选择攻击技能时此技能先制+3且{0}%令对手{1}`
- `param`: `1,1,1`
### Effect 2274
- `argsNum`: `1`
- `info`: `恢复自身全部PP值每恢复1点吸取对手{0}点体力`
## 备注
- 该清单按当前仓库静态注册结果生成如果某个 effect 实际通过其他模块或运行时路径实现需要先复核后再落代码
- `201``445` 这类占位 effect优先补核心逻辑或补充明确的不可实现说明

View File

@@ -1,35 +0,0 @@
# Task 332: Effects 2275-2279
## 目标
- 补齐以下 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 2275
- `argsNum`: `0`
- `info`: `未击败对手则将对手标记为狩杀之的`
### Effect 2276
- `argsNum`: `0`
- `info`: `将对方场下对自身克制系数最高且存活的一只非狩杀之的精灵标记为狩杀之的`
### Effect 2277
- `argsNum`: `0`
- `info`: `将对方场下对自身克制系数最低且存活的一只非狩杀之的精灵标记为狩杀之的`
### Effect 2278
- `argsNum`: `3`
- `info`: `若对手为狩杀之的则造成的攻击伤害提升{0}%,若对手不为狩杀之的则自身当回合无法造成攻击伤害且附加等同于狩杀之的体力{1}%的百分比伤害,自身拥有狩杀之魂时上述效果提升{2}%`
### Effect 2279
- `argsNum`: `1`
- `info`: `自身击败狩杀之的时对方下只精灵出战时将被标记为狩杀之的且自身每有1层狩杀之魂自身下次攻击造成的伤害提升{0}%`
## 备注
- 该清单按当前仓库静态注册结果生成如果某个 effect 实际通过其他模块或运行时路径实现需要先复核后再落代码
- `201``445` 这类占位 effect优先补核心逻辑或补充明确的不可实现说明

View File

@@ -1,35 +0,0 @@
# Task 333: Effects 2280-2284
## 目标
- 补齐以下 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 2280
- `argsNum`: `0`
- `info`: `技能无效时,对手下回合无法主动切换精灵`
### Effect 2281
- `argsNum`: `0`
- `info`: `若先出手则自身下次受到致死伤害时保留1点体力`
### Effect 2282
- `argsNum`: `3`
- `info`: `{0}回合内使用技能恢复自身最大体力1/{1}并造成等量百分比伤害,若对手免疫百分比伤害则附加自身最大体力{2}%的真实伤害`
### Effect 2283
- `argsNum`: `3`
- `info`: `消耗自身等同于护盾、护罩之和的体力平均恢复至不在场精灵,若消耗的点数大于{0}点则额外消除双方的护盾、护罩并获得消除量{1}%的临时体力上限直到下场,最高叠加至体力上限的{2}%`
### Effect 2284
- `argsNum`: `2`
- `info`: `若对手不为普通系则剥夺对手的系别每剥夺1种附加1种控制类异常状态每有1种异常状态未触发自身下{0}次行动结束后进行额外行动以触发咒骸魂契:附加{1}点所剥夺系别的伤害`
## 备注
- 该清单按当前仓库静态注册结果生成如果某个 effect 实际通过其他模块或运行时路径实现需要先复核后再落代码
- `201``445` 这类占位 effect优先补核心逻辑或补充明确的不可实现说明

View File

@@ -1,35 +0,0 @@
# Task 334: Effects 2285-2289
## 目标
- 补齐以下 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 2285
- `argsNum`: `3`
- `info`: `{0}回合内对手无法通过技能恢复体力,若对手当回合未选择技能则改为{1}回合内对手体力恢复效果减少{2}%`
### Effect 2286
- `argsNum`: `2`
- `info`: `对手下{0}次回合类效果、能力提升效果被消除时,体力上限减少{1}点`
### Effect 2287
- `argsNum`: `2`
- `info`: `{0}回合内使用技能恢复自身最大体力的1/{1}并造成等量百分比伤害,恢复效果溢出时转化为等量的护盾`
### Effect 2288
- `argsNum`: `0`
- `info`: `技能无效时为对手附加3回合的琼花仙荼`
### Effect 2289
- `argsNum`: `2`
- `info`: `自身每点亮1颗雷辰威力提升{0}点若自身未点亮雷辰则己方每存活1只精灵造成的攻击伤害不低于{1}`
## 备注
- 该清单按当前仓库静态注册结果生成如果某个 effect 实际通过其他模块或运行时路径实现需要先复核后再落代码
- `201``445` 这类占位 effect优先补核心逻辑或补充明确的不可实现说明

View File

@@ -1,35 +0,0 @@
# Task 335: Effects 2290-2294
## 目标
- 补齐以下 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 2290
- `argsNum`: `0`
- `info`: `自身每点亮1颗雷辰全属性+1`
### Effect 2291
- `argsNum`: `3`
- `info`: `自身武道衍化轨迹为正转时{0}回合内对手无法通过技能恢复体力,为逆转时{1}回合内对手造成的攻击伤害减少{2}%`
### Effect 2292
- `argsNum`: `1`
- `info`: `将自身的武道衍化轨迹改为逆转并将武道正转期间抵挡的伤害以固定伤害的形式反馈给对手,最高不超过自身最大体力的{0}%`
### Effect 2293
- `argsNum`: `1`
- `info`: `复原自身的武道衍化轨迹,下{0}次衍化时时全属性+1`
### Effect 2294
- `argsNum`: `0`
- `info`: `衍化自身的武道`
## 备注
- 该清单按当前仓库静态注册结果生成如果某个 effect 实际通过其他模块或运行时路径实现需要先复核后再落代码
- `201``445` 这类占位 effect优先补核心逻辑或补充明确的不可实现说明

View File

@@ -33,5 +33,5 @@ func (e *NewSel113) DamageLockEx(t *info.DamageZone) bool {
return true return true
} }
func init() { func init() {
input.InitEffect(input.EffectType.NewSel, 113, &NewSel113{}) input.InitEffect(input.EffectType.NewSel, 213, &NewSel113{})
} }

View File

@@ -33,5 +33,5 @@ func (e *NewSel144) Action_end_ex() bool {
return true return true
} }
func init() { func init() {
input.InitEffect(input.EffectType.NewSel, 144, &NewSel144{}) input.InitEffect(input.EffectType.NewSel, 244, &NewSel144{})
} }

View File

@@ -40,5 +40,5 @@ func (e *NewSel224) Skill_Use() bool {
return true return true
} }
func init() { func init() {
input.InitEffect(input.EffectType.NewSel, 224, &NewSel224{}) input.InitEffect(input.EffectType.NewSel, 324, &NewSel224{})
} }

View File

@@ -22,5 +22,5 @@ func (e *NewSel41) Skill_Use_ex() bool {
return true return true
} }
func init() { func init() {
input.InitEffect(input.EffectType.NewSel, 41, &NewSel41{}) input.InitEffect(input.EffectType.NewSel, 141, &NewSel41{})
} }

View File

@@ -0,0 +1,327 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/util/grand"
)
func bossJsonTransferPositive(from, to *input.Input) bool {
changed := false
for i := 0; i < 6; i++ {
if from.AttackValue.Prop[i] <= 0 {
continue
}
changed = true
stage := from.AttackValue.Prop[i]
from.AttackValue.Prop[i] = 0
to.AttackValue.Prop[i] += stage
if to.AttackValue.Prop[i] > 6 {
to.AttackValue.Prop[i] = 6
}
}
return changed
}
func bossJsonActionMatches(skill *info.SkillEntity, act *action.SelectSkillAction, userID uint32) bool {
if skill == nil || act == nil || act.SkillEntity == nil || act.PlayerID != userID {
return false
}
return act.SkillEntity.XML.ID == skill.XML.ID
}
// 41: 每回合恢复固定体力
type BossJsonEid41 struct{ BossJsonEid0 }
func (e *BossJsonEid41) TurnStart(fattack, sattack *action.SelectSkillAction) {
if !e.ownerActive() {
return
}
heal := bossJsonFirstPositiveArg(e.Args(), 0)
if heal > 0 {
e.Ctx().Our.Heal(e.Ctx().Our, nil, alpacadecimal.NewFromInt(int64(heal)))
}
}
// 113: 对手使用技能后全属性-1
type BossJsonEid113 struct{ BossJsonEid0 }
func (e *BossJsonEid113) Action_end_ex() bool {
if !e.ownerActive() || e.Ctx().SkillEntity == nil {
return true
}
stage := int8(bossJsonFirstPositiveArg(e.Args(), 1))
bossJsonBoostAll(e.Ctx().Opp, e.Ctx().Our, -stage)
return true
}
// 144: 对手技能结束时,消除对手的能力提升
type BossJsonEid144 struct{ BossJsonEid0 }
func (e *BossJsonEid144) Action_end_ex() bool {
if !e.ownerActive() || e.Ctx().SkillEntity == nil {
return true
}
bossJsonClearPositive(e.Ctx().Opp)
return true
}
// 224: 首次死亡后恢复全部体力,并使全能力等级等于指定值
type BossJsonEid224 struct {
BossJsonEid0
used bool
}
func (e *BossJsonEid224) revive() bool {
if !e.ownerActive() || e.used || e.Ctx().Our.CurrentPet.Info.Hp != 0 {
return true
}
e.used = true
e.Ctx().Our.CurrentPet.Info.Hp = e.Ctx().Our.CurrentPet.Info.MaxHp
e.Ctx().Our.HealPP(-1)
stageSeed := bossJsonIntArg(e.Args(), len(e.Args())-1, 6)
stage := int8(stageSeed - 6)
if stage < 0 {
stage = 0
}
if stage > 6 {
stage = 6
}
bossJsonSetAll(e.Ctx().Our, stage)
return true
}
func (e *BossJsonEid224) Action_end() bool { return e.revive() }
func (e *BossJsonEid224) Action_end_ex() bool { return e.revive() }
// 2095: 对手存在能力提升或回合类效果时伤害提升
type BossJsonEid2095 struct{ BossJsonEid0 }
func (e *BossJsonEid2095) Damage_Mul(zone *info.DamageZone) bool {
if !e.ownerActive() || zone.Type != info.DamageType.Red {
return true
}
if !bossJsonHasPositive(e.Ctx().Opp) && !bossJsonAnyTurnEffect(e.Ctx().Opp) {
return true
}
bonus := bossJsonFirstPositiveArg(e.Args(), 0)
zone.Damage = zone.Damage.Mul(alpacadecimal.NewFromInt(int64(100 + bonus))).Div(alpacadecimal.NewFromInt(100))
return true
}
// 2104: 回合开始提升体力上限;回合结束恢复已损失体力的一定比例
type BossJsonEid2104 struct{ BossJsonEid0 }
func (e *BossJsonEid2104) TurnStart(fattack, sattack *action.SelectSkillAction) {
if !e.ownerActive() {
return
}
growPercent := bossJsonIntArg(e.Args(), 1, 0)
if growPercent <= 0 {
return
}
add := int(e.Ctx().Our.CurrentPet.Info.MaxHp) * growPercent / 100
if add <= 0 {
return
}
e.Ctx().Our.CurrentPet.Info.MaxHp += uint32(add)
e.Ctx().Our.CurrentPet.Info.Hp += uint32(add)
}
func (e *BossJsonEid2104) TurnEnd() {
if !e.ownerActive() {
return
}
recoverPercent := bossJsonIntArg(e.Args(), 0, 0)
if recoverPercent <= 0 || e.Ctx().Our.CurrentPet.Info.MaxHp <= e.Ctx().Our.CurrentPet.Info.Hp {
return
}
missing := int(e.Ctx().Our.CurrentPet.Info.MaxHp - e.Ctx().Our.CurrentPet.Info.Hp)
heal := missing * recoverPercent / 100
if heal > 0 {
e.Ctx().Our.Heal(e.Ctx().Our, nil, alpacadecimal.NewFromInt(int64(heal)))
}
}
// 2103: 吸取对手能力提升,否则随机削弱并附加给自身;回合结束吸取固定体力
type BossJsonEid2103 struct{ BossJsonEid0 }
func (e *BossJsonEid2103) TurnStart(fattack, sattack *action.SelectSkillAction) {
if !e.ownerActive() {
return
}
if bossJsonTransferPositive(e.Ctx().Opp, e.Ctx().Our) {
return
}
count := bossJsonIntArg(e.Args(), 0, 0)
stage := int8(bossJsonIntArg(e.Args(), 1, 0))
if count <= 0 || stage <= 0 {
return
}
used := map[int]struct{}{}
for len(used) < count && len(used) < 6 {
used[grand.Intn(6)] = struct{}{}
}
for idx := range used {
e.Ctx().Opp.SetProp(e.Ctx().Our, int8(idx), -stage)
e.Ctx().Our.SetProp(e.Ctx().Our, int8(idx), stage)
}
}
func (e *BossJsonEid2103) TurnEnd() {
if !e.ownerActive() {
return
}
value := bossJsonIntArg(e.Args(), 2, 0)
if value <= 0 {
return
}
damage := alpacadecimal.NewFromInt(int64(value))
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: damage})
e.Ctx().Our.Heal(e.Ctx().Our, nil, damage)
}
// 2105: 低血量时获得若干回合力量(这里按伤害翻倍处理)
type BossJsonEid2105 struct {
BossJsonEid0
rounds int
}
func (e *BossJsonEid2105) Damage_Mul(zone *info.DamageZone) bool {
if !e.ownerActive() || e.rounds <= 0 || zone.Type != info.DamageType.Red {
return true
}
zone.Damage = zone.Damage.Mul(alpacadecimal.NewFromInt(2))
return true
}
func (e *BossJsonEid2105) TurnEnd() {
if !e.ownerActive() {
return
}
if e.rounds > 0 {
e.rounds--
}
threshold := bossJsonIntArg(e.Args(), 0, 0)
grant := bossJsonIntArg(e.Args(), 1, 0)
if threshold <= 0 || grant <= 0 || e.Ctx().Our.CurrentPet.Info.MaxHp == 0 {
return
}
if int(e.Ctx().Our.CurrentPet.Info.Hp)*100 < int(e.Ctx().Our.CurrentPet.Info.MaxHp)*threshold && e.rounds < grant {
e.rounds = grant
}
}
// 2137: 恢复效果逐回合衰减,衰减前自身增伤并附加固伤
type BossJsonEid2137 struct {
BossJsonEid0
healRate int
}
func (e *BossJsonEid2137) TurnStart(fattack, sattack *action.SelectSkillAction) {
if !e.ownerActive() {
return
}
if e.healRate == 0 {
e.healRate = 100
}
dec := bossJsonIntArg(e.Args(), 0, 0)
if dec > 0 {
e.healRate -= dec
if e.healRate < 0 {
e.healRate = 0
}
}
}
func (e *BossJsonEid2137) Heal_Pre(_ action.BattleActionI, amount *int) bool {
if !e.ownerActive() || e.healRate >= 100 {
return true
}
*amount = *amount * e.healRate / 100
return true
}
func (e *BossJsonEid2137) DamageAdd(zone *info.DamageZone) bool {
if !e.ownerActive() || e.healRate <= 0 || zone.Type != info.DamageType.Red {
return true
}
fixed := bossJsonIntArg(e.Args(), 2, 0)
if fixed > 0 {
zone.Damage = zone.Damage.Add(alpacadecimal.NewFromInt(int64(fixed)))
}
return true
}
func (e *BossJsonEid2137) Damage_Mul(zone *info.DamageZone) bool {
if !e.ownerActive() || e.healRate <= 0 || zone.Type != info.DamageType.Red {
return true
}
bonus := bossJsonIntArg(e.Args(), 1, 0)
zone.Damage = zone.Damage.Mul(alpacadecimal.NewFromInt(int64(100 + bonus))).Div(alpacadecimal.NewFromInt(100))
return true
}
// 1621: 对手攻击技能后出手则无效
type BossJsonEid1621 struct{ BossJsonEid0 }
func (e *BossJsonEid1621) ActionStart(firstAttack, secondAttack *action.SelectSkillAction) bool {
if !e.ownerActive() || !bossJsonIsAttackSkill(e.Ctx().SkillEntity) {
return true
}
userID := e.Ctx().Our.Player.GetInfo().UserID
if !bossJsonActionMatches(e.Ctx().SkillEntity, secondAttack, userID) {
return true
}
if firstAttack == nil || !bossJsonIsAttackSkill(firstAttack.SkillEntity) {
return true
}
return false
}
// 1254: 先出手则当回合免疫受到的攻击伤害
type BossJsonEid1254 struct {
BossJsonEid0
immuneThisTurn bool
}
func (e *BossJsonEid1254) ActionStart(firstAttack, secondAttack *action.SelectSkillAction) bool {
e.immuneThisTurn = false
if !e.ownerActive() || !bossJsonIsAttackSkill(e.Ctx().SkillEntity) {
return true
}
userID := e.Ctx().Our.Player.GetInfo().UserID
e.immuneThisTurn = bossJsonActionMatches(e.Ctx().SkillEntity, firstAttack, userID)
return true
}
func (e *BossJsonEid1254) DamageLockEx(zone *info.DamageZone) bool {
if !e.ownerActive() || !e.immuneThisTurn || zone.Type != info.DamageType.Red || e.Ctx().SkillEntity == nil {
return true
}
if bossJsonIsAttackSkill(e.Ctx().SkillEntity) {
zone.Damage = alpacadecimal.Zero
}
return true
}
func (e *BossJsonEid1254) TurnEnd() {
e.immuneThisTurn = false
}
func init() {
input.InitEffect(input.EffectType.NewSel, 41, &BossJsonEid41{})
input.InitEffect(input.EffectType.NewSel, 113, &BossJsonEid113{})
input.InitEffect(input.EffectType.NewSel, 144, &BossJsonEid144{})
input.InitEffect(input.EffectType.NewSel, 224, &BossJsonEid224{})
input.InitEffect(input.EffectType.NewSel, 2095, &BossJsonEid2095{})
input.InitEffect(input.EffectType.NewSel, 2104, &BossJsonEid2104{})
input.InitEffect(input.EffectType.NewSel, 2103, &BossJsonEid2103{})
input.InitEffect(input.EffectType.NewSel, 2105, &BossJsonEid2105{})
input.InitEffect(input.EffectType.NewSel, 2137, &BossJsonEid2137{})
input.InitEffect(input.EffectType.NewSel, 1621, &BossJsonEid1621{})
input.InitEffect(input.EffectType.NewSel, 1254, &BossJsonEid1254{})
}

View File

@@ -0,0 +1,379 @@
package effect
import (
"blazing/logic/service/fight/action"
"blazing/logic/service/fight/info"
"blazing/logic/service/fight/input"
"github.com/alpacahq/alpacadecimal"
"github.com/gogf/gf/v2/util/grand"
)
type BossJsonEid0 struct {
NewSel0
}
func (e *BossJsonEid0) ownerActive() bool {
return e.IsOwner()
}
func bossJsonIntArg(args []alpacadecimal.Decimal, idx int, def int) int {
if idx >= 0 && idx < len(args) {
return int(args[idx].IntPart())
}
return def
}
func bossJsonFirstPositiveArg(args []alpacadecimal.Decimal, def int) int {
for _, arg := range args {
if arg.IntPart() > 0 {
return int(arg.IntPart())
}
}
return def
}
func bossJsonIsAttackSkill(skill *info.SkillEntity) bool {
return skill != nil && skill.Category() != info.Category.STATUS
}
func bossJsonBoostAll(target, source *input.Input, delta int8) {
for i := 0; i < 6; i++ {
target.SetProp(source, int8(i), delta)
}
}
func bossJsonSetAll(target *input.Input, value int8) {
for i := 0; i < 6; i++ {
target.AttackValue.Prop[i] = value
}
}
func bossJsonClearPositive(target *input.Input) bool {
changed := false
for i := 0; i < 6; i++ {
if target.AttackValue.Prop[i] > 0 {
target.AttackValue.Prop[i] = 0
changed = true
}
}
return changed
}
func bossJsonHasPositive(target *input.Input) bool {
for i := 0; i < 6; i++ {
if target.AttackValue.Prop[i] > 0 {
return true
}
}
return false
}
func bossJsonAnyTurnEffect(target *input.Input) bool {
for _, effect := range target.Effects {
if effect.Alive() && effect.Duration() > 0 {
return true
}
}
return false
}
// 1261/2346: 神话
// 免疫异常状态和能力下降状态所有技能必中且PP值无限
type BossJsonEidMyth struct{ BossJsonEid0 }
func (e *BossJsonEidMyth) PropBefer(in *input.Input, prop int8, level int8) bool {
if !e.ownerActive() || in == e.Ctx().Our {
return true
}
return level >= 0
}
func (e *BossJsonEidMyth) EFFect_Befer(in *input.Input, effEffect input.Effect) bool {
if !e.ownerActive() || in != e.Ctx().Opp {
return true
}
return !input.IS_Stat(effEffect)
}
func (e *BossJsonEidMyth) ActionStart(a, b *action.SelectSkillAction) bool {
if !e.ownerActive() || e.Ctx().SkillEntity == nil {
return true
}
e.Ctx().SkillEntity.XML.MustHit = 1
return true
}
func (e *BossJsonEidMyth) HookPP(count *int) bool {
if !e.ownerActive() {
return true
}
*count = 0
return true
}
// 600: 所有技能必定先手
type BossJsonEid600 struct{ BossJsonEid0 }
func (e *BossJsonEid600) ComparePre(fattack, sattack *action.SelectSkillAction) bool {
if !e.ownerActive() {
return true
}
userID := e.Ctx().Our.Player.GetInfo().UserID
if fattack != nil && fattack.PlayerID == userID && fattack.SkillEntity != nil {
fattack.SkillEntity.XML.Priority += 1000
}
if sattack != nil && sattack.PlayerID == userID && sattack.SkillEntity != nil {
sattack.SkillEntity.XML.Priority += 1000
}
return true
}
// 390: 自身使用技能后全属性+1
type BossJsonEid390 struct{ BossJsonEid0 }
func (e *BossJsonEid390) Action_end() bool {
if !e.ownerActive() || e.Ctx().SkillEntity == nil {
return true
}
bossJsonBoostAll(e.Ctx().Our, e.Ctx().Our, int8(bossJsonFirstPositiveArg(e.Args(), 1)))
return true
}
// 792: 格挡物攻/特攻
type BossJsonEid792 struct{ BossJsonEid0 }
func (e *BossJsonEid792) DamageLockEx(zone *info.DamageZone) bool {
if !e.ownerActive() || zone.Type != info.DamageType.Red || e.Ctx().SkillEntity == nil {
return true
}
mode := bossJsonIntArg(e.Args(), 0, 0)
if mode == 1 && e.Ctx().SkillEntity.Category() == info.Category.PHYSICAL {
zone.Damage = alpacadecimal.Zero
}
if mode == 2 && e.Ctx().SkillEntity.Category() == info.Category.SPECIAL {
zone.Damage = alpacadecimal.Zero
}
return true
}
// 219: 抵挡未打出致命一击的攻击
type BossJsonEid219 struct{ BossJsonEid0 }
func (e *BossJsonEid219) DamageLockEx(zone *info.DamageZone) bool {
if !e.ownerActive() || zone.Type != info.DamageType.Red || e.Ctx().SkillEntity == nil {
return true
}
if e.Ctx().SkillEntity.Crit == 0 {
zone.Damage = alpacadecimal.Zero
}
return true
}
// 208: 抵挡非先制攻击
type BossJsonEid208 struct{ BossJsonEid0 }
func (e *BossJsonEid208) DamageLockEx(zone *info.DamageZone) bool {
if !e.ownerActive() || zone.Type != info.DamageType.Red || e.Ctx().SkillEntity == nil {
return true
}
if e.Ctx().SkillEntity.XML.Priority <= 0 {
zone.Damage = alpacadecimal.Zero
}
return true
}
// 1216: 免疫固定伤害、百分比伤害
type BossJsonEid1216 struct{ BossJsonEid0 }
func (e *BossJsonEid1216) DamageLockEx(zone *info.DamageZone) bool {
if !e.ownerActive() {
return true
}
if zone.Type == info.DamageType.Fixed || zone.Type == info.DamageType.Percent {
zone.Damage = alpacadecimal.Zero
}
return true
}
// 805: 死亡后概率满血复活
type BossJsonEid805 struct {
BossJsonEid0
used bool
}
func (e *BossJsonEid805) revive() bool {
if !e.ownerActive() || e.used || e.Ctx().Our.CurrentPet.Info.Hp != 0 {
return true
}
e.used = true
if !grand.Meet(bossJsonFirstPositiveArg(e.Args(), 0), 100) {
return true
}
e.Ctx().Our.CurrentPet.Info.Hp = e.Ctx().Our.CurrentPet.Info.MaxHp
e.Ctx().Our.HealPP(-1)
return true
}
func (e *BossJsonEid805) Action_end() bool { return e.revive() }
func (e *BossJsonEid805) Action_end_ex() bool { return e.revive() }
// 1215: 攻击技能概率造成倍数伤害
type BossJsonEid1215 struct{ BossJsonEid0 }
func (e *BossJsonEid1215) Damage_Mul(zone *info.DamageZone) bool {
if !e.ownerActive() || zone.Type != info.DamageType.Red || !bossJsonIsAttackSkill(e.Ctx().SkillEntity) {
return true
}
chance := bossJsonIntArg(e.Args(), 0, 0)
multiple := bossJsonIntArg(e.Args(), 1, 1)
if chance > 0 && multiple > 1 && grand.Meet(chance, 100) {
zone.Damage = zone.Damage.Mul(alpacadecimal.NewFromInt(int64(multiple)))
}
return true
}
// 280: 闪避率提升n%
type BossJsonEid280 struct{ BossJsonEid0 }
func (e *BossJsonEid280) SkillHit_ex() bool {
if !e.ownerActive() || e.Ctx().SkillEntity == nil {
return true
}
if e.Ctx().SkillEntity.AttackTime == 2 {
return true
}
hit, _, _ := e.Input.Player.Roll(bossJsonFirstPositiveArg(e.Args(), 0), 100)
if hit {
e.Ctx().SkillEntity.SetMiss()
}
return true
}
// 377: 与对手精灵互换属性
type BossJsonEid377 struct {
BossJsonEid0
used bool
}
func (e *BossJsonEid377) Fight_Start() bool {
if !e.ownerActive() || e.used || e.Ctx().Our.CurrentPet == nil || e.Ctx().Opp.CurrentPet == nil {
return true
}
e.used = true
e.Ctx().Our.CurrentPet.Type, e.Ctx().Opp.CurrentPet.Type = e.Ctx().Opp.CurrentPet.Type, e.Ctx().Our.CurrentPet.Type
return true
}
// 121: 概率令对手属性技能失效
type BossJsonEid121 struct{ BossJsonEid0 }
func (e *BossJsonEid121) SkillHit_ex() bool {
if !e.ownerActive() || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() != info.Category.STATUS {
return true
}
hit, _, _ := e.Input.Player.Roll(bossJsonFirstPositiveArg(e.Args(), 0), 100)
if hit {
e.Ctx().SkillEntity.SetMiss()
}
return true
}
// 735: 每回合吸取固定体力
type BossJsonEid735 struct{ BossJsonEid0 }
func (e *BossJsonEid735) TurnStart(fattack, sattack *action.SelectSkillAction) {
if !e.ownerActive() {
return
}
value := alpacadecimal.NewFromInt(int64(bossJsonFirstPositiveArg(e.Args(), 0)))
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: value})
e.Ctx().Our.Heal(e.Ctx().Our, nil, value)
}
// 261: 每回合按最大体力比例恢复
type BossJsonEid261 struct{ BossJsonEid0 }
func (e *BossJsonEid261) TurnStart(fattack, sattack *action.SelectSkillAction) {
if !e.ownerActive() {
return
}
num := bossJsonIntArg(e.Args(), 0, 0)
den := bossJsonIntArg(e.Args(), 1, 1)
if num <= 0 || den <= 0 {
return
}
heal := e.Ctx().Our.CurrentPet.GetMaxHP().Mul(alpacadecimal.NewFromInt(int64(num))).Div(alpacadecimal.NewFromInt(int64(den)))
e.Ctx().Our.Heal(e.Ctx().Our, nil, heal)
}
// 209: 每n回合恢复固定体力
type BossJsonEid209 struct {
BossJsonEid0
turns int
}
func (e *BossJsonEid209) TurnEnd() {
if !e.ownerActive() {
return
}
e.turns++
interval := bossJsonIntArg(e.Args(), 0, 0)
value := bossJsonIntArg(e.Args(), 1, 0)
if interval > 0 && value > 0 && e.turns%interval == 0 {
e.Ctx().Our.Heal(e.Ctx().Our, nil, alpacadecimal.NewFromInt(int64(value)))
}
}
// 342: 免疫低于/高于阈值的伤害
type BossJsonEid342 struct{ BossJsonEid0 }
func (e *BossJsonEid342) DamageLockEx(zone *info.DamageZone) bool {
if !e.ownerActive() {
return true
}
mode := bossJsonIntArg(e.Args(), 0, 0)
limit := bossJsonIntArg(e.Args(), 1, 0)
dmg := int(zone.Damage.IntPart())
if mode == 1 && dmg < limit {
zone.Damage = alpacadecimal.Zero
}
if mode == 2 && dmg > limit {
zone.Damage = alpacadecimal.Zero
}
return true
}
// 1233: 免疫并反弹致命一击伤害
type BossJsonEid1233 struct{ BossJsonEid0 }
func (e *BossJsonEid1233) DamageLockEx(zone *info.DamageZone) bool {
if !e.ownerActive() || zone.Type != info.DamageType.Red || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Crit == 0 {
return true
}
reflectDamage := zone.Damage
zone.Damage = alpacadecimal.Zero
e.Ctx().Opp.Damage(e.Ctx().Our, &info.DamageZone{Type: info.DamageType.Fixed, Damage: reflectDamage})
return true
}
func init() {
input.InitEffect(input.EffectType.NewSel, 1261, &BossJsonEidMyth{})
input.InitEffect(input.EffectType.NewSel, 2346, &BossJsonEidMyth{})
input.InitEffect(input.EffectType.NewSel, 600, &BossJsonEid600{})
input.InitEffect(input.EffectType.NewSel, 390, &BossJsonEid390{})
input.InitEffect(input.EffectType.NewSel, 792, &BossJsonEid792{})
input.InitEffect(input.EffectType.NewSel, 219, &BossJsonEid219{})
input.InitEffect(input.EffectType.NewSel, 208, &BossJsonEid208{})
input.InitEffect(input.EffectType.NewSel, 1216, &BossJsonEid1216{})
input.InitEffect(input.EffectType.NewSel, 805, &BossJsonEid805{})
input.InitEffect(input.EffectType.NewSel, 1215, &BossJsonEid1215{})
input.InitEffect(input.EffectType.NewSel, 280, &BossJsonEid280{})
input.InitEffect(input.EffectType.NewSel, 377, &BossJsonEid377{})
input.InitEffect(input.EffectType.NewSel, 121, &BossJsonEid121{})
input.InitEffect(input.EffectType.NewSel, 735, &BossJsonEid735{})
input.InitEffect(input.EffectType.NewSel, 261, &BossJsonEid261{})
input.InitEffect(input.EffectType.NewSel, 209, &BossJsonEid209{})
input.InitEffect(input.EffectType.NewSel, 342, &BossJsonEid342{})
input.InitEffect(input.EffectType.NewSel, 1233, &BossJsonEid1233{})
}

View File

@@ -493,8 +493,8 @@ func (e *Effect2244Sub) EFFect_Befer(in *input.Input, effEffect input.Effect) bo
// Effect 2240: 解除自身异常状态,解除成功则本次攻击威力提高{0}% // Effect 2240: 解除自身异常状态,解除成功则本次攻击威力提高{0}%
type Effect2240 struct{ node.EffectNode } type Effect2240 struct{ node.EffectNode }
func (e *Effect2240) Skill_Use() bool { func (e *Effect2240) SkillHit() bool {
if len(e.Args()) == 0 { if len(e.Args()) == 0 || e.Ctx().SkillEntity == nil || e.Ctx().SkillEntity.Category() == info.Category.STATUS {
return true return true
} }
@@ -513,10 +513,7 @@ func (e *Effect2240) Skill_Use() bool {
return true return true
} }
sub := e.Ctx().Our.InitEffect(input.EffectType.Sub, 2240, int(e.Args()[0].IntPart())) e.Ctx().SkillEntity.XML.Power = e.Ctx().SkillEntity.XML.Power * (100 + int(e.Args()[0].IntPart())) / 100
if sub != nil {
e.Ctx().Our.AddEffect(e.Ctx().Our, sub)
}
return true return true
} }
@@ -540,18 +537,7 @@ func (e *Effect2241) Skill_Use() bool {
return true return true
} }
cleared := false if !clearPositiveProps(e.Ctx().Opp, e.Ctx().Our) {
for _, eff := range e.Ctx().Opp.Effects {
if eff == nil || !eff.Alive() {
continue
}
if eff.ID().GetEffectType() != input.EffectType.Status {
continue
}
eff.Alive(false)
cleared = true
}
if !cleared {
return true return true
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
package cmd
import (
"encoding/json"
"os"
"path/filepath"
"sort"
"sync"
)
const (
robotGroupConfigFile = "robot_groups.json"
robotBlacklistFile = "robot_blacklist.json"
)
type robotGroupConfig struct {
NotifyGroupIDs []int64 `json:"notify_group_ids"`
ManageGroupIDs []int64 `json:"manage_group_ids"`
}
type robotBlacklist struct {
UserIDs []int64 `json:"user_ids"`
}
var robotBlacklistMu sync.Mutex
func loadRobotGroupConfig() robotGroupConfig {
path := resolveRobotConfigPath(robotGroupConfigFile)
ensureRobotConfigFile(path, []byte("{\n \"notify_group_ids\": [],\n \"manage_group_ids\": []\n}\n"))
var cfg robotGroupConfig
data, err := os.ReadFile(path)
if err != nil {
return cfg
}
if err = json.Unmarshal(data, &cfg); err != nil {
return robotGroupConfig{}
}
cfg.NotifyGroupIDs = normalizeInt64IDs(cfg.NotifyGroupIDs)
cfg.ManageGroupIDs = normalizeInt64IDs(cfg.ManageGroupIDs)
return cfg
}
func loadRobotBlacklist() robotBlacklist {
path := resolveRobotConfigPath(robotBlacklistFile)
ensureRobotConfigFile(path, []byte("{\n \"user_ids\": []\n}\n"))
var cfg robotBlacklist
data, err := os.ReadFile(path)
if err != nil {
return cfg
}
if err = json.Unmarshal(data, &cfg); err != nil {
return robotBlacklist{}
}
cfg.UserIDs = normalizeInt64IDs(cfg.UserIDs)
return cfg
}
func saveRobotBlacklist(cfg robotBlacklist) error {
path := resolveRobotConfigPath(robotBlacklistFile)
cfg.UserIDs = normalizeInt64IDs(cfg.UserIDs)
ensureRobotConfigFile(path, []byte("{\n \"user_ids\": []\n}\n"))
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
data = append(data, '\n')
return os.WriteFile(path, data, 0o644)
}
func addRobotBlacklistUser(userID int64) error {
if userID <= 0 {
return nil
}
robotBlacklistMu.Lock()
defer robotBlacklistMu.Unlock()
cfg := loadRobotBlacklist()
for _, id := range cfg.UserIDs {
if id == userID {
return nil
}
}
cfg.UserIDs = append(cfg.UserIDs, userID)
return saveRobotBlacklist(cfg)
}
func removeRobotBlacklistUser(userID int64) error {
if userID <= 0 {
return nil
}
robotBlacklistMu.Lock()
defer robotBlacklistMu.Unlock()
cfg := loadRobotBlacklist()
ids := make([]int64, 0, len(cfg.UserIDs))
for _, id := range cfg.UserIDs {
if id != userID {
ids = append(ids, id)
}
}
cfg.UserIDs = ids
return saveRobotBlacklist(cfg)
}
func isRobotBlacklisted(userID int64) bool {
if userID <= 0 {
return false
}
cfg := loadRobotBlacklist()
for _, id := range cfg.UserIDs {
if id == userID {
return true
}
}
return false
}
func resolveRobotConfigPath(name string) string {
candidates := []string{
filepath.Join("public", "config", name),
filepath.Join("..", "public", "config", name),
filepath.Join("..", "..", "public", "config", name),
}
for _, candidate := range candidates {
if _, err := os.Stat(candidate); err == nil {
return candidate
}
}
return candidates[0]
}
func ensureRobotConfigFile(path string, fallback []byte) {
if _, err := os.Stat(path); err == nil {
return
}
_ = os.MkdirAll(filepath.Dir(path), 0o755)
_ = os.WriteFile(path, fallback, 0o644)
}
func normalizeInt64IDs(ids []int64) []int64 {
uniq := make(map[int64]struct{}, len(ids))
for _, id := range ids {
if id > 0 {
uniq[id] = struct{}{}
}
}
res := make([]int64, 0, len(uniq))
for id := range uniq {
res = append(res, id)
}
sort.Slice(res, func(i, j int) bool {
return res[i] < res[j]
})
return res
}
func isConfiguredGroup(groupID int64, groups []int64) bool {
for _, id := range groups {
if id == groupID {
return true
}
}
return false
}

View File

@@ -0,0 +1,212 @@
package cmd
import (
"fmt"
"regexp"
"strconv"
"strings"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
const (
robotNotifyModeNormal = "普通"
robotNotifyModeAtAll = "艾特全体"
robotNotifyModeEssence = "群精华"
)
var qqCodePattern = regexp.MustCompile(`qq=(\d+)`)
func init() {
zero.OnCommand("群通知", zero.SuperUserPermission).Handle(handleRobotGroupNotify)
zero.OnCommand("通知群列表", zero.SuperUserPermission).Handle(handleRobotNotifyGroupList)
zero.OnCommand("全群拉黑", zero.SuperUserPermission).Handle(handleRobotBlacklistAdd)
zero.OnCommand("全群解黑", zero.SuperUserPermission).Handle(handleRobotBlacklistRemove)
zero.OnCommand("黑名单列表", zero.SuperUserPermission).Handle(handleRobotBlacklistList)
zero.OnRequest().Handle(handleRobotBlacklistRequest)
zero.OnNotice().Handle(handleRobotBlacklistNotice)
}
func handleRobotGroupNotify(ctx *zero.Ctx) {
cfg := loadRobotGroupConfig()
if len(cfg.NotifyGroupIDs) == 0 {
ctx.Send("未配置通知群,请先编辑 public/config/robot_groups.json 的 notify_group_ids")
return
}
mode, content := parseNotifyModeAndContent(ctx.Event.Message.String())
if content == "" {
ctx.Send("用法:/群通知 [普通|艾特全体|群精华] 内容")
return
}
success := make([]string, 0, len(cfg.NotifyGroupIDs))
for _, groupID := range cfg.NotifyGroupIDs {
switch mode {
case robotNotifyModeAtAll:
ctx.SendGroupMessage(groupID, message.Message{message.AtAll(), message.Text("\n", content)})
case robotNotifyModeEssence:
msgID := ctx.SendGroupMessage(groupID, message.Text("【群通知】\n", content))
if msgID != 0 {
ctx.SetGroupEssenceMessage(msgID)
}
default:
ctx.SendGroupMessage(groupID, message.Text("【群通知】\n", content))
}
success = append(success, strconv.FormatInt(groupID, 10))
}
ctx.Send(fmt.Sprintf("已按[%s]发送群通知,共 %d 个群:%s", mode, len(success), strings.Join(success, ", ")))
}
func handleRobotNotifyGroupList(ctx *zero.Ctx) {
cfg := loadRobotGroupConfig()
ctx.Send(formatRobotGroups("通知群", cfg.NotifyGroupIDs) + "\n支持模式普通、艾特全体、群精华")
}
func handleRobotBlacklistAdd(ctx *zero.Ctx) {
cfg := loadRobotGroupConfig()
if len(cfg.ManageGroupIDs) == 0 {
ctx.Send("未配置管理群,请先编辑 public/config/robot_groups.json 的 manage_group_ids")
return
}
userID := extractCommandUserID(ctx.Event.Message.String(), "全群拉黑")
if userID == 0 {
ctx.Send("用法:/全群拉黑 @某人 或 /全群拉黑 QQ号")
return
}
if userID == ctx.Event.SelfID {
ctx.Send("不能把机器人自己加入黑名单")
return
}
if err := addRobotBlacklistUser(userID); err != nil {
ctx.Send("写入黑名单失败:" + err.Error())
return
}
for _, groupID := range cfg.ManageGroupIDs {
ctx.SetGroupKick(groupID, userID, true)
}
ctx.Send(fmt.Sprintf("已将 %d 加入全群黑名单,并在 %d 个管理群执行踢出/拒绝重加", userID, len(cfg.ManageGroupIDs)))
}
func handleRobotBlacklistRemove(ctx *zero.Ctx) {
userID := extractCommandUserID(ctx.Event.Message.String(), "全群解黑")
if userID == 0 {
ctx.Send("用法:/全群解黑 @某人 或 /全群解黑 QQ号")
return
}
if err := removeRobotBlacklistUser(userID); err != nil {
ctx.Send("移出黑名单失败:" + err.Error())
return
}
ctx.Send(fmt.Sprintf("已将 %d 从全群黑名单移除", userID))
}
func handleRobotBlacklistList(ctx *zero.Ctx) {
cfg := loadRobotBlacklist()
if len(cfg.UserIDs) == 0 {
ctx.Send("当前黑名单为空")
return
}
lines := make([]string, 0, len(cfg.UserIDs))
for _, userID := range cfg.UserIDs {
lines = append(lines, strconv.FormatInt(userID, 10))
}
ctx.Send("当前全群黑名单:\n" + strings.Join(lines, "\n"))
}
func handleRobotBlacklistRequest(ctx *zero.Ctx) {
cfg := loadRobotGroupConfig()
if ctx.Event.RequestType != "group" {
return
}
if !isConfiguredGroup(ctx.Event.GroupID, cfg.ManageGroupIDs) {
return
}
if !isRobotBlacklisted(ctx.Event.UserID) {
return
}
ctx.SetGroupAddRequest(ctx.Event.Flag, ctx.Event.SubType, false, "该账号已被机器人拉黑")
}
func handleRobotBlacklistNotice(ctx *zero.Ctx) {
cfg := loadRobotGroupConfig()
if ctx.Event.NoticeType != "group_increase" {
return
}
if !isConfiguredGroup(ctx.Event.GroupID, cfg.ManageGroupIDs) {
return
}
if !isRobotBlacklisted(ctx.Event.UserID) || ctx.Event.UserID == ctx.Event.SelfID {
return
}
ctx.SetGroupKick(ctx.Event.GroupID, ctx.Event.UserID, true)
ctx.SendGroupMessage(ctx.Event.GroupID, message.Text("已自动移除黑名单成员:", strconv.FormatInt(ctx.Event.UserID, 10)))
}
func extractCommandBody(raw, command string) string {
text := strings.TrimSpace(raw)
text = strings.TrimPrefix(text, "/")
text = strings.TrimSpace(text)
text = strings.TrimPrefix(text, command)
return strings.TrimSpace(text)
}
func extractCommandUserID(raw, command string) int64 {
if matches := qqCodePattern.FindStringSubmatch(raw); len(matches) == 2 {
if userID, err := strconv.ParseInt(matches[1], 10, 64); err == nil && userID > 0 {
return userID
}
}
text := extractCommandBody(raw, command)
for _, field := range strings.Fields(text) {
if userID, err := strconv.ParseInt(field, 10, 64); err == nil && userID > 0 {
return userID
}
}
return 0
}
func parseNotifyModeAndContent(raw string) (string, string) {
body := extractCommandBody(raw, "群通知")
if body == "" {
return robotNotifyModeNormal, ""
}
parts := strings.Fields(body)
if len(parts) == 0 {
return robotNotifyModeNormal, ""
}
switch parts[0] {
case robotNotifyModeNormal, robotNotifyModeAtAll, robotNotifyModeEssence:
return parts[0], strings.TrimSpace(strings.Join(parts[1:], " "))
default:
return robotNotifyModeNormal, body
}
}
func formatRobotGroups(title string, groups []int64) string {
if len(groups) == 0 {
return title + ":未配置"
}
lines := make([]string, 0, len(groups)+1)
lines = append(lines, title+"")
for _, groupID := range groups {
lines = append(lines, strconv.FormatInt(groupID, 10))
}
return strings.Join(lines, "\n")
}

View File

@@ -0,0 +1,3 @@
{
"user_ids": []
}

View File

@@ -0,0 +1,4 @@
{
"notify_group_ids": ["274639498"],
"manage_group_ids": []
}