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

@@ -43,7 +43,7 @@ func (Controller) PlayerFightBoss(req *ChallengeBossInboundInfo, p *player.Playe
} }
p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC p.Fightinfo.Status = fightinfo.BattleMode.FIGHT_WITH_NPC
p.Fightinfo.Mode = fightinfo.BattleMode.MULTI_MODE p.Fightinfo.Mode = resolveMapNodeFightMode(mapNode)
ai := player.NewAI_player(monsterInfo) ai := player.NewAI_player(monsterInfo)
ai.CanCapture = resolveBossCaptureRate(bossConfigs[0].IsCapture, leadMonsterID) ai.CanCapture = resolveBossCaptureRate(bossConfigs[0].IsCapture, leadMonsterID)
@@ -76,12 +76,23 @@ func startMapBossFight(
oppPets := ai.GetPetInfo(0) oppPets := ai.GetPetInfo(0)
if mapNode != nil && mapNode.IsGroupBoss != 0 { if mapNode != nil && mapNode.IsGroupBoss != 0 {
if len(ourPets) > 0 && len(oppPets) > 0 { if len(ourPets) > 0 && len(oppPets) > 0 {
return fight.NewLegacyGroupFightSingleController(p, ai, ourPets, oppPets, groupBossSlotLimit, fn) slotLimit := groupBossSlotLimit
if mapNode.PkFlag != 0 {
slotLimit = 1
}
return fight.NewLegacyGroupFightSingleController(p, ai, ourPets, oppPets, slotLimit, fn)
} }
} }
return fight.NewFight(p, ai, ourPets, oppPets, fn) return fight.NewFight(p, ai, ourPets, oppPets, fn)
} }
func resolveMapNodeFightMode(mapNode *configmodel.MapNode) uint32 {
if mapNode != nil && mapNode.PkFlag != 0 {
return fightinfo.BattleMode.SINGLE_MODE
}
return fightinfo.BattleMode.MULTI_MODE
}
// OnPlayerFightNpcMonster 战斗野怪 // OnPlayerFightNpcMonster 战斗野怪
func (Controller) OnPlayerFightNpcMonster(req *FightNpcMonsterInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) { func (Controller) OnPlayerFightNpcMonster(req *FightNpcMonsterInboundInfo, p *player.Player) (result *fight.NullOutboundInfo, err errorcode.ErrorCode) {
if err = p.CanFight(); err != 0 { if err = p.CanFight(); err != 0 {

View File

@@ -77,31 +77,16 @@ func (h Controller) CompleteTask(data1 *CompleteTaskInboundInfo, c *player.Playe
// if service.NewTaskService().IsAcceptable(data1.TaskId) == nil { // if service.NewTaskService().IsAcceptable(data1.TaskId) == nil {
// return nil, errorcode.ErrorCodes.ErrSystemError // return nil, errorcode.ErrorCodes.ErrSystemError
// } // }
c.Info.SetTask(int(data1.TaskId), model.Completed)
result = &task.CompleteTaskOutboundInfo{ result = &task.CompleteTaskOutboundInfo{
TaskId: data1.TaskId, TaskId: data1.TaskId,
ItemList: make([]data.ItemInfo, 0), ItemList: make([]data.ItemInfo, 0),
} }
taskInfo := task.GetTaskInfo(int(data1.TaskId), int(data1.OutState)) if _, err = c.ApplyTaskCompletion(data1.TaskId, int(data1.OutState), result); err != 0 {
if taskInfo == nil { return nil, err
return nil, errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize
} }
if taskErr := c.Info.SetTask(int(data1.TaskId), model.Completed); taskErr != nil {
if taskInfo.Pet != nil { return nil, errorcode.ErrorCodes.ErrSystemError
c.Service.Pet.PetAdd(taskInfo.Pet, 0)
result.CaptureTime = taskInfo.Pet.CatchTime
result.PetTypeId = taskInfo.Pet.ID
}
for _, item := range taskInfo.ItemList {
success := c.ItemAdd(item.ItemId, item.ItemCnt)
if success {
result.ItemList = append(result.ItemList, item)
}
} }
return result, 0 //通过PUB/SUB回包 return result, 0 //通过PUB/SUB回包

View File

@@ -1,23 +1,11 @@
package player package player
import ( import (
"blazing/logic/service/fight/info"
"blazing/logic/service/task"
"blazing/modules/player/model" "blazing/modules/player/model"
"github.com/pointernil/bitset32" "github.com/pointernil/bitset32"
) )
// 辅助函数:获取任务奖励,封装逻辑便于复用和统一检查
// 返回nil表示无奖励
func (p *Player) getTaskGift(taskID int, ot int) *task.TaskResult {
// 防御性检查taskID非法时直接返回nil
if taskID <= 0 {
return nil
}
return task.GetTaskInfo(taskID, ot)
}
// SptCompletedTask 完成任务(单分支) // SptCompletedTask 完成任务(单分支)
// 优化点:仅当奖励存在时,才完成任务并发放奖励 // 优化点:仅当奖励存在时,才完成任务并发放奖励
func (p *Player) SptCompletedTask(taskID int, ot int) { func (p *Player) SptCompletedTask(taskID int, ot int) {
@@ -29,15 +17,17 @@ func (p *Player) SptCompletedTask(taskID int, ot int) {
return return
} }
// 2. 核心逻辑:先检查奖励是否存在,无奖励则直接返回(不完成任务) if !p.canCompleteTaskReward(taskID, ot) {
gift := p.getTaskGift(taskID, ot) return
if gift == nil { }
granted, err := p.ApplyTaskCompletion(uint32(taskID), ot, nil)
if err != 0 {
return return
} }
// 3. 奖励存在时,才标记任务完成 + 发放奖励
p.Info.SetTask(taskID, model.Completed) p.Info.SetTask(taskID, model.Completed)
p.bossgive(taskID, ot) p.SendTaskCompletionBonus(uint32(taskID), granted)
} }
// TawerCompletedTask 完成塔类任务(多分支) // TawerCompletedTask 完成塔类任务(多分支)
@@ -48,10 +38,12 @@ func (p *Player) TawerCompletedTask(taskID int, ot int) {
} }
// 处理默认分支ot=-1仅奖励存在时才完成主任务 // 处理默认分支ot=-1仅奖励存在时才完成主任务
if p.Info.GetTask(taskID) != model.Completed { if p.Info.GetTask(taskID) != model.Completed {
defaultGift := p.getTaskGift(taskID, -1) if p.canCompleteTaskReward(taskID, -1) {
if defaultGift != nil { // 奖励存在才标记主任务完成 granted, err := p.ApplyTaskCompletion(uint32(taskID), -1, nil)
p.Info.SetTask(taskID, model.Completed) if err == 0 {
p.bossgive(taskID, -1) p.Info.SetTask(taskID, model.Completed)
p.SendTaskCompletionBonus(uint32(taskID), granted)
}
} }
} }
@@ -61,53 +53,19 @@ func (p *Player) TawerCompletedTask(taskID int, ot int) {
return return
} }
// 核心检查:指定分支的奖励是否存在 if !p.canCompleteTaskReward(taskID, ot) {
branchGift := p.getTaskGift(taskID, ot)
if branchGift == nil {
return return
} }
r := bitset32.From(taskData.Data) r := bitset32.From(taskData.Data)
if !r.Test(uint(ot)) { if !r.Test(uint(ot)) {
r.Set(uint(ot)) r.Set(uint(ot))
p.bossgive(taskID, ot) granted, rewardErr := p.ApplyTaskCompletion(uint32(taskID), ot, nil)
if rewardErr != 0 {
return
}
p.SendTaskCompletionBonus(uint32(taskID), granted)
taskData.Data = r.Bytes() taskData.Data = r.Bytes()
_ = p.Service.Task.SetTask(taskData) _ = p.Service.Task.SetTask(taskData)
} }
} }
// bossgive 发放任务奖励(逻辑保持不变,仅补充注释)
func (p *Player) bossgive(taskID int, ot int) {
gift := p.getTaskGift(taskID, ot)
if gift == nil {
return
}
res := &info.S2C_GET_BOSS_MONSTER{
BonusID: uint32(taskID),
}
// 发放宠物奖励
if gift.Pet != nil {
p.Service.Pet.PetAdd(gift.Pet, 0)
res.PetID = gift.Pet.ID
res.CaptureTm = gift.Pet.CatchTime
}
// 发放道具奖励(仅成功添加的道具才返回给前端)
for _, item := range gift.ItemList {
if success := p.ItemAdd(item.ItemId, item.ItemCnt); success {
res.AddItemInfo(item)
}
}
// 发放称号奖励
if gift.Title != 0 {
p.GiveTitle(gift.Title)
}
// 发送奖励通知给前端
if res.HasReward() {
p.SendPackCmd(8004, res)
}
}

View File

@@ -0,0 +1,193 @@
package player
import (
"blazing/common/data"
"blazing/common/socket/errorcode"
fightinfo "blazing/logic/service/fight/info"
tasklogic "blazing/logic/service/task"
configmodel "blazing/modules/config/model"
configservice "blazing/modules/config/service"
playermodel "blazing/modules/player/model"
"sync"
)
type TaskCompletionContext struct {
TaskID uint32
OutState int
Config *configmodel.TaskConfig
Reward *tasklogic.TaskResult
Result *tasklogic.CompleteTaskOutboundInfo
SkipDefaultReward bool
}
type TaskCompletionHandler func(*Player, *TaskCompletionContext) errorcode.ErrorCode
var taskCompletionRegistry = struct {
sync.RWMutex
handlers map[uint32]TaskCompletionHandler
}{
handlers: make(map[uint32]TaskCompletionHandler),
}
type taskRewardGrantResult struct {
Pet *playermodel.PetInfo
Items []data.ItemInfo
}
func RegisterTaskCompletionHandler(taskID uint32, handler TaskCompletionHandler) {
if taskID == 0 || handler == nil {
return
}
taskCompletionRegistry.Lock()
taskCompletionRegistry.handlers[taskID] = handler
taskCompletionRegistry.Unlock()
}
func RegisterTaskTalkLimitHandler(taskID, talkID, needCount uint32) {
RegisterTaskCompletionHandler(taskID, func(p *Player, _ *TaskCompletionContext) errorcode.ErrorCode {
if p == nil || p.Service == nil || p.Service.Talk == nil {
return errorcode.ErrorCodes.ErrSystemError
}
currentCount, ok := p.Service.Talk.Progress(int(talkID))
if !ok || currentCount < needCount {
return errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize
}
return 0
})
}
func (p *Player) getTaskGift(taskID int, outState int) *tasklogic.TaskResult {
if taskID <= 0 {
return nil
}
return tasklogic.GetTaskInfo(taskID, outState)
}
func hasTaskCompletionHandler(taskID uint32) bool {
taskCompletionRegistry.RLock()
_, ok := taskCompletionRegistry.handlers[taskID]
taskCompletionRegistry.RUnlock()
return ok
}
func getTaskCompletionHandler(taskID uint32) TaskCompletionHandler {
taskCompletionRegistry.RLock()
handler := taskCompletionRegistry.handlers[taskID]
taskCompletionRegistry.RUnlock()
return handler
}
func (p *Player) canCompleteTaskReward(taskID, outState int) bool {
if taskID <= 0 {
return false
}
return p.getTaskGift(taskID, outState) != nil || hasTaskCompletionHandler(uint32(taskID))
}
func (p *Player) ApplyTaskCompletion(taskID uint32, outState int, result *tasklogic.CompleteTaskOutboundInfo) (*taskRewardGrantResult, errorcode.ErrorCode) {
if p == nil {
return nil, errorcode.ErrorCodes.ErrSystemError
}
ctx := &TaskCompletionContext{
TaskID: taskID,
OutState: outState,
Config: configservice.NewTaskService().Get(int(taskID), outState),
Reward: tasklogic.GetTaskInfo(int(taskID), outState),
Result: result,
}
if ctx.Reward == nil && !hasTaskCompletionHandler(taskID) {
return nil, errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize
}
if handler := getTaskCompletionHandler(taskID); handler != nil {
if err := handler(p, ctx); err != 0 {
return nil, err
}
}
if ctx.SkipDefaultReward {
if result != nil {
result.ItemLen = uint32(len(result.ItemList))
}
return &taskRewardGrantResult{Items: make([]data.ItemInfo, 0)}, 0
}
if ctx.Reward == nil {
return nil, errorcode.ErrorCodes.ErrNeedCompleteTaskForPrize
}
return p.grantTaskReward(ctx.Reward, result), 0
}
func (p *Player) grantTaskReward(reward *tasklogic.TaskResult, result *tasklogic.CompleteTaskOutboundInfo) *taskRewardGrantResult {
granted := &taskRewardGrantResult{
Items: make([]data.ItemInfo, 0),
}
if reward == nil {
if result != nil {
result.ItemLen = uint32(len(result.ItemList))
}
return granted
}
if reward.Pet != nil {
p.Service.Pet.PetAdd(reward.Pet, 0)
granted.Pet = reward.Pet
if result != nil {
result.CaptureTime = reward.Pet.CatchTime
result.PetTypeId = reward.Pet.ID
}
}
for _, item := range reward.ItemList {
if !p.ItemAdd(item.ItemId, item.ItemCnt) {
continue
}
granted.Items = append(granted.Items, item)
if result != nil {
result.ItemList = append(result.ItemList, item)
}
}
if reward.Title != 0 {
p.GiveTitle(reward.Title)
}
if reward.RewardPetID != 0 {
p.GrantTaskPetRewards(reward.RewardPetID, reward.TrainSkillIDs, reward.SkinIDs)
}
if result != nil {
result.ItemLen = uint32(len(result.ItemList))
}
return granted
}
func (p *Player) SendTaskCompletionBonus(bonusID uint32, granted *taskRewardGrantResult) {
if p == nil {
return
}
res := &fightinfo.S2C_GET_BOSS_MONSTER{
BonusID: bonusID,
}
if granted != nil && granted.Pet != nil {
res.PetID = granted.Pet.ID
res.CaptureTm = granted.Pet.CatchTime
}
if granted != nil {
for _, item := range granted.Items {
res.AddItemInfo(item)
}
}
if res.HasReward() {
p.SendPackCmd(8004, res)
}
}

View File

@@ -0,0 +1,115 @@
package player
import "blazing/modules/player/model"
func applyTaskRewardToPetInfo(pet *model.PetInfo, trainSkillIDs, skinIDs []uint32) bool {
if pet == nil {
return false
}
changed := false
mergedSkills := mergeTaskRewardIDs(pet.ExtSKill, trainSkillIDs)
if len(mergedSkills) != len(pet.ExtSKill) {
pet.ExtSKill = mergedSkills
changed = true
}
mergedSkins := mergeTaskRewardIDs(pet.ExtSkin, skinIDs)
if len(mergedSkins) != len(pet.ExtSkin) {
pet.ExtSkin = mergedSkins
changed = true
}
if pet.SkinID == 0 {
for _, skinID := range mergedSkins {
if skinID == 0 {
continue
}
pet.SkinID = skinID
changed = true
break
}
}
return changed
}
func mergeTaskRewardIDs(dst []uint32, src []uint32) []uint32 {
if len(src) == 0 {
return dst
}
seen := make(map[uint32]struct{}, len(dst)+len(src))
result := make([]uint32, 0, len(dst)+len(src))
for _, id := range dst {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
for _, id := range src {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
return result
}
func (p *Player) GrantTaskPetRewards(targetPetID uint32, trainSkillIDs, skinIDs []uint32) bool {
if p == nil || targetPetID == 0 || (len(trainSkillIDs) == 0 && len(skinIDs) == 0) {
return false
}
changed := false
for i := range p.Info.PetList {
if p.Info.PetList[i].ID != targetPetID {
continue
}
if applyTaskRewardToPetInfo(&p.Info.PetList[i], trainSkillIDs, skinIDs) {
changed = true
}
}
for i := range p.Info.BackupPetList {
if p.Info.BackupPetList[i].ID != targetPetID {
continue
}
if applyTaskRewardToPetInfo(&p.Info.BackupPetList[i], trainSkillIDs, skinIDs) {
changed = true
}
}
if p.Service == nil || p.Service.Pet == nil {
return changed
}
allPets := p.Service.Pet.PetInfo(0)
for i := range allPets {
if allPets[i].Data.ID != targetPetID {
continue
}
if !applyTaskRewardToPetInfo(&allPets[i].Data, trainSkillIDs, skinIDs) {
continue
}
allPets[i].Data.CatchTime = allPets[i].CatchTime
if p.Service.Pet.Update(allPets[i].Data) {
changed = true
}
}
return changed
}

View File

@@ -10,8 +10,11 @@ import (
type TaskResult struct { type TaskResult struct {
Pet *model.PetInfo `json:"petTypeId" description:"发放的精灵ID"` // 发放的精灵ID Pet *model.PetInfo `json:"petTypeId" description:"发放的精灵ID"` // 发放的精灵ID
ItemList []data.ItemInfo `json:"itemList" description:"发放物品的数组"` // 发放物品的数组, ItemList []data.ItemInfo `json:"itemList" description:"发放物品的数组"` // 发放物品的数组,
Title uint32 `json:"title" description:"称号奖励"` Title uint32 `json:"title" description:"称号奖励"`
RewardPetID uint32 `json:"rewardPetId" description:"宠物相关奖励目标精灵ID"`
TrainSkillIDs []uint32 `json:"trainSkillIds" description:"特训技能奖励"`
SkinIDs []uint32 `json:"skinIds" description:"皮肤奖励"`
} }
func GetTaskInfo(id, ot int) *TaskResult { func GetTaskInfo(id, ot int) *TaskResult {
@@ -26,6 +29,12 @@ func GetTaskInfo(id, ot int) *TaskResult {
if pet != nil { if pet != nil {
ret.Pet = model.GenPetInfo(int(pet.MonID), int(pet.DV), int(pet.Nature), int(pet.Effect), int(pet.Lv), nil, 0) ret.Pet = model.GenPetInfo(int(pet.MonID), int(pet.DV), int(pet.Nature), int(pet.Effect), int(pet.Lv), nil, 0)
} }
ret.RewardPetID = r.RewardPetID
ret.TrainSkillIDs = append(ret.TrainSkillIDs, r.TrainSkillIDs...)
ret.SkinIDs = append(ret.SkinIDs, r.SkinIDs...)
if ret.Pet != nil {
applyTaskRewardPetExtras(ret.Pet, ret.TrainSkillIDs, ret.SkinIDs)
}
for _, itemID := range r.ItemRewardIds { for _, itemID := range r.ItemRewardIds {
iteminfo := service.NewItemService().GetItemCount(itemID) iteminfo := service.NewItemService().GetItemCount(itemID)
@@ -39,3 +48,59 @@ func GetTaskInfo(id, ot int) *TaskResult {
return ret return ret
} }
func applyTaskRewardPetExtras(pet *model.PetInfo, trainSkillIDs, skinIDs []uint32) {
if pet == nil {
return
}
if merged := mergeTaskRewardIDs(pet.ExtSKill, trainSkillIDs); len(merged) != len(pet.ExtSKill) {
pet.ExtSKill = merged
}
mergedSkins := mergeTaskRewardIDs(pet.ExtSkin, skinIDs)
if len(mergedSkins) != len(pet.ExtSkin) {
pet.ExtSkin = mergedSkins
}
if pet.SkinID == 0 {
for _, skinID := range mergedSkins {
if skinID != 0 {
pet.SkinID = skinID
break
}
}
}
}
func mergeTaskRewardIDs(dst []uint32, src []uint32) []uint32 {
if len(src) == 0 {
return dst
}
seen := make(map[uint32]struct{}, len(dst)+len(src))
result := make([]uint32, 0, len(dst)+len(src))
for _, id := range dst {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
for _, id := range src {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
result = append(result, id)
}
return result
}

View File

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

View File

@@ -32,6 +32,7 @@ type MapNode struct {
IsBroadcast uint32 `gorm:"type:int;default:0;comment:'广播模型ID(0表示不广播)'" json:"is_broadcast"` 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"` 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"` 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:"奖励物品数组"` 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"` 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:"绑定奖励称号"` TitleRewardIds uint32 `gorm:"not null;default:0;comment:'绑定奖励称号'" json:"title_reward_ids" description:"绑定奖励称号"`

View File

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

BIN
modules/model.test Executable file

Binary file not shown.

View File

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