Files
bl/modules/player/model/info.go
昔念 3a13bcc99c ```
feat(game): 实现扭蛋系统批量物品添加功能并优化地图逻辑

- 新增ItemAddBatch方法用于批量添加物品,支持普通道具和特殊道具的分别处理
- 优化扭蛋游戏玩法中的物品添加逻辑,使用新的批量接口提升性能
- 在扭蛋机器人命令中实现完整的物品检查和批量添加流程

refactor(map): 重构地图控制器代码结构并添加注释

- 为EnterMap、LeaveMap、GetMapPlayerList等方法添加中文注释
- 统一地图相关的命名规范,如enter map替换进入地图
- 调整地图玩家列表中BOSS广播命令ID,2021和2022进行对调

refactor(boss): 重构定时BOSS代码并优化注释

- 将原有的中文注释改为英文注释,统一代码风格
- 简化TimeBossRule结构体定义和相关配置
- 优化定时任务注册逻辑,去除冗余的注释和变量

refactor(space): 清理地图空间服务代码注释

- 移除多余的中文注释和说明文字
- 统一代码格式,移除不必要的空行和注释
- 保持原有的天气系统和地图刷怪逻辑不变

fix(role): 修复系统角色权限查询逻辑

- 修改BaseSysRoleService中的查询条件,正确处理管理员权限
- 使用Extend方法替代Where进行复杂的权限判断逻辑
- 确保超级管理员可以访问所有角色,其他用户受限于权限范围

refactor(dict): 添加字典服务批量查询方法

- 新增GetMaxMap方法用于批量获取物品最大持有上限
- 优化数据库查询,减少多次单个查询的开销
- 支持一次请求多个物品的最大数量限制

fix(player): 修复玩家信息保存异常处理

- 将panic方式改为错误日志记录,避免程序崩溃
- 优化Save方法的重试逻辑,统一错误处理方式
- 在本地文件回退时记录详细错误信息

feat(robot): 扩展扭蛋机器人功能

- 添加用户验证和角色创建检查
- 实现批量扭蛋的完整逻辑,支持1-10次抽取
- 集成物品数量检查和批量添加功能
```
2026-04-02 02:33:05 +08:00

241 lines
12 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/cool"
"fmt"
"math"
"github.com/creasty/defaults"
"github.com/gogf/gf/v2/os/gtime"
)
const TableNamePlayerInfo = "player_info"
type Player struct {
*cool.Model
PlayerID uint64 `gorm:"not null;uniqueIndex;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
LastResetTime *gtime.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务
WeekLastResetTime *gtime.Time `struc:"skip" json:"week_last_reset_time"`
Data PlayerInfo `gorm:"type:jsonb;not null;comment:'全部数据'" json:"data"`
}
type Pos struct {
X uint32 `struc:"uint32" default:"0"`
Y uint32 `struc:"uint32" default:"0"`
}
// 计算两个uint32的差值绝对值转为int64避免溢出
func absDiff(a, b uint32) int64 {
return int64(math.Abs(float64(int64(a) - int64(b))))
}
// 判断两个Pos的X和Y差值的绝对值是否均小于50
func bothDiffsLessThan50(pos1, pos2 Pos) bool {
xDiff := absDiff(pos1.X, pos2.X)
yDiff := absDiff(pos1.Y, pos2.Y)
return xDiff < 50 && yDiff < 50
}
func (p Pos) BothLessThan50(t Pos) bool {
return bothDiffsLessThan50(p, t)
}
// PeopleItemInfo 穿戴装备信息结构PeopleItemInfo
type PeopleItemInfo struct {
ID uint32 `struc:"uint32"` // 装备id
Level uint32 `struc:"uint32" default:"1"` // 未知字段默认值1@Builder.Default
}
// InitDefaults 初始化默认值(确保默认值正确赋值)
func (p *PeopleItemInfo) InitDefaults() {
p.Level = 1 // 未知字段默认值1与Java的@Builder.Default保持一致
}
func NewPlayerInfo() PlayerInfo {
l := PlayerInfo{
Clothes: make([]PeopleItemInfo, 0),
PetList: make([]PetInfo, 0),
BackupPetList: make([]PetInfo, 0),
}
// 自动填充 struct tag 里的 default 值
if err := defaults.Set(&l); err != nil {
panic(err) // 方便发现 default 设置错误
}
return l
}
// TaskStatus 任务状态(与 AS3 对应)
type TaskStatus uint8
const (
Unaccepted TaskStatus = 0 // 未接受AS3 中 0 或 2 映射至此)
Accepted TaskStatus = 1 // 已接受
Completed TaskStatus = 3 // 已完成
Reserved TaskStatus = 2 // 预留AS3 中映射为未接受)
)
// 定义检查函数判断84-87索引中是否有任意一个元素不等于3
func (lw *PlayerInfo) IsNewPlayer() bool {
// 遍历84到87的索引
for i := 85; i <= 88; i++ {
if lw.GetTask(i) != Completed {
return true // 只要有一个不等于3就返回true
}
}
return false // 全部等于3则返回false
}
// SetTask 设置第 i 个任务的状态0 ≤ i < 2000
func (m *PlayerInfo) SetTask(i int, status TaskStatus) error {
i-- //下标减1
if i < 0 || i >= 4000 {
return fmt.Errorf("index out of range: %d (must be 0-1999)", i)
}
byteIdx := i / 4
bitOffset := (i % 4) * 2
// 清除原有 2 位
m.TaskList[byteIdx] &^= 0x3 << bitOffset
// 设置新状态(确保只取低 2 位)
m.TaskList[byteIdx] |= byte(status&0x3) << bitOffset
return nil
}
// GetTask 获取第 i 个任务的状态
func (m *PlayerInfo) GetTask(i int) TaskStatus {
i-- //下标减1
if i < 0 || i >= 2000 {
return Reserved
}
byteIdx := i / 4
bitOffset := (i % 4) * 2
return TaskStatus((m.TaskList[byteIdx] >> bitOffset) & 0x3)
}
type PlayerInfo struct {
ExpPool int64 `struc:"skip" json:"exp_pool"` // 累计经验池
OnlineTime int64 `struc:"skip" json:"online_time"` //在线分钟数
//FightTime int64 `struc:"skip" json:"fight_time"` //战斗分钟数
// OutInfo 字段
UserID uint32 `struc:"uint32" json:"user_id"` // 米米号 通过sid拿到
RegisterTime uint32 `struc:"uint32" json:"register_time"` // 注册时间(秒时间戳)
Nick string `struc:"[16]byte" default:"seer" json:"nick"` // 16字节昵称
Title uint32 `struc:"uint32" json:"title"` // 称号
Vip uint16 `struc:"uint16" json:"vip"` // 固定0
Viped uint16 `struc:"uint16" default:"15" json:"viped"` // 固定15
DSFlag uint32 `struc:"uint32" json:"ds_flag"` // 固定0
Color uint32 `struc:"uint32" json:"color"` // 机器人颜色RGB颜色值(uint32,实际为3个uint8)
Texture uint32 `struc:"uint32" json:"texture"` // 固定0
Energy uint32 `struc:"uint32" default:"3000" json:"energy"` // 固定3000
Coins int64 `struc:"uint32" json:"coins"` // 赛尔豆
EVPool int64 `struc:"uint32" json:"ev_pool"` //累计学习力
FightBadge uint32 `struc:"uint32" json:"fight_badge"` // 固定0
MapID uint32 `struc:"uint32" default:"1" json:"map_id"` // 上线地图ID
Pos Pos `json:"pos"` // 坐标
TimeToday int64 `struc:"uint32" default:"0" json:"time_today"` // 已消耗时间(秒)
TimeLimit int64 `struc:"uint32" default:"43200" json:"time_limit"` // 总电池限制(秒)
IsClothHalfDay byte `struc:"byte" json:"is_cloth_half_day"` // 活动标志0/1
IsRoomHalfDay byte `struc:"byte" json:"is_room_half_day"` // 活动标志0/1
IFortressHalfDay byte `struc:"byte" json:"i_fortress_half_day"` // 活动标志0/1
IsHQHalfDay byte `struc:"byte" json:"is_hq_half_day"` // 活动标志0/1
LoginCount uint32 `struc:"uint32" json:"login_count"` // 固定0
Inviter uint32 `struc:"uint32" json:"inviter"` // 固定0
NewInviteeCount uint32 `struc:"uint32" json:"new_invitee_count"` // 固定0
VipLevel uint32 `struc:"uint32" default:"8" json:"vip_level"` // 固定8
VipValue uint32 `struc:"uint32" default:"80000" json:"vip_value"` // 固定80000
VipStage uint32 `struc:"uint32" default:"1" json:"vip_stage"` // 超no的外形等级建议固定1
AutoCharge uint32 `struc:"uint32" default:"1" json:"auto_charge"` // nono是否自动充电
VipEndTime uint32 `struc:"uint32" default:"4294967295" json:"vip_end_time"` // 超no的结束时间建议尽可能大
FreshManBonus uint32 `struc:"uint32" json:"fresh_man_bonus"` // 邀请活动建议先给固定值0
//NonoChipList [80]byte `struc:"[80]byte" json:"-"` // 超no芯片列表
DailyResArr [50]byte `struc:"[50]byte" default:"0" json:"daily_res_arr"` // 每日任务状态 40+是谱尼的
Study struct {
TeacherID uint32 `struc:"uint32" json:"teacher_id"` // 教官id
StudentID uint32 `struc:"uint32" json:"student_id"` // 学员id
GraduationCount uint32 `struc:"uint32" default:"0" json:"graduation_count"` // 毕业人数
}
MaxPuniLv uint32 `struc:"uint32" default:"0" json:"max_puni_lv"` // 默认0, 虚无 元素 能量 生命 轮回 永恒 圣洁 最高为8
PetMaxLevel uint32 `struc:"uint32" json:"pet_max_level"` // 精灵最高等级
AllPetNumber uint32 `struc:"uint32" json:"all_pet_number"` // 精灵数量
MonKingWin uint32 `struc:"uint32" json:"mon_king_win"` // 精灵王胜场
MessWin uint32 `struc:"skip" json:"mess_win"` // 大乱斗胜场
CurrentStage uint32 `struc:"uint32" default:"1" json:"current_stage"` // 勇者之塔层数
MaxStage uint32 `struc:"uint32" json:"max_stage"` // 试炼之塔最高层
CurrentFreshStage uint32 `struc:"uint32" default:"1" json:"current_fresh_stage"` // 当前试炼层数
MaxFreshStage uint32 `struc:"uint32" json:"max_fresh_stage"` // 最高试炼层
MaxArenaWins uint32 `struc:"uint32" json:"max_arena_wins"` // 星际擂台连胜
TwoTimes int32 `struc:"uint32" default:"0" json:"two_times"` // 双倍经验加速器剩余使用次数
ThreeTimes int32 `struc:"uint32" default:"0" json:"three_times"` // 三倍经验加速器剩余使用次数
AutoFight uint32 `struc:"uint32" default:"0" json:"auto_fight"` // 是否自动战斗
AutoFightTime uint32 `struc:"uint32" default:"0" json:"auto_fight_time"` // 自动战斗剩余的场次
EnergyTime int32 `struc:"uint32" default:"0" json:"energy_time"` // 能量吸收仪剩余次数
LearnTimes uint32 `struc:"uint32" default:"0" json:"learn_times"` // 学习力吸收仪剩余次数
MonBattleMedal uint32 `struc:"uint32" default:"0" json:"mon_battle_medal"` // 默认0
RecordCount uint32 `struc:"uint32" default:"0" json:"record_count"` // 默认0
ObtainTm uint32 `struc:"uint32" default:"0" json:"obtain_tm"` // 默认0
SoulBeadItemID uint32 `struc:"uint32" json:"soul_bead_item_id"` // 当前元神珠id
ExpireTm uint32 `struc:"uint32" default:"0" json:"expire_tm"` // 默认0
FuseTimes uint32 `struc:"uint32" default:"0" json:"fuse_times"` // 默认0
NONO struct {
//fieldDescription:"1为跟随 0为收回 且如果为收回 那么后续结构不需要发送, 不序列化"
Flag uint32 `struc:"skip" json:"nono_flag"`
HasNono uint32 `struc:"uint32" default:"1" json:"has_nono"` // 玩家是否有nono
SuperNono uint32 `struc:"uint32" default:"1" json:"super_nono"` // 玩家是否有超能nono
NonoState uint32 `struc:"uint32" default:"4294967295" json:"nono_state"` // 默认-1
NonoColor uint32 `struc:"uint32" json:"nono_color"` // nono颜色
Nick string `struc:"[16]byte" default:"nono" json:"nono_nick"` // nono名字16字节
}
TeamInfo TeamInfo `struc:"struct" json:"team_info"` // 战队信息24字节
TeamPkInfo TeamPKInfo `struc:"struct" json:"team_pk_info"` // 8字节
Reserved byte `struc:"byte" json:"reserved"` // 1字节无内容
Badge uint32 `struc:"uint32" default:"0" json:"badge"` // 默认0
Reserved1 [27]byte `struc:"[27]byte" default:"3" json:"reserved1"` // 27字节默认3
TaskList [1000]byte `struc:"[1000]byte" default:"0" json:"task_list"` // 任务状态数组500字节默认3
PetListCount uint32 `struc:"sizeof=PetList" json:"pet_list_count"` // 精灵列表长度
PetList []PetInfo ` json:"pet_list"` // 精灵背包内信息
BackupPetList []PetInfo `struc:"skip" json:"backup_pet_list"` // 精灵并列备用列表
ClothesCount uint32 `struc:"sizeof=Clothes" json:"clothes_count"` // 穿戴装备数量
Clothes []PeopleItemInfo ` json:"clothes"` // 穿戴装备
}
// trace("个人装扮是否半价:",MainManager.isClothHalfDay);
// trace("小屋装扮是否半价:",MainManager.isRoomHalfDay);
// trace("要塞装扮是否半价:",MainManager.iFortressHalfDay);
// trace("总部装扮是否半价:",MainManager.isHQHalfDay);
//
// TableName PlayerInfo's table name
func (*Player) TableName() string {
return TableNamePlayerInfo
}
// GroupName PlayerInfo's table group
func (*Player) GroupName() string {
return "default"
}
// NewPlayerInfo create a new PlayerInfo
func NewPlayer() *Player {
return &Player{
Model: cool.NewModel(),
}
}
// init 创建表
func init() {
cool.CreateTable(&Player{})
}