Files
bl/modules/player/model/pvp.go
昔念 e161e3626f
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
```
fix(fight): 修复单输入战斗中效果处理逻辑错误

- 在Effect201的OnSkill方法中调整了多输入战斗检查的位置,
  确保单输入战斗中的单目标效果被正确忽略

- 添加了针对单输入战斗中单目标效果的测试用例

- 移除了重复的多输入战斗检查代码

feat(fight): 添加战斗初始化时捕获标识
2026-04-13 09:59:09 +08:00

195 lines
7.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package model
import (
"blazing/common/data"
"blazing/cool"
"github.com/gogf/gf/v2/os/gtime"
"github.com/tnnmigga/enum"
)
// 表名常量
const TableNamePlayerPVP = "player_pvp"
const Curpvp = 0
// PVP 对应数据库表 player_pvp用于记录用户PVP赛季数据及场次统计
type PVP struct {
*cool.Model
PlayerID uint32 `gorm:"not null;index:idx_pvp_player_id;comment:'所属玩家ID'" json:"player_id"`
//第几赛季
Season uint32 `gorm:"not null;default:0;comment:'赛季R'" json:"season"`
//本赛季排名信息'通过下标来确认当前赛季
RankInfo PVPRankInfo `gorm:"type:jsonb;not null;comment:'赛季核心数据'" json:"rank_info"`
}
func (u *PVP) Less(than *PVP) bool {
if u.RankInfo.Score == than.RankInfo.Score {
return u.RankInfo.LastMatchTime.Unix() < than.RankInfo.LastMatchTime.Unix()
}
return u.RankInfo.Score < than.RankInfo.Score
}
func NewPVP() *PVP {
return &PVP{
Model: cool.NewModel(),
}
}
// PVPSeasonData PVP赛季核心统计数据
// 聚合维度:总场次、胜负、积分、胜率等
type PVPRankInfo struct {
Rank uint32 `json:"rank"` // 本赛季全服排名0=未上榜)
Score int `json:"score"` // 当前积分
WinStreak uint32 `json:"win_streak"` // 当前连胜场次
TotalMatch uint32 `json:"total_match"` // 本赛季总场次
WinMatch uint32 `json:"win_match"` // 本赛季胜利场次
LoseMatch uint32 `json:"lose_match"` // 本赛季失败场次
DrawMatch uint32 `json:"draw_match"` // 本赛季平局场次
TotalScore int32 `json:"total_score"` // 本赛季总积分(胜加负减)
HighestScore int32 `json:"highest_score"` // 本赛季最高积分
ContinuousWin uint32 `json:"continuous_win"` // 本赛季最高连胜次数
LastMatchTime *gtime.Time `json:"last_match_time"` // 最后一场PVP时间时间戳
}
// func (u *PVPRankInfo) Key() uint32 {
// return uint32(u.ID)
// }
// 如果分数不对的话,就按时间排序
// NoteReadyToFightInfo 战斗准备就绪消息结构体NoteReadyToFightInfo
type NoteReadyToFightInfo struct {
//MAXPET uint32 `struc:"skip"` // 最大精灵数 struc:"skip"`
// 战斗类型ID与野怪战斗为3与人战斗为1前端似乎未使用
Status uint32 `fieldDesc:"战斗类型ID 但前端好像没有用到 与野怪战斗为3与人战斗似乎是1" `
//Mode uint32 `struc:"skip"`
// 我方信息
OurInfo FightUserInfo `fieldDesc:"我方信息" serialize:"struct"`
// Our *socket.Player `struc:"skip"`
OurPetListLen uint32 `struc:"sizeof=OurPetList"`
// 我方携带精灵的信息
// ArrayList<ReadyFightPetInfo>,使用切片模拟动态列表
OurPetList []ReadyFightPetInfo `fieldDesc:"我方携带精灵的信息" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
// 对方信息
OpponentInfo FightUserInfo `fieldDesc:"对方信息" serialize:"struct"`
//Opp *socket.Player `struc:"skip"`
OpponentPetListLen uint32 `struc:"sizeof=OpponentPetList"`
// 敌方的精灵信息
// 野怪战斗时客户端接收此包前已生成精灵PetInfo将部分信息写入该列表
OpponentPetList []ReadyFightPetInfo `fieldDesc:"敌方的精灵信息 如果是野怪 那么再给客户端发送这个包体时就提前生成好了这只精灵的PetInfo,然后把从PetInfo中把部分信息写入到这个敌方的精灵信息中再发送这个包结构体" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
}
type FightUserInfo struct {
// 用户ID野怪为0
UserID uint32 `fieldDesc:"userID 如果为野怪则为0" `
// 玩家名称野怪为UTF-8的'-'固定16字节
// 使用[16]byte存储固定长度的字节数组
Nick string `struc:"[16]byte"`
}
// ReadyFightPetInfo 准备战斗的精灵信息结构体ReadyFightPetInfo类
type ReadyFightPetInfo struct {
// 精灵ID
ID uint32 `fieldDesc:"精灵ID" `
// 精灵等级
Level uint32 `fieldDesc:"精灵等级" `
// 精灵当前HP
Hp uint32 `fieldDesc:"精灵HP" `
// 精灵最大HP
MaxHp uint32 `fieldDesc:"最大HP" `
SkillListLen uint32 `struc:"sizeof=SkillList"`
// 技能信息列表固定4个元素技能ID和剩余PP无技能则为0
// List<SkillInfo>初始化容量为4
SkillList []SkillInfo `fieldDesc:"技能信息 技能ID跟剩余PP 固定32字节 没有给0" serialize:"fixedLength=4,type=structArray"`
// 精灵捕获时间
CatchTime uint32 `fieldDesc:"精灵捕获时间" `
// 捕捉地图固定给0
CatchMap uint32 `fieldDesc:"捕捉地图 给0" `
// 固定给0
CatchRect uint32 `fieldDesc:"给0" `
// 固定给0
CatchLevel uint32 `fieldDesc:"给0" `
SkinID uint32 `fieldDesc:"精灵皮肤ID" `
ShinyLen uint32 `json:"-" struc:"sizeof=ShinyInfo"`
ShinyInfo []data.GlowFilter `json:"ShinyInfo,omitempty"`
IsCapture uint32 `json:"isCapture"`
}
type FightOverInfo struct {
//0 正常结束
//1=isPlayerLost 对方玩家退出
// 2=isOvertime 超时
// 3=isDraw 双方平手
// 4=isSysError 系统错误
// 5=isNpcEscape 精灵主动逃跑
Winpet *PetInfo `struc:"skip"`
Round uint32 `struc:"skip"`
// Duration uint32 `struc:"skip"` // 对局时长(秒)
//7 切磋结束
Reason EnumBattleOverReason // 固定值0
WinnerId uint32 // 胜者的米米号 野怪为0
TwoTimes uint32 // 双倍经验剩余次数
ThreeTimes uint32 // 三倍经验剩余次数
AutoFightTimes uint32 // 自动战斗剩余次数
EnergyTime uint32 // 能量吸收器剩余次数
LearnTimes uint32 // 双倍学习器剩余次数
}
// 战斗结束原因枚举
type EnumBattleOverReason int
var BattleOverReason = enum.New[struct {
PlayerOffline EnumBattleOverReason `enum:"1"` //掉线
PlayerOVerTime EnumBattleOverReason `enum:"2"` //超时
NOTwind EnumBattleOverReason `enum:"3"` //打成平手
DefaultEnd EnumBattleOverReason `enum:"4"` //默认结束
PlayerEscape EnumBattleOverReason `enum:"5"` //逃跑
Cacthok EnumBattleOverReason `enum:"6"`
}]()
// AttackValue 战斗中的攻击数值信息
type AttackValue struct {
UserID uint32 `json:"userId" fieldDescription:"玩家的米米号 与野怪对战userid = 0"`
ActorIndex uint32 `json:"actorIndex" fieldDescription:"当前动作所属战斗位"`
TargetIndex uint32 `json:"targetIndex" fieldDescription:"当前动作目标战斗位"`
SkillID uint32 `json:"skillId" fieldDescription:"使用技能的id"`
AttackTime uint32 `json:"attackTime" fieldDescription:"是否击中 如果为0 则miss 如果为1 则击中,2为必中"`
LostHp uint32 `json:"lostHp" fieldDescription:"我方造成的伤害"`
GainHp int32 `json:"gainHp" fieldDescription:"我方获得血量"`
RemainHp int32 `json:"remainHp" fieldDescription:"我方剩余血量"`
MaxHp uint32 `json:"maxHp" fieldDescription:"我方最大血量"`
//颜色
State uint32 `json:"state" `
SkillListLen uint32 `struc:"sizeof=SkillList"`
SkillList []SkillInfo `json:"skillList" fieldDescription:"根据精灵的数据插入技能 最多4条 不定长"`
IsCritical uint32 `json:"isCritical" fieldDescription:"是否暴击"`
Status [20]int8 //精灵的状态
// 攻击,防御,特供,特防,速度,命中
Prop [6]int8
Offensive float32
// OwnerMaxShield uint32 `json:"ownerMaxShield" fieldDescription:"我方最大护盾"`
// OwnerCurrentShield uint32 `json:"ownerCurrentShield" fieldDescription:"我方当前护盾"`
}
// TableName 返回表名
func (*PVP) TableName() string {
return TableNamePlayerPVP
}
// GroupName 返回表组名
func (*PVP) GroupName() string {
return "default"
}
// init 程序启动时自动创建表
func init() {
cool.CreateTable(&PVP{})
}