|
| 1 | +package masterdata |
| 2 | + |
| 3 | +import ( |
| 4 | + "log" |
| 5 | + "sort" |
| 6 | + |
| 7 | + "lunar-tear/server/internal/utils" |
| 8 | +) |
| 9 | + |
| 10 | +type LabyrinthChapter struct { |
| 11 | + EventQuestChapterId int32 |
| 12 | + LatestSeasonNumber int32 |
| 13 | + StageOrders []int32 |
| 14 | +} |
| 15 | + |
| 16 | +type LabyrinthStageTier struct { |
| 17 | + QuestMissionClearCount int32 |
| 18 | + Rewards []RewardItem |
| 19 | +} |
| 20 | + |
| 21 | +type LabyrinthSeasonMilestone struct { |
| 22 | + HeadQuestId int32 |
| 23 | + HeadStageOrder int32 |
| 24 | + Rewards []RewardItem |
| 25 | +} |
| 26 | + |
| 27 | +type labyrinthStageKey struct { |
| 28 | + ChapterId int32 |
| 29 | + StageOrder int32 |
| 30 | +} |
| 31 | + |
| 32 | +type LabyrinthCatalog struct { |
| 33 | + ChaptersByOrder []LabyrinthChapter |
| 34 | + ClearRewardsByStage map[labyrinthStageKey][]RewardItem |
| 35 | + AccumTiersByStage map[labyrinthStageKey][]LabyrinthStageTier |
| 36 | + SeasonMilestonesByChapter map[int32][]LabyrinthSeasonMilestone |
| 37 | +} |
| 38 | + |
| 39 | +func (c *LabyrinthCatalog) StageClearReward(chapterId, stageOrder int32) []RewardItem { |
| 40 | + return c.ClearRewardsByStage[labyrinthStageKey{chapterId, stageOrder}] |
| 41 | +} |
| 42 | + |
| 43 | +func (c *LabyrinthCatalog) CollectAccumulationRewards(chapterId, stageOrder, oldCount, targetCount int32) ([]RewardItem, int32) { |
| 44 | + var items []RewardItem |
| 45 | + highest := int32(0) |
| 46 | + for _, t := range c.AccumTiersByStage[labyrinthStageKey{chapterId, stageOrder}] { |
| 47 | + if t.QuestMissionClearCount > oldCount && t.QuestMissionClearCount <= targetCount { |
| 48 | + items = append(items, t.Rewards...) |
| 49 | + if t.QuestMissionClearCount > highest { |
| 50 | + highest = t.QuestMissionClearCount |
| 51 | + } |
| 52 | + } |
| 53 | + } |
| 54 | + return items, highest |
| 55 | +} |
| 56 | + |
| 57 | +func (c *LabyrinthCatalog) SeasonMilestones(chapterId int32) []LabyrinthSeasonMilestone { |
| 58 | + return c.SeasonMilestonesByChapter[chapterId] |
| 59 | +} |
| 60 | + |
| 61 | +func LoadLabyrinthCatalog() *LabyrinthCatalog { |
| 62 | + seasonRows, err := utils.ReadTable[EntityMEventQuestLabyrinthSeason]("m_event_quest_labyrinth_season") |
| 63 | + if err != nil { |
| 64 | + log.Printf("[labyrinth] m_event_quest_labyrinth_season unavailable, labyrinth disabled: %v", err) |
| 65 | + return &LabyrinthCatalog{} |
| 66 | + } |
| 67 | + stageRows, err := utils.ReadTable[EntityMEventQuestLabyrinthStage]("m_event_quest_labyrinth_stage") |
| 68 | + if err != nil { |
| 69 | + log.Printf("[labyrinth] m_event_quest_labyrinth_stage unavailable, labyrinth disabled: %v", err) |
| 70 | + return &LabyrinthCatalog{} |
| 71 | + } |
| 72 | + |
| 73 | + // chapterId -> highest SeasonNumber |
| 74 | + latestSeason := make(map[int32]int32) |
| 75 | + for _, r := range seasonRows { |
| 76 | + if r.SeasonNumber > latestSeason[r.EventQuestChapterId] { |
| 77 | + latestSeason[r.EventQuestChapterId] = r.SeasonNumber |
| 78 | + } |
| 79 | + } |
| 80 | + // chapterId -> stage orders |
| 81 | + stagesByChapter := make(map[int32][]int32) |
| 82 | + for _, r := range stageRows { |
| 83 | + stagesByChapter[r.EventQuestChapterId] = append(stagesByChapter[r.EventQuestChapterId], r.StageOrder) |
| 84 | + } |
| 85 | + |
| 86 | + chapters := make([]LabyrinthChapter, 0, len(latestSeason)) |
| 87 | + for chapterId, season := range latestSeason { |
| 88 | + stages := stagesByChapter[chapterId] |
| 89 | + sort.Slice(stages, func(i, j int) bool { return stages[i] < stages[j] }) |
| 90 | + chapters = append(chapters, LabyrinthChapter{ |
| 91 | + EventQuestChapterId: chapterId, |
| 92 | + LatestSeasonNumber: season, |
| 93 | + StageOrders: stages, |
| 94 | + }) |
| 95 | + } |
| 96 | + sort.Slice(chapters, func(i, j int) bool { |
| 97 | + return chapters[i].EventQuestChapterId < chapters[j].EventQuestChapterId |
| 98 | + }) |
| 99 | + |
| 100 | + clearRewards, accumTiers, seasonMilestones := loadLabyrinthRewards(seasonRows, stageRows) |
| 101 | + |
| 102 | + log.Printf("labyrinth catalog loaded: %d chapters, %d stages with clear rewards, %d with accumulation rewards, %d chapters with season rewards", |
| 103 | + len(chapters), len(clearRewards), len(accumTiers), len(seasonMilestones)) |
| 104 | + return &LabyrinthCatalog{ |
| 105 | + ChaptersByOrder: chapters, |
| 106 | + ClearRewardsByStage: clearRewards, |
| 107 | + AccumTiersByStage: accumTiers, |
| 108 | + SeasonMilestonesByChapter: seasonMilestones, |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +func loadLabyrinthRewards(seasonRows []EntityMEventQuestLabyrinthSeason, stageRows []EntityMEventQuestLabyrinthStage) ( |
| 113 | + clearRewards map[labyrinthStageKey][]RewardItem, |
| 114 | + accumTiers map[labyrinthStageKey][]LabyrinthStageTier, |
| 115 | + seasonMilestones map[int32][]LabyrinthSeasonMilestone, |
| 116 | +) { |
| 117 | + rewardGroupRows, err := utils.ReadTable[EntityMEventQuestLabyrinthRewardGroup]("m_event_quest_labyrinth_reward_group") |
| 118 | + if err != nil { |
| 119 | + log.Printf("[labyrinth] m_event_quest_labyrinth_reward_group unavailable, rewards disabled: %v", err) |
| 120 | + return nil, nil, nil |
| 121 | + } |
| 122 | + |
| 123 | + // reward group id -> reward items |
| 124 | + itemsByRewardGroup := make(map[int32][]RewardItem) |
| 125 | + for _, r := range rewardGroupRows { |
| 126 | + itemsByRewardGroup[r.EventQuestLabyrinthRewardGroupId] = append(itemsByRewardGroup[r.EventQuestLabyrinthRewardGroupId], RewardItem{ |
| 127 | + PossessionType: r.PossessionType, |
| 128 | + PossessionId: r.PossessionId, |
| 129 | + Count: r.Count, |
| 130 | + }) |
| 131 | + } |
| 132 | + |
| 133 | + // per-stage one-time clear reward |
| 134 | + clearRewards = make(map[labyrinthStageKey][]RewardItem) |
| 135 | + for _, r := range stageRows { |
| 136 | + if r.StageClearRewardGroupId == 0 { |
| 137 | + continue |
| 138 | + } |
| 139 | + if items := itemsByRewardGroup[r.StageClearRewardGroupId]; len(items) > 0 { |
| 140 | + clearRewards[labyrinthStageKey{r.EventQuestChapterId, r.StageOrder}] = items |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + if accumGroupRows, err := utils.ReadTable[EntityMEventQuestLabyrinthStageAccumulationRewardGroup]("m_event_quest_labyrinth_stage_accumulation_reward_group"); err != nil { |
| 145 | + log.Printf("[labyrinth] m_event_quest_labyrinth_stage_accumulation_reward_group unavailable, accumulation rewards disabled: %v", err) |
| 146 | + } else { |
| 147 | + // accumulation group id -> tiers (threshold + resolved reward items) |
| 148 | + tiersByGroup := make(map[int32][]LabyrinthStageTier) |
| 149 | + for _, r := range accumGroupRows { |
| 150 | + tiersByGroup[r.EventQuestLabyrinthStageAccumulationRewardGroupId] = append(tiersByGroup[r.EventQuestLabyrinthStageAccumulationRewardGroupId], LabyrinthStageTier{ |
| 151 | + QuestMissionClearCount: r.QuestMissionClearCount, |
| 152 | + Rewards: itemsByRewardGroup[r.EventQuestLabyrinthRewardGroupId], |
| 153 | + }) |
| 154 | + } |
| 155 | + accumTiers = make(map[labyrinthStageKey][]LabyrinthStageTier) |
| 156 | + for _, r := range stageRows { |
| 157 | + if r.StageAccumulationRewardGroupId == 0 { |
| 158 | + continue |
| 159 | + } |
| 160 | + tiers := tiersByGroup[r.StageAccumulationRewardGroupId] |
| 161 | + sort.Slice(tiers, func(i, j int) bool { |
| 162 | + return tiers[i].QuestMissionClearCount < tiers[j].QuestMissionClearCount |
| 163 | + }) |
| 164 | + accumTiers[labyrinthStageKey{r.EventQuestChapterId, r.StageOrder}] = tiers |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + // per-chapter season-reward milestones |
| 169 | + if seasonRewardRows, err := utils.ReadTable[EntityMEventQuestLabyrinthSeasonRewardGroup]("m_event_quest_labyrinth_season_reward_group"); err != nil { |
| 170 | + log.Printf("[labyrinth] m_event_quest_labyrinth_season_reward_group unavailable, season rewards disabled: %v", err) |
| 171 | + } else { |
| 172 | + seasonMilestones = buildLabyrinthSeasonMilestones(seasonRows, seasonRewardRows, itemsByRewardGroup) |
| 173 | + } |
| 174 | + |
| 175 | + return clearRewards, accumTiers, seasonMilestones |
| 176 | +} |
| 177 | + |
| 178 | +func buildLabyrinthSeasonMilestones( |
| 179 | + seasonRows []EntityMEventQuestLabyrinthSeason, |
| 180 | + seasonRewardRows []EntityMEventQuestLabyrinthSeasonRewardGroup, |
| 181 | + itemsByRewardGroup map[int32][]RewardItem, |
| 182 | +) map[int32][]LabyrinthSeasonMilestone { |
| 183 | + // chapter -> SeasonRewardGroupId (all seasons of a chapter share one) |
| 184 | + groupByChapter := make(map[int32]int32) |
| 185 | + for _, r := range seasonRows { |
| 186 | + groupByChapter[r.EventQuestChapterId] = r.SeasonRewardGroupId |
| 187 | + } |
| 188 | + // SeasonRewardGroupId -> its rows, in table order |
| 189 | + rowsByGroup := make(map[int32][]EntityMEventQuestLabyrinthSeasonRewardGroup) |
| 190 | + for _, r := range seasonRewardRows { |
| 191 | + rowsByGroup[r.EventQuestLabyrinthSeasonRewardGroupId] = append(rowsByGroup[r.EventQuestLabyrinthSeasonRewardGroupId], r) |
| 192 | + } |
| 193 | + |
| 194 | + milestones := make(map[int32][]LabyrinthSeasonMilestone) |
| 195 | + for chapterId, seasonGroupId := range groupByChapter { |
| 196 | + rows := rowsByGroup[seasonGroupId] |
| 197 | + if len(rows) == 0 { |
| 198 | + continue |
| 199 | + } |
| 200 | + // rank distinct reward-group ids ascending -> 1-based head stage order |
| 201 | + stageByRewardGroup := make(map[int32]int32) |
| 202 | + var distinct []int32 |
| 203 | + for _, r := range rows { |
| 204 | + if _, seen := stageByRewardGroup[r.EventQuestLabyrinthRewardGroupId]; !seen { |
| 205 | + stageByRewardGroup[r.EventQuestLabyrinthRewardGroupId] = 0 |
| 206 | + distinct = append(distinct, r.EventQuestLabyrinthRewardGroupId) |
| 207 | + } |
| 208 | + } |
| 209 | + sort.Slice(distinct, func(i, j int) bool { return distinct[i] < distinct[j] }) |
| 210 | + for i, gid := range distinct { |
| 211 | + stageByRewardGroup[gid] = int32(i + 1) |
| 212 | + } |
| 213 | + |
| 214 | + list := make([]LabyrinthSeasonMilestone, 0, len(rows)) |
| 215 | + for _, r := range rows { |
| 216 | + list = append(list, LabyrinthSeasonMilestone{ |
| 217 | + HeadQuestId: r.HeadQuestId, |
| 218 | + HeadStageOrder: stageByRewardGroup[r.EventQuestLabyrinthRewardGroupId], |
| 219 | + Rewards: itemsByRewardGroup[r.EventQuestLabyrinthRewardGroupId], |
| 220 | + }) |
| 221 | + } |
| 222 | + milestones[chapterId] = list |
| 223 | + } |
| 224 | + return milestones |
| 225 | +} |
0 commit comments