feat(fight): 调整技能效果命中逻辑与回合开始处理

- 修改了技能效果命中的判定顺序,确保暴击计算在效果添加之前执行
- 修复了回合开始时敌我双方状态结算的上下文错误
- 优化了效果缓存初始化逻辑,避免重复添加相同效果
- 增加了效果去重判断,防止完全相同的效果被重复添加
- 调整了战斗循环中结束逻辑的位置,确保广播和通道关闭正确执行
- 更新了部分日志提示信息,使其更符合实际业务含义
- 移除了部分无用代码和注释,提高
This commit is contained in:
2025-11-11 01:10:26 +08:00
parent 996e342459
commit da9286d3d8
18 changed files with 150 additions and 99 deletions

View File

@@ -8,11 +8,11 @@ import (
"github.com/antlabs/cronex" "github.com/antlabs/cronex"
"github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/os/glog"
"github.com/xiaoqidun/limit"
sensitive "github.com/zmexing/go-sensitive-word" sensitive "github.com/zmexing/go-sensitive-word"
) )
var Limiter = limit.New() // ar Limiter = ratelimit.New(100) // per second
// var Limiter = limit.New()
var ctx = context.TODO() var ctx = context.TODO()
var ( var (

View File

@@ -10,12 +10,14 @@ require github.com/antchfx/xmlquery v1.4.4
require ( require (
github.com/antchfx/xpath v1.3.3 // indirect github.com/antchfx/xpath v1.3.3 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/butoften/array v1.0.9 // indirect github.com/butoften/array v1.0.9 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/stretchr/testify v1.11.1 // indirect github.com/stretchr/testify v1.11.1 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect
) )
require ( require (

View File

@@ -8,6 +8,8 @@ github.com/antchfx/xmlquery v1.4.4/go.mod h1:AEPEEPYE9GnA2mj5Ur2L5Q5/2PycJ0N9Fus
github.com/antchfx/xpath v1.3.3 h1:tmuPQa1Uye0Ym1Zn65vxPgfltWb/Lxu2jeqIGteJSRs= github.com/antchfx/xpath v1.3.3 h1:tmuPQa1Uye0Ym1Zn65vxPgfltWb/Lxu2jeqIGteJSRs=
github.com/antchfx/xpath v1.3.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.3.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/butoften/array v1.0.9 h1:/kPHAc+fHz72u5B23p2W1RzIoT2eOYvhsY0tKMvsHEc= github.com/butoften/array v1.0.9 h1:/kPHAc+fHz72u5B23p2W1RzIoT2eOYvhsY0tKMvsHEc=
github.com/butoften/array v1.0.9/go.mod h1:RgJ3XIUy/Z2rQllTkXmS4LtfqJeD3mjYJ4XoP3odTqM= github.com/butoften/array v1.0.9/go.mod h1:RgJ3XIUy/Z2rQllTkXmS4LtfqJeD3mjYJ4XoP3odTqM=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
@@ -86,6 +88,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0=
go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
@@ -187,3 +191,4 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -59,7 +59,7 @@ func init() { //默认初始化扫描
for _, func_cmd := range getcmd(methodValue.Type().In(0)) { for _, func_cmd := range getcmd(methodValue.Type().In(0)) {
if func_cmd == 0 { //说明不是注册方法 if func_cmd == 0 { //说明不是注册方法
glog.Warning(context.Background(), "方法参数必须是结构体", method.Name, "跳过注册") glog.Warning(context.Background(), "方法参数必须包含CMD参数", method.Name, "跳过注册")
continue continue
} }

View File

@@ -26,7 +26,7 @@ func (e *Effect20) OnSkill(input.Ctx) bool {
return true return true
} }
t := input.Geteffect(input.EffectType.Status, int(info.PetStatus.Tired)) t := input.Geteffect(input.EffectType.Status, int(info.PetStatus.Tired))
t.Duration(e.SideEffectArgs[1] + 1) t.Duration(e.SideEffectArgs[1])
e.Input.AddEffect(t) e.Input.AddEffect(t)
return true return true
} }

View File

@@ -4,6 +4,7 @@ import (
"blazing/logic/service/fight/info" "blazing/logic/service/fight/info"
"blazing/logic/service/fight/input" "blazing/logic/service/fight/input"
"blazing/logic/service/fight/node" "blazing/logic/service/fight/node"
"fmt"
"sync" "sync"
"github.com/shopspring/decimal" "github.com/shopspring/decimal"
@@ -30,7 +31,7 @@ type Effect62_sub struct {
// 这个实际上在对方回合执行的 // 这个实际上在对方回合执行的
func (e *Effect62_sub) OnSkill(ctx input.Ctx) bool { func (e *Effect62_sub) OnSkill(ctx input.Ctx) bool {
defer e.Alive(false) //defer e.Alive(false)
if e.bindpet == e.bind.CurrentPet { //说明对方没有切换精灵 if e.bindpet == e.bind.CurrentPet { //说明对方没有切换精灵
//直接扣除所有血量OnSkill //直接扣除所有血量OnSkill
@@ -50,46 +51,55 @@ func init() {
} }
func (e *Effect62) Turn_Start(ctx input.Ctx) { func (e *Effect62) Turn_Start(ctx input.Ctx) {
if ctx.Player != e.Input.Player { //如果对面还是我方放技能时候的玩家
return // if ctx.Player != e.opp.Player {
} // return
// }
if e.Duration() != 1 { //说明还没到生效节点 fmt.Println(e.Duration(), "镇魂歌剩余回合")
if e.Duration() != 0 { //说明还没到生效节点
e.Hide = true //隐藏效果 e.Hide = true //隐藏效果
} else { } else {
e.Hide = false
}
if !e.Hide { //说明是自身回合//如果还在隐藏,就直接返回
//t.Duration(e.SideEffectArgs[0])
e.opp.AddEffect(e.e) e.opp.AddEffect(e.e)
//defer e.EffectNode.NotALive() //失效
//应该是对方固定伤害等于自身血量
//e.Input.Death() //本只死亡
//否则触发秒杀 在对面使用技能后
//return true
} }
// if !e.Hide { //说明是自身回合//如果还在隐藏,就直接返回
// //t.Duration(e.SideEffectArgs[0])
// e.opp.AddEffect(e.e)
// //defer e.EffectNode.NotALive() //失效
// //应该是对方固定伤害等于自身血量
// //e.Input.Death() //本只死亡
// //否则触发秒杀 在对面使用技能后
// //return true
// }
} }
func (e *Effect62) OnSkill(ctx input.Ctx) bool { func (e *Effect62) OnSkill(ctx input.Ctx) bool {
if !e.Hit() { if !e.Hit() {
//e.Alive(false) //e.Alive(false)
return true return true
} }
e.l.Do(func() {
e.opp = ctx.Input //e.Duration(1) //必须保持到下一回合,这样才会被复制
e.e = &Effect62_sub{ e.opp = ctx.Input
EffectNode: node.EffectNode{}, e.e = &Effect62_sub{
bindpet: ctx.CurrentPet, EffectNode: node.EffectNode{},
bind: ctx.Input, bindpet: ctx.CurrentPet,
} bind: ctx.Input,
e.e.ID(e.ID() + int(input.EffectType.Sub)) //子效果ID }
e.e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0]) e.e.ID(e.ID() + int(input.EffectType.Sub)) //子效果ID
//给对方添加我方施加的buff //e.e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0])
e.e.SetArgs(e.Input, e.SideEffectArgs...) //给对方添加我方施加的buff
e.e.SetArgs(e.Input, e.SideEffectArgs...)
})
return true return true
} }
func (e *Effect62) SetArgs(t *input.Input, a ...int) {
e.EffectNode.SetArgs(t, a...)
e.EffectNode.Duration(e.EffectNode.SideEffectArgs[0])
}
// // 因为对方切精灵,这个效果也要无效掉 // // 因为对方切精灵,这个效果也要无效掉
// func (this *Effect62) OnSwitchIn(input.Ctx) bool { // func (this *Effect62) OnSwitchIn(input.Ctx) bool {

View File

@@ -277,8 +277,8 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *info.Ski
// 记录技能信息 // 记录技能信息
attacker.SkillID = uint32(a.ID) //获取技能ID attacker.SkillID = uint32(a.ID) //获取技能ID
if attacker.AttackTime > 0 { //如果命中 if attacker.AttackTime > 0 { //如果命中
attacker.AddEffects(attacker.EffectCache...) //命中再添加效果
attacker.CalculateCrit(defender, a) //暴击计算 attacker.CalculateCrit(defender, a) //暴击计算
attacker.AttackValue.IsCritical = a.Crit attacker.AttackValue.IsCritical = a.Crit
attacker.DamageZone.Damage = attacker.CalculatePower(defender, a) attacker.DamageZone.Damage = attacker.CalculatePower(defender, a)
@@ -299,6 +299,7 @@ func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *info.Ski
} }
} }
attacker.AddEffects(attacker.EffectCache...) //命中再添加效果
for _, e := range attacker.EffectCache { for _, e := range attacker.EffectCache {
//这里实现应该参考本地技能是否命中,然后 //这里实现应该参考本地技能是否命中,然后
e.Hit(a.AttackTime != 0) //我方效果命中 e.Hit(a.AttackTime != 0) //我方效果命中
@@ -421,13 +422,13 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
f.First.ExecCace(func(t input.Effect) bool { //回合开始前 f.First.ExecCace(func(t input.Effect) bool { //回合开始前
//结算状态 //结算状态
t.Turn_Start(input.Ctx{Input: f.First}) t.Turn_Start(input.Ctx{Input: f.Second})
return true return true
}) })
f.Second.ExecCace(func(t input.Effect) bool { //回合开始前 f.Second.ExecCace(func(t input.Effect) bool { //回合开始前
//结算状态 //结算状态
t.Turn_Start(input.Ctx{Input: f.Second}) t.Turn_Start(input.Ctx{Input: f.First})
return true return true
}) })
//开始回合操作 //开始回合操作
@@ -446,7 +447,7 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
currentskill = oldskill currentskill = oldskill
//fmt.Println("开始攻击威力", oldskill.Power) // fmt.Println("开始攻击威力", oldskill.Power)
canuseskill := true canuseskill := true
// 实际上攻击方 还有系统选择放弃出手的 // 实际上攻击方 还有系统选择放弃出手的
if !action.CanUse(currentskill) || attacker.CurrentPet.Info.Hp <= 0 { if !action.CanUse(currentskill) || attacker.CurrentPet.Info.Hp <= 0 {
@@ -520,7 +521,7 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
f.FightOverInfo.WinnerId = defender.UserID f.FightOverInfo.WinnerId = defender.UserID
f.closefight = true f.closefight = true
break // break
} }
} }
@@ -567,8 +568,7 @@ func (f *FightC) enterturn(fattack, sattack *action.SelectSkillAction) {
f.First.GenInfo() f.First.GenInfo()
f.Second.GenInfo() f.Second.GenInfo()
// ret.FAttack = *f.First.AttackValue
// ret.SAttack = *f.Second.AttackValue
ret := info.AttackValueS{ ret := info.AttackValueS{
FAttack: *f.First.AttackValue, FAttack: *f.First.AttackValue,

View File

@@ -151,6 +151,7 @@ func getSkillName(move *SkillEntity) string {
// 计算是否命中 // 计算是否命中
func (s *SkillEntity) AttackTimeC(level int) { func (s *SkillEntity) AttackTimeC(level int) {
s.AttackTime = 0 //先重置上一次的 s.AttackTime = 0 //先重置上一次的
if s.MustHit != 0 { if s.MustHit != 0 {
s.AttackTime = 2 s.AttackTime = 2
} }

View File

@@ -128,12 +128,11 @@ func (c *Input) AddEffects(e ...Effect) {
for _, v := range e { for _, v := range e {
// v.Alive()
c.AddEffect(v) c.AddEffect(v)
} }
} }
func (c *Input) AddEffect(e Effect) { func (c *Input) AddEffect(e Effect) {
e.Alive(true) //添加后默认激活 e.Alive(true) //添加后默认激活
@@ -142,11 +141,14 @@ func (c *Input) AddEffect(e Effect) {
fmt.Println("产生回合数", e.ID(), e.Duration()) fmt.Println("产生回合数", e.ID(), e.Duration())
// 如果已有同 ID 的效果,尝试叠加 // 如果已有同 ID 的效果,尝试叠加
for _, v := range c.Effects { for _, v := range c.Effects {
if v == e {
return //完全相同,跳过执行
}
//如果效果相同,id相同,参数相同,就是同一个,确认是否可以叠加,正常来说本身就可以共存 //如果效果相同,id相同,参数相同,就是同一个,确认是否可以叠加,正常来说本身就可以共存
//衰弱本身参数也是相同的,区别只是传入的回合数不一样和层数不一样 //衰弱本身参数也是相同的,区别只是传入的回合数不一样和层数不一样
if e.ID() != 0 && v.ID() == e.ID() && if v.ID() == e.ID() && //找到相同的效果id
v.Alive() && v.Alive() && //如果之前的效果还存活
equalInts(v.GetArgs(), e.GetArgs()) { //如果层数可以叠加或者是无限层数 equalInts(v.GetArgs(), e.GetArgs()) { //如果层数可以叠加或者是无限层数
if v.MaxStack() == 0 { if v.MaxStack() == 0 {
v.Alive(false) //不允许叠层,取消效果 v.Alive(false) //不允许叠层,取消效果
@@ -196,12 +198,9 @@ func (c *Input) Exec(fn func(Effect) bool) bool {
func (c *Input) ExecCace(fn func(Effect) bool) bool { func (c *Input) ExecCace(fn func(Effect) bool) bool {
result := true result := true
for _, value := range c.EffectCache { for _, value := range c.EffectCache {
if value.Alive() {
if !fn(value) { //存在false,但是仍然要向下执行
result = false //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
}
if !fn(value) { //存在false,但是仍然要向下执行
result = false //如果是false,说明存在阻止向下执行的effect比如免疫能力提升效果
} }
} }

View File

@@ -11,16 +11,19 @@ import (
) )
type Input struct { type Input struct {
CanChange bool //是否可以死亡切换CanChange CanChange bool //是否可以死亡切换CanChange
CurrentPet *info.BattlePetEntity //当前精灵 CurrentPet *info.BattlePetEntity //当前精灵
AllPet []*info.BattlePetEntity AllPet []*info.BattlePetEntity
Player common.PlayerI Player common.PlayerI
EffectCache []Effect
Finished bool //是否加载完成 Finished bool //是否加载完成
*info.AttackValue *info.AttackValue
FightC common.FightI FightC common.FightI
// info.BattleActionI // info.BattleActionI
Effects []Effect //effects 实际上全局就是effect无限回合 //effects容器 技能的 Effects []Effect //effects 实际上全局就是effect无限回合 //effects容器 技能的
EffectCache []Effect //这里是命中前执行的容器,也就是命中前执行的所有逻辑相关,理论上一个effect被激活,就应该同时将其他的effect取消激活
//NewEffects []Effect
DamageZone struct { DamageZone struct {
Damage decimal.Decimal //伤害 Damage decimal.Decimal //伤害
BeforeADD decimal.Decimal //攻击伤害 BeforeADD decimal.Decimal //攻击伤害
@@ -60,15 +63,11 @@ func (input *Input) GenSataus() {
t := input.GetEffect(EffectType.Status, i) t := input.GetEffect(EffectType.Status, i)
if t != nil { //状态都是叠层类的 if t != nil && t.Alive() { //状态都是叠层类的
input.AttackValue.Status[i] = int8(t.Duration()) input.Status[i] = int8(t.Duration())
} }
// t = f.Second.GetEffect(input.EffectType.Status, i)
// if t != nil {
// ret.SAttack.Status[i] = int8(t.Duration()) + 1
// }
} }
@@ -87,6 +86,8 @@ func (input *Input) GenInfo() {
func (i *Input) ResetAttackValue() { func (i *Input) ResetAttackValue() {
i.AttackValue.SkillID = 0 i.AttackValue.SkillID = 0
i.AttackValue.IsCritical = 0 i.AttackValue.IsCritical = 0
i.AttackValue.GainHp = 0
i.AttackValue.LostHp = 0
} }
@@ -135,9 +136,26 @@ func (i *Input) GetStatusBonus() float64 {
return maxBonus return maxBonus
} }
func (i *Input) initeffectcache() {
i.EffectCache = make([]Effect, 0) //先把上一回合数据清空,但是应该把本身延续类效果集成过来
for _, v := range i.Effects {
if v.Alive() { //说明存活效果而且是延续类效果,将之添加到初始化列表中
//这里添加的效果是已经生效的效果对effect的复制,相当于技能施的效果的前置比如改命中的效果等
i.EffectCache = append(i.EffectCache, v)
}
}
}
// 解析并 施加effect // 解析并 施加effect
func (i *Input) Parseskill(defender *Input, skill *action.SelectSkillAction) { func (i *Input) Parseskill(defender *Input, skill *action.SelectSkillAction) {
i.EffectCache = make([]Effect, 0) //先把上一回合数据清空 i.initeffectcache() //这里说明是延续的效果,每次复制出来一个新的就好了
//i.NewEffects = make([]Effect, 0) //这里说明是新增的效果
temparg := skill.SideEffectArgS temparg := skill.SideEffectArgS
for _, v := range skill.SideEffectS { for _, v := range skill.SideEffectS {
@@ -159,8 +177,9 @@ func (i *Input) Parseskill(defender *Input, skill *action.SelectSkillAction) {
//i.AddEffect(t) //i.AddEffect(t)
// } // }
//这里是临时缓存buff,后面确认命中后修改HIT状态 //这里是临时缓存buff,后面确认命中后修改HIT状态
// t.Alive() //先让效果保持存活
i.EffectCache = append(i.EffectCache, t) i.EffectCache = append(i.EffectCache, t)
// i.NewEffects = append(i.NewEffects, t)
} }
temparg = temparg[args:] temparg = temparg[args:]

View File

@@ -15,16 +15,7 @@ import (
) )
func (f *FightC) battleLoop() { func (f *FightC) battleLoop() {
defer func() {
f.Broadcast(func(ff *input.Input) {
//todo 将血量和技能pp传回enterturn
//<-time.After(10000)
ff.Player.SendFightEndInfo(f.FightOverInfo)
})
close(f.actionChan)
close(f.overchan)
}()
f.actionChan = make(chan action.BattleActionI, 2) f.actionChan = make(chan action.BattleActionI, 2)
fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime) fmt.Println("战斗开始精灵", f.Our.Player.GetInfo().PetList[0].CatchTime)
@@ -32,10 +23,6 @@ func (f *FightC) battleLoop() {
oppID := f.Opp.Player.GetInfo().UserID oppID := f.Opp.Player.GetInfo().UserID
for { for {
if f.closefight {
break
}
f.Round++ f.Round++
fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round) fmt.Printf("—— 第 %d 回合开始 ——\n", f.Round)
@@ -43,9 +30,21 @@ func (f *FightC) battleLoop() {
actions := f.collectPlayerActions(ourID, oppID) actions := f.collectPlayerActions(ourID, oppID)
f.resolveRound(actions[ourID], actions[oppID]) f.resolveRound(actions[ourID], actions[oppID])
if f.closefight {
break
}
} }
fmt.Println("战斗循环结束") fmt.Println("战斗循环结束")
f.Broadcast(func(ff *input.Input) {
//todo 将血量和技能pp传回enterturn
ff.Player.SendFightEndInfo(f.FightOverInfo)
})
close(f.actionChan)
close(f.overchan)
} }
// 收集玩家动作(含超时判定) // 收集玩家动作(含超时判定)

View File

@@ -15,12 +15,18 @@ func (e *EffectNode) Turn_Start(ctx input.Ctx) {
func (e *EffectNode) Turn_End(ctx input.Ctx) { func (e *EffectNode) Turn_End(ctx input.Ctx) {
if e.duration == 0 { // 保留 (负数表示永久) if e.duration == 0 { // 保留 (负数表示永久)
if ctx.Input.FightC.IsFirst(ctx.Input.Player) { //如果对方先手
e.duration++
return
}
e.Alive(false) e.Alive(false)
} else { } else {
e.trunl.Do(func() {
if ctx.Input.FightC.IsFirst(ctx.Input.Player) { //如果对方先手
e.duration++
// e.Alive(true)
}
})
e.duration-- e.duration--
} }

View File

@@ -3,6 +3,7 @@ package node
import ( import (
"blazing/logic/service/fight/info" "blazing/logic/service/fight/info"
"blazing/logic/service/fight/input" "blazing/logic/service/fight/input"
"sync"
) )
// 检查,激活,延后 // 检查,激活,延后
@@ -21,7 +22,7 @@ type EffectNode struct {
Flag int //过滤掉的战斗类型 pvp pve boss战斗,野怪全部生效 Flag int //过滤掉的战斗类型 pvp pve boss战斗,野怪全部生效
alive bool // 是否失效 effect返回值是否被取消是否被删除 alive bool // 是否失效 effect返回值是否被取消是否被删除
hit bool hit bool
trunl sync.Once
//增加owner target如果owner target都为自身就回合效果结束后再使用回合效果 //增加owner target如果owner target都为自身就回合效果结束后再使用回合效果
} }

View File

@@ -1,14 +1,12 @@
package maps package maps
import ( import (
"blazing/cool"
"blazing/logic/service/common" "blazing/logic/service/common"
"blazing/logic/service/player" "blazing/logic/service/player"
"blazing/logic/service/space" "blazing/logic/service/space"
"blazing/modules/blazing/model" "blazing/modules/blazing/model"
"github.com/gogf/gf/v2/util/gconv"
"golang.org/x/time/rate" "golang.org/x/time/rate"
) )
@@ -24,10 +22,12 @@ type WalkInInfo struct {
Path string Path string
} }
func (t *WalkInInfo) Broadcast(mapid uint32, o WalkOutInfo) { var limiter = rate.NewLimiter(rate.Limit(10), 1)
r := cool.Limiter.Get("Broadcast"+gconv.String(mapid), rate.Limit(10), 5) func (t *WalkInInfo) Broadcast(mapid uint32, o WalkOutInfo) {
if !r.Allow() { // cool.Limiter.Take()
//r := cool.Limiter.Get("Broadcast"+gconv.String(mapid), rate.Limit(10), 5)
if !limiter.Allow() {
return return
} }
space.GetSpace(mapid).User.IterCb(func(playerID uint32, player common.PlayerI) { space.GetSpace(mapid).User.IterCb(func(playerID uint32, player common.PlayerI) {

View File

@@ -1,9 +1,12 @@
package pet package pet
import "blazing/logic/service/player"
// PetBargeListInboundInfo 对应Java的PetBargeListInboundInfo实现InboundMessage接口 // PetBargeListInboundInfo 对应Java的PetBargeListInboundInfo实现InboundMessage接口
type PetBargeListInboundInfo struct { type PetBargeListInboundInfo struct {
StartPetId uint32 `description:"开始精灵id" codec:"startPetId"` // @UInt long 对应Go的uint32无符号64位 Head player.TomeeHeader `cmd:"2309" struc:"[0]pad"`
EndPetId uint32 `description:"结束精灵id" codec:"endPetId"` // 字段标签模拟注解功能(描述、编解码标识 StartPetId uint32 `description:"开始精灵id" codec:"startPetId"` // @UInt long 对应Go的uint32无符号64位
EndPetId uint32 `description:"结束精灵id" codec:"endPetId"` // 字段标签模拟注解功能(描述、编解码标识)
} }
// PetBargeListInfo 对应Java的PetBargeListInfo类 // PetBargeListInfo 对应Java的PetBargeListInfo类

View File

@@ -1,6 +1,8 @@
package player package player
import "blazing/logic/service/fight/info" import (
"blazing/logic/service/fight/info"
)
func (p *Player) SendAttackValue(b info.AttackValueS) { func (p *Player) SendAttackValue(b info.AttackValueS) {
t1 := NewTomeeHeader(2505, p.Info.UserID) t1 := NewTomeeHeader(2505, p.Info.UserID)
@@ -26,6 +28,7 @@ func (p *Player) SendNoteReadyToFightInfo(b info.NoteReadyToFightInfo) {
} }
func (p *Player) SendFightEndInfo(b info.FightOverInfo) { func (p *Player) SendFightEndInfo(b info.FightOverInfo) {
p.FightC = nil p.FightC = nil
//<-time.After(10000)
t1 := NewTomeeHeader(2506, p.Info.UserID) t1 := NewTomeeHeader(2506, p.Info.UserID)
p.SendPack(t1.Pack(&b)) p.SendPack(t1.Pack(&b))

View File

@@ -52,15 +52,16 @@ func beforeServeHook(r *ghttp.Request) {
r.Response.CORSDefault() r.Response.CORSDefault()
} }
var limiter = rate.NewLimiter(rate.Limit(10), 1)
// Limiter is a middleware that implements rate limiting for all HTTP requests. // Limiter is a middleware that implements rate limiting for all HTTP requests.
// It returns HTTP 429 (Too Many Requests) when the rate limit is exceeded. // It returns HTTP 429 (Too Many Requests) when the rate limit is exceeded.
func Limiter(r *ghttp.Request) { func Limiter(r *ghttp.Request) {
// 3. 为任意键 "some-key" 获取一个速率限制器 // 3. 为任意键 "some-key" 获取一个速率限制器
// - rate.Limit(2): 表示速率为 "每秒2个请求" // - rate.Limit(2): 表示速率为 "每秒2个请求"
// - 2: 表示桶的容量 (Burst)允许瞬时处理2个请求 // - 2: 表示桶的容量 (Burst)允许瞬时处理2个请求
rateLimiter := cool.Limiter.Get(r.GetClientIp(), rate.Limit(10), 5)
if !rateLimiter.Allow() { if !limiter.Allow() {
r.Response.WriteStatusExit(429) // Return 429 Too Many Requests r.Response.WriteStatusExit(429) // Return 429 Too Many Requests
r.ExitAll() r.ExitAll()
} }

View File

@@ -18,8 +18,8 @@ import (
"github.com/gogf/gf/v2/crypto/gmd5" "github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
"github.com/hashicorp/golang-lru/v2/expirable"
) )
func (s *BaseSysLoginService) Reg(ctx context.Context, req *v1.BaseOpenLoginReq) (result *TokenResult, err error) { func (s *BaseSysLoginService) Reg(ctx context.Context, req *v1.BaseOpenLoginReq) (result *TokenResult, err error) {
@@ -56,7 +56,7 @@ func (s *BaseSysLoginService) Reg(ctx context.Context, req *v1.BaseOpenLoginReq)
// 10分钟内限制请求 ip->次数 邮箱->次数 // 10分钟内限制请求 ip->次数 邮箱->次数
// 验证码对redis传,防止被重复操作 // 验证码对redis传,防止被重复操作
// var emailcache = expirable.NewLRU[string, int](0, nil, time.Millisecond*10) // var emailcache = expirable.NewLRU[string, int](0, nil, time.Millisecond*10)
var ipcache = expirable.NewLRU[string, int](0, nil, time.Millisecond*10) var ipcache = gcache.New(1000)
func (s *BaseSysLoginService) Email(ctx context.Context, req *v1.BaseOpenEmailReq) (result *TokenResult, err error) { func (s *BaseSysLoginService) Email(ctx context.Context, req *v1.BaseOpenEmailReq) (result *TokenResult, err error) {
var ( var (
@@ -65,14 +65,16 @@ func (s *BaseSysLoginService) Email(ctx context.Context, req *v1.BaseOpenEmailRe
) )
ip := r.GetClientIp() ip := r.GetClientIp()
v, ok := ipcache.Get(ip) v, ok2 := ipcache.Get(context.Background(), ip)
if ok && v > 3 {
if ok2 != nil || v.Int() > 3 {
return nil, gerror.New("操作过于频繁,请稍后再试") return nil, gerror.New("操作过于频繁,请稍后再试")
} }
ok, _ = share.ShareManager.EmailCodeExists(req.Email) ipcache.Set(context.Background(), ip, v.Int()+1, 10*time.Minute)
if ok { ok1, _ := share.ShareManager.EmailCodeExists(req.Email)
if ok1 {
return nil, gerror.New("注册码已下发 ") return nil, gerror.New("注册码已下发 ")
} }
// 发送验证码 // 发送验证码