feat: 重构任务奖励系统并增加宠物技能和皮肤奖励
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

将任务奖励逻辑重构到单独的文件中,增加对宠物技能和皮肤奖励的支持,优化任务完成处理流程
This commit is contained in:
xinian
2026-04-11 19:25:59 +08:00
parent ecc483a11a
commit f6aa0c3339
13 changed files with 536 additions and 188 deletions

View File

@@ -71,6 +71,10 @@ func roundFloat32(val float32, decimals int) float32 {
return float32(math.Round(float64(val*shift))) / shift
}
func randomFloat32Range(r *rand.Rand, min, max float32) float32 {
return min + float32(r.Float64())*(max-min)
}
// 预处理矩阵:自动修正越界参数,确保合规
func ProcessOffspringMatrix(mat [4][5]float32) [4][5]float32 {
var processedMat [4][5]float32
@@ -118,71 +122,62 @@ func calculateMatrixHash(mm MonsterMatrix) string {
return hex.EncodeToString(h.Sum(nil))
}
// GenerateRandomParentMatrix 调整随机参数范围,解决暗色问题,贴合你的示例
// GenerateRandomParentMatrix 生成色域更大的父矩阵,同时抬高黑场避免整只精灵发黑
func GenerateRandomParentMatrix() MonsterMatrix {
// 1. 保留高熵种子逻辑(确保唯一性,不变)
salt := "player-matrix-random-seed"
now := time.Now().UnixNano()
randomNum := rand.Int63()
seed := now ^ randomNum ^ int64(hashString(salt))
r := rand.New(rand.NewSource(seed))
// 2. 调整随机参数范围(贴合你的示例,解决暗色问题)
// 对比你的示例:亮度大多在[-30,30],对比度[0.7,1.7],饱和度[0.4,1.4],偏移[-10,10]
params := struct {
brightness float32 // 从[-125,125]调整为[-30,60],避免过低亮度
contrast float32 // 从[0.4,2.2]调整为[0.7,1.7],贴合你的示例
saturation float32 // 从[0.1,2.0]调整为[0.4,1.4],避免低饱和度灰暗
hueRotate float32 // 从[-180,180]调整为[-50,50],减少极端色相导致的暗沉
rOffset float32 // 从[-50,50]调整为[-10,10],避免亮度偏移过大
gOffset float32 // 同上
bOffset float32 // 同上
}{
brightness: float32(r.Float64()*90 - 30), // [-30, 60](贴合示例亮度范围,减少暗色)
contrast: float32(r.Float64()*1.0 + 0.7), // [0.7, 1.7](与你的示例完全匹配)
saturation: float32(r.Float64()*1.0 + 0.4), // [0.4, 1.4](与你的示例完全匹配)
hueRotate: float32(r.Float64()*100 - 50), // [-50, 50](避免极端色相)
rOffset: float32(r.Float64()*20 - 10), // [-10, 10](小幅亮度偏移,贴合示例)
gOffset: float32(r.Float64()*20 - 10), // 同上
bOffset: float32(r.Float64()*20 - 10), // 同上
matrix := newIdentityMatrix()
dominant := r.Intn(3)
accent := (dominant + 1 + r.Intn(2)) % 3
baseLift := randomFloat32Range(r, 10, 24)
for row := 0; row < 3; row++ {
for col := 0; col < 3; col++ {
value := randomFloat32Range(r, -0.22, 0.22)
if row == col {
value = randomFloat32Range(r, 0.8, 1.45)
}
if row == dominant && col == dominant {
value += randomFloat32Range(r, 0.35, 0.7)
}
if row == accent && col == accent {
value += randomFloat32Range(r, 0.1, 0.3)
}
if row == dominant && col != row {
value += randomFloat32Range(r, -0.12, 0.28)
}
matrix[row][col] = clampFloat32(roundFloat32(value, FloatPrecision), MatrixMinVal, MatrixMaxVal)
}
}
// 3. 矩阵计算逻辑不变(确保与你的示例参数分布一致)
matrix := newIdentityMatrix()
contrast := params.contrast
matrix[0][0] *= contrast
matrix[1][1] *= contrast
matrix[2][2] *= contrast
matrix[0][4] = baseLift + randomFloat32Range(r, -5, 10)
matrix[1][4] = baseLift + randomFloat32Range(r, -5, 10)
matrix[2][4] = baseLift + randomFloat32Range(r, -5, 10)
matrix[dominant][4] += randomFloat32Range(r, 8, 18)
matrix[accent][4] += randomFloat32Range(r, 2, 8)
sat := params.saturation
grayR, grayG, grayB := float32(0.299)*(1-sat), float32(0.587)*(1-sat), float32(0.114)*(1-sat)
matrix[0][0] = grayR + sat*matrix[0][0]
matrix[0][1], matrix[0][2] = grayG, grayB
matrix[1][0] = grayR
matrix[1][1] = grayG + sat*matrix[1][1]
matrix[1][2] = grayB
matrix[2][0] = grayR
matrix[2][1] = grayG
matrix[2][2] = grayB + sat*matrix[2][2]
avgBrightness := (matrix[0][4] + matrix[1][4] + matrix[2][4]) / 3
if avgBrightness < 12 {
lift := 12 - avgBrightness + randomFloat32Range(r, 0, 6)
for row := 0; row < 3; row++ {
matrix[row][4] += lift
}
}
if matrix[dominant][4] < 16 {
matrix[dominant][4] = 16 + randomFloat32Range(r, 0, 10)
}
angle := params.hueRotate * float32(math.Pi) / 180
cos, sin := float32(math.Cos(float64(angle))), float32(math.Sin(float64(angle)))
r1, g1, b1 := matrix[0][0], matrix[0][1], matrix[0][2]
r2, g2, b2 := matrix[1][0], matrix[1][1], matrix[1][2]
for row := 0; row < 3; row++ {
for col := 0; col < 3; col++ {
matrix[row][col] = clampFloat32(roundFloat32(matrix[row][col], FloatPrecision), MatrixMinVal, MatrixMaxVal)
}
matrix[row][4] = clampFloat32(roundFloat32(matrix[row][4], FloatPrecision), BrightnessMinVal, BrightnessMaxVal)
}
matrix[0][0] = r1*cos - r2*sin
matrix[0][1] = r1*sin + r2*cos
matrix[1][0] = g1*cos - g2*sin
matrix[1][1] = g1*sin + g2*cos
matrix[2][0] = b1*cos - b2*sin
matrix[2][1] = b1*sin + b2*cos
// 亮度偏移:范围缩小后,避免过暗/过亮
matrix[0][4] = params.brightness + params.rOffset
matrix[1][4] = params.brightness + params.gOffset
matrix[2][4] = params.brightness + params.bOffset
// 4. 封装为MonsterMatrix不变
parent := MonsterMatrix{
Matrix: matrix,
Hash: "",

View File

@@ -32,6 +32,7 @@ type MapNode struct {
IsBroadcast uint32 `gorm:"type:int;default:0;comment:'广播模型ID(0表示不广播)'" json:"is_broadcast"`
IsGroupBoss uint32 `gorm:"type:int;default:0;comment:'是否为组队Boss(0否1是)'" json:"is_group_boss" description:"是否为组队Boss"`
PkFlag uint32 `gorm:"type:int;default:0;comment:'是否单精灵对战(0否1是)'" json:"pk_flag" description:"是否单精灵对战"`
TriggerPlotID uint32 `gorm:"default:0;comment:'触发剧情ID0表示无剧情'" json:"trigger_plot_id" description:"触发剧情ID"`

View File

@@ -26,6 +26,9 @@ type TaskConfig struct {
// 奖励配置
ItemRewardIds []uint32 `gorm:"not null;type:jsonb;default:'[]';comment:'绑定奖励物品ID数组关联item_gift表主键'" json:"item_reward_ids" description:"奖励物品数组"`
ElfRewardIds uint32 `gorm:"not null;default:0;comment:'绑定奖励精灵ID关联elf_gift表主键'" json:"elf_reward_ids" description:"绑定奖励精灵ID"`
RewardPetID uint32 `gorm:"not null;default:0;comment:'宠物相关奖励生效的目标精灵ID0表示不对已有精灵发放'" json:"reward_pet_id" description:"目标精灵ID"`
TrainSkillIDs []uint32 `gorm:"not null;type:jsonb;default:'[]';comment:'任务奖励的特训技能ID数组'" json:"train_skill_ids" description:"特训技能奖励数组"`
SkinIDs []uint32 `gorm:"not null;type:jsonb;default:'[]';comment:'任务奖励的皮肤ID数组'" json:"skin_ids" description:"皮肤奖励数组"`
//绑定奖励
TitleRewardIds uint32 `gorm:"not null;default:0;comment:'绑定奖励称号'" json:"title_reward_ids" description:"绑定奖励称号"`

View File

@@ -2,7 +2,6 @@ package service
import (
"blazing/common/data"
"blazing/common/utils"
"blazing/cool"
"blazing/modules/config/model"
"context"
@@ -43,18 +42,52 @@ func (s *ShinyService) ModifyBefore(ctx context.Context, method string, param g.
}
return nil
}
func (s *ShinyService) RandShiny(id uint32) *data.GlowFilter {
func (s *ShinyService) listByPetID(id uint32) []model.ColorfulSkin {
var ret []model.ColorfulSkin
// 执行 Raw SQL 并扫描返回值
dbm_enable(s.Model).
Wheref(`bind_elf_ids @> ?::jsonb`, id).
Wheref(`jsonb_typeof(bind_elf_ids) = ?`, "array").Scan(&ret)
Wheref(`jsonb_typeof(bind_elf_ids) = ?`, "array").
Scan(&ret)
return ret
}
func pickColorfulSkinByWeight(items []model.ColorfulSkin) *model.ColorfulSkin {
if len(items) == 0 {
return nil
}
totalWeight := 0
for _, item := range items {
if item.ElfProbability > 0 {
totalWeight += int(item.ElfProbability)
}
}
if totalWeight <= 0 {
return &items[grand.Intn(len(items))]
}
randomValue := grand.Intn(totalWeight)
for i := range items {
weight := int(items[i].ElfProbability)
if weight <= 0 {
continue
}
if randomValue < weight {
return &items[i]
}
randomValue -= weight
}
return &items[0]
}
func (s *ShinyService) RandShiny(id uint32) *data.GlowFilter {
ret := s.listByPetID(id)
for _, v := range ret {
//print(v.ID)
id := v.ID
if grand.Meet(int(v.ElfProbability), 1000) {
@@ -74,32 +107,16 @@ func (s *ShinyService) RandShiny(id uint32) *data.GlowFilter {
var t = data.GetDef()
t.ColorMatrixFilter = model.GenerateRandomOffspringMatrix().Get()
t.ColorMatrixFilter = model.GenerateRandomParentMatrix().Get()
return &t
}
func (s *ShinyService) RandomByWeightShiny(id uint32) *data.GlowFilter {
var ret []model.ColorfulSkin
// 执行 Raw SQL 并扫描返回值
dbm_enable(s.Model).
Wheref(`bind_elf_ids @> ?::jsonb`, id).
Wheref(`jsonb_typeof(bind_elf_ids) = ?`, "array").Scan(&ret)
if len(ret) == 0 {
r := pickColorfulSkinByWeight(s.listByPetID(id))
if r == nil {
return nil
}
var rets []model.ColorfulSkin
var props []int
for _, v := range ret {
rets = append(rets, v)
props = append(props, int(v.ElfProbability))
}
r, _ := utils.RandomByWeight(rets, props)
if cool.Config.ServerInfo.IsVip == 0 {
m := cool.DBM(s.Model).Where("id", r.ID)
m.Increment("refresh_count", 1)

BIN
modules/model.test Executable file

Binary file not shown.

View File

@@ -3,6 +3,7 @@ package service
import (
"blazing/common/utils"
"blazing/cool"
configmodel "blazing/modules/config/model"
config "blazing/modules/config/service"
"blazing/modules/player/model"
@@ -27,54 +28,58 @@ func (s *TalkService) reset(flag int) {
s.dbm(s.Model).Where("talk_id", flag).Data("count", 0, "last_reset_time", gtime.Now()).Update()
}
//实现挖矿次数确认
func (s *TalkService) Cheak(mapid uint32, flag int) (int, bool) {
m1 := s.dbm(s.Model)
func (s *TalkService) progress(flag int) (uint32, *model.Talk, *configmodel.MineralCollectionConfig, bool) {
var talks *model.Talk
m1.Where("talk_id", flag).Scan(&talks)
s.dbm(s.Model).Where("talk_id", flag).Scan(&talks)
cfg := config.NewTalkConfigService().GetCache(flag)
if cfg == nil {
return 0, talks, nil, false
}
if talks == nil {
return 0, true //如果表里没有记载数据,那么就可以直接挖矿
return 0, nil, cfg, true
}
c := config.NewTalkConfigService().GetCache(flag)
//因为这个是挖一次更新一次,而且是实时更新的,如果更新日期是今天,那么就可以确认不用再重置,否则就需要重置挖矿记录
if c == nil {
return 0, false //没在地图
}
switch c.Limit {
switch cfg.Limit {
case 0:
if !utils.IsToday(talks.LastResetTime) {
s.reset(flag)
return int(0), true
return 0, talks, cfg, true
}
case 1:
if !utils.IsWEEK(talks.LastResetTime) {
s.reset(flag)
return int(0), true
return 0, talks, cfg, true
}
case 2:
if !utils.IsMon(talks.LastResetTime) {
s.reset(flag)
return int(0), true
return 0, talks, cfg, true
}
}
if uint32(mapid) != c.MapID {
return talks.Count, talks, cfg, true
}
func (s *TalkService) Progress(flag int) (uint32, bool) {
count, _, _, ok := s.progress(flag)
return count, ok
}
//实现挖矿次数确认
func (s *TalkService) Cheak(mapid uint32, flag int) (int, bool) {
count, _, cfg, ok := s.progress(flag)
if !ok || cfg == nil {
return 0, false //没在地图
}
if talks.Count >= c.DailyCollectCount {
if uint32(mapid) != cfg.MapID {
return 0, false //没在地图
}
if count >= cfg.DailyCollectCount {
return 0, false
}
return int(talks.Count), true //int(config.MaxDailyCnt - talks.Count)
return int(count), true //int(config.MaxDailyCnt - talks.Count)
}

BIN
modules/service.test Executable file

Binary file not shown.