mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43:41 +03:00
Initial commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package questflow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func (h *QuestHandler) HandleBigHuntQuestStart(user *store.UserState, questId, userDeckNumber int32, nowMillis int64) {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleBigHuntQuestStart", questId))
|
||||
}
|
||||
|
||||
h.initQuestState(user, questId)
|
||||
|
||||
if quest.Stamina > 0 {
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.ConsumeStamina(user, quest.Stamina, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
questState := user.Quests[questId]
|
||||
questState.UserDeckNumber = userDeckNumber
|
||||
questState.QuestStateType = model.UserQuestStateTypeActive
|
||||
questState.LatestStartDatetime = nowMillis
|
||||
user.Quests[questId] = questState
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleBigHuntQuestFinish(user *store.UserState, questId int32, isRetired, isAnnihilated bool, nowMillis int64) FinishOutcome {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleBigHuntQuestFinish", questId))
|
||||
}
|
||||
|
||||
outcome := h.evaluateFinishOutcome(user, questId)
|
||||
if !isRetired {
|
||||
h.applyQuestVictory(user, questId, outcome, nowMillis)
|
||||
}
|
||||
|
||||
if isRetired && !isAnnihilated && quest.Stamina > 1 {
|
||||
refund := quest.Stamina - 1
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.RecoverStamina(user, refund*1000, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
h.clearQuestMissions(user, questId, nowMillis)
|
||||
|
||||
return outcome
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package questflow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func (h *QuestHandler) HandleEventQuestStart(user *store.UserState, eventQuestChapterId, questId int32, isBattleOnly bool, userDeckNumber int32, nowMillis int64) {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleEventQuestStart", questId))
|
||||
}
|
||||
|
||||
h.initQuestState(user, questId)
|
||||
|
||||
if quest.Stamina > 0 {
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.ConsumeStamina(user, quest.Stamina, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
questState := user.Quests[questId]
|
||||
questState.IsBattleOnly = isBattleOnly
|
||||
questState.UserDeckNumber = userDeckNumber
|
||||
questState.QuestStateType = model.UserQuestStateTypeActive
|
||||
questState.LatestStartDatetime = nowMillis
|
||||
user.Quests[questId] = questState
|
||||
|
||||
user.EventQuest.CurrentEventQuestChapterId = eventQuestChapterId
|
||||
user.EventQuest.CurrentQuestId = questId
|
||||
if sceneIds := h.SceneIdsByQuestId[questId]; len(sceneIds) > 0 {
|
||||
user.EventQuest.CurrentQuestSceneId = sceneIds[0]
|
||||
user.EventQuest.HeadQuestSceneId = sceneIds[0]
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleEventQuestFinish(user *store.UserState, eventQuestChapterId, questId int32, isRetired, isAnnihilated bool, nowMillis int64) FinishOutcome {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleEventQuestFinish", questId))
|
||||
}
|
||||
|
||||
outcome := h.evaluateFinishOutcome(user, questId)
|
||||
if !isRetired {
|
||||
h.applyQuestVictory(user, questId, outcome, nowMillis)
|
||||
}
|
||||
|
||||
if isRetired && !isAnnihilated && quest.Stamina > 1 {
|
||||
refund := quest.Stamina - 1
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.RecoverStamina(user, refund*1000, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
user.EventQuest.CurrentQuestId = 0
|
||||
user.EventQuest.CurrentQuestSceneId = 0
|
||||
user.EventQuest.HeadQuestSceneId = 0
|
||||
|
||||
h.clearQuestMissions(user, questId, nowMillis)
|
||||
|
||||
return outcome
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleEventQuestRestart(user *store.UserState, eventQuestChapterId, questId int32, nowMillis int64) {
|
||||
h.HandleQuestRestart(user, questId, nowMillis)
|
||||
|
||||
user.EventQuest.CurrentEventQuestChapterId = eventQuestChapterId
|
||||
user.EventQuest.CurrentQuestId = questId
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleEventQuestSceneProgress(user *store.UserState, questSceneId int32, nowMillis int64) {
|
||||
scene, ok := h.SceneById[questSceneId]
|
||||
if !ok {
|
||||
log.Printf("[HandleEventQuestSceneProgress] unknown sceneId=%d, skipping", questSceneId)
|
||||
return
|
||||
}
|
||||
|
||||
user.EventQuest.CurrentQuestSceneId = questSceneId
|
||||
if h.isSceneAhead(questSceneId, user.EventQuest.HeadQuestSceneId) {
|
||||
user.EventQuest.HeadQuestSceneId = questSceneId
|
||||
}
|
||||
|
||||
h.applySceneGrants(user, questSceneId, nowMillis)
|
||||
|
||||
if scene.QuestResultType == model.QuestResultTypeHalfResult {
|
||||
h.clearQuestMissions(user, scene.QuestId, nowMillis)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package questflow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func (h *QuestHandler) HandleExtraQuestStart(user *store.UserState, questId, userDeckNumber int32, nowMillis int64) {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleExtraQuestStart", questId))
|
||||
}
|
||||
|
||||
h.initQuestState(user, questId)
|
||||
|
||||
if quest.Stamina > 0 {
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.ConsumeStamina(user, quest.Stamina, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
questState := user.Quests[questId]
|
||||
questState.UserDeckNumber = userDeckNumber
|
||||
questState.QuestStateType = model.UserQuestStateTypeActive
|
||||
questState.LatestStartDatetime = nowMillis
|
||||
user.Quests[questId] = questState
|
||||
|
||||
user.ExtraQuest.CurrentQuestId = questId
|
||||
if sceneIds := h.SceneIdsByQuestId[questId]; len(sceneIds) > 0 {
|
||||
user.ExtraQuest.CurrentQuestSceneId = sceneIds[0]
|
||||
user.ExtraQuest.HeadQuestSceneId = sceneIds[0]
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleExtraQuestFinish(user *store.UserState, questId int32, isRetired, isAnnihilated bool, nowMillis int64) FinishOutcome {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleExtraQuestFinish", questId))
|
||||
}
|
||||
|
||||
outcome := h.evaluateFinishOutcome(user, questId)
|
||||
if !isRetired {
|
||||
h.applyQuestVictory(user, questId, outcome, nowMillis)
|
||||
}
|
||||
|
||||
if isRetired && !isAnnihilated && quest.Stamina > 1 {
|
||||
refund := quest.Stamina - 1
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.RecoverStamina(user, refund*1000, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
user.ExtraQuest.CurrentQuestId = 0
|
||||
user.ExtraQuest.CurrentQuestSceneId = 0
|
||||
user.ExtraQuest.HeadQuestSceneId = 0
|
||||
|
||||
h.clearQuestMissions(user, questId, nowMillis)
|
||||
|
||||
return outcome
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleExtraQuestRestart(user *store.UserState, questId int32, nowMillis int64) {
|
||||
h.HandleQuestRestart(user, questId, nowMillis)
|
||||
|
||||
user.ExtraQuest.CurrentQuestId = questId
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleExtraQuestSceneProgress(user *store.UserState, questSceneId int32, nowMillis int64) {
|
||||
scene, ok := h.SceneById[questSceneId]
|
||||
if !ok {
|
||||
log.Printf("[HandleExtraQuestSceneProgress] unknown sceneId=%d, skipping", questSceneId)
|
||||
return
|
||||
}
|
||||
|
||||
user.ExtraQuest.CurrentQuestSceneId = questSceneId
|
||||
if h.isSceneAhead(questSceneId, user.ExtraQuest.HeadQuestSceneId) {
|
||||
user.ExtraQuest.HeadQuestSceneId = questSceneId
|
||||
}
|
||||
|
||||
h.applySceneGrants(user, questSceneId, nowMillis)
|
||||
|
||||
if scene.QuestResultType == model.QuestResultTypeHalfResult {
|
||||
h.clearQuestMissions(user, scene.QuestId, nowMillis)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package questflow
|
||||
|
||||
import (
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
type RewardGrant struct {
|
||||
PossessionType model.PossessionType
|
||||
PossessionId int32
|
||||
Count int32
|
||||
}
|
||||
|
||||
type FinishOutcome struct {
|
||||
DropRewards []RewardGrant
|
||||
FirstClearRewards []RewardGrant
|
||||
ReplayFlowFirstClearRewards []RewardGrant
|
||||
MissionClearRewards []RewardGrant
|
||||
MissionClearCompleteRewards []RewardGrant
|
||||
BigWinClearedQuestMissionIds []int32
|
||||
IsBigWin bool
|
||||
}
|
||||
|
||||
type QuestHandler struct {
|
||||
*masterdata.QuestCatalog
|
||||
Config *masterdata.GameConfig
|
||||
Granter *store.PossessionGranter
|
||||
}
|
||||
|
||||
func NewQuestHandler(catalog *masterdata.QuestCatalog, config *masterdata.GameConfig) *QuestHandler {
|
||||
granter := BuildGranter(catalog)
|
||||
return &QuestHandler{QuestCatalog: catalog, Config: config, Granter: granter}
|
||||
}
|
||||
|
||||
func BuildGranter(catalog *masterdata.QuestCatalog) *store.PossessionGranter {
|
||||
costumeById := make(map[int32]store.CostumeRef, len(catalog.CostumeById))
|
||||
for id, cm := range catalog.CostumeById {
|
||||
costumeById[id] = store.CostumeRef{CharacterId: cm.CharacterId}
|
||||
}
|
||||
weaponById := make(map[int32]store.WeaponRef, len(catalog.WeaponById))
|
||||
for id, wm := range catalog.WeaponById {
|
||||
weaponById[id] = store.WeaponRef{
|
||||
WeaponSkillGroupId: wm.WeaponSkillGroupId,
|
||||
WeaponAbilityGroupId: wm.WeaponAbilityGroupId,
|
||||
WeaponStoryReleaseConditionGroupId: wm.WeaponStoryReleaseConditionGroupId,
|
||||
}
|
||||
}
|
||||
releaseConditions := make(map[int32][]store.WeaponStoryReleaseCond, len(catalog.ReleaseConditionsByGroupId))
|
||||
for groupId, rows := range catalog.ReleaseConditionsByGroupId {
|
||||
conds := make([]store.WeaponStoryReleaseCond, len(rows))
|
||||
for i, r := range rows {
|
||||
conds[i] = store.WeaponStoryReleaseCond{
|
||||
StoryIndex: r.StoryIndex,
|
||||
WeaponStoryReleaseConditionType: r.WeaponStoryReleaseConditionType,
|
||||
ConditionValue: r.ConditionValue,
|
||||
}
|
||||
}
|
||||
releaseConditions[groupId] = conds
|
||||
}
|
||||
return &store.PossessionGranter{
|
||||
CostumeById: costumeById,
|
||||
WeaponById: weaponById,
|
||||
WeaponSkillSlots: catalog.WeaponSkillSlots,
|
||||
WeaponAbilitySlots: catalog.WeaponAbilitySlots,
|
||||
ReleaseConditions: releaseConditions,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package questflow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func (h *QuestHandler) initQuestState(user *store.UserState, questId int32) {
|
||||
quest := user.Quests[questId]
|
||||
quest.QuestId = questId
|
||||
user.Quests[questId] = quest
|
||||
|
||||
for _, missionId := range h.MissionIdsByQuestId[questId] {
|
||||
key := store.QuestMissionKey{QuestId: questId, QuestMissionId: missionId}
|
||||
mission := user.QuestMissions[key]
|
||||
mission.QuestId = questId
|
||||
mission.QuestMissionId = missionId
|
||||
user.QuestMissions[key] = mission
|
||||
}
|
||||
}
|
||||
|
||||
func isMainQuestPlayable(quest masterdata.QuestRow) bool {
|
||||
return !quest.IsRunInTheBackground && quest.IsCountedAsQuest
|
||||
}
|
||||
|
||||
func (h *QuestHandler) clearQuestMissions(user *store.UserState, questId int32, nowMillis int64) {
|
||||
for _, missionId := range h.MissionIdsByQuestId[questId] {
|
||||
key := store.QuestMissionKey{QuestId: questId, QuestMissionId: missionId}
|
||||
mission := user.QuestMissions[key]
|
||||
mission.IsClear = true
|
||||
mission.ProgressValue = 1
|
||||
mission.LatestClearDatetime = nowMillis
|
||||
user.QuestMissions[key] = mission
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleQuestStart(user *store.UserState, questId int32, isBattleOnly bool, userDeckNumber int32, nowMillis int64) {
|
||||
h.handleQuestStartInternal(user, questId, isBattleOnly, userDeckNumber, false, nowMillis)
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleQuestStartReplay(user *store.UserState, questId int32, isBattleOnly bool, userDeckNumber int32, nowMillis int64) {
|
||||
h.handleQuestStartInternal(user, questId, isBattleOnly, userDeckNumber, true, nowMillis)
|
||||
}
|
||||
|
||||
func (h *QuestHandler) handleQuestStartInternal(user *store.UserState, questId int32, isBattleOnly bool, userDeckNumber int32, isReplayFlow bool, nowMillis int64) {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleQuestStart", questId))
|
||||
}
|
||||
|
||||
h.initQuestState(user, questId)
|
||||
|
||||
if quest.Stamina > 0 {
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.ConsumeStamina(user, quest.Stamina, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
questState := user.Quests[questId]
|
||||
if questState.QuestStateType == model.UserQuestStateTypeCleared {
|
||||
if isReplayFlow {
|
||||
user.MainQuest.SavedCurrentQuestSceneId = user.MainQuest.CurrentQuestSceneId
|
||||
user.MainQuest.SavedHeadQuestSceneId = user.MainQuest.HeadQuestSceneId
|
||||
user.MainQuest.CurrentQuestFlowType = int32(model.QuestFlowTypeReplayFlow)
|
||||
user.MainQuest.ReplayFlowCurrentQuestSceneId = 0
|
||||
user.MainQuest.ReplayFlowHeadQuestSceneId = 0
|
||||
user.MainQuest.LatestVersion = nowMillis
|
||||
questState.QuestStateType = model.UserQuestStateTypeActive
|
||||
questState.LatestStartDatetime = nowMillis
|
||||
questState.IsBattleOnly = isBattleOnly
|
||||
questState.UserDeckNumber = userDeckNumber
|
||||
user.Quests[questId] = questState
|
||||
log.Printf("[HandleQuestStart] replay flow started for quest %d, saved scene=%d head=%d",
|
||||
questId, user.MainQuest.SavedCurrentQuestSceneId, user.MainQuest.SavedHeadQuestSceneId)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
questState.IsBattleOnly = isBattleOnly
|
||||
questState.UserDeckNumber = userDeckNumber
|
||||
if isMainQuestPlayable(quest) {
|
||||
user.MainQuest.CurrentQuestFlowType = int32(model.QuestFlowTypeMainFlow)
|
||||
questState.QuestStateType = model.UserQuestStateTypeActive
|
||||
questState.LatestStartDatetime = nowMillis
|
||||
} else {
|
||||
questState.QuestStateType = model.UserQuestStateTypeCleared
|
||||
questState.ClearCount = 1
|
||||
questState.DailyClearCount = 1
|
||||
questState.LastClearDatetime = nowMillis
|
||||
|
||||
if sceneIds := h.SceneIdsByQuestId[questId]; len(sceneIds) > 0 {
|
||||
firstSceneId := sceneIds[0]
|
||||
prevSceneId := user.MainQuest.CurrentQuestSceneId
|
||||
user.MainQuest.CurrentQuestSceneId = firstSceneId
|
||||
if h.isSceneAhead(firstSceneId, user.MainQuest.HeadQuestSceneId) {
|
||||
user.MainQuest.HeadQuestSceneId = firstSceneId
|
||||
}
|
||||
user.MainQuest.CurrentQuestFlowType = int32(model.QuestFlowTypeMainFlow)
|
||||
lastSceneId := h.getChapterLastSceneId(questId)
|
||||
user.MainQuest.IsReachedLastQuestScene = firstSceneId == lastSceneId
|
||||
if routeId, ok := h.RouteIdByQuestId[questId]; ok {
|
||||
if seasonId, ok := h.SeasonIdByRouteId[routeId]; ok {
|
||||
user.MainQuest.MainQuestSeasonId = seasonId
|
||||
}
|
||||
}
|
||||
log.Printf("[HandleQuestStart] background quest %d auto-cleared, scene %d -> %d", questId, prevSceneId, firstSceneId)
|
||||
}
|
||||
}
|
||||
user.Quests[questId] = questState
|
||||
}
|
||||
|
||||
func (h *QuestHandler) applyQuestVictory(user *store.UserState, questId int32, outcome FinishOutcome, nowMillis int64) {
|
||||
questState := user.Quests[questId]
|
||||
if !questState.IsRewardGranted {
|
||||
h.applyQuestRewards(user, questId, nowMillis)
|
||||
h.grantWeaponStoryUnlocksForQuestScene(user, questId, model.QuestResultTypeHalfResult, nowMillis)
|
||||
h.grantWeaponStoryUnlocksForQuestScene(user, questId, model.QuestResultTypeFullResult, nowMillis)
|
||||
questState.IsRewardGranted = true
|
||||
}
|
||||
for _, drop := range outcome.DropRewards {
|
||||
h.applyRewardPossession(user, drop.PossessionType, drop.PossessionId, drop.Count, nowMillis)
|
||||
}
|
||||
for _, reward := range outcome.ReplayFlowFirstClearRewards {
|
||||
h.applyRewardPossession(user, reward.PossessionType, reward.PossessionId, reward.Count, nowMillis)
|
||||
}
|
||||
questState.QuestStateType = model.UserQuestStateTypeCleared
|
||||
questState.ClearCount++
|
||||
questState.DailyClearCount++
|
||||
questState.LastClearDatetime = nowMillis
|
||||
user.Quests[questId] = questState
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleQuestFinish(user *store.UserState, questId int32, isRetired, isAnnihilated bool, nowMillis int64) FinishOutcome {
|
||||
quest, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleQuestFinish", questId))
|
||||
}
|
||||
|
||||
outcome := h.evaluateFinishOutcome(user, questId)
|
||||
if !isRetired {
|
||||
h.applyQuestVictory(user, questId, outcome, nowMillis)
|
||||
}
|
||||
|
||||
if isRetired && !isAnnihilated && quest.Stamina > 1 {
|
||||
refund := quest.Stamina - 1
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.RecoverStamina(user, refund*1000, maxMillis, nowMillis)
|
||||
}
|
||||
|
||||
wasReplay := user.MainQuest.CurrentQuestFlowType == int32(model.QuestFlowTypeReplayFlow)
|
||||
|
||||
user.MainQuest.ProgressQuestSceneId = 0
|
||||
user.MainQuest.ProgressHeadQuestSceneId = 0
|
||||
user.MainQuest.ProgressQuestFlowType = 0
|
||||
user.MainQuest.CurrentQuestFlowType = int32(model.QuestFlowTypeUnknown)
|
||||
|
||||
if wasReplay {
|
||||
if user.MainQuest.SavedCurrentQuestSceneId > 0 {
|
||||
user.MainQuest.CurrentQuestSceneId = user.MainQuest.SavedCurrentQuestSceneId
|
||||
}
|
||||
if user.MainQuest.SavedHeadQuestSceneId > 0 {
|
||||
user.MainQuest.HeadQuestSceneId = user.MainQuest.SavedHeadQuestSceneId
|
||||
}
|
||||
user.MainQuest.SavedCurrentQuestSceneId = 0
|
||||
user.MainQuest.SavedHeadQuestSceneId = 0
|
||||
user.MainQuest.ReplayFlowCurrentQuestSceneId = 0
|
||||
user.MainQuest.ReplayFlowHeadQuestSceneId = 0
|
||||
log.Printf("[HandleQuestFinish] replay flow ended for quest %d, restored scene=%d head=%d",
|
||||
questId, user.MainQuest.CurrentQuestSceneId, user.MainQuest.HeadQuestSceneId)
|
||||
}
|
||||
|
||||
h.clearQuestMissions(user, questId, nowMillis)
|
||||
|
||||
return outcome
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleQuestSkip(user *store.UserState, questId, skipCount int32, nowMillis int64) FinishOutcome {
|
||||
questDef, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleQuestSkip", questId))
|
||||
}
|
||||
|
||||
maxMillis := h.MaxStaminaByLevel[user.Status.Level] * 1000
|
||||
store.ConsumeStamina(user, skipCount, maxMillis, nowMillis)
|
||||
|
||||
skipTicketId := h.Config.ConsumableItemIdForQuestSkipTicket
|
||||
user.ConsumableItems[skipTicketId] -= skipCount
|
||||
if user.ConsumableItems[skipTicketId] < 0 {
|
||||
user.ConsumableItems[skipTicketId] = 0
|
||||
}
|
||||
|
||||
var allDrops []RewardGrant
|
||||
for range skipCount {
|
||||
drops := h.computeDropRewards(questDef)
|
||||
for _, drop := range drops {
|
||||
h.applyRewardPossession(user, drop.PossessionType, drop.PossessionId, drop.Count, nowMillis)
|
||||
}
|
||||
allDrops = append(allDrops, drops...)
|
||||
|
||||
if questDef.Gold != 0 {
|
||||
user.ConsumableItems[h.Config.ConsumableItemIdForGold] += questDef.Gold
|
||||
}
|
||||
h.applyExpRewards(user, questId, nowMillis)
|
||||
}
|
||||
|
||||
questState := user.Quests[questId]
|
||||
questState.ClearCount += skipCount
|
||||
questState.DailyClearCount += skipCount
|
||||
questState.LastClearDatetime = nowMillis
|
||||
user.Quests[questId] = questState
|
||||
|
||||
log.Printf("[HandleQuestSkip] questId=%d skipCount=%d drops=%d gold=%d", questId, skipCount, len(allDrops), questDef.Gold*skipCount)
|
||||
return FinishOutcome{DropRewards: allDrops}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleQuestRestart(user *store.UserState, questId int32, nowMillis int64) {
|
||||
questDef, ok := h.QuestById[questId]
|
||||
if ok && isMainQuestPlayable(questDef) {
|
||||
user.MainQuest.CurrentQuestFlowType = int32(model.QuestFlowTypeMainFlow)
|
||||
}
|
||||
|
||||
quest := user.Quests[questId]
|
||||
quest.QuestId = questId
|
||||
quest.QuestStateType = model.UserQuestStateTypeActive
|
||||
quest.IsBattleOnly = false
|
||||
quest.LatestStartDatetime = nowMillis
|
||||
user.Quests[questId] = quest
|
||||
|
||||
for _, missionId := range h.MissionIdsByQuestId[questId] {
|
||||
key := store.QuestMissionKey{QuestId: questId, QuestMissionId: missionId}
|
||||
m := user.QuestMissions[key]
|
||||
m.QuestId = questId
|
||||
m.QuestMissionId = missionId
|
||||
m.IsClear = false
|
||||
m.ProgressValue = 0
|
||||
m.LatestClearDatetime = 0
|
||||
user.QuestMissions[key] = m
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
package questflow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"lunar-tear/server/internal/gameutil"
|
||||
"lunar-tear/server/internal/masterdata"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func (h *QuestHandler) isQuestCleared(user *store.UserState, questId int32) bool {
|
||||
quest, ok := user.Quests[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for isQuestCleared", questId))
|
||||
}
|
||||
return quest.QuestStateType == model.UserQuestStateTypeCleared
|
||||
}
|
||||
|
||||
func appendMissionRewards(dst []RewardGrant, src []masterdata.QuestMissionRewardRow) []RewardGrant {
|
||||
for _, r := range src {
|
||||
dst = append(dst, RewardGrant{
|
||||
PossessionType: r.PossessionType,
|
||||
PossessionId: r.PossessionId,
|
||||
Count: r.Count,
|
||||
})
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func (h *QuestHandler) firstClearRewardGroupId(user *store.UserState, questDef masterdata.QuestRow) int32 {
|
||||
rewardGroupId := questDef.QuestFirstClearRewardGroupId
|
||||
for _, switchRow := range h.FirstClearRewardSwitchesByQuestId[questDef.QuestId] {
|
||||
if h.isQuestCleared(user, switchRow.SwitchConditionClearQuestId) {
|
||||
rewardGroupId = switchRow.QuestFirstClearRewardGroupId
|
||||
break
|
||||
}
|
||||
}
|
||||
return rewardGroupId
|
||||
}
|
||||
|
||||
func (h *QuestHandler) evaluateFinishOutcome(user *store.UserState, questId int32) FinishOutcome {
|
||||
outcome := FinishOutcome{}
|
||||
questState, ok := user.Quests[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for evaluateFinishOutcome", questId))
|
||||
}
|
||||
questDef, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for evaluateFinishOutcome", questId))
|
||||
}
|
||||
|
||||
if !questState.IsRewardGranted {
|
||||
rewardGroupId := h.firstClearRewardGroupId(user, questDef)
|
||||
for _, reward := range h.FirstClearRewardsByGroupId[rewardGroupId] {
|
||||
outcome.FirstClearRewards = append(outcome.FirstClearRewards, RewardGrant{
|
||||
PossessionType: reward.PossessionType,
|
||||
PossessionId: reward.PossessionId,
|
||||
Count: reward.Count,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if user.MainQuest.CurrentQuestFlowType == int32(model.QuestFlowTypeReplayFlow) && questDef.QuestReplayFlowRewardGroupId > 0 {
|
||||
for _, reward := range h.ReplayFlowRewardsByGroupId[questDef.QuestReplayFlowRewardGroupId] {
|
||||
outcome.ReplayFlowFirstClearRewards = append(outcome.ReplayFlowFirstClearRewards, RewardGrant{
|
||||
PossessionType: reward.PossessionType,
|
||||
PossessionId: reward.PossessionId,
|
||||
Count: reward.Count,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pendingClearCount := 0
|
||||
regularMissionCount := 0
|
||||
for _, questMissionId := range h.MissionIdsByQuestId[questId] {
|
||||
missionDef, ok := h.MissionById[questMissionId]
|
||||
if !ok || missionDef.QuestMissionConditionType == model.QuestMissionConditionTypeComplete {
|
||||
continue
|
||||
}
|
||||
regularMissionCount++
|
||||
|
||||
key := store.QuestMissionKey{QuestId: questId, QuestMissionId: questMissionId}
|
||||
mission := user.QuestMissions[key]
|
||||
|
||||
if !mission.IsClear {
|
||||
pendingClearCount++
|
||||
outcome.MissionClearRewards = appendMissionRewards(
|
||||
outcome.MissionClearRewards,
|
||||
h.MissionRewardsByMissionId[missionDef.QuestMissionRewardId],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
priorClearCount := regularMissionCount - pendingClearCount
|
||||
// On our server every mission auto-clears, so priorClearCount + pendingClearCount
|
||||
// always equals regularMissionCount. The two-variable form is kept to mirror the
|
||||
// original game's intent where individual missions could fail their conditions.
|
||||
allRegularWillClear := regularMissionCount > 0 && (priorClearCount+pendingClearCount) == regularMissionCount
|
||||
if allRegularWillClear {
|
||||
for _, questMissionId := range h.MissionIdsByQuestId[questId] {
|
||||
missionDef, ok := h.MissionById[questMissionId]
|
||||
if !ok || missionDef.QuestMissionConditionType != model.QuestMissionConditionTypeComplete {
|
||||
continue
|
||||
}
|
||||
key := store.QuestMissionKey{QuestId: questId, QuestMissionId: questMissionId}
|
||||
if !user.QuestMissions[key].IsClear {
|
||||
outcome.MissionClearCompleteRewards = appendMissionRewards(
|
||||
outcome.MissionClearCompleteRewards,
|
||||
h.MissionRewardsByMissionId[missionDef.QuestMissionRewardId],
|
||||
)
|
||||
outcome.BigWinClearedQuestMissionIds = append(outcome.BigWinClearedQuestMissionIds, questMissionId)
|
||||
}
|
||||
}
|
||||
outcome.IsBigWin = len(outcome.BigWinClearedQuestMissionIds) > 0
|
||||
}
|
||||
|
||||
outcome.DropRewards = h.computeDropRewards(questDef)
|
||||
return outcome
|
||||
}
|
||||
|
||||
func (h *QuestHandler) computeDropRewards(questDef masterdata.QuestRow) []RewardGrant {
|
||||
if questDef.QuestPickupRewardGroupId == 0 {
|
||||
return nil
|
||||
}
|
||||
var drops []RewardGrant
|
||||
for _, dropId := range h.PickupRewardIdsByGroupId[questDef.QuestPickupRewardGroupId] {
|
||||
if bdr, ok := h.BattleDropRewardById[dropId]; ok {
|
||||
drops = append(drops, RewardGrant{
|
||||
PossessionType: bdr.PossessionType,
|
||||
PossessionId: bdr.PossessionId,
|
||||
Count: bdr.Count,
|
||||
})
|
||||
}
|
||||
}
|
||||
return drops
|
||||
}
|
||||
|
||||
func (h *QuestHandler) applyExpRewards(user *store.UserState, questId int32, nowMillis int64) {
|
||||
questDef, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
oldLevel := user.Status.Level
|
||||
user.Status.Exp += questDef.UserExp
|
||||
user.Status.Level, user.Status.Exp = gameutil.LevelAndCap(user.Status.Exp, h.UserExpThresholds)
|
||||
log.Printf("[applyExpRewards] questId=%d user: +%d exp -> total=%d level=%d", questId, questDef.UserExp, user.Status.Exp, user.Status.Level)
|
||||
|
||||
if user.Status.Level > oldLevel {
|
||||
if maxStamina, ok := h.MaxStaminaByLevel[user.Status.Level]; ok {
|
||||
store.ReplenishStamina(user, maxStamina*1000, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
if h.RentalQuestIds[questId] {
|
||||
log.Printf("[applyExpRewards] questId=%d skipping character/costume exp (rental deck)", questId)
|
||||
return
|
||||
}
|
||||
|
||||
deckCostumeUuids, deckCharacterIds := h.resolveDeckUnits(user, questId)
|
||||
if deckCostumeUuids == nil {
|
||||
log.Printf("[applyExpRewards] questId=%d skipping character/costume exp (deck not resolved)", questId)
|
||||
return
|
||||
}
|
||||
|
||||
if questDef.CharacterExp != 0 {
|
||||
for id := range deckCharacterIds {
|
||||
row := user.Characters[id]
|
||||
row.Exp += questDef.CharacterExp
|
||||
row.Level, row.Exp = gameutil.LevelAndCap(row.Exp, h.CharacterExpThresholds)
|
||||
user.Characters[id] = row
|
||||
log.Printf("[applyExpRewards] questId=%d character=%d: +%d exp -> total=%d level=%d", questId, id, questDef.CharacterExp, row.Exp, row.Level)
|
||||
}
|
||||
}
|
||||
|
||||
if questDef.CostumeExp != 0 {
|
||||
for key := range deckCostumeUuids {
|
||||
row := user.Costumes[key]
|
||||
cm, ok := h.CostumeById[row.CostumeId]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if maxLevelFunc, hasMax := h.CostumeMaxLevelByRarity[cm.RarityType]; hasMax {
|
||||
maxLevel := maxLevelFunc.Evaluate(row.LimitBreakCount)
|
||||
if row.Level >= maxLevel {
|
||||
log.Printf("[applyExpRewards] questId=%d costume=%d (key=%s): at max level %d, skipping", questId, row.CostumeId, key, row.Level)
|
||||
continue
|
||||
}
|
||||
}
|
||||
row.Exp += questDef.CostumeExp
|
||||
if thresholds, ok := h.CostumeExpByRarity[cm.RarityType]; ok {
|
||||
row.Level, row.Exp = gameutil.LevelAndCap(row.Exp, thresholds)
|
||||
if maxLevelFunc, hasMax := h.CostumeMaxLevelByRarity[cm.RarityType]; hasMax {
|
||||
maxLevel := maxLevelFunc.Evaluate(row.LimitBreakCount)
|
||||
if row.Level > maxLevel && int(maxLevel) < len(thresholds) {
|
||||
row.Level = maxLevel
|
||||
row.Exp = thresholds[maxLevel]
|
||||
}
|
||||
}
|
||||
}
|
||||
user.Costumes[key] = row
|
||||
log.Printf("[applyExpRewards] questId=%d costume=%d (key=%s): +%d exp -> total=%d level=%d", questId, row.CostumeId, key, questDef.CostumeExp, row.Exp, row.Level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) resolveDeckUnits(user *store.UserState, questId int32) (costumeUuids map[string]bool, characterIds map[int32]bool) {
|
||||
dn := user.Quests[questId].UserDeckNumber
|
||||
if dn == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
deck, ok := user.Decks[store.DeckKey{DeckType: model.DeckTypeQuest, UserDeckNumber: dn}]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
costumeUuids = make(map[string]bool)
|
||||
characterIds = make(map[int32]bool)
|
||||
for _, dcUuid := range []string{deck.UserDeckCharacterUuid01, deck.UserDeckCharacterUuid02, deck.UserDeckCharacterUuid03} {
|
||||
if dcUuid == "" {
|
||||
continue
|
||||
}
|
||||
dc, ok := user.DeckCharacters[dcUuid]
|
||||
if !ok || dc.UserCostumeUuid == "" {
|
||||
continue
|
||||
}
|
||||
costumeUuids[dc.UserCostumeUuid] = true
|
||||
if costume, ok := user.Costumes[dc.UserCostumeUuid]; ok {
|
||||
if cm, ok := h.CostumeById[costume.CostumeId]; ok {
|
||||
characterIds[cm.CharacterId] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(costumeUuids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return costumeUuids, characterIds
|
||||
}
|
||||
|
||||
func (h *QuestHandler) applyQuestRewards(user *store.UserState, questId int32, nowMillis int64) {
|
||||
questDef, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
h.applyExpRewards(user, questId, nowMillis)
|
||||
|
||||
if questDef.Gold != 0 {
|
||||
user.ConsumableItems[h.Config.ConsumableItemIdForGold] += questDef.Gold
|
||||
log.Printf("[applyQuestRewards] questId=%d gold: +%d -> total=%d", questId, questDef.Gold, user.ConsumableItems[h.Config.ConsumableItemIdForGold])
|
||||
}
|
||||
|
||||
rewardGroupId := h.firstClearRewardGroupId(user, questDef)
|
||||
for _, reward := range h.FirstClearRewardsByGroupId[rewardGroupId] {
|
||||
h.applyRewardPossession(user, reward.PossessionType, reward.PossessionId, reward.Count, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) applyRewardPossession(user *store.UserState, possType model.PossessionType, possId, count int32, nowMillis int64) {
|
||||
switch possType {
|
||||
case model.PossessionTypeCompanion:
|
||||
h.grantCompanion(user, possId, nowMillis)
|
||||
case model.PossessionTypeParts:
|
||||
h.grantParts(user, possId, nowMillis)
|
||||
default:
|
||||
h.Granter.GrantFull(user, possType, possId, count, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) grantCompanion(user *store.UserState, companionId int32, nowMillis int64) {
|
||||
for _, row := range user.Companions {
|
||||
if row.CompanionId == companionId {
|
||||
return
|
||||
}
|
||||
}
|
||||
key := fmt.Sprintf("reward-companion-%d", companionId)
|
||||
user.Companions[key] = store.CompanionState{
|
||||
UserCompanionUuid: key,
|
||||
CompanionId: companionId,
|
||||
Level: 1,
|
||||
HeadupDisplayViewId: 1,
|
||||
AcquisitionDatetime: nowMillis,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) grantParts(user *store.UserState, partsId int32, nowMillis int64) {
|
||||
for _, row := range user.Parts {
|
||||
if row.PartsId == partsId {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var mainStatId int32
|
||||
if partsDef, ok := h.PartsById[partsId]; ok {
|
||||
mainStatId = h.DefaultPartsStatusMainByLotteryGroup[partsDef.PartsStatusMainLotteryGroupId]
|
||||
|
||||
if _, exists := user.PartsGroupNotes[partsDef.PartsGroupId]; !exists {
|
||||
user.PartsGroupNotes[partsDef.PartsGroupId] = store.PartsGroupNoteState{
|
||||
PartsGroupId: partsDef.PartsGroupId,
|
||||
FirstAcquisitionDatetime: nowMillis,
|
||||
LatestVersion: nowMillis,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("reward-parts-%d", partsId)
|
||||
user.Parts[key] = store.PartsState{
|
||||
UserPartsUuid: key,
|
||||
PartsId: partsId,
|
||||
Level: 1,
|
||||
PartsStatusMainId: mainStatId,
|
||||
AcquisitionDatetime: nowMillis,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) grantWeaponStoryUnlock(user *store.UserState, weaponId, storyIndex int32, nowMillis int64) {
|
||||
store.GrantWeaponStoryUnlock(user, weaponId, storyIndex, nowMillis)
|
||||
}
|
||||
|
||||
var tutorialCompanionChoices = map[int32]int32{
|
||||
1: 2, // bear + fire (Cat=1, Attr=2)
|
||||
2: 1, // bear + wind (Cat=1, Attr=6)
|
||||
3: 7, // doll + fire (Cat=3, Attr=2)
|
||||
4: 10, // doll + wind (Cat=3, Attr=6)
|
||||
}
|
||||
|
||||
func (h *QuestHandler) ApplyTutorialReward(user *store.UserState, tutorialType model.TutorialType, choiceId int32, nowMillis int64) []RewardGrant {
|
||||
switch tutorialType {
|
||||
case model.TutorialTypeCompanion:
|
||||
return h.applyCompanionTutorialReward(user, choiceId, nowMillis)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) applyCompanionTutorialReward(user *store.UserState, choiceId int32, nowMillis int64) []RewardGrant {
|
||||
companionId, ok := tutorialCompanionChoices[choiceId]
|
||||
if !ok {
|
||||
log.Printf("[QuestHandler] unknown companion tutorial choiceId=%d", choiceId)
|
||||
return nil
|
||||
}
|
||||
h.grantCompanion(user, companionId, nowMillis)
|
||||
return []RewardGrant{{
|
||||
PossessionType: model.PossessionTypeCompanion,
|
||||
PossessionId: companionId,
|
||||
Count: 1,
|
||||
}}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) BattleDropRewards(questId int32) []masterdata.BattleDropInfo {
|
||||
return h.BattleDropsByQuestId[questId]
|
||||
}
|
||||
|
||||
func (h *QuestHandler) grantWeaponStoryUnlocksForQuestScene(user *store.UserState, questId int32, resultType model.QuestResultType, nowMillis int64) {
|
||||
if resultType == model.QuestResultTypeHalfResult {
|
||||
questDef, ok := h.QuestById[questId]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
rewardGroupId := h.firstClearRewardGroupId(user, questDef)
|
||||
for _, reward := range h.FirstClearRewardsByGroupId[rewardGroupId] {
|
||||
if reward.PossessionType != model.PossessionTypeWeapon {
|
||||
continue
|
||||
}
|
||||
weaponId := reward.PossessionId
|
||||
weapon, ok := h.WeaponById[weaponId]
|
||||
if !ok || weapon.WeaponStoryReleaseConditionGroupId == 0 {
|
||||
continue
|
||||
}
|
||||
groupId := weapon.WeaponStoryReleaseConditionGroupId
|
||||
for _, cond := range h.ReleaseConditionsByGroupId[groupId] {
|
||||
if cond.WeaponStoryReleaseConditionType == model.WeaponStoryReleaseConditionTypeAcquisition && cond.ConditionValue == 0 {
|
||||
h.grantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if resultType == model.QuestResultTypeFullResult {
|
||||
for groupId, conditions := range h.ReleaseConditionsByGroupId {
|
||||
for _, cond := range conditions {
|
||||
if cond.WeaponStoryReleaseConditionType == model.WeaponStoryReleaseConditionTypeQuestClear && cond.ConditionValue == questId {
|
||||
for _, weaponId := range h.WeaponIdsByReleaseConditionGroupId[groupId] {
|
||||
h.grantWeaponStoryUnlock(user, weaponId, cond.StoryIndex, nowMillis)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package questflow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"lunar-tear/server/internal/gametime"
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func (h *QuestHandler) applySceneGrants(user *store.UserState, questSceneId int32, nowMillis int64) {
|
||||
grants, ok := h.SceneGrantsBySceneId[questSceneId]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, g := range grants {
|
||||
h.applyRewardPossession(user, g.PossessionType, g.PossessionId, g.Count, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) isSceneAhead(newSceneId, currentHeadId int32) bool {
|
||||
if currentHeadId == 0 {
|
||||
return true
|
||||
}
|
||||
return h.SceneById[newSceneId].SortOrder > h.SceneById[currentHeadId].SortOrder
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleMainFlowSceneProgress(user *store.UserState, questSceneId int32, nowMillis int64) {
|
||||
scene, ok := h.SceneById[questSceneId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown sceneId=%d for HandleMainFlowSceneProgress", questSceneId))
|
||||
}
|
||||
|
||||
quest, ok := h.QuestById[scene.QuestId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleMainFlowSceneProgress", questSceneId))
|
||||
}
|
||||
|
||||
user.MainQuest.CurrentQuestSceneId = questSceneId
|
||||
if h.isSceneAhead(questSceneId, user.MainQuest.HeadQuestSceneId) {
|
||||
user.MainQuest.HeadQuestSceneId = questSceneId
|
||||
}
|
||||
user.MainQuest.CurrentQuestFlowType = int32(model.QuestFlowTypeMainFlow)
|
||||
|
||||
if user.SideStoryActiveProgress.CurrentSideStoryQuestId != 0 {
|
||||
user.SideStoryActiveProgress = store.SideStoryActiveProgress{
|
||||
LatestVersion: nowMillis,
|
||||
}
|
||||
}
|
||||
|
||||
lastSceneId := h.getChapterLastSceneId(scene.QuestId)
|
||||
user.MainQuest.IsReachedLastQuestScene = questSceneId == lastSceneId
|
||||
|
||||
routeId, ok := h.RouteIdByQuestId[quest.QuestId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleMainFlowSceneProgress setting currentMainQuestRouteId", quest.QuestId))
|
||||
}
|
||||
user.MainQuest.CurrentMainQuestRouteId = routeId
|
||||
|
||||
user.PortalCageStatus.IsCurrentProgress = false
|
||||
user.PortalCageStatus.LatestVersion = nowMillis
|
||||
|
||||
h.applySceneGrants(user, questSceneId, nowMillis)
|
||||
}
|
||||
|
||||
func (h *QuestHandler) advanceTutorialsForScene(user *store.UserState, sceneId int32) {
|
||||
currentScene, ok := h.SceneById[sceneId]
|
||||
if !ok {
|
||||
log.Printf("[advanceTutorialsForScene] unknown sceneId=%d", sceneId)
|
||||
return
|
||||
}
|
||||
for _, cond := range h.TutorialUnlockConditions {
|
||||
condScene, ok := h.SceneById[cond.ConditionValue]
|
||||
if !ok {
|
||||
log.Printf("[advanceTutorialsForScene] unknown conditionValue=%d", cond.ConditionValue)
|
||||
continue
|
||||
}
|
||||
if currentScene.SortOrder >= condScene.SortOrder {
|
||||
if _, exists := user.Tutorials[cond.TutorialType]; !exists {
|
||||
user.Tutorials[cond.TutorialType] = store.TutorialProgressState{
|
||||
TutorialType: cond.TutorialType,
|
||||
ProgressPhase: 99999,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) getLastMainFlowSceneId(questId int32) int32 {
|
||||
sceneIds := h.SceneIdsByQuestId[questId]
|
||||
if len(sceneIds) == 0 {
|
||||
panic(fmt.Sprintf("no scenes found for questId=%d", questId))
|
||||
}
|
||||
return sceneIds[len(sceneIds)-1]
|
||||
}
|
||||
|
||||
func (h *QuestHandler) getChapterLastSceneId(questId int32) int32 {
|
||||
if id, ok := h.ChapterLastSceneByQuestId[questId]; ok {
|
||||
return id
|
||||
}
|
||||
return h.getLastMainFlowSceneId(questId)
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleReplayFlowSceneProgress(user *store.UserState, questSceneId int32, nowMillis int64) {
|
||||
user.MainQuest.ReplayFlowCurrentQuestSceneId = questSceneId
|
||||
if user.MainQuest.ReplayFlowHeadQuestSceneId == 0 || h.isSceneAhead(questSceneId, user.MainQuest.ReplayFlowHeadQuestSceneId) {
|
||||
user.MainQuest.ReplayFlowHeadQuestSceneId = questSceneId
|
||||
}
|
||||
user.MainQuest.LatestVersion = nowMillis
|
||||
log.Printf("[HandleReplayFlowSceneProgress] sceneId=%d replayHead=%d", questSceneId, user.MainQuest.ReplayFlowHeadQuestSceneId)
|
||||
}
|
||||
|
||||
func (h *QuestHandler) HandleMainQuestSceneProgress(user *store.UserState, questSceneId int32) {
|
||||
scene, ok := h.SceneById[questSceneId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown sceneId=%d for HandleMainQuestSceneProgress", questSceneId))
|
||||
}
|
||||
|
||||
quest, ok := h.QuestById[scene.QuestId]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unknown questId=%d for HandleMainQuestSceneProgress", questSceneId))
|
||||
}
|
||||
|
||||
if isMainQuestPlayable(quest) {
|
||||
if scene.QuestResultType == model.QuestResultTypeHalfResult {
|
||||
nowMillis := gametime.NowMillis()
|
||||
h.clearQuestMissions(user, quest.QuestId, nowMillis)
|
||||
}
|
||||
|
||||
user.MainQuest.ProgressQuestSceneId = questSceneId
|
||||
if h.isSceneAhead(questSceneId, user.MainQuest.ProgressHeadQuestSceneId) {
|
||||
user.MainQuest.ProgressHeadQuestSceneId = questSceneId
|
||||
}
|
||||
user.MainQuest.CurrentQuestFlowType = int32(model.QuestFlowTypeSubFlow)
|
||||
user.MainQuest.ProgressQuestFlowType = int32(model.QuestFlowTypeSubFlow)
|
||||
} else {
|
||||
user.MainQuest.CurrentQuestSceneId = questSceneId
|
||||
if h.isSceneAhead(questSceneId, user.MainQuest.HeadQuestSceneId) {
|
||||
user.MainQuest.HeadQuestSceneId = questSceneId
|
||||
}
|
||||
lastSceneId := h.getChapterLastSceneId(quest.QuestId)
|
||||
user.MainQuest.IsReachedLastQuestScene = questSceneId == lastSceneId
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user