2 Commits

Author SHA1 Message Date
Ilya Groshev 956dbfaefd Fix retire wiping the cleared status of event and extra quests
Build and Push Docker images to Docker Hub / build-and-push (push) Has been cancelled
2026-05-17 11:29:16 +03:00
Ilya Groshev 0d46ee4557 Fix replay flow loop on background quests and another-route flow type 2026-05-17 11:02:40 +03:00
4 changed files with 50 additions and 21 deletions
+2
View File
@@ -54,6 +54,8 @@ func (h *QuestHandler) HandleEventQuestFinish(user *store.UserState, eventQuestC
store.RecoverStamina(user, refund*1000, maxMillis, nowMillis) store.RecoverStamina(user, refund*1000, maxMillis, nowMillis)
} }
restoreClearedAfterRetire(user, questId, isRetired)
user.EventQuest.CurrentEventQuestChapterId = 0 user.EventQuest.CurrentEventQuestChapterId = 0
user.EventQuest.CurrentQuestId = 0 user.EventQuest.CurrentQuestId = 0
user.EventQuest.CurrentQuestSceneId = 0 user.EventQuest.CurrentQuestSceneId = 0
+2
View File
@@ -51,6 +51,8 @@ func (h *QuestHandler) HandleExtraQuestFinish(user *store.UserState, questId int
store.RecoverStamina(user, refund*1000, maxMillis, nowMillis) store.RecoverStamina(user, refund*1000, maxMillis, nowMillis)
} }
restoreClearedAfterRetire(user, questId, isRetired)
user.ExtraQuest.CurrentQuestId = 0 user.ExtraQuest.CurrentQuestId = 0
user.ExtraQuest.CurrentQuestSceneId = 0 user.ExtraQuest.CurrentQuestSceneId = 0
user.ExtraQuest.HeadQuestSceneId = 0 user.ExtraQuest.HeadQuestSceneId = 0
+32 -19
View File
@@ -84,7 +84,7 @@ func (h *QuestHandler) handleQuestStartInternal(user *store.UserState, questId i
} }
case isReplayFlow: case isReplayFlow:
h.applyReplayStart(user, questId, isBattleOnly, nowMillis) h.applyReplayStart(user, quest, questId, isBattleOnly, nowMillis)
return return
} }
@@ -131,9 +131,7 @@ func snapshotMainQuestIfNeeded(user *store.UserState) {
} }
} }
// Preserve CurrentQuestFlowType when HandleReplayFlowSceneProgress already func (h *QuestHandler) applyReplayStart(user *store.UserState, quest masterdata.EntityMQuest, questId int32, isBattleOnly bool, nowMillis int64) {
// set it: replay-variant ids (30000+) aren't in RouteIdByQuestId.
func (h *QuestHandler) applyReplayStart(user *store.UserState, questId int32, isBattleOnly bool, nowMillis int64) {
flowType := h.replayFlowTypeFromQuestId(user, questId) flowType := h.replayFlowTypeFromQuestId(user, questId)
if model.IsReplayQuestFlowType(user.MainQuest.CurrentQuestFlowType) { if model.IsReplayQuestFlowType(user.MainQuest.CurrentQuestFlowType) {
flowType = model.QuestFlowType(user.MainQuest.CurrentQuestFlowType) flowType = model.QuestFlowType(user.MainQuest.CurrentQuestFlowType)
@@ -142,12 +140,26 @@ func (h *QuestHandler) applyReplayStart(user *store.UserState, questId int32, is
user.MainQuest.LatestVersion = nowMillis user.MainQuest.LatestVersion = nowMillis
questState := user.Quests[questId] questState := user.Quests[questId]
questState.QuestStateType = model.UserQuestStateTypeActive
questState.LatestStartDatetime = nowMillis questState.LatestStartDatetime = nowMillis
user.Quests[questId] = questState
log.Printf("[HandleQuestStart] replay quest=%d flowType=%s isBattleOnly=%v current=%d head=%d", if isMainQuestPlayable(quest) {
questId, flowType, isBattleOnly, questState.QuestStateType = model.UserQuestStateTypeActive
user.Quests[questId] = questState
} else {
if questState.QuestStateType != model.UserQuestStateTypeCleared {
questState.QuestStateType = model.UserQuestStateTypeCleared
questState.ClearCount++
questState.DailyClearCount++
questState.LastClearDatetime = nowMillis
}
user.Quests[questId] = questState
if sceneIds := h.SceneIdsByQuestId[questId]; len(sceneIds) > 0 {
h.advanceReplayFlowScene(user, sceneIds[0])
}
}
log.Printf("[HandleQuestStart] replay quest=%d flowType=%s isBattleOnly=%v playable=%v current=%d head=%d",
questId, flowType, isBattleOnly, isMainQuestPlayable(quest),
user.MainQuest.ReplayFlowCurrentQuestSceneId, user.MainQuest.ReplayFlowCurrentQuestSceneId,
user.MainQuest.ReplayFlowHeadQuestSceneId) user.MainQuest.ReplayFlowHeadQuestSceneId)
} }
@@ -221,6 +233,17 @@ func (h *QuestHandler) finalizeChainPreviousQuest(user *store.UserState, questId
log.Printf("[HandleMainQuestSceneProgress] finalized chain-previous quest %d (cleared)", questId) log.Printf("[HandleMainQuestSceneProgress] finalized chain-previous quest %d (cleared)", questId)
} }
func restoreClearedAfterRetire(user *store.UserState, questId int32, isRetired bool) {
if !isRetired {
return
}
qs := user.Quests[questId]
if qs.ClearCount > 0 && qs.QuestStateType == model.UserQuestStateTypeActive {
qs.QuestStateType = model.UserQuestStateTypeCleared
user.Quests[questId] = qs
}
}
func (h *QuestHandler) HandleQuestFinish(user *store.UserState, questId int32, isRetired, isAnnihilated bool, nowMillis int64) FinishOutcome { func (h *QuestHandler) HandleQuestFinish(user *store.UserState, questId int32, isRetired, isAnnihilated bool, nowMillis int64) FinishOutcome {
quest, ok := h.QuestById[questId] quest, ok := h.QuestById[questId]
if !ok { if !ok {
@@ -248,17 +271,7 @@ func (h *QuestHandler) HandleQuestFinish(user *store.UserState, questId int32, i
store.RecoverStamina(user, refund*1000, maxMillis, nowMillis) store.RecoverStamina(user, refund*1000, maxMillis, nowMillis)
} }
// On retire of a previously-cleared quest (cage Menu Pick replay or restoreClearedAfterRetire(user, questId, isRetired)
// Map Play replay), HandleQuestStart marked QuestStateType=Active for
// the run. With applyQuestVictory skipped on retire, that Active sticks
// and the cage UI shows the quest as locked. Restore Cleared.
if isRetired {
qs := user.Quests[questId]
if qs.ClearCount > 0 && qs.QuestStateType == model.UserQuestStateTypeActive {
qs.QuestStateType = model.UserQuestStateTypeCleared
user.Quests[questId] = qs
}
}
user.MainQuest.ProgressQuestSceneId = 0 user.MainQuest.ProgressQuestSceneId = 0
user.MainQuest.ProgressHeadQuestSceneId = 0 user.MainQuest.ProgressHeadQuestSceneId = 0
+14 -2
View File
@@ -51,8 +51,14 @@ func (h *QuestHandler) advanceMainFlowScene(user *store.UserState, questId, scen
} }
} }
// Backs IUserMainQuestSeasonRoute: the client needs the history to load func (h *QuestHandler) advanceReplayFlowScene(user *store.UserState, sceneId int32) {
// scene metadata when cage menu-replay jumps to older chapters. if !h.isSceneAhead(sceneId, user.MainQuest.ReplayFlowHeadQuestSceneId) {
return
}
user.MainQuest.ReplayFlowCurrentQuestSceneId = sceneId
user.MainQuest.ReplayFlowHeadQuestSceneId = sceneId
}
func RecordSeasonRoute(user *store.UserState, seasonId, routeId int32, nowMillis int64) { func RecordSeasonRoute(user *store.UserState, seasonId, routeId int32, nowMillis int64) {
if seasonId <= 0 || routeId <= 0 { if seasonId <= 0 || routeId <= 0 {
return return
@@ -176,6 +182,12 @@ func (h *QuestHandler) replayFlowTypeForRoute(user *store.UserState, routeId int
return model.QuestFlowTypeAnotherRouteReplayFlow 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.QuestFlowTypeReplayFlow return model.QuestFlowTypeReplayFlow
} }