mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 05:43: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:
@@ -85,6 +85,7 @@ func CloneUserState(u UserState) UserState {
|
||||
out.CostumeLotteryEffectPending = maps.Clone(u.CostumeLotteryEffectPending)
|
||||
out.AutoSaleSettings = maps.Clone(u.AutoSaleSettings)
|
||||
out.CharacterRebirths = maps.Clone(u.CharacterRebirths)
|
||||
out.QuestAutoOrbit.AccumulatedDrops = append([]AutoOrbitDropEntry(nil), u.QuestAutoOrbit.AccumulatedDrops...)
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@ type PossessionGranter struct {
|
||||
PartsSubStatusPool map[int32][]int32
|
||||
PartsSubStatusDefs map[int32]PartsStatusSubDef
|
||||
|
||||
PartsSellPriceL1ByRarity map[int32]int32
|
||||
GoldConsumableItemId int32
|
||||
|
||||
LastChangedStoryWeaponIds []int32
|
||||
}
|
||||
|
||||
@@ -201,19 +204,51 @@ func (g *PossessionGranter) GrantCompanion(user *UserState, companionId int32, n
|
||||
}
|
||||
|
||||
func (g *PossessionGranter) GrantParts(user *UserState, requestedPartsId int32, nowMillis int64) {
|
||||
ref, refOk := g.PartsById[requestedPartsId]
|
||||
if !refOk {
|
||||
key := uuid.New().String()
|
||||
user.Parts[key] = PartsState{
|
||||
UserPartsUuid: key,
|
||||
PartsId: requestedPartsId,
|
||||
Level: 1,
|
||||
AcquisitionDatetime: nowMillis,
|
||||
}
|
||||
log.Printf("[GrantParts] unknown partsId=%d, granted as-is with no variant roll", requestedPartsId)
|
||||
chosenPartsId, chosenRef, ok := g.rollPartsVariant(requestedPartsId)
|
||||
if !ok {
|
||||
g.grantBareParts(user, requestedPartsId, nowMillis)
|
||||
return
|
||||
}
|
||||
g.createParts(user, chosenPartsId, chosenRef, nowMillis)
|
||||
}
|
||||
|
||||
// The rolled variant sets both rarity and rank, so the auto-sale decision can
|
||||
// only happen after the roll. Returns the rolled variant id and whether it sold.
|
||||
func (g *PossessionGranter) GrantOrSellPartsDrop(user *UserState, requestedPartsId int32, raritySet, rankSet map[int32]bool, nowMillis int64) (int32, bool) {
|
||||
chosenPartsId, chosenRef, ok := g.rollPartsVariant(requestedPartsId)
|
||||
if !ok {
|
||||
g.grantBareParts(user, requestedPartsId, nowMillis)
|
||||
return requestedPartsId, false
|
||||
}
|
||||
rarity := chosenRef.RarityType
|
||||
rank := chosenRef.PartsInitialLotteryId
|
||||
if price, ok := g.PartsSellPriceL1ByRarity[rarity]; ok && raritySet[rarity] && rankSet[rank] {
|
||||
user.ConsumableItems[g.GoldConsumableItemId] += price
|
||||
log.Printf("[GrantParts] auto-sold chosen=%d rarity=%d rank=%d -> %d gold", chosenPartsId, rarity, rank, price)
|
||||
return chosenPartsId, true
|
||||
}
|
||||
g.createParts(user, chosenPartsId, chosenRef, nowMillis)
|
||||
return chosenPartsId, false
|
||||
}
|
||||
|
||||
func (g *PossessionGranter) grantBareParts(user *UserState, partsId int32, nowMillis int64) {
|
||||
key := uuid.New().String()
|
||||
user.Parts[key] = PartsState{
|
||||
UserPartsUuid: key,
|
||||
PartsId: partsId,
|
||||
Level: 1,
|
||||
AcquisitionDatetime: nowMillis,
|
||||
}
|
||||
log.Printf("[GrantParts] unknown partsId=%d, granted as-is with no variant roll", partsId)
|
||||
}
|
||||
|
||||
// rollPartsVariant picks one of a parts group's 5 variants at random; the five
|
||||
// carry distinct PartsInitialLotteryId 1..5, which is the part's rank.
|
||||
func (g *PossessionGranter) rollPartsVariant(requestedPartsId int32) (int32, PartsRef, bool) {
|
||||
ref, refOk := g.PartsById[requestedPartsId]
|
||||
if !refOk {
|
||||
return requestedPartsId, PartsRef{}, false
|
||||
}
|
||||
chosenPartsId := requestedPartsId
|
||||
chosenRef := ref
|
||||
if variants := g.PartsVariantsByGroupRarity[ref.PartsGroupId][ref.RarityType]; len(variants) == 5 {
|
||||
@@ -222,7 +257,10 @@ func (g *PossessionGranter) GrantParts(user *UserState, requestedPartsId int32,
|
||||
} else {
|
||||
log.Printf("[GrantParts] no 5-variant set for group=%d rarity=%d (have %d), granting requested=%d", ref.PartsGroupId, ref.RarityType, len(variants), requestedPartsId)
|
||||
}
|
||||
return chosenPartsId, chosenRef, true
|
||||
}
|
||||
|
||||
func (g *PossessionGranter) createParts(user *UserState, chosenPartsId int32, chosenRef PartsRef, nowMillis int64) {
|
||||
mainStatId := g.DefaultPartsStatusMainByLotteryGroup[chosenRef.PartsStatusMainLotteryGroupId]
|
||||
if _, exists := user.PartsGroupNotes[chosenRef.PartsGroupId]; !exists {
|
||||
user.PartsGroupNotes[chosenRef.PartsGroupId] = PartsGroupNoteState{
|
||||
@@ -266,7 +304,7 @@ func (g *PossessionGranter) GrantParts(user *UserState, requestedPartsId int32,
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[GrantParts] requested=%d chosen=%d variant=%d group=%d rarity=%d preUnlockedSubs=%d", requestedPartsId, chosenPartsId, initialCount, chosenRef.PartsGroupId, chosenRef.RarityType, initialCount-1)
|
||||
log.Printf("[GrantParts] chosen=%d group=%d rarity=%d preUnlockedSubs=%d", chosenPartsId, chosenRef.PartsGroupId, chosenRef.RarityType, initialCount-1)
|
||||
}
|
||||
|
||||
func (g *PossessionGranter) GrantWeapon(user *UserState, weaponId int32, nowMillis int64) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
@@ -210,6 +211,17 @@ func load1to1(db *sql.DB, uid int64, u *store.UserState) {
|
||||
Scan(&u.GuerrillaFreeOpen.StartDatetime, &u.GuerrillaFreeOpen.OpenMinutes,
|
||||
&u.GuerrillaFreeOpen.DailyOpenedCount, &u.GuerrillaFreeOpen.LatestVersion)
|
||||
|
||||
var accumulatedDropsJSON string
|
||||
_ = db.QueryRow(`SELECT quest_type, chapter_id, quest_id, max_auto_orbit_count, cleared_auto_orbit_count, last_clear_datetime, latest_version, accumulated_drops_json
|
||||
FROM user_quest_auto_orbit WHERE user_id=?`, uid).
|
||||
Scan(&u.QuestAutoOrbit.QuestType, &u.QuestAutoOrbit.ChapterId, &u.QuestAutoOrbit.QuestId,
|
||||
&u.QuestAutoOrbit.MaxAutoOrbitCount, &u.QuestAutoOrbit.ClearedAutoOrbitCount,
|
||||
&u.QuestAutoOrbit.LastClearDatetime, &u.QuestAutoOrbit.LatestVersion,
|
||||
&accumulatedDropsJSON)
|
||||
if accumulatedDropsJSON != "" && accumulatedDropsJSON != "[]" {
|
||||
_ = json.Unmarshal([]byte(accumulatedDropsJSON), &u.QuestAutoOrbit.AccumulatedDrops)
|
||||
}
|
||||
|
||||
var isTicket int
|
||||
_ = db.QueryRow(`SELECT is_use_explore_ticket, playing_explore_id, latest_play_datetime, latest_version
|
||||
FROM user_explore WHERE user_id=?`, uid).
|
||||
|
||||
@@ -2,12 +2,24 @@ package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"lunar-tear/server/internal/model"
|
||||
"lunar-tear/server/internal/store"
|
||||
)
|
||||
|
||||
func marshalAutoOrbitDrops(drops []store.AutoOrbitDropEntry) string {
|
||||
if len(drops) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
b, err := json.Marshal(drops)
|
||||
if err != nil {
|
||||
return "[]"
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func boolToInt(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
@@ -109,6 +121,13 @@ func writeUserState(tx *sql.Tx, uid int64, u *store.UserState) error {
|
||||
uid, u.GuerrillaFreeOpen.StartDatetime, u.GuerrillaFreeOpen.OpenMinutes, u.GuerrillaFreeOpen.DailyOpenedCount, u.GuerrillaFreeOpen.LatestVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := exec(`INSERT INTO user_quest_auto_orbit (user_id, quest_type, chapter_id, quest_id, max_auto_orbit_count, cleared_auto_orbit_count, last_clear_datetime, latest_version, accumulated_drops_json) VALUES (?,?,?,?,?,?,?,?,?)`,
|
||||
uid, u.QuestAutoOrbit.QuestType, u.QuestAutoOrbit.ChapterId, u.QuestAutoOrbit.QuestId,
|
||||
u.QuestAutoOrbit.MaxAutoOrbitCount, u.QuestAutoOrbit.ClearedAutoOrbitCount,
|
||||
u.QuestAutoOrbit.LastClearDatetime, u.QuestAutoOrbit.LatestVersion,
|
||||
marshalAutoOrbitDrops(u.QuestAutoOrbit.AccumulatedDrops)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := exec(`INSERT INTO user_explore (user_id, is_use_explore_ticket, playing_explore_id, latest_play_datetime, latest_version) VALUES (?,?,?,?,?)`,
|
||||
uid, boolToInt(u.Explore.IsUseExploreTicket), u.Explore.PlayingExploreId, u.Explore.LatestPlayDatetime, u.Explore.LatestVersion); err != nil {
|
||||
return err
|
||||
@@ -674,6 +693,15 @@ func diffAndSave(tx *sql.Tx, uid int64, before, after *store.UserState) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !before.QuestAutoOrbit.Equal(after.QuestAutoOrbit) {
|
||||
if err := exec(`UPDATE user_quest_auto_orbit SET quest_type=?, chapter_id=?, quest_id=?, max_auto_orbit_count=?, cleared_auto_orbit_count=?, last_clear_datetime=?, latest_version=?, accumulated_drops_json=? WHERE user_id=?`,
|
||||
after.QuestAutoOrbit.QuestType, after.QuestAutoOrbit.ChapterId, after.QuestAutoOrbit.QuestId,
|
||||
after.QuestAutoOrbit.MaxAutoOrbitCount, after.QuestAutoOrbit.ClearedAutoOrbitCount,
|
||||
after.QuestAutoOrbit.LastClearDatetime, after.QuestAutoOrbit.LatestVersion,
|
||||
marshalAutoOrbitDrops(after.QuestAutoOrbit.AccumulatedDrops), uid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if before.Explore != after.Explore {
|
||||
if err := exec(`UPDATE user_explore SET is_use_explore_ticket=?, playing_explore_id=?, latest_play_datetime=?, latest_version=? WHERE user_id=?`,
|
||||
boolToInt(after.Explore.IsUseExploreTicket), after.Explore.PlayingExploreId, after.Explore.LatestPlayDatetime, after.Explore.LatestVersion, uid); err != nil {
|
||||
|
||||
@@ -95,6 +95,7 @@ func (s *SQLiteStore) ImportUser(u *store.UserState) error {
|
||||
"user_viewed_movies",
|
||||
"user_navi_cutin_played",
|
||||
"user_auto_sale_settings",
|
||||
"user_quest_auto_orbit",
|
||||
"user_explore_scores",
|
||||
"user_tutorials",
|
||||
"user_premium_items",
|
||||
|
||||
@@ -119,6 +119,7 @@ type UserState struct {
|
||||
CostumeLotteryEffectPending map[string]CostumeLotteryEffectPendingState // key: userCostumeUuid
|
||||
AutoSaleSettings map[int32]AutoSaleSettingState
|
||||
CharacterRebirths map[int32]CharacterRebirthState
|
||||
QuestAutoOrbit QuestAutoOrbitState
|
||||
}
|
||||
|
||||
func (u *UserState) EnsureMaps() {
|
||||
@@ -331,6 +332,45 @@ type GuerrillaFreeOpenState struct {
|
||||
LatestVersion int64
|
||||
}
|
||||
|
||||
type AutoOrbitDropEntry struct {
|
||||
PossessionType int32
|
||||
PossessionId int32
|
||||
Count int32
|
||||
IsAutoSale bool
|
||||
}
|
||||
|
||||
type QuestAutoOrbitState struct {
|
||||
QuestType int32
|
||||
ChapterId int32
|
||||
QuestId int32
|
||||
MaxAutoOrbitCount int32
|
||||
ClearedAutoOrbitCount int32
|
||||
LastClearDatetime int64
|
||||
LatestVersion int64
|
||||
AccumulatedDrops []AutoOrbitDropEntry
|
||||
}
|
||||
|
||||
func (s QuestAutoOrbitState) Equal(other QuestAutoOrbitState) bool {
|
||||
if s.QuestType != other.QuestType ||
|
||||
s.ChapterId != other.ChapterId ||
|
||||
s.QuestId != other.QuestId ||
|
||||
s.MaxAutoOrbitCount != other.MaxAutoOrbitCount ||
|
||||
s.ClearedAutoOrbitCount != other.ClearedAutoOrbitCount ||
|
||||
s.LastClearDatetime != other.LastClearDatetime ||
|
||||
s.LatestVersion != other.LatestVersion {
|
||||
return false
|
||||
}
|
||||
if len(s.AccumulatedDrops) != len(other.AccumulatedDrops) {
|
||||
return false
|
||||
}
|
||||
for i := range s.AccumulatedDrops {
|
||||
if s.AccumulatedDrops[i] != other.AccumulatedDrops[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type PortalCageStatusState struct {
|
||||
IsCurrentProgress bool
|
||||
DropItemStartDatetime int64
|
||||
|
||||
Reference in New Issue
Block a user