refactor(blazing): 重构任务系统并优化相关功能

- 重构了任务系统的数据结构和执行逻辑
- 优化了地图加载和怪物刷新机制
- 改进了宠物系统的基础架构
- 调整了玩家信息和背包的处理方式
- 统一了数据访问层的接口和实现
This commit is contained in:
2025-08-30 21:59:52 +08:00
parent 2ed5c2db27
commit 75e428f62e
23 changed files with 326 additions and 230 deletions

View File

@@ -7,11 +7,16 @@ import (
const TableNamePlayerBagItem = "player_bag_item"
// PlayerBagItem mapped from table <player_bag_item>
type PlayerBagItem struct {
type PlayerBag struct {
*cool.Model
PlayerID uint64 `gorm:"not null;index:idx_player_bag_item_by_player_id;comment:'所属玩家ID'" json:"player_id"`
ItemID int32 `gorm:"not null;comment:'道具唯一编号'" json:"item_id"`
Quantity int32 `gorm:"not null;default:0;comment:'拥有数量uint16'" json:"quantity"`
Data string `gorm:"type:text;not null;comment:'全部数据'" json:"data"`
}
type PlayerBagItem struct {
ID int32 `gorm:"not null;comment:'道具唯一编号'" json:"item_id"`
Count int32 `gorm:"not null;default:0;comment:'拥有数量 '" json:"count"`
Max int32 `gorm:"not null;default:0;comment:'最大数量 '" json:"max"`
Total int32 `gorm:"not null;default:0;comment:'使用次数'" json:"total"`
}
// TableName PlayerBagItem's table name
@@ -25,13 +30,13 @@ func (*PlayerBagItem) GroupName() string {
}
// NewPlayerBagItem create a new PlayerBagItem
func NewPlayerBagItem() *PlayerBagItem {
return &PlayerBagItem{
func NewPlayerBag() *PlayerBag {
return &PlayerBag{
Model: cool.NewModel(),
}
}
// init 创建表
func init() {
cool.CreateTable(&PlayerBagItem{})
cool.CreateTable(&PlayerBag{})
}

View File

@@ -12,15 +12,14 @@ const (
// MonsterRefresh 怪物刷新规则模型对应XML中的<monster>标签)
type MonsterRefresh struct {
*cool.Model
MapID int32 `gorm:"not null;index:idx_refresh_by_map_id;comment:'所属地图ID'" json:"map_id"`
MonsterID int32 `gorm:"not null;comment:'怪物唯一编号'" json:"monster_id"`
//Desc string `gorm:"type:varchar(100);not null;comment:'怪物名称(如皮皮)'" json:"desc"`
MinLevel int32 `gorm:"not null;comment:'最低等级'" json:"min_level"`
MaxLevel int32 `gorm:"not null;comment:'最高等级'" json:"max_level"`
Capturable bool `gorm:"not null;comment:'是否可捕捉'" json:"capturable"`
Rate float64 `gorm:"not null;comment:'刷新概率(百分比)'" json:"rate"` //未设置概率的就是默认刷新
Value string `gorm:"type:text;not null;comment:'限制值如19:00-24:00'" json:"value"` //这里是js文本暂定传入时间
MonsterID int32 `gorm:"not null;comment:'对应原怪物唯一编号'" json:"monster_id"`
ShinyID int32 `gorm:"not null;uniqueIndex;comment:'异色唯一标识ID'" json:"shiny_id"`
RefreshScript string `gorm:"type:text;not null;comment:'刷新脚本JS格式可包含时间/天气/地形等条件)'" json:"refresh_script"`
ShinyFilter string `gorm:"type:text;not null;comment:'异色滤镜效果(文本描述或配置参数)'" json:"shiny_filter"`
ShinyEffect string `gorm:"type:text;not null;comment:'异色光效效果(文本描述或配置参数)'" json:"shiny_effect"`
// TODO: 增加ruffle的显示异色效果
}
// TableName MonsterRefresh's table name

View File

@@ -10,11 +10,13 @@ const TableNamePet = "pet"
type Pet struct {
*cool.Model
PlayerID uint32 `gorm:"not null;index:idx_pet_by_player_id;comment:'所属玩家ID'" json:"player_id"`
InBag bool `gorm:"not null;comment:'是否在背包中'" json:"in_bag"` //"0为放入仓库1为放入背包
Data string `gorm:"type:text;not null;comment:'精灵全部数据'" json:"data"`
}
// PetInfo 精灵信息结构(合并后的优化版本)
type PetInfo struct {
Owner uint32 `struc:"skip"` //仅作为存储
// 精灵编号(@UInt long → uint32
ID uint32 `fieldDesc:"精灵编号" `

View File

@@ -2,6 +2,7 @@ package model
import (
"blazing/cool"
"time"
"github.com/creasty/defaults"
)
@@ -40,37 +41,14 @@ func NewPlayerInfo() *PlayerInfo {
panic(err) // 方便发现 default 设置错误
}
// 填充需要重复值的数组
fillBytes(&l.DailyResArr, 3)
fillBytes(&l.Reserved1, 3)
fillBytes(&l.TaskList, 3)
return l
}
// 工具函数:给数组/切片批量赋同一个 byte 值
func fillBytes(arr any, val byte) {
switch a := arr.(type) {
case *[50]byte:
for i := range a {
a[i] = val
}
case *[27]byte:
for i := range a {
a[i] = val
}
case *[500]byte:
for i := range a {
a[i] = val
}
}
}
type PlayerInfo struct {
GoldBean int32 `struc:"skip" json:"nieo_gold_bean"` // 金豆(特殊货币)
ExpPool int64 `struc:"skip" json:"exp_pool"` // 累计经验池
ExpPool int64 `struc:"skip" json:"exp_pool"` // 累计经验池
LastResetTime time.Time `struc:"skip" json:"last_reset_time"` // 重置时间,比如电池和每日任务
// OutInfo 字段
UserID uint32 `struc:"uint32" json:"user_id"` // 米米号 通过sid拿到
RegisterTime uint32 `struc:"uint32" json:"register_time"` // 注册时间(秒时间戳)
@@ -136,7 +114,7 @@ type PlayerInfo struct {
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 [500]byte `struc:"[500]byte" default:"3" json:"task_list"` // 任务状态数组500字节默认3
TaskList [500]byte `struc:"[500]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"` // 穿戴装备数量

View File

@@ -2,9 +2,9 @@ package model
import (
"blazing/cool"
"time"
)
// todo 还需要做一个记录任务奖励的表
const TableNameTask = "task"
// Task mapped from table <task>
@@ -18,17 +18,17 @@ type Task struct {
type TaskInfo struct {
// TaskInfo 任务步骤信息对应Java的@ArraySerialize(FIXED_LENGTH=20)注解
// struc:"[20]byte" 确保二进制序列化时固定20字节长度json标签指定JSON字段名
TaskType uint32 `json:"task_type"` //区分是每日任务还是常规任务,常规为0,每日为1
TaskInfo []uint32 `struc:"[20]byte" json:"task_info"`
LastResetTime time.Time `gorm:"not null;comment:'上次重置时间UTC'" json:"last_reset_time"` //这里是每天重置
TaskID uint32 `json:"task_id"` //区分是每日任务还是常规任务,常规为0,每日为1
TaskInfo []uint32 `struc:"[20]byte" json:"task_info"`
//LastResetTime time.Time `gorm:"not null;comment:'上次重置时间UTC'" json:"last_reset_time"` //这里是每天重置
// Status 任务整体状态0-未接受1-已接受2-已完成未领取3-已完成已领取
// json标签指定JSON字段名与业务状态说明保持一致
Status byte `json:"status"`
//Status byte `json:"status"`
}
// TableName PlayerInfo's table name
func (*Task) TableName() string {
return TableNamePlayerInfo
return TableNameTask
}
// GroupName PlayerInfo's table group
@@ -36,6 +36,13 @@ 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{

View File

@@ -0,0 +1,19 @@
package service
import (
"blazing/cool"
"blazing/modules/blazing/model"
"encoding/json"
)
// 获取精灵信息
func (s *UserService) GetPetList() (ret []model.PetInfo) {
m := cool.DBM(s.pet.Model).Where("player_id", s.userid)
var tt model.Pet
m.Scan(&tt)
json.Unmarshal([]byte(tt.Data), &ret)
return
}

View File

@@ -38,7 +38,7 @@ func (s *UserService) Reg(nick string, color uint32) {
t1.Nick = nick
t1.Color = color
t1.RegisterTime = uint32(time.Now().Unix())//写入注册时间
t1.RegisterTime = uint32(time.Now().Unix()) //写入注册时间
t22, err := json.Marshal(t1)
if err != nil {
return
@@ -50,7 +50,7 @@ func (s *UserService) Reg(nick string, color uint32) {
glog.Error(context.Background(), err)
return
}
go s.InitTask()
}
func (s *UserService) Person() (ret *model.PlayerInfo) {
@@ -72,7 +72,10 @@ func (s *UserService) Save(data *model.PlayerInfo) {
temp, _ := json.Marshal(data)
tt.Data = string(temp)
m.Save(tt)
_, err := m.Update(tt)
if err != nil {
panic(err)
}
return

View File

@@ -4,54 +4,81 @@ import (
"blazing/cool"
"blazing/modules/blazing/model"
"encoding/json"
"reflect"
"time"
)
func Exec[T, F any](userid uint32, s *cool.Service, processFunc func(F) bool) bool {
func Exec[T cool.UserModel, F any](userid uint32, s *cool.Service, processFunc func(F) F) bool {
//todo待测试
var player T
// 方法2使用反射获取
// 获取反射值对象
val := reflect.ValueOf(player)
var dataField reflect.Value
// 检查是否为结构体
if val.Kind() == reflect.Struct {
// 通过字段名获取Data字段
dataField = val.FieldByName("Data")
}
m1 := cool.DBM(s.Model).Where("player_id", userid)
m1.Scan(&player)
var tt F
json.Unmarshal([]byte(dataField.Interface().(string)), &tt)
processFunc(tt)
tmep, _ := json.Marshal(tt)
dataField.SetString(string(tmep))
// 方法2使用反射获取
// 获取反射值对象
ttt := player
//fmt.Println(dataField.Interface().(string))
var tt F
err := json.Unmarshal([]byte(ttt.GetData()), &tt)
if err != nil {
panic(err)
}
tt1 := processFunc(tt)
tmep, err := json.Marshal(tt1)
if err != nil {
panic(err)
}
ttt.SetData(string(tmep))
m1.Save(player)
return false
}
func (s *UserService) TaskExec(t func(map[uint32]model.TaskInfo) bool, isdaliy bool) (ret bool) {
if isdaliy {
Exec[model.Task](s.userid, s.task, func(tt map[uint32]model.TaskInfo) bool {
func (s *UserService) InitTask() {
//先重置每日
for _, v := range tt {
if v.TaskType == 1 && !IsToday(v.LastResetTime) {
tt := model.NewTask()
tt.PlayerID = uint64(s.userid)
v.Status = 0 //重置+自动接受每日任务
v.LastResetTime = time.Now().UTC()
}
var ggg []model.TaskInfo
}
return true
for i := 0; i < 500; i++ {
ggg = append(ggg, model.TaskInfo{
TaskID: (uint32(i)),
TaskInfo: make([]uint32, 0),
})
}
return Exec[model.Task](s.userid, s.task, t)
ffgg, _ := json.Marshal(ggg)
tt.Data = string(ffgg)
_, err := cool.DBM(s.task.Model).Data(tt).FieldsEx("id").Insert()
if err != nil {
panic(err)
}
//panic(err)
}
func (s *UserService) TaskExec(t func([]model.TaskInfo) []model.TaskInfo, isdaliy bool) (ret bool) {
//待实现检测是否为每日任务
// if isdaliy {
// Exec[*model.Task](s.userid, s.task, func(tt []model.TaskInfo) []model.TaskInfo {
// var ttt = make([]model.TaskInfo, 500)
// //先重置每日
// for k, v := range tt {
// if k > 400 && !IsToday(v.LastResetTime) { //判断是每日任务
// v.Status = 0 //重置+自动接受每日任务
// v.LastResetTime = time.Now()
// }
// ttt = append(ttt, v)
// }
// return ttt
// })
// }
return Exec[*model.Task](s.userid, s.task, t)
// m := cool.DBM(s.task.Model).Where("player_id", s.userid)
// var tt model.Task
// m.Scan(&tt)
@@ -102,6 +129,23 @@ func IsToday(t time.Time) bool {
// return conditions.Status == 3
// }
// return false
// return false
// })
// }
// func (s *UserService) GenTask() [500]byte {
// ret := [500]byte{}
// Exec[*model.Task](s.userid, s.task, func(tt []model.TaskInfo) []model.TaskInfo {
// //先重置每日
// for _, v := range tt {
// ret[v.TaskID] = v.Status
// }
// return tt
// })
// return ret
// }

View File

@@ -10,6 +10,7 @@ type UserService struct {
task *cool.Service //任务
reg *cool.Service //注册
pet *cool.Service //精灵
}
func NewUserService(id uint32) *UserService {
@@ -21,6 +22,7 @@ func NewUserService(id uint32) *UserService {
reg: &cool.Service{
Model: model.NewPlayer(),
},
pet: &cool.Service{Model: model.NewPet()},
}
}