feat(cache): 添加复合键缓存操作支持

添加了基于 uint32+string 组合键的缓存操作方法,包括
GetByCompoundKey、SetByCompoundKey、DelByCompoundKey 和
ContainsByCompoundKey 方法,用于处理用户ID和会话ID的组合缓存场景

fix(vscode): 添加 cSpell 配置支持 struc 词汇

refactor(session): 移除过时的会话管理方法

移除了基于单一字符串键的会话管理方法,因为已迁移到使用
复合键的缓存操作方式
```
This commit is contained in:
昔念
2026-01-19 18:51:56 +08:00
parent 08ebf849eb
commit 026689f3ed
120 changed files with 1428 additions and 629 deletions

View File

@@ -0,0 +1,92 @@
package model
import (
"blazing/cool"
"errors"
)
// 表名常量定义:精灵捕捉击杀数量记录表
const (
TableNamePetCatchKillCount = "player_catch_kill_count" // 精灵捕捉击杀数量表(记录每个精灵的捕捉总数量、击杀总数量)
)
// PetBargeListInfo 精灵捕捉击杀数量核心模型(简化版,直接记录数量,摒弃状态判断)
type PetBargeListInfo struct {
Base
PlayerID uint64 `gorm:"not null;index:idx_milestone_by_player_id;comment:'所属玩家ID'" json:"player_id"`
PetId uint32 `gorm:"not null;default:0;comment:'精灵ID关联config_pet_boss表主键'" json:"pet_id" description:"精灵ID"`
EnCntCnt uint32 `gorm:"not null;default:0;comment:'预留未知字段,暂未使用'" json:"en_cnt_cnt" description:"未知"`
CatchedCount uint32 `gorm:"not null;default:0;comment:'精灵捕捉总数量'" json:"catched_count" description:"捕捉数量"` // 替换原IsCatched记录捕捉总数
KilledCount uint32 `gorm:"not null;default:0;comment:'精灵击杀总数量'" json:"killed_count" description:"击杀数量"` // 替换原IsKilled记录击杀总数
}
// -------------------------- 核心配套方法 --------------------------
// TableName 指定模型对应的数据库表名(遵循项目规范)
func (*PetBargeListInfo) TableName() string {
return TableNamePetCatchKillCount
}
// GroupName 指定表所属分组(与其他精灵表保持一致)
func (*PetBargeListInfo) GroupName() string {
return "default"
}
// NewPetBargeListInfo 创建精灵捕捉击杀数量实例(初始化默认值)
func NewPetBargeListInfo() *PetBargeListInfo {
return &PetBargeListInfo{
Base: *NewBase(),
}
}
// AddCatchedCount 增加捕捉数量支持批量累加默认累加1
// addNum要增加的数量需大于0
func (p *PetBargeListInfo) AddCatchedCount(addNum uint32) error {
if addNum <= 0 {
return errors.New("增加的捕捉数量必须大于0")
}
if p.PetId == 0 {
return errors.New("精灵ID不能为空无法累加捕捉数量")
}
p.CatchedCount += addNum
return nil
}
// AddKilledCount 增加击杀数量支持批量累加默认累加1
// addNum要增加的数量需大于0
func (p *PetBargeListInfo) AddKilledCount(addNum uint32) error {
if addNum <= 0 {
return errors.New("增加的击杀数量必须大于0")
}
if p.PetId == 0 {
return errors.New("精灵ID不能为空无法累加击杀数量")
}
p.KilledCount += addNum
return nil
}
// SetCatchedCount 直接设置捕捉数量(适用于批量初始化/重置)
// count目标捕捉数量非负数
func (p *PetBargeListInfo) SetCatchedCount(count uint32) error {
if p.PetId == 0 {
return errors.New("精灵ID不能为空无法设置捕捉数量")
}
p.CatchedCount = count
return nil
}
// SetKilledCount 直接设置击杀数量(适用于批量初始化/重置)
// count目标击杀数量非负数
func (p *PetBargeListInfo) SetKilledCount(count uint32) error {
if p.PetId == 0 {
return errors.New("精灵ID不能为空无法设置击杀数量")
}
p.KilledCount = count
return nil
}
// -------------------------- 表结构自动同步 --------------------------
func init() {
// 程序启动时自动创建/同步精灵捕捉击杀数量表
cool.CreateTable(&PetBargeListInfo{})
}

View File

@@ -0,0 +1,46 @@
package model
import (
"blazing/cool"
)
// 表名常量定义:金豆消费记录表
const (
TableNameGoldBeanConsume = "player_gold_log" // 金豆消费记录表(记录用户金豆消耗的明细、类型、关联业务等信息)
)
// 通过金豆消费时间来确认金豆物品的购买重置周期
// GoldBeanConsume 金豆消费核心模型(与数据库表字段一一对应,存储消费明细)
type GoldBeanConsume struct {
Base
UID uint32 `gorm:"not null;default:0;index;comment:'玩家唯一ID关联玩家表主键'" json:"uid" description:"玩家ID"`
ConsumeNum uint32 `gorm:"not null;default:0;comment:'金豆消费数量(非负数)'" json:"consume_num" description:"消费金豆数量"`
BizID uint32 `gorm:"not null;default:0;comment:'关联业务ID如道具ID/扭蛋池ID无则填0'" json:"biz_id" description:"关联业务ID"`
BeforeBalance uint32 `gorm:"not null;default:0;comment:'消费前金豆余额'" json:"before_balance" description:"消费前余额"`
}
// -------------------------- 核心配套方法 --------------------------
// TableName 指定GoldBeanConsume对应的数据库表名遵循项目规范
func (*GoldBeanConsume) TableName() string {
return TableNameGoldBeanConsume
}
// GroupName 指定表所属分组(与其他精灵/玩家相关表保持一致)
func (*GoldBeanConsume) GroupName() string {
return "default"
}
// NewGoldBeanConsume 创建金豆消费记录实例初始化通用Model及默认值
func NewGoldBeanConsume() *GoldBeanConsume {
return &GoldBeanConsume{
Base: *NewBase(),
}
}
// -------------------------- 表结构自动同步 --------------------------
func init() {
// 程序启动时自动创建/同步金豆消费记录表
cool.CreateTable(&GoldBeanConsume{})
}

View File

@@ -0,0 +1,34 @@
package model
// TeamInfo 战队信息结构
type TeamInfo struct {
//Head common.TomeeHeader `cmd:"1001" struc:"skip"` // 命令头
ID uint32 `struc:"uint32" default:"0"` // 默认值0
Priv uint32 `struc:"uint32" default:"1"` // 默认值1
SuperCore uint32 `struc:"uint32" default:"1"` // 默认值1
IsShow uint32 `struc:"uint32" default:"1"` // 默认值1
AllContribution uint32 `struc:"uint32" default:"1"` // 默认值1
CanExContribution uint32 `struc:"uint32" default:"1"` // 默认值1
}
// InitDefaults 初始化默认值
func (t *TeamInfo) InitDefaults() {
t.ID = 0
t.Priv = 1
t.SuperCore = 1
t.IsShow = 1
t.AllContribution = 1
t.CanExContribution = 1
}
// TeamPKInfo 战队PK相关信息结构
type TeamPKInfo struct {
GroupID uint32 `struc:"uint32" default:"1"` // 分组ID默认值1@UInt long
HomeTeamID uint32 `struc:"uint32" default:"1"` // 主队ID默认值1@UInt long
}
// InitDefaults 初始化默认值(确保字段默认值正确赋值)
func (t *TeamPKInfo) InitDefaults() {
t.GroupID = 1
t.HomeTeamID = 1
}

View File

@@ -0,0 +1,12 @@
package model
import "blazing/cool"
type Base struct {
*cool.Model
//是否测试服ItemCnt
IsVip uint32 `gorm:"default:0;not null;comment:'是否为VIP服务器'" json:"is_vip"`
}
func NewBase() *Base { return &Base{} }

View File

@@ -0,0 +1,37 @@
package model
import (
"blazing/cool"
)
// 表名常量
const TableNamePlayerCdkLog = "player_cdk_log"
// CdkLog 对应数据库表 player_cdk_log用于记录CDK兑换日志
type CdkLog struct {
Base
PlayerID uint64 `gorm:"not null;index:idx_player_cdk_log_by_player_id;comment:'所属玩家ID'" json:"player_id"`
CodeID uint32 `gorm:"not null;comment:'CDK编号'" json:"code_id"`
}
// TableName 返回表名
func (*CdkLog) TableName() string {
return TableNamePlayerCdkLog
}
// GroupName 返回表组名
func (*CdkLog) GroupName() string {
return "default"
}
// NewCdkLog 创建一个新的CDK记录
func NewCdkLog() *CdkLog {
return &CdkLog{
Base: *NewBase(),
}
}
// init 程序启动时自动创建表
func init() {
cool.CreateTable(&CdkLog{})
}

View File

@@ -0,0 +1,79 @@
package model
import (
"blazing/cool"
"github.com/samber/lo"
"github.com/tnnmigga/enum"
)
type EnumMilestone int
var MilestoneMode = enum.New[struct {
BOSS EnumMilestone //boss类 地图ID->BOSSID ,胜利次数 mapid bossid petid防止换boss后数据不可用
ITEM EnumMilestone //物品类 物品ID 使用精灵
Fight EnumMilestone //挑战类 对战模式->对战类型->1是赢,0是总局数
Moster EnumMilestone //野怪统计 地图ID->怪物ID
Task EnumMilestone
}]()
const TableNameMilestone = "player_milestone"
// Milestone 数据库存储结构体映射milestone表
type Milestone struct {
Base
PlayerID uint64 `gorm:"not null;index:idx_milestone_by_player_id;comment:'所属玩家ID'" json:"player_id"`
DoneType EnumMilestone `gorm:"not null;comment:'里程碑类型'" json:"done_type"`
Args string `gorm:"type:text;not null;comment:'里程碑ID'" json:"args"`
// 注:不单独设置"里程碑ID",通过 PlayerID + DoneType + IDs 组合唯一标识一个里程碑(更灵活)
Results string `gorm:"type:jsonb;not null;comment:'里程碑参数'" json:"results"`
Count uint32 `gorm:"not null;comment:'里程碑完成次数'" json:"count"`
}
// MilestoneEX 里程碑扩展结构体,用于业务层解析后的数据操作
type MilestoneEX struct {
Milestone
Args []uint32 // 解析后的里程碑详细数据
Results []uint32 `json:"results"` // 解析后的里程碑详细数据
}
// 检查是否触发过,成功返回触发的次数,失败返回0
func (m *MilestoneEX) CheakNoNumber(count uint32) bool {
// if v.DoneType == model.MilestoneMode.BOSS && IsPrefixBasicSlice(v.Args, []uint32{mapid, bossid}) && v.Count == count {
_, ok := lo.Find(m.Results, func(v1 uint32) bool { //寻找是否触发过
//大于触发值就触发然后1的返回false因为没有奖励这样就可以一直触发
return v1 == count //大于等于就触发
})
//没找到且次数满足才能返回真
if !ok && m.Count >= count {
return true
}
//已经触发过
return false
// }
}
// TableName 返回表名
func (*Milestone) TableName() string {
return TableNameMilestone
}
// GroupName 返回表组名
func (*Milestone) GroupName() string {
return "default"
}
// NewMilestone 创建新里程碑实例
func NewMilestone() *Milestone {
return &Milestone{
Base: *NewBase(),
}
}
// init 初始化表
func init() {
cool.CreateTable(&Milestone{})
}

View File

@@ -0,0 +1,51 @@
package model
import (
"blazing/cool"
)
const TableNamePlayerBagItem = "player_item"
// PlayerBagItem mapped from table <player_bag_item>
type Item struct {
Base
PlayerID uint64 `gorm:"not null;index:idx_player_bag_item_by_player_id;comment:'所属玩家ID'" json:"player_id"`
// 物品Id
ItemId uint32 `json:"item_id"`
// 物品数量,
ItemCnt uint32 `json:"item_cnt"`
}
type SingleItemInfo struct {
// 物品Id
ItemId uint32 `json:"itemId"`
// 物品数量,
ItemCnt uint32 `json:"itemCnt"`
// 固定值360000
LeftTime uint32 `json:"leftTime"`
// 固定值0
ItemLevel uint32 `json:"itemLevel"`
}
// TableName PlayerBagItem's table name
func (*Item) TableName() string {
return TableNamePlayerBagItem
}
// GroupName PlayerBagItem's table group
func (*Item) GroupName() string {
return "default"
}
// NewPlayerBagItem create a new PlayerBagItem
func NewPlayerBag() *Item {
return &Item{
Base: *NewBase(),
}
}
// init 创建表
func init() {
cool.CreateTable(&Item{})
}

539
modules/player/model/pet.go Normal file
View File

@@ -0,0 +1,539 @@
package model
import (
"blazing/common/data"
"blazing/common/data/xmlres"
"blazing/common/utils"
"blazing/cool"
"blazing/modules/config/service"
"errors"
"fmt"
"math"
"time"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/grand"
"github.com/jinzhu/copier"
"github.com/samber/lo"
)
const TableNamePet = "player_pet"
// Pet mapped from table <pet>
type Pet struct {
Base
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
Free int `gorm:"not null;default:0;comment:'是否放生'" json:"free"` //"0为放入仓库1为放入背包
CatchTime uint32 `gorm:"not null;unique;comment:'捕捉时间'" json:"catch_time"` //唯一键
// Owner uint32 `struc:"skip"` //仅作为存储
// FreedTime uint32 `struc:"skip"` //放生时间
//是否可交易这里应该定义在精灵ID里
//是否上架
Data string `gorm:"type:jsonb;not null;comment:'精灵全部数据'" json:"data"`
}
type PetEX struct {
Pet
Data PetInfo `orm:"data" json:"data"`
}
type Attr uint32
func (r Attr) sub() uint32 {
if r > 0 {
return uint32(r) - 1
}
return 0
}
// PetInfo 精灵信息结构(合并后的优化版本)
type PetInfo struct {
// 精灵编号(@UInt long → uint32
ID uint32 `fieldDesc:"精灵编号" `
// 名字默认为全0补齐到16字节固定长度 → [16]byte
Name string `struc:"[16]byte" json:"Name,omitempty"`
//generation
Generation uint32 `fieldDesc:"世代" `
// 个体值(@UInt long → uint32
Dv uint32 `struc:"uint32" `
// 性格(@UInt long → uint32
Nature uint32 `fieldDesc:"性格" `
// 等级(@UInt long → uint32
Level uint32 `fieldDesc:"等级" `
// 当前等级已获得经验(@UInt long → uint32
Exp uint32
// 当前等级所需经验(@UInt long → uint32
LvExp uint32
// 升到下一级的经验(@UInt long → uint32
NextLvExp uint32
// 当前生命(@UInt long → uint32
Hp uint32
// 最大生命(@UInt long → uint32
MaxHp uint32 `fieldDesc:"最大生命" `
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
Prop [5]uint32 `fieldDesc:"属性" `
// * ev:生命学习力,攻击学习力,防御学习力,特攻学习力,特防学习力,速度学习力
Ev [6]uint32 `fieldDesc:"属性" `
SkillListLen uint32 `struc:"sizeof=SkillList" json:"-"`
// 技能信息固定4条空则赋值0固定长度List → [4]SkillInfo零值即符合“赋值0”
SkillList []SkillInfo
// 捕捉时间(@UInt long → 若为时间戳用uint32若需时间类型可改为time.Time需配合序列化处理
CatchTime uint32 //显式忽略,不参与序列化
OldCatchTime uint32 `struc:"skip" fieldDesc:"旧捕捉时间" `
// 捕捉地图(@UInt long → uint32
CatchMap uint32 `json:"CatchMap,omitempty"`
// 未知默认0@UInt long → uint32
CatchRect uint32 `json:"CatchRect,omitempty"`
// 捕获等级默认0@UInt long → uint32
CatchLevel uint32 `fieldDesc:"捕获等级 默认为0" `
EffectInfoLen uint16 `struc:"sizeof=EffectInfo" json:"-"`
// 特性列表长度用UShort存储变长List → []PetEffectInfo + 长度前缀规则) 第一个一定是特性
EffectInfo []PetEffectInfo `fieldDesc:"特性列表, 长度在头部以UShort存储" serialize:"lengthFirst,lengthType=uint16,type=structArray"`
// 皮肤ID默认0@UInt long → uint32
SkinID uint32 `fieldDesc:"皮肤id默认为0" `
// 是否闪光(@UInt long → uint320=否1=是)
ShinyLen uint32 `json:"-" struc:"sizeof=ShinyInfo"`
ShinyInfo []data.GlowFilter `json:"ShinyInfo,omitempty"`
//时间轮转然后effect根据type同时只共存一个特性是1 特质是1柱子是两种魂印是一个然后异色字段然后特训技能字段
ExtSKill []uint32 `struc:"skip"` //特训技能
ExtSkin []uint32 `struc:"skip"` //可用皮肤
}
// 定义常量,提升可维护性(避免魔法数字)
const (
maxSingleEV uint32 = 255 // 单个EV最大值
maxTotalEV uint32 = 510 // 6个EV总和最大值
evFieldCount = 6 // EV字段数量固定6个
)
// AddEV 优化后的EV值增加方法符合Go命名规范大写导出动词开头
// 功能为宠物6个EV值增加增量保证单个≤255、总和≤510
// 参数evadd - 6个EV字段的增量数组长度必须为6
// 返回error - 参数非法/逻辑异常时返回错误bool - 是否触发了超额削减(方便业务监控)
func (pet *PetInfo) AddEV(ev_add []uint32) (bool, error) {
// 1. 参数安全校验避免数组越界panic
if len(ev_add) != evFieldCount {
return false, fmt.Errorf("evadd长度必须为%d当前为%d", evFieldCount, len(ev_add))
}
if len(pet.Ev) != evFieldCount {
return false, errors.New("pet.Ev未初始化或长度不为6")
}
// 2. 第一步:直接添加并限制单项最大值(按索引顺序处理)
var tempEV [evFieldCount]uint32
for i := 0; i < evFieldCount; i++ {
// 直接累加增量
tempEV[i] = pet.Ev[i] + ev_add[i]
// 单项不超过255
if tempEV[i] > maxSingleEV {
tempEV[i] = maxSingleEV
}
}
// 3. 计算增量后的总和检查是否超过510
totalTemp := lo.Sum(tempEV[:])
// 4. 若总和超额按索引顺序0→5削减优先削减前面的字段
hasCut := false
if totalTemp > maxTotalEV {
overTotal := totalTemp - maxTotalEV // 需要削减的总量
hasCut = true
// 按索引顺序遍历削减从第0个字段开始依次处理
for i := 0; i < evFieldCount && overTotal > 0; i++ {
// 可削减的最大值最多削减到原始值不触碰添加前的基础EV
cutAble := tempEV[i] - pet.Ev[i]
if cutAble <= 0 {
continue // 该字段无增量可削减,跳过
}
// 实际削减量:取"可削减量"和"剩余需削减量"的较小值
cut := cutAble
if cut > overTotal {
cut = overTotal
}
// 执行削减
tempEV[i] -= cut
overTotal -= cut
}
// 极端情况即使削减所有增量后仍超额如原始EV总和已超510继续按顺序削减原始值
if overTotal > 0 {
for i := 0; i < evFieldCount && overTotal > 0; i++ {
// 此时可削减到0根据业务需求调整也可返回错误
cutAble := tempEV[i]
if cutAble <= 0 {
continue
}
cut := cutAble
if cut > overTotal {
cut = overTotal
}
tempEV[i] -= cut
overTotal -= cut
}
}
}
// 5. 将处理后的结果赋值给原EV数组
copy(pet.Ev[:], tempEV[:])
return hasCut, nil
}
func (pet *PetInfo) Cure() {
pet.Hp = pet.MaxHp
for i := 0; i < len(pet.SkillList); i++ {
maxPP, ok := xmlres.SkillMap[int(pet.SkillList[i].ID)]
// 恢复至最大PP值从配置表获取
if pet.SkillList[i].ID != 0 && ok {
pet.SkillList[i].PP = uint32(maxPP.MaxPP)
}
}
}
func (pet *PetInfo) RandShiny() {
co := service.NewShinyService().RandShiny(pet.ID)
if co != nil {
pet.ShinyInfo = append(pet.ShinyInfo, *co)
}
//o.ShinyInfo[0].ColorMatrixFilter = GenerateRandomOffspringMatrix().Get()
//g.Dump(ttt.ShinyInfo)
// ttt.Shiny = 0 //待确认是否刷新异色
}
func (pet *PetInfo) FixShiny() {
co := service.NewShinyService().FixShiny(pet.ID)
if co != nil {
pet.ShinyInfo = append(pet.ShinyInfo, *co)
}
//o.ShinyInfo[0].ColorMatrixFilter = GenerateRandomOffspringMatrix().Get()
//g.Dump(ttt.ShinyInfo)
// ttt.Shiny = 0 //待确认是否刷新异色
}
func (pet *PetInfo) IsShiny() bool {
return len(pet.ShinyInfo) > 0
}
// 随机特性
func (pet *PetInfo) RnadEffect() {
for _, v := range xmlres.PlayerEffectMAP {
if gconv.Int(v.StarLevel) == 0 {
ret := &PetEffectInfo{
Idx: uint16(gconv.Int16(v.Idx)),
Status: 1,
EID: uint16(gconv.Int16(v.Eid)),
Args: v.ArgsS,
}
_, eff, ok := pet.GetEffect(1)
if ok {
copier.Copy(eff, ret)
} else {
pet.EffectInfo = append(pet.EffectInfo, *ret)
}
break
}
}
}
// 1是特性特质<!-- Stat: 精灵特效Stat: 0: 无效(默认值), 1: 永久, 2: 有`有效次数'的特效 3: 爆发特效 4: 异能精灵特质5特训6魂印-->
func (pet *PetInfo) GetEffect(ptype int) (int, *PetEffectInfo, bool) {
return utils.FindWithIndex(pet.EffectInfo, func(item PetEffectInfo) bool {
return gconv.Int(xmlres.EffectMAP[int(item.Idx)].Stat) == 1
})
}
func (pet *PetInfo) Downgrade(level uint32) {
for pet.Level > uint32(level) {
basic, ok := xmlres.PetMAP[int(pet.ID)]
if ok {
if basic.EvolvesFrom != 0 {
pet.ID = uint32(basic.EvolvesFrom)
}
}
pet.Level--
//进行降级操作
}
}
// 执行进化逻辑 ,是否进化
func (petinfo *PetInfo) Update(isup bool) {
// 最大进化次数限制(防止配置表闭环导致死循环)
maxEvolveTimes := 10
evolveCount := 0
// 循环进化:直到不满足进化条件 或 达到最大进化次数
for {
// 防止死循环,超出次数直接退出
if evolveCount >= maxEvolveTimes {
break
}
// 进化完成后,统一更新经验(原逻辑保留)
petinfo.LvExp = petinfo.NextLvExp
// 获取当前宠物形态的配置
basic, ok := xmlres.PetMAP[int(petinfo.ID)]
// 配置不存在,直接退出循环
if !ok {
break
}
petinfo.NextLvExp = calculateExperience(petinfo.Level, basic.GetBasic())
if !isup {
return
}
// 检查是否满足进化条件
canEvolve := basic.EvolvesTo != 0 && // 有明确的进化目标
int(petinfo.Level) >= basic.EvolvingLv && // 等级达到进化要求
basic.IsLarge == 0 // 非最终形态
// 不满足进化条件,退出循环
if !canEvolve {
break
}
// 执行进化更新宠物ID为进化后形态
petinfo.ID = uint32(basic.EvolvesTo)
evolveCount++ // 进化次数+1
}
}
// calculateExperience 计算指定等级和种族值所需的经验值
// level: 当前等级
// baseValue: 种族值
func calculateExperience(level uint32, baseValue uint32) uint32 {
// 计算 A 部分:向上取整(3.75 * a * (a + 1))
partA := math.Ceil(3.75 * float64(level) * float64(level+1))
// 计算 B 部分:向上取整(b * log(1 + a / 100))
// 这里使用自然对数 math.Log如果想换底数可以用换底公式
partB := math.Log(1.0 + float64(level)/100.0)
partB = float64(baseValue) * partB
partB = math.Ceil(partB)
// 总经验是两部分之和,并向上取整
totalExp := math.Ceil(partA + partB)
return uint32(totalExp)
}
// PetEffectInfo 精灵特性信息结构
// <!-- NewSeIdx: 精灵特效索引 (默认0: 无效) -->
// <!-- Type: 0 - 仅单人战斗; 1 - 仅组队战斗; 2 - both; (默认0: 仅单人) -->
// <!-- Eid: 精灵特效eid (默认0: 无效) -->
// <!-- Stat: 精灵特效Stat: 0: 无效(默认值), 1: 永久, 2: 有`有效次数'的特效 3: 爆发特效 4: 异能精灵特质-->
// <!-- Times: 精灵特效可使用次数: 当type==2时有效 (默认值:0) -->
// <!-- Args: 特效参数, 不超过8个 (注意: 每个参数不能超过 65535) -->
// <!-- AdditionType:特效加成类型 1 种族值加成 2 技能威力加成 -->
type PetEffectInfo struct {
ItemID uint32 `struc:"uint32" json:"item_id"` //如果是能量珠,就显示
Idx uint16 `struc:"skip" json:"new_se_idx"`
Type byte `struc:"skip" json:"type"` //pvp pve特性区分,待前端修改实现
Status byte `struc:"byte" json:"status"` //特性为1,能量珠为2
LeftCount byte `struc:"byte" json:"left_count"` //剩余次数
EID uint16 `struc:"uint16" json:"effect_id"` //特效ID
ArgsLen uint32 `struc:"sizeof=Args" json:"-"`
Args []int ` json:"Args"` //自定义参数装载
}
// SkillInfo 精灵技能信息结构SkillInfo
type SkillInfo struct {
ID uint32
PP uint32
}
// TableName Pet's table name
func (*Pet) TableName() string {
return TableNamePet
}
// GroupName Pet's table group
func (*Pet) GroupName() string {
return "default"
}
// NewPet create a new Pet
func NewPet() *Pet {
return &Pet{
Base: *NewBase(),
}
}
// init 创建表
func init() {
_ = cool.CreateTable(&Pet{})
// fmt.Println(err)
}
// GenPetInfo 生成一个新的精灵实例
// - 参数为 -1 时表示随机生成对应属性
// * @param petTypeId 精灵类型ID
// * @param individualValue 个体值0-31
// * @param natureId 性格ID0-24
// * @param abilityTypeEnum 特性类型ID0=无, >0=指定, -1=随机)
// * @param shinyid 闪光ID-1=随机)
// * @param level 等级1-100
// * @return 生成的精灵实体
func GenPetInfo(
id int,
dv, natureId, abilityTypeEnum, level int, shinyid []data.GlowFilter,
) *PetInfo {
// 创建随机源
//rng := rand.New(rand.NewSource(time.Now().UnixNano()))
// 初始化精灵
p := &PetInfo{
ID: uint32(id),
CatchTime: uint32(time.Now().Unix()),
Level: uint32(level),
EffectInfo: make([]PetEffectInfo, 0),
}
// ---- 处理闪光 ----
if shinyid != nil {
//todo 待实现异色字段
p.ShinyInfo = shinyid
// p.Shiny = uint32(shinyid)
}
// ---- 性格 ----
if natureId == -1 {
p.Nature = uint32(grand.Intn(25))
} else {
p.Nature = uint32(natureId)
}
// ---- 个体值DV----
if dv == -1 {
p.Dv = uint32(CalculateIndividualValue())
} else {
if dv < 0 {
dv = 0
} else if dv > 31 {
dv = 31
}
p.Dv = uint32(dv)
}
// ---- 特性 ----
switch {
case abilityTypeEnum == 0:
// 无特性
case abilityTypeEnum > 0:
// 指定特性
if v, ok := xmlres.PlayerEffectMAP[int(abilityTypeEnum)]; ok {
p.EffectInfo = append(p.EffectInfo, PetEffectInfo{
Idx: uint16(gconv.Int16(v.Idx)),
Status: 1,
EID: uint16(gconv.Int16(v.Eid)),
Args: v.ArgsS,
})
}
case abilityTypeEnum == -1:
for _, v := range xmlres.PlayerEffectMAP {
if gconv.Int(v.StarLevel) == 0 {
p.EffectInfo = append(p.EffectInfo, PetEffectInfo{
Idx: uint16(gconv.Int16(v.Idx)),
Status: 1,
EID: uint16(gconv.Int16(v.Eid)),
Args: v.ArgsS,
})
break
}
}
}
// ---- 技能学习 ----
skills := utils.LastFourElements(p.GetLevelRangeCanLearningSkills(0, p.Level), 4) // 最后四个技能
for i := 0; i < len(skills) && i < 4; i++ {
skillID := skills[i]
if info, ok := xmlres.SkillMap[int(skillID)]; ok {
p.SkillList = append(p.SkillList, SkillInfo{ID: skillID, PP: uint32(info.MaxPP)})
}
}
if len(p.SkillList) > 4 {
p.SkillList = p.SkillList[:4]
}
// ---- 属性计算 ----
p.CalculatePetPane(true)
p.Update(false)
return p
}
// 除数数组放大100倍
// 数组按递增顺序排列,用于判断个体值等级
var divisors = []int{
600, 1200, 1900, 2700, 3600, 4600, 5700, 6900, 8200, 9600,
11100, 12700, 14400, 16200, 18100, 20100, 22100, 24000,
25800, 27500, 29100, 30600, 32000, 33300, 34500, 35600,
36600, 37500, 38300, 39000, 39600,
}
// CalculateIndividual 根据给定的a值计算个体值
// 返回值表示a大于等于多少个除数范围0-31
func CalculateIndividual(a int) int {
individual := 0
for _, divisor := range divisors {
if a >= divisor {
individual++
} else {
break // 数组是递增的,可提前跳出循环
}
}
return individual
}
// CalculateIndividualValue 计算个体值0-31
// 接收外部随机数生成器,便于控制随机性和复用
func CalculateIndividualValue() int {
// 生成0-40000的随机数作为个体值计算的输入
a := grand.Intn(40001)
return CalculateIndividual(a)
}

View File

@@ -0,0 +1,94 @@
package model
import (
"blazing/common/data/xmlres"
)
// 实现获取等级范围内可学习的技能
func (p *PetInfo) GetLevelRangeCanLearningSkills(from, to uint32) []uint32 {
var skills []uint32
for _, skillIDs := range xmlres.PetMAP[int(p.ID)].LearnableMoves.Moves {
if skillIDs.LearningLv >= from && skillIDs.LearningLv <= to {
skills = append(skills, skillIDs.ID)
}
}
return skills
}
// 计算HP面板值无性格修正
func (c *PetInfo) calculatePetHPPanelSize(base, dv, level, ev uint32) uint32 {
return uint32((float64(base)*2+float64(ev)/4.0+float64(dv))*(float64(level)/100.0) + float64(level) + 10)
}
// 计算其他属性面板值(带性格修正)
func (c *PetInfo) calculatePetPanelSize(base, ev uint32, natureCorrect float64) uint32 {
base1 := float64((float64(base)*2+float64(ev)/4.0+float64(c.Dv))*(float64(c.Level)/100.0) + 5)
return uint32(float64(base1) * natureCorrect)
}
// 计算生成面板,只允许第一次生成超过100比如boss,不允许额外超过
func (p *PetInfo) CalculatePetPane(frist bool) {
if !frist {
if p.Level > 100 {
oldlveel := p.Level
p.Level = 100
defer func() {
p.Level = oldlveel
}()
}
}
naxml := xmlres.NatureRootMap[int(p.Nature)]
petxml := xmlres.PetMAP[int(p.ID)]
// 计算各项属性
p.MaxHp = p.calculatePetHPPanelSize(
uint32(petxml.HP),
p.Dv,
p.Level,
p.Ev[0],
)
p.Hp = p.MaxHp
// * battle_lv: atk(0), def(1), sp_atk(2), sp_def(3), spd(4), accuracy(5)
p.Prop[0] = p.calculatePetPanelSize(
uint32(petxml.Atk),
p.Ev[1],
naxml.AttackCorrect,
)
p.Prop[1] = p.calculatePetPanelSize(
uint32(petxml.Def),
p.Ev[2],
naxml.DefenseCorrect,
)
p.Prop[2] = p.calculatePetPanelSize(
uint32(petxml.SpAtk),
p.Ev[3],
naxml.SaCorrect,
)
p.Prop[3] = p.calculatePetPanelSize(
uint32(petxml.SpDef),
p.Ev[4],
naxml.SdCorrect,
)
p.Prop[4] = p.calculatePetPanelSize(
uint32(petxml.Spd),
p.Ev[5],
naxml.SpeedCorrect,
)
}

View File

@@ -0,0 +1,230 @@
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;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
LastResetTime *gtime.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务
Data string `gorm:"type:jsonb;not null;comment:'全部数据'" json:"data"`
}
type PlayerEX struct {
Player
Data PlayerInfo `orm:"data" 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),
}
// 自动填充 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 中映射为未接受)
)
// 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 0, fmt.Errorf("index out of range: %d", i)
// }
byteIdx := i / 4
bitOffset := (i % 4) * 2
return TaskStatus((m.TaskList[byteIdx] >> bitOffset) & 0x3)
}
type PlayerInfo struct {
ExpPool uint32 `struc:"skip" json:"exp_pool"` // 累计经验池
OnlineTime uint32 `struc:"skip" json:"online_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 uint32 `struc:"uint32" json:"coins"` // 赛尔豆
EVPool uint32 `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 uint32 `struc:"uint32" default:"0" json:"time_today"` // 已消耗时间(秒)
TimeLimit uint32 `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 uint32 `struc:"uint32" default:"0" json:"two_times"` // 双倍经验加速器剩余使用次数
ThreeTimes uint32 `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 uint32 `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"` // 精灵背包内信息
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() *PlayerEX {
return &PlayerEX{
Player: Player{
Model: cool.NewModel(),
},
}
}
// init 创建表
func init() {
cool.CreateTable(&Player{})
}

View File

@@ -0,0 +1,69 @@
package model
import (
"blazing/cool"
)
// 基地房型表名
const TableNameBaseHouse = "player_room_house"
// NewBaseHouse 构造函数:创建基地房型实例
func NewBaseHouse() *BaseHouse {
return &BaseHouse{
Base: *NewBase(),
}
}
// BaseHouse 基地房型核心结构体
// 包含:基地展示精灵、基地拥有物品、基地摆放物品三大核心字段
type BaseHouse struct {
Base
// 基础关联字段
PlayerID uint64 `gorm:"not null;index:idx_player_house;comment:'所属玩家ID'" json:"player_id"`
// 核心业务字段
// ShowPokemon 基地展示精灵ID列表支持展示多个精灵
ShowPokemon []uint32 `gorm:"type:jsonb;default:'[]';comment:'基地展示精灵ID列表'" json:"show_pokemon"`
UsedItems string `gorm:"type:jsonb;default:'{}';comment:'用户物品列表物品ID:数量)'" json:"used_items"`
// PlacedItems 基地摆放物品包含物品ID、摆放坐标、朝向等信息
PlacedItems string `gorm:"type:jsonb;default:'[]';comment:'基地摆放物品列表(含位置/朝向)'" json:"placed_items"`
}
func (*BaseHouse) TableName() string {
return TableNameBaseHouse
}
type BaseHouseEx struct {
BaseHouse
PlacedItems []FitmentShowInfo `json:"placed_items"`
//OwnedItems map[uint32]uint32 `json:"owned_items"`
UsedItems map[uint32]uint32 `json:"used_items"`
}
// FitmentShowInfo 表示家具展示信息
type FitmentShowInfo struct {
// 家具id 或 默认房型id: 500001
Id uint32 `json:"id"`
// x坐标
X uint32 `json:"x"`
// y坐标
Y uint32 `json:"y"`
// 默认0
Dir uint32 `json:"dir"`
// 默认0
Status uint32 `json:"status"`
}
// UpdateShowPokemon 更新基地展示精灵列表
func (bh *BaseHouse) UpdateShowPokemon(pokemonIDs []uint32) {
bh.ShowPokemon = pokemonIDs
}
// --------------- 初始化创建表 ---------------
func init() {
// 初始化时创建基地房型表与现有Talk表初始化逻辑一致
cool.CreateTable(&BaseHouse{})
}

View File

@@ -0,0 +1,44 @@
package model
import (
"blazing/cool"
)
// 表名常量(遵循小写+下划线的命名规范)
const TableNameSignInRecord = "player_sign_in_log"
// SignInRecord 玩家签到明细记录表
// 记录玩家每一次的签到行为,关联签到活动表
type SignInRecord struct {
Base
// 核心关联字段
PlayerID uint32 `gorm:"not null;index:idx_player_id;comment:'玩家ID'" json:"player_id"`
SignInID uint32 `gorm:"not null;index:idx_sign_in_id;comment:'关联的签到活动ID对应player_sign_in表的SignInID'" json:"sign_in_id"`
IsCompleted bool `gorm:"not null;default:false;comment:'签到是否完成0-未完成 1-已完成)'" json:"is_completed"`
//通过bitset来实现签到的进度记录
SignInProgress []uint32 `gorm:"type:json;not null;comment:'签到进度(状压实现,存储每日签到状态)'" json:"sign_in_progress"`
}
// TableName 指定表名(遵循现有规范)
func (*SignInRecord) TableName() string {
return TableNameSignInRecord
}
// GroupName 指定表分组与现有表保持一致的default分组
func (*SignInRecord) GroupName() string {
return "default"
}
// NewSignInRecord 创建签到明细记录实例初始化基础Model
func NewSignInRecord() *SignInRecord {
return &SignInRecord{
Base: *NewBase(),
}
}
// init 程序启动时自动创建表与现有SignIn表初始化逻辑一致
func init() {
cool.CreateTable(&SignInRecord{})
}

View File

@@ -0,0 +1,76 @@
package model
import (
"blazing/cool"
)
// 资源采集计数表名
const TableNameResourceCollection = "player_talk"
func NewTalk() *Talk {
return &Talk{
Base: *NewBase(),
}
}
// ResourceCollection 记录玩家每种资源的采集计数
type Talk struct {
Base
PlayerID uint64 `gorm:"not null;index:idx_player_resource;comment:'所属玩家ID'" json:"player_id"`
TalkID uint32 `gorm:"not null;comment:'资源ID'" json:"talk_id"`
Count uint32 `gorm:"not null;comment:'采集计数'" json:"count"`
}
// TableName 资源采集表名
func (*Talk) TableName() string {
return TableNameResourceCollection
}
// GroupName 资源采集表分组
func (*Talk) GroupName() string {
return "default"
}
// // 检查是否可以采集(未超过每日上限)
// func (rc *ResourceCollection) CanCollect(maxDaily uint32) bool {
// // 先检查是否需要重置计数
// rc.checkAndReset()
// return rc.CollectCnt < maxDaily
// }
// // 增加采集计数,返回是否成功
// func (rc *ResourceCollection) AddCollectCount(maxDaily uint32) bool {
// if !rc.CanCollect(maxDaily) {
// return false
// }
// rc.CollectCnt++
// return true
// }
// // 检查并重置每日计数
// func (rc *ResourceCollection) checkAndReset() {
// now := time.Now()
// if now.After(rc.DailyReset) {
// rc.CollectCnt = 0
// // 重置为明天24点
// rc.DailyReset = now.Truncate(24 * time.Hour).Add(24 * time.Hour)
// }
// }
// // ResourceConfig 资源配置信息对应XML中的配置
// type ResourceConfig struct {
// Type uint32 `json:"type"` // 资源类型ID
// MapID uint32 `json:"map_id"` // 所在地图ID
// Name string `json:"name"` // 资源名称
// CollectType string `json:"collect_type"` // 采集类型
// MaxDailyCnt uint32 `json:"max_daily_cnt"` // 每日最大采集次数
// Unit string `json:"unit"` // 单位
// Dir uint32 `json:"dir"` // 方向
// }
// 初始化创建表
func init() {
cool.CreateTable(&Talk{})
// 可以在这里加载资源配置
// LoadResourceConfigsFromXML()
}

View File

@@ -0,0 +1,55 @@
package model
import (
"blazing/cool"
)
// todo 还需要做一个记录任务奖励的表
const TableNameTask = "player_task"
// Task mapped from table <task>
type Task struct {
Base
PlayerID uint64 `gorm:"not null;index:idx_task_by_player_id;comment:'所属玩家ID'" json:"player_id"`
TaskID uint32 `gorm:"not null;comment:'任务ID'" json:"task_id"`
Data string `gorm:"type:jsonb;not null;comment:'全部数据'" json:"data"`
}
// TaskEX 单个任务的详细信息,包含任务步骤状态和整体状态
type TaskEX struct {
Task
Data []uint32 `struc:"[20]byte" orm:"data" json:"data"`
//LastResetTime time.Time `gorm:"not null;comment:'上次重置时间UTC'" json:"last_reset_time"` //这里是每天重置
// Status 任务整体状态0-未接受1-已接受2-已完成未领取3-已完成已领取
// json标签指定JSON字段名与业务状态说明保持一致
//Status byte `json:"status"`
}
// TableName PlayerInfo's table name
func (*Task) TableName() string {
return TableNameTask
}
// GroupName PlayerInfo's table group
func (*Task) GroupName() string {
return "default"
}
func (t *Task) GetData() string {
return t.Data
}
func (t *Task) SetData(t1 string) {
t.Data = t1
}
// NewPlayerInfo create a new PlayerInfo
func NewTask() *Task {
return &Task{
Base: *NewBase(),
}
}
// init 创建表
func init() {
cool.CreateTable(&Task{})
}

View File

@@ -0,0 +1,39 @@
package model
import (
"blazing/cool"
)
// 表名常量
const TableNamePlayerTitle = "player_title"
// PlayerTitle 对应数据库表 <player_title>
type Title struct {
Base
PlayerID uint64 `gorm:"not null;index:idx_player_title_by_player_id;comment:'所属玩家ID'" json:"player_id"`
//TitleID uint32 `gorm:"not null;comment:'称号ID'" json:"title_id"`
//可用称号
AvailableTitle []uint32 `gorm:"type:json; comment:'可用称号'" json:"available_title"`
}
// TableName 返回表名
func (*Title) TableName() string {
return TableNamePlayerTitle
}
// GroupName 返回表组名
func (*Title) GroupName() string {
return "default"
}
// NewPlayerTitle 创建一个新的称号记录
func NewPlayerTitle() *Title {
return &Title{
Base: *NewBase(),
}
}
// init 程序启动时自动创建表
func init() {
cool.CreateTable(&Title{})
}