mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Derive main-quest season routes at projection time
This commit is contained in:
@@ -40,6 +40,7 @@ var childTables = []string{
|
|||||||
"user_big_hunt_max_scores",
|
"user_big_hunt_max_scores",
|
||||||
"user_quest_limit_content_status",
|
"user_quest_limit_content_status",
|
||||||
"user_side_story_quests",
|
"user_side_story_quests",
|
||||||
|
"user_main_quest_season_routes",
|
||||||
"user_missions",
|
"user_missions",
|
||||||
"user_quest_missions",
|
"user_quest_missions",
|
||||||
"user_quests",
|
"user_quests",
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ type QuestCatalog struct {
|
|||||||
TutorialUnlockConditions []EntityMTutorialUnlockCondition
|
TutorialUnlockConditions []EntityMTutorialUnlockCondition
|
||||||
ChapterLastSceneByQuestId map[int32]int32
|
ChapterLastSceneByQuestId map[int32]int32
|
||||||
SeasonIdByRouteId map[int32]int32
|
SeasonIdByRouteId map[int32]int32
|
||||||
|
RoutesBySeason map[int32][]int32
|
||||||
|
RouteCompletionQuestId map[int32]int32
|
||||||
BattleOnlyTargetSceneByQuestId map[int32]int32
|
BattleOnlyTargetSceneByQuestId map[int32]int32
|
||||||
|
|
||||||
UserExpThresholds []int32
|
UserExpThresholds []int32
|
||||||
@@ -114,8 +116,53 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
|||||||
return nil, fmt.Errorf("load main quest route table: %w", err)
|
return nil, fmt.Errorf("load main quest route table: %w", err)
|
||||||
}
|
}
|
||||||
seasonIdByRouteId := make(map[int32]int32, len(routes))
|
seasonIdByRouteId := make(map[int32]int32, len(routes))
|
||||||
|
routesBySeason := make(map[int32][]int32, len(routes))
|
||||||
|
sortOrderByRoute := make(map[int32]int32, len(routes))
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
seasonIdByRouteId[r.MainQuestRouteId] = r.MainQuestSeasonId
|
seasonIdByRouteId[r.MainQuestRouteId] = r.MainQuestSeasonId
|
||||||
|
routesBySeason[r.MainQuestSeasonId] = append(routesBySeason[r.MainQuestSeasonId], r.MainQuestRouteId)
|
||||||
|
sortOrderByRoute[r.MainQuestRouteId] = r.SortOrder
|
||||||
|
}
|
||||||
|
for seasonId, ids := range routesBySeason {
|
||||||
|
s := ids
|
||||||
|
sort.Slice(s, func(i, j int) bool { return sortOrderByRoute[s[i]] > sortOrderByRoute[s[j]] })
|
||||||
|
routesBySeason[seasonId] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
anotherReplayConds, err := utils.ReadTable[EntityMMainQuestRouteAnotherReplayFlowUnlockCondition]("m_main_quest_route_another_replay_flow_unlock_condition")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load main quest route another replay flow unlock condition table: %w", err)
|
||||||
|
}
|
||||||
|
evaluateConds, err := utils.ReadTable[EntityMEvaluateCondition]("m_evaluate_condition")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load evaluate condition table: %w", err)
|
||||||
|
}
|
||||||
|
valueGroupByConditionId := make(map[int32]int32, len(evaluateConds))
|
||||||
|
for _, c := range evaluateConds {
|
||||||
|
valueGroupByConditionId[c.EvaluateConditionId] = c.EvaluateConditionValueGroupId
|
||||||
|
}
|
||||||
|
evaluateValueGroups, err := utils.ReadTable[EntityMEvaluateConditionValueGroup]("m_evaluate_condition_value_group")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load evaluate condition value group table: %w", err)
|
||||||
|
}
|
||||||
|
valueByGroupId := make(map[int32]int32, len(evaluateValueGroups))
|
||||||
|
for _, vg := range evaluateValueGroups {
|
||||||
|
if _, exists := valueByGroupId[vg.EvaluateConditionValueGroupId]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
valueByGroupId[vg.EvaluateConditionValueGroupId] = int32(vg.Value)
|
||||||
|
}
|
||||||
|
routeCompletionQuestId := make(map[int32]int32, len(anotherReplayConds))
|
||||||
|
for _, c := range anotherReplayConds {
|
||||||
|
valueGroupId, ok := valueGroupByConditionId[c.UnlockEvaluateConditionId]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
questId, ok := valueByGroupId[valueGroupId]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
routeCompletionQuestId[c.MainQuestRouteId] = questId
|
||||||
}
|
}
|
||||||
|
|
||||||
firstClearSwitches, err := utils.ReadTable[EntityMQuestFirstClearRewardSwitch]("m_quest_first_clear_reward_switch")
|
firstClearSwitches, err := utils.ReadTable[EntityMQuestFirstClearRewardSwitch]("m_quest_first_clear_reward_switch")
|
||||||
@@ -539,6 +586,8 @@ func LoadQuestCatalog(partsCatalog *PartsCatalog) (*QuestCatalog, error) {
|
|||||||
TutorialUnlockConditions: tutorialUnlockConds,
|
TutorialUnlockConditions: tutorialUnlockConds,
|
||||||
ChapterLastSceneByQuestId: chapterLastSceneByQuestId,
|
ChapterLastSceneByQuestId: chapterLastSceneByQuestId,
|
||||||
SeasonIdByRouteId: seasonIdByRouteId,
|
SeasonIdByRouteId: seasonIdByRouteId,
|
||||||
|
RoutesBySeason: routesBySeason,
|
||||||
|
RouteCompletionQuestId: routeCompletionQuestId,
|
||||||
BattleOnlyTargetSceneByQuestId: battleOnlyTargetSceneByQuestId,
|
BattleOnlyTargetSceneByQuestId: battleOnlyTargetSceneByQuestId,
|
||||||
|
|
||||||
UserExpThresholds: BuildExpThresholds(paramMapRows, 1),
|
UserExpThresholds: BuildExpThresholds(paramMapRows, 1),
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ func (h *QuestHandler) advanceMainFlowScene(user *store.UserState, questId, scen
|
|||||||
user.MainQuest.CurrentMainQuestRouteId = routeId
|
user.MainQuest.CurrentMainQuestRouteId = routeId
|
||||||
if seasonId, ok := h.SeasonIdByRouteId[routeId]; ok {
|
if seasonId, ok := h.SeasonIdByRouteId[routeId]; ok {
|
||||||
user.MainQuest.MainQuestSeasonId = seasonId
|
user.MainQuest.MainQuestSeasonId = seasonId
|
||||||
RecordSeasonRoute(user, seasonId, routeId, gametime.NowMillis())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,22 +58,27 @@ func (h *QuestHandler) advanceReplayFlowScene(user *store.UserState, sceneId int
|
|||||||
user.MainQuest.ReplayFlowHeadQuestSceneId = sceneId
|
user.MainQuest.ReplayFlowHeadQuestSceneId = sceneId
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecordSeasonRoute(user *store.UserState, seasonId, routeId int32, nowMillis int64) {
|
func (h *QuestHandler) SeasonRoutesFor(user *store.UserState) map[int32]int32 {
|
||||||
if seasonId <= 0 || routeId <= 0 {
|
out := make(map[int32]int32)
|
||||||
return
|
for seasonId, routes := range h.RoutesBySeason {
|
||||||
|
if seasonId <= 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, routeId := range routes {
|
||||||
|
finalQuestId, ok := h.RouteCompletionQuestId[routeId]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if q, ok := user.Quests[finalQuestId]; ok && q.ClearCount > 0 {
|
||||||
|
out[seasonId] = routeId
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if user.MainQuestSeasonRoutes == nil {
|
if cur := user.MainQuest.MainQuestSeasonId; cur >= 2 && user.MainQuest.CurrentMainQuestRouteId > 0 {
|
||||||
user.MainQuestSeasonRoutes = make(map[store.SeasonRouteKey]store.SeasonRouteEntry)
|
out[cur] = user.MainQuest.CurrentMainQuestRouteId
|
||||||
}
|
|
||||||
key := store.SeasonRouteKey{MainQuestSeasonId: seasonId, MainQuestRouteId: routeId}
|
|
||||||
if _, exists := user.MainQuestSeasonRoutes[key]; exists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
user.MainQuestSeasonRoutes[key] = store.SeasonRouteEntry{
|
|
||||||
MainQuestSeasonId: seasonId,
|
|
||||||
MainQuestRouteId: routeId,
|
|
||||||
LatestVersion: nowMillis,
|
|
||||||
}
|
}
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *QuestHandler) HandleMainFlowSceneProgress(user *store.UserState, questSceneId int32, nowMillis int64) {
|
func (h *QuestHandler) HandleMainFlowSceneProgress(user *store.UserState, questSceneId int32, nowMillis int64) {
|
||||||
@@ -177,15 +181,8 @@ func (h *QuestHandler) replayFlowTypeForRoute(user *store.UserState, routeId int
|
|||||||
if !ok {
|
if !ok {
|
||||||
return model.QuestFlowTypeReplayFlow
|
return model.QuestFlowTypeReplayFlow
|
||||||
}
|
}
|
||||||
for key, entry := range user.MainQuestSeasonRoutes {
|
pairs := h.SeasonRoutesFor(user)
|
||||||
if key.MainQuestSeasonId == seasonId && entry.MainQuestRouteId != routeId {
|
if recorded, ok := pairs[seasonId]; ok && recorded != routeId {
|
||||||
return model.QuestFlowTypeAnotherRouteReplayFlow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(user.MainQuestSeasonRoutes) == 0 &&
|
|
||||||
user.MainQuest.MainQuestSeasonId == seasonId &&
|
|
||||||
user.MainQuest.CurrentMainQuestRouteId != 0 &&
|
|
||||||
user.MainQuest.CurrentMainQuestRouteId != routeId {
|
|
||||||
return model.QuestFlowTypeAnotherRouteReplayFlow
|
return model.QuestFlowTypeAnotherRouteReplayFlow
|
||||||
}
|
}
|
||||||
return model.QuestFlowTypeReplayFlow
|
return model.QuestFlowTypeReplayFlow
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"lunar-tear/server/internal/masterdata"
|
"lunar-tear/server/internal/masterdata"
|
||||||
"lunar-tear/server/internal/masterdata/memorydb"
|
"lunar-tear/server/internal/masterdata/memorydb"
|
||||||
"lunar-tear/server/internal/questflow"
|
"lunar-tear/server/internal/questflow"
|
||||||
|
"lunar-tear/server/internal/userdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// buildCatalogs runs the full Load*/Build*/Enrich* sequence against whatever
|
// buildCatalogs runs the full Load*/Build*/Enrich* sequence against whatever
|
||||||
@@ -35,6 +36,7 @@ func buildCatalogs() (*Catalogs, error) {
|
|||||||
}
|
}
|
||||||
sideStoryCatalog := masterdata.LoadSideStoryCatalog()
|
sideStoryCatalog := masterdata.LoadSideStoryCatalog()
|
||||||
questHandler := questflow.NewQuestHandler(questCatalog, gameConfig, sideStoryCatalog)
|
questHandler := questflow.NewQuestHandler(questCatalog, gameConfig, sideStoryCatalog)
|
||||||
|
userdata.SetQuestHandler(questHandler)
|
||||||
|
|
||||||
gachaEntries, medalInfo, err := masterdata.LoadGachaCatalog()
|
gachaEntries, medalInfo, err := masterdata.LoadGachaCatalog()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -198,7 +198,6 @@ func (s *QuestServiceServer) SetRoute(ctx context.Context, req *pb.SetRouteReque
|
|||||||
user.MainQuest.CurrentMainQuestRouteId = req.MainQuestRouteId
|
user.MainQuest.CurrentMainQuestRouteId = req.MainQuestRouteId
|
||||||
if seasonId, ok := engine.SeasonIdByRouteId[req.MainQuestRouteId]; ok {
|
if seasonId, ok := engine.SeasonIdByRouteId[req.MainQuestRouteId]; ok {
|
||||||
user.MainQuest.MainQuestSeasonId = seasonId
|
user.MainQuest.MainQuestSeasonId = seasonId
|
||||||
questflow.RecordSeasonRoute(user, seasonId, req.MainQuestRouteId, gametime.NowMillis())
|
|
||||||
}
|
}
|
||||||
now := gametime.NowMillis()
|
now := gametime.NowMillis()
|
||||||
user.PortalCageStatus.IsCurrentProgress = false
|
user.PortalCageStatus.IsCurrentProgress = false
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ func initMaps(u *store.UserState) {
|
|||||||
u.CharacterRebirths = make(map[int32]store.CharacterRebirthState)
|
u.CharacterRebirths = make(map[int32]store.CharacterRebirthState)
|
||||||
u.AutoSaleSettings = make(map[int32]store.AutoSaleSettingState)
|
u.AutoSaleSettings = make(map[int32]store.AutoSaleSettingState)
|
||||||
u.SideStoryQuests = make(map[int32]store.SideStoryQuestProgress)
|
u.SideStoryQuests = make(map[int32]store.SideStoryQuestProgress)
|
||||||
u.MainQuestSeasonRoutes = make(map[store.SeasonRouteKey]store.SeasonRouteEntry)
|
|
||||||
u.QuestLimitContentStatus = make(map[int32]store.QuestLimitContentStatus)
|
u.QuestLimitContentStatus = make(map[int32]store.QuestLimitContentStatus)
|
||||||
u.BigHuntMaxScores = make(map[int32]store.BigHuntMaxScore)
|
u.BigHuntMaxScores = make(map[int32]store.BigHuntMaxScore)
|
||||||
u.BigHuntStatuses = make(map[int32]store.BigHuntStatus)
|
u.BigHuntStatuses = make(map[int32]store.BigHuntStatus)
|
||||||
@@ -378,16 +377,6 @@ func loadMapTables(db *sql.DB, uid int64, u *store.UserState) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
queryRows(db, `SELECT main_quest_season_id, main_quest_route_id, latest_version
|
|
||||||
FROM user_main_quest_season_routes WHERE user_id=?`, uid, func(rows *sql.Rows) {
|
|
||||||
var seasonId, routeId int32
|
|
||||||
var lv int64
|
|
||||||
rows.Scan(&seasonId, &routeId, &lv)
|
|
||||||
u.MainQuestSeasonRoutes[store.SeasonRouteKey{MainQuestSeasonId: seasonId, MainQuestRouteId: routeId}] = store.SeasonRouteEntry{
|
|
||||||
MainQuestSeasonId: seasonId, MainQuestRouteId: routeId, LatestVersion: lv,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
queryRows(db, `SELECT limit_content_id, limit_content_quest_status_type, event_quest_chapter_id, latest_version
|
queryRows(db, `SELECT limit_content_id, limit_content_quest_status_type, event_quest_chapter_id, latest_version
|
||||||
FROM user_quest_limit_content_status WHERE user_id=?`, uid, func(rows *sql.Rows) {
|
FROM user_quest_limit_content_status WHERE user_id=?`, uid, func(rows *sql.Rows) {
|
||||||
var id int32
|
var id int32
|
||||||
|
|||||||
@@ -224,12 +224,6 @@ func writeUserState(tx *sql.Tx, uid int64, u *store.UserState) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, v := range u.MainQuestSeasonRoutes {
|
|
||||||
if err := exec(`INSERT INTO user_main_quest_season_routes (user_id, main_quest_season_id, main_quest_route_id, latest_version) VALUES (?,?,?,?)`,
|
|
||||||
uid, k.MainQuestSeasonId, k.MainQuestRouteId, v.LatestVersion); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for id, v := range u.QuestLimitContentStatus {
|
for id, v := range u.QuestLimitContentStatus {
|
||||||
if err := exec(`INSERT INTO user_quest_limit_content_status (user_id, limit_content_id, limit_content_quest_status_type, event_quest_chapter_id, latest_version) VALUES (?,?,?,?,?)`,
|
if err := exec(`INSERT INTO user_quest_limit_content_status (user_id, limit_content_id, limit_content_quest_status_type, event_quest_chapter_id, latest_version) VALUES (?,?,?,?,?)`,
|
||||||
uid, id, v.LimitContentQuestStatusType, v.EventQuestChapterId, v.LatestVersion); err != nil {
|
uid, id, v.LimitContentQuestStatusType, v.EventQuestChapterId, v.LatestVersion); err != nil {
|
||||||
@@ -798,17 +792,6 @@ func diffAndSave(tx *sql.Tx, uid int64, before, after *store.UserState) error {
|
|||||||
return []any{0, v.HeadSideStoryQuestSceneId, int32(v.SideStoryQuestStateType), v.LatestVersion}
|
return []any{0, v.HeadSideStoryQuestSceneId, int32(v.SideStoryQuestStateType), v.LatestVersion}
|
||||||
}, "side_story_quest_id, head_side_story_quest_scene_id, side_story_quest_state_type, latest_version")
|
}, "side_story_quest_id, head_side_story_quest_scene_id, side_story_quest_state_type, latest_version")
|
||||||
|
|
||||||
for k, v := range after.MainQuestSeasonRoutes {
|
|
||||||
if old, ok := before.MainQuestSeasonRoutes[k]; !ok || old != v {
|
|
||||||
exec(`INSERT OR REPLACE INTO user_main_quest_season_routes (user_id, main_quest_season_id, main_quest_route_id, latest_version) VALUES (?,?,?,?)`,
|
|
||||||
uid, k.MainQuestSeasonId, k.MainQuestRouteId, v.LatestVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for k := range before.MainQuestSeasonRoutes {
|
|
||||||
if _, ok := after.MainQuestSeasonRoutes[k]; !ok {
|
|
||||||
exec(`DELETE FROM user_main_quest_season_routes WHERE user_id=? AND main_quest_season_id=? AND main_quest_route_id=?`, uid, k.MainQuestSeasonId, k.MainQuestRouteId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diffMapInt32(tx, uid, before.QuestLimitContentStatus, after.QuestLimitContentStatus, "user_quest_limit_content_status", "limit_content_id",
|
diffMapInt32(tx, uid, before.QuestLimitContentStatus, after.QuestLimitContentStatus, "user_quest_limit_content_status", "limit_content_id",
|
||||||
func(v store.QuestLimitContentStatus) []any {
|
func(v store.QuestLimitContentStatus) []any {
|
||||||
return []any{0, v.LimitContentQuestStatusType, v.EventQuestChapterId, v.LatestVersion}
|
return []any{0, v.LimitContentQuestStatusType, v.EventQuestChapterId, v.LatestVersion}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ type UserState struct {
|
|||||||
LoginBonus UserLoginBonusState
|
LoginBonus UserLoginBonusState
|
||||||
Tutorials map[int32]TutorialProgressState
|
Tutorials map[int32]TutorialProgressState
|
||||||
MainQuest MainQuestState
|
MainQuest MainQuestState
|
||||||
MainQuestSeasonRoutes map[SeasonRouteKey]SeasonRouteEntry
|
|
||||||
EventQuest EventQuestState
|
EventQuest EventQuestState
|
||||||
ExtraQuest ExtraQuestState
|
ExtraQuest ExtraQuestState
|
||||||
SideStoryQuests map[int32]SideStoryQuestProgress
|
SideStoryQuests map[int32]SideStoryQuestProgress
|
||||||
@@ -162,9 +161,6 @@ func (u *UserState) EnsureMaps() {
|
|||||||
if u.SideStoryQuests == nil {
|
if u.SideStoryQuests == nil {
|
||||||
u.SideStoryQuests = make(map[int32]SideStoryQuestProgress)
|
u.SideStoryQuests = make(map[int32]SideStoryQuestProgress)
|
||||||
}
|
}
|
||||||
if u.MainQuestSeasonRoutes == nil {
|
|
||||||
u.MainQuestSeasonRoutes = make(map[SeasonRouteKey]SeasonRouteEntry)
|
|
||||||
}
|
|
||||||
if u.QuestLimitContentStatus == nil {
|
if u.QuestLimitContentStatus == nil {
|
||||||
u.QuestLimitContentStatus = make(map[int32]QuestLimitContentStatus)
|
u.QuestLimitContentStatus = make(map[int32]QuestLimitContentStatus)
|
||||||
}
|
}
|
||||||
@@ -590,17 +586,6 @@ type SideStoryActiveProgress struct {
|
|||||||
LatestVersion int64
|
LatestVersion int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type SeasonRouteKey struct {
|
|
||||||
MainQuestSeasonId int32
|
|
||||||
MainQuestRouteId int32
|
|
||||||
}
|
|
||||||
|
|
||||||
type SeasonRouteEntry struct {
|
|
||||||
MainQuestSeasonId int32
|
|
||||||
MainQuestRouteId int32
|
|
||||||
LatestVersion int64
|
|
||||||
}
|
|
||||||
|
|
||||||
type QuestLimitContentStatus struct {
|
type QuestLimitContentStatus struct {
|
||||||
LimitContentQuestStatusType int32
|
LimitContentQuestStatusType int32
|
||||||
EventQuestChapterId int32
|
EventQuestChapterId int32
|
||||||
|
|||||||
@@ -101,8 +101,9 @@ func ChangedTables(before, after *store.UserState) []string {
|
|||||||
add("IUserMainQuestMainFlowStatus")
|
add("IUserMainQuestMainFlowStatus")
|
||||||
add("IUserMainQuestProgressStatus")
|
add("IUserMainQuestProgressStatus")
|
||||||
add("IUserMainQuestReplayFlowStatus")
|
add("IUserMainQuestReplayFlowStatus")
|
||||||
}
|
// IUserMainQuestSeasonRoute is derived from MainQuest + Quests at projection
|
||||||
if !mapsEqualStruct(before.MainQuestSeasonRoutes, after.MainQuestSeasonRoutes) {
|
// time (see proj_quest.go / questflow.QuestHandler.SeasonRoutesFor) — flag it
|
||||||
|
// whenever either of those upstream inputs changes.
|
||||||
add("IUserMainQuestSeasonRoute")
|
add("IUserMainQuestSeasonRoute")
|
||||||
}
|
}
|
||||||
if before.EventQuest != after.EventQuest {
|
if before.EventQuest != after.EventQuest {
|
||||||
@@ -202,6 +203,7 @@ func ChangedTables(before, after *store.UserState) []string {
|
|||||||
}
|
}
|
||||||
if !mapsEqualStruct(before.Quests, after.Quests) {
|
if !mapsEqualStruct(before.Quests, after.Quests) {
|
||||||
add("IUserQuest")
|
add("IUserQuest")
|
||||||
|
add("IUserMainQuestSeasonRoute")
|
||||||
}
|
}
|
||||||
if !mapsEqualStruct(before.QuestMissions, after.QuestMissions) {
|
if !mapsEqualStruct(before.QuestMissions, after.QuestMissions) {
|
||||||
add("IUserQuestMission")
|
add("IUserQuestMission")
|
||||||
|
|||||||
@@ -116,38 +116,29 @@ func init() {
|
|||||||
return s
|
return s
|
||||||
})
|
})
|
||||||
register("IUserMainQuestSeasonRoute", func(user store.UserState) string {
|
register("IUserMainQuestSeasonRoute", func(user store.UserState) string {
|
||||||
if len(user.MainQuestSeasonRoutes) == 0 {
|
if questHandler == nil {
|
||||||
// Fallback to current (season, route) for legacy saves with no history.
|
return "[]"
|
||||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
|
||||||
"userId": user.UserId,
|
|
||||||
"mainQuestSeasonId": user.MainQuest.MainQuestSeasonId,
|
|
||||||
"mainQuestRouteId": user.MainQuest.CurrentMainQuestRouteId,
|
|
||||||
"latestVersion": user.MainQuest.LatestVersion,
|
|
||||||
})
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
keys := make([]store.SeasonRouteKey, 0, len(user.MainQuestSeasonRoutes))
|
pairs := questHandler.SeasonRoutesFor(&user)
|
||||||
for k := range user.MainQuestSeasonRoutes {
|
if len(pairs) == 0 {
|
||||||
keys = append(keys, k)
|
return "[]"
|
||||||
}
|
}
|
||||||
sort.Slice(keys, func(i, j int) bool {
|
seasons := make([]int32, 0, len(pairs))
|
||||||
if keys[i].MainQuestSeasonId != keys[j].MainQuestSeasonId {
|
for s := range pairs {
|
||||||
return keys[i].MainQuestSeasonId < keys[j].MainQuestSeasonId
|
seasons = append(seasons, s)
|
||||||
}
|
}
|
||||||
return keys[i].MainQuestRouteId < keys[j].MainQuestRouteId
|
sort.Slice(seasons, func(i, j int) bool { return seasons[i] < seasons[j] })
|
||||||
})
|
records := make([]map[string]any, 0, len(seasons))
|
||||||
records := make([]map[string]any, 0, len(keys))
|
for _, s := range seasons {
|
||||||
for _, k := range keys {
|
|
||||||
e := user.MainQuestSeasonRoutes[k]
|
|
||||||
records = append(records, map[string]any{
|
records = append(records, map[string]any{
|
||||||
"userId": user.UserId,
|
"userId": user.UserId,
|
||||||
"mainQuestSeasonId": e.MainQuestSeasonId,
|
"mainQuestSeasonId": s,
|
||||||
"mainQuestRouteId": e.MainQuestRouteId,
|
"mainQuestRouteId": pairs[s],
|
||||||
"latestVersion": e.LatestVersion,
|
"latestVersion": user.MainQuest.LatestVersion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
s, _ := utils.EncodeJSONMaps(records...)
|
out, _ := utils.EncodeJSONMaps(records...)
|
||||||
return s
|
return out
|
||||||
})
|
})
|
||||||
register("IUserEventQuestProgressStatus", func(user store.UserState) string {
|
register("IUserEventQuestProgressStatus", func(user store.UserState) string {
|
||||||
s, _ := utils.EncodeJSONMaps(map[string]any{
|
s, _ := utils.EncodeJSONMaps(map[string]any{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package userdata
|
|||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"lunar-tear/server/internal/questflow"
|
||||||
"lunar-tear/server/internal/store"
|
"lunar-tear/server/internal/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -10,6 +11,12 @@ type Projector func(user store.UserState) string
|
|||||||
|
|
||||||
var projectors = make(map[string]Projector)
|
var projectors = make(map[string]Projector)
|
||||||
|
|
||||||
|
var questHandler *questflow.QuestHandler
|
||||||
|
|
||||||
|
func SetQuestHandler(h *questflow.QuestHandler) {
|
||||||
|
questHandler = h
|
||||||
|
}
|
||||||
|
|
||||||
func register(tableName string, fn Projector) {
|
func register(tableName string, fn Projector) {
|
||||||
projectors[tableName] = fn
|
projectors[tableName] = fn
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user