mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 13:53:41 +03:00
Add auto-repeat quest and memoir auto-sell
Build and Push Docker images to Docker Hub / build-and-push (push) Has been cancelled
Build and Push Docker images to Docker Hub / build-and-push (push) Has been cancelled
This commit is contained in:
@@ -35,8 +35,9 @@ func (h *QuestHandler) HandleBigHuntQuestFinish(user *store.UserState, questId i
|
||||
}
|
||||
|
||||
target := h.targetForBigHunt(questId)
|
||||
outcome := h.evaluateFinishOutcome(user, questId, target, nowMillis)
|
||||
if !isRetired {
|
||||
var outcome FinishOutcome
|
||||
if !isRetired && !isAnnihilated {
|
||||
outcome = h.evaluateFinishOutcome(user, questId, target, nowMillis)
|
||||
h.applyQuestVictory(user, questId, &outcome, nowMillis, false)
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,9 @@ func (h *QuestHandler) HandleEventQuestFinish(user *store.UserState, eventQuestC
|
||||
}
|
||||
|
||||
target := h.targetForEvent(eventQuestChapterId, questId)
|
||||
outcome := h.evaluateFinishOutcome(user, questId, target, nowMillis)
|
||||
if !isRetired {
|
||||
var outcome FinishOutcome
|
||||
if !isRetired && !isAnnihilated {
|
||||
outcome = h.evaluateFinishOutcome(user, questId, target, nowMillis)
|
||||
h.applyQuestVictory(user, questId, &outcome, nowMillis, false)
|
||||
h.recordSideStoryLimitContentStatus(user, questId, nowMillis)
|
||||
}
|
||||
|
||||
@@ -42,8 +42,9 @@ func (h *QuestHandler) HandleExtraQuestFinish(user *store.UserState, questId int
|
||||
}
|
||||
|
||||
target := h.targetForExtra(questId)
|
||||
outcome := h.evaluateFinishOutcome(user, questId, target, nowMillis)
|
||||
if !isRetired {
|
||||
var outcome FinishOutcome
|
||||
if !isRetired && !isAnnihilated {
|
||||
outcome = h.evaluateFinishOutcome(user, questId, target, nowMillis)
|
||||
h.applyQuestVictory(user, questId, &outcome, nowMillis, false)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ type RewardGrant struct {
|
||||
PossessionType model.PossessionType
|
||||
PossessionId int32
|
||||
Count int32
|
||||
IsAutoSale bool
|
||||
}
|
||||
|
||||
type FinishOutcome struct {
|
||||
@@ -36,7 +37,7 @@ type QuestHandler struct {
|
||||
}
|
||||
|
||||
func NewQuestHandler(catalog *masterdata.QuestCatalog, config *masterdata.GameConfig, sideStory *masterdata.SideStoryCatalog, campaigns *campaign.Catalog, characterRebirth *masterdata.CharacterRebirthCatalog) *QuestHandler {
|
||||
granter := BuildGranter(catalog)
|
||||
granter := BuildGranter(catalog, config)
|
||||
var sideStoryChapters map[int32]int32
|
||||
if sideStory != nil {
|
||||
sideStoryChapters = sideStory.ChapterByEventQuestId
|
||||
@@ -51,7 +52,7 @@ func NewQuestHandler(catalog *masterdata.QuestCatalog, config *masterdata.GameCo
|
||||
}
|
||||
}
|
||||
|
||||
func BuildGranter(catalog *masterdata.QuestCatalog) *store.PossessionGranter {
|
||||
func BuildGranter(catalog *masterdata.QuestCatalog, config *masterdata.GameConfig) *store.PossessionGranter {
|
||||
costumeById := make(map[int32]store.CostumeRef, len(catalog.CostumeById))
|
||||
for id, cm := range catalog.CostumeById {
|
||||
costumeById[id] = store.CostumeRef{CharacterId: cm.CharacterId}
|
||||
@@ -111,6 +112,15 @@ func BuildGranter(catalog *masterdata.QuestCatalog) *store.PossessionGranter {
|
||||
}
|
||||
}
|
||||
|
||||
partsSellPriceL1 := make(map[int32]int32, len(catalog.SellPriceByRarity))
|
||||
for rarity, fn := range catalog.SellPriceByRarity {
|
||||
partsSellPriceL1[int32(rarity)] = fn.Evaluate(1)
|
||||
}
|
||||
var goldItemId int32
|
||||
if config != nil {
|
||||
goldItemId = config.ConsumableItemIdForGold
|
||||
}
|
||||
|
||||
return &store.PossessionGranter{
|
||||
CostumeById: costumeById,
|
||||
WeaponById: weaponById,
|
||||
@@ -122,5 +132,7 @@ func BuildGranter(catalog *masterdata.QuestCatalog) *store.PossessionGranter {
|
||||
PartsVariantsByGroupRarity: partsVariants,
|
||||
PartsSubStatusPool: catalog.SubStatusPool,
|
||||
PartsSubStatusDefs: partsSubDefs,
|
||||
PartsSellPriceL1ByRarity: partsSellPriceL1,
|
||||
GoldConsumableItemId: goldItemId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +204,8 @@ func (h *QuestHandler) applyQuestVictory(user *store.UserState, questId int32, o
|
||||
}
|
||||
questState.IsRewardGranted = true
|
||||
}
|
||||
for _, drop := range outcome.DropRewards {
|
||||
h.applyRewardPossession(user, drop.PossessionType, drop.PossessionId, drop.Count, nowMillis)
|
||||
}
|
||||
raritySet, rankSet := parseAutoSaleRules(user.AutoSaleSettings)
|
||||
h.grantDropRewards(user, outcome.DropRewards, raritySet, rankSet, nowMillis)
|
||||
for _, reward := range outcome.ReplayFlowFirstClearRewards {
|
||||
h.applyRewardPossession(user, reward.PossessionType, reward.PossessionId, reward.Count, nowMillis)
|
||||
}
|
||||
@@ -260,11 +259,12 @@ func (h *QuestHandler) HandleQuestFinish(user *store.UserState, questId int32, i
|
||||
|
||||
h.initQuestState(user, questId)
|
||||
|
||||
outcome := h.evaluateFinishOutcome(user, questId, h.targetForMain(questId), nowMillis)
|
||||
wasReplay := model.IsReplayQuestFlowType(user.MainQuest.CurrentQuestFlowType)
|
||||
wasMenuReplay := user.MainQuest.SavedContext.Active
|
||||
|
||||
if !isRetired {
|
||||
var outcome FinishOutcome
|
||||
if !isRetired && !isAnnihilated {
|
||||
outcome = h.evaluateFinishOutcome(user, questId, h.targetForMain(questId), nowMillis)
|
||||
h.applyQuestVictory(user, questId, &outcome, nowMillis, wasReplay)
|
||||
|
||||
// A replay-flow finish must NOT move the MainFlow scene pointer: the
|
||||
@@ -334,12 +334,11 @@ func (h *QuestHandler) HandleQuestSkip(user *store.UserState, questId, skipCount
|
||||
if user.ConsumableItems[skipTicketId] < 0 {
|
||||
user.ConsumableItems[skipTicketId] = 0
|
||||
}
|
||||
raritySet, rankSet := parseAutoSaleRules(user.AutoSaleSettings)
|
||||
var allDrops []RewardGrant
|
||||
for range skipCount {
|
||||
drops := h.computeDropRewards(questDef, target, nowMillis)
|
||||
for _, drop := range drops {
|
||||
h.applyRewardPossession(user, drop.PossessionType, drop.PossessionId, drop.Count, nowMillis)
|
||||
}
|
||||
h.grantDropRewards(user, drops, raritySet, rankSet, nowMillis)
|
||||
allDrops = append(allDrops, drops...)
|
||||
|
||||
if questDef.Gold != 0 {
|
||||
|
||||
@@ -3,6 +3,8 @@ package questflow
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"lunar-tear/server/internal/campaign"
|
||||
"lunar-tear/server/internal/gameutil"
|
||||
@@ -128,6 +130,54 @@ func (h *QuestHandler) evaluateFinishOutcome(user *store.UserState, questId int3
|
||||
return outcome
|
||||
}
|
||||
|
||||
var autoSaleRarityTiers = map[int32]bool{10: true, 20: true, 30: true, 40: true, 50: true}
|
||||
|
||||
// Rarity tiers (10..50) and ranks (1..5) are disjoint, so the delimited values
|
||||
// are classified by range — independent of the client's map key or delimiter.
|
||||
func parseAutoSaleRules(settings map[int32]store.AutoSaleSettingState) (raritySet, rankSet map[int32]bool) {
|
||||
raritySet = map[int32]bool{}
|
||||
rankSet = map[int32]bool{}
|
||||
for _, s := range settings {
|
||||
for _, n := range extractInts(s.PossessionAutoSaleItemValue) {
|
||||
switch {
|
||||
case autoSaleRarityTiers[n]:
|
||||
raritySet[n] = true
|
||||
case n >= 1 && n <= 5:
|
||||
rankSet[n] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return raritySet, rankSet
|
||||
}
|
||||
|
||||
func extractInts(s string) []int32 {
|
||||
fields := strings.FieldsFunc(s, func(r rune) bool { return r < '0' || r > '9' })
|
||||
out := make([]int32, 0, len(fields))
|
||||
for _, f := range fields {
|
||||
if v, err := strconv.Atoi(f); err == nil {
|
||||
out = append(out, int32(v))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (h *QuestHandler) grantDropRewards(user *store.UserState, drops []RewardGrant, raritySet, rankSet map[int32]bool, nowMillis int64) {
|
||||
for i := range drops {
|
||||
d := drops[i]
|
||||
if d.PossessionType == model.PossessionTypeParts || d.PossessionType == model.PossessionTypePartsEnhanced {
|
||||
chosenId, sold := h.Granter.GrantOrSellPartsDrop(user, d.PossessionId, raritySet, rankSet, nowMillis)
|
||||
if sold {
|
||||
// Sold parts have no inventory row, so the popup needs the rolled
|
||||
// variant id; kept parts read theirs from the parts table diff.
|
||||
drops[i].PossessionId = chosenId
|
||||
drops[i].IsAutoSale = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
h.applyRewardPossession(user, d.PossessionType, d.PossessionId, d.Count, nowMillis)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *QuestHandler) computeDropRewards(questDef masterdata.EntityMQuest, target campaign.QuestTarget, nowMillis int64) []RewardGrant {
|
||||
var drops []RewardGrant
|
||||
var dropRate campaign.DropRateMul
|
||||
|
||||
Reference in New Issue
Block a user