mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Adv 2.0 post-L5 audit cleanup: drop redundant dual-writes + refresh stale comments
Audit confirmed saveAdvCharacter's fan-out (upsertAllPlayerMetaFromAdvChar) covers all 13 migrated subsystems, so the per-call-site upsertPlayerMetaXxx calls retained "as defense" through the L4-L5 migration are redundant post-L5h. Removed 16 such call sites across arena (2), scheduler (4), hospital (1), consumables (2), rival (1), housing (11), and reordered masterwork to drop a now-pointless explicit upsert before the load+save. Refreshed nine stale doc comments referencing the legacy adventure_characters table or "soak window / fallback" language that no longer applies after the L5 close-out (commit 596b2b7). No behavior change; go vet + go test ./... pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -468,13 +468,6 @@ func (p *AdventurePlugin) resolveArenaDeath(ctx MessageContext, run *ArenaRun, c
|
|||||||
if err := saveAdvCharacter(char); err != nil {
|
if err := saveAdvCharacter(char); err != nil {
|
||||||
slog.Error("arena: failed to save character after death", "user", ctx.Sender, "err", err)
|
slog.Error("arena: failed to save character after death", "user", ctx.Sender, "err", err)
|
||||||
}
|
}
|
||||||
// Dual-write to player_meta (Adv 2.0 Phase L2 step 5).
|
|
||||||
if err := upsertPlayerMetaArena(ctx.Sender, char.ArenaWins, char.ArenaLosses, char.InvasionScore); err != nil {
|
|
||||||
slog.Error("player_meta: arena loss dual-write failed", "user", ctx.Sender, "err", err)
|
|
||||||
}
|
|
||||||
if err := upsertPlayerMetaSkillState(ctx.Sender, skillStateFromAdvChar(char)); err != nil {
|
|
||||||
slog.Error("player_meta: arena loss skill dual-write failed", "user", ctx.Sender, "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
run.Status = "dead"
|
run.Status = "dead"
|
||||||
@@ -550,13 +543,6 @@ func (p *AdventurePlugin) arenaCompleteSession(userID id.UserID, run *ArenaRun,
|
|||||||
if err := saveAdvCharacter(char); err != nil {
|
if err := saveAdvCharacter(char); err != nil {
|
||||||
slog.Error("arena: failed to save character after session complete", "user", userID, "err", err)
|
slog.Error("arena: failed to save character after session complete", "user", userID, "err", err)
|
||||||
}
|
}
|
||||||
// Dual-write to player_meta (Adv 2.0 Phase L2 step 5).
|
|
||||||
if err := upsertPlayerMetaArena(userID, char.ArenaWins, char.ArenaLosses, char.InvasionScore); err != nil {
|
|
||||||
slog.Error("player_meta: arena win dual-write failed", "user", userID, "err", err)
|
|
||||||
}
|
|
||||||
if err := upsertPlayerMetaSkillState(userID, skillStateFromAdvChar(char)); err != nil {
|
|
||||||
slog.Error("player_meta: arena win skill dual-write failed", "user", userID, "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// End run if not already ended
|
// End run if not already ended
|
||||||
if run.Status != "completed" && run.Status != "cashed_out" {
|
if run.Status != "completed" && run.Status != "cashed_out" {
|
||||||
|
|||||||
@@ -483,7 +483,6 @@ func autoCraftConsumables(userID id.UserID, items []AdvItem, foragingLevel int)
|
|||||||
char.CraftsSucceeded++
|
char.CraftsSucceeded++
|
||||||
checkAdvLevelUp(char, "foraging")
|
checkAdvLevelUp(char, "foraging")
|
||||||
_ = saveAdvCharacter(char)
|
_ = saveAdvCharacter(char)
|
||||||
_ = upsertPlayerMetaSkillState(userID, skillStateFromAdvChar(char))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Failure: all ingredients destroyed
|
// Failure: all ingredients destroyed
|
||||||
@@ -497,7 +496,6 @@ func autoCraftConsumables(userID id.UserID, items []AdvItem, foragingLevel int)
|
|||||||
char.ForagingXP += craftXPFailure[bestRecipe.Tier]
|
char.ForagingXP += craftXPFailure[bestRecipe.Tier]
|
||||||
checkAdvLevelUp(char, "foraging")
|
checkAdvLevelUp(char, "foraging")
|
||||||
_ = saveAdvCharacter(char)
|
_ = saveAdvCharacter(char)
|
||||||
_ = upsertPlayerMetaSkillState(userID, skillStateFromAdvChar(char))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,10 +199,9 @@ func (p *AdventurePlugin) resolveHospitalPay(ctx MessageContext, interaction *ad
|
|||||||
trackTaxPaid(ctx.Sender, potCut)
|
trackTaxPaid(ctx.Sender, potCut)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revive. Phase L4a: restore DnDCharacter HP to full (canonical
|
// Revive. Restore DnDCharacter HP to full (canonical post-L5 path);
|
||||||
// post-L5 path) and keep flipping AdvCharacter.Alive during soak
|
// AdvCharacter.Alive flip + HospitalVisits++ propagate through the
|
||||||
// since other systems still read it. HospitalVisits dual-writes
|
// saveAdvCharacter fan-out into player_meta.
|
||||||
// to player_meta + AdvCharacter (§11).
|
|
||||||
char.Alive = true
|
char.Alive = true
|
||||||
char.DeadUntil = nil
|
char.DeadUntil = nil
|
||||||
char.HospitalVisits++
|
char.HospitalVisits++
|
||||||
@@ -212,9 +211,6 @@ func (p *AdventurePlugin) resolveHospitalPay(ctx MessageContext, interaction *ad
|
|||||||
p.euro.Credit(ctx.Sender, float64(cost), "hospital_revival_refund")
|
p.euro.Credit(ctx.Sender, float64(cost), "hospital_revival_refund")
|
||||||
return p.SendDM(ctx.Sender, "Something went wrong during recovery. Your payment has been refunded.")
|
return p.SendDM(ctx.Sender, "Something went wrong during recovery. Your payment has been refunded.")
|
||||||
}
|
}
|
||||||
if err := upsertPlayerMetaHospitalVisits(char.UserID, char.HospitalVisits); err != nil {
|
|
||||||
slog.Error("hospital: failed to dual-write hospital_visits to player_meta", "user", char.UserID, "err", err)
|
|
||||||
}
|
|
||||||
if dnd, err := LoadDnDCharacter(char.UserID); err == nil && dnd != nil {
|
if dnd, err := LoadDnDCharacter(char.UserID); err == nil && dnd != nil {
|
||||||
dnd.HPCurrent = dnd.HPMax
|
dnd.HPCurrent = dnd.HPMax
|
||||||
if err := SaveDnDCharacter(dnd); err != nil {
|
if err := SaveDnDCharacter(dnd); err != nil {
|
||||||
|
|||||||
@@ -253,7 +253,6 @@ func (p *AdventurePlugin) handleThomCmd(ctx MessageContext) error {
|
|||||||
userMu.Lock()
|
userMu.Lock()
|
||||||
char.ThomAnimalLineFired = true
|
char.ThomAnimalLineFired = true
|
||||||
_ = saveAdvCharacter(char)
|
_ = saveAdvCharacter(char)
|
||||||
_ = upsertPlayerMetaNPCState(char.UserID, npcStateFromAdvChar(char))
|
|
||||||
userMu.Unlock()
|
userMu.Unlock()
|
||||||
}
|
}
|
||||||
return p.SendDM(ctx.Sender, text)
|
return p.SendDM(ctx.Sender, text)
|
||||||
@@ -331,8 +330,6 @@ func (p *AdventurePlugin) handleThomBuy(ctx MessageContext, char *AdventureChara
|
|||||||
p.euro.Credit(ctx.Sender, float64(totalCost), "house_purchase_refund")
|
p.euro.Credit(ctx.Sender, float64(totalCost), "house_purchase_refund")
|
||||||
return p.SendDM(ctx.Sender, "Failed to save. You've been refunded.")
|
return p.SendDM(ctx.Sender, "Failed to save. You've been refunded.")
|
||||||
}
|
}
|
||||||
// L4e dual-write: mirror house state into player_meta.
|
|
||||||
_ = upsertPlayerMetaHouseState(char.UserID, houseStateFromAdvChar(char))
|
|
||||||
return p.SendDM(ctx.Sender, fmt.Sprintf("🏠 **%s** purchased outright for €%d.\n\nNo loan. No weekly payments. Thom is visibly disappointed.", def.Name, totalCost))
|
return p.SendDM(ctx.Sender, fmt.Sprintf("🏠 **%s** purchased outright for €%d.\n\nNo loan. No weekly payments. Thom is visibly disappointed.", def.Name, totalCost))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,8 +361,6 @@ func (p *AdventurePlugin) handleThomBuy(ctx MessageContext, char *AdventureChara
|
|||||||
}
|
}
|
||||||
return p.SendDM(ctx.Sender, "Failed to save. You've been refunded.")
|
return p.SendDM(ctx.Sender, "Failed to save. You've been refunded.")
|
||||||
}
|
}
|
||||||
// L4e dual-write: mirror house state into player_meta.
|
|
||||||
_ = upsertPlayerMetaHouseState(char.UserID, houseStateFromAdvChar(char))
|
|
||||||
|
|
||||||
weekly := houseWeeklyPayment(loanAmount, rate)
|
weekly := houseWeeklyPayment(loanAmount, rate)
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
@@ -414,8 +409,6 @@ func (p *AdventurePlugin) handleThomPayoff(ctx MessageContext, char *AdventureCh
|
|||||||
p.euro.Credit(ctx.Sender, payoffAmount, "house_payoff_refund")
|
p.euro.Credit(ctx.Sender, payoffAmount, "house_payoff_refund")
|
||||||
return p.SendDM(ctx.Sender, "Failed to save. Your money has been refunded.")
|
return p.SendDM(ctx.Sender, "Failed to save. Your money has been refunded.")
|
||||||
}
|
}
|
||||||
// L4e dual-write: mirror house state into player_meta.
|
|
||||||
_ = upsertPlayerMetaHouseState(char.UserID, houseStateFromAdvChar(char))
|
|
||||||
|
|
||||||
if char.HasPet() {
|
if char.HasPet() {
|
||||||
return p.SendDM(ctx.Sender, fmt.Sprintf("🏠 %s", fmt.Sprintf(thomPaidOffPet, char.PetName)))
|
return p.SendDM(ctx.Sender, fmt.Sprintf("🏠 %s", fmt.Sprintf(thomPaidOffPet, char.PetName)))
|
||||||
@@ -469,8 +462,6 @@ func (p *AdventurePlugin) handleThomPay(ctx MessageContext, char *AdventureChara
|
|||||||
p.euro.Credit(ctx.Sender, float64(amount), "house_extra_payment_refund")
|
p.euro.Credit(ctx.Sender, float64(amount), "house_extra_payment_refund")
|
||||||
return p.SendDM(ctx.Sender, "Failed to save. Your money has been refunded.")
|
return p.SendDM(ctx.Sender, "Failed to save. Your money has been refunded.")
|
||||||
}
|
}
|
||||||
// L4e dual-write: mirror house state into player_meta.
|
|
||||||
_ = upsertPlayerMetaHouseState(char.UserID, houseStateFromAdvChar(char))
|
|
||||||
|
|
||||||
if char.HouseLoanBalance == 0 {
|
if char.HouseLoanBalance == 0 {
|
||||||
if char.HasPet() {
|
if char.HasPet() {
|
||||||
@@ -502,8 +493,6 @@ func (p *AdventurePlugin) handleThomAutopay(ctx MessageContext, char *AdventureC
|
|||||||
if err := saveAdvCharacter(char); err != nil {
|
if err := saveAdvCharacter(char); err != nil {
|
||||||
return p.SendDM(ctx.Sender, "Failed to save.")
|
return p.SendDM(ctx.Sender, "Failed to save.")
|
||||||
}
|
}
|
||||||
// L4e dual-write: mirror house state into player_meta.
|
|
||||||
_ = upsertPlayerMetaHouseState(char.UserID, houseStateFromAdvChar(char))
|
|
||||||
|
|
||||||
if char.HouseAutopay {
|
if char.HouseAutopay {
|
||||||
return p.SendDM(ctx.Sender, "✅ Autopay enabled. 2% discount on weekly payments. Thom calls this a favour. It is not.")
|
return p.SendDM(ctx.Sender, "✅ Autopay enabled. 2% discount on weekly payments. Thom calls this a favour. It is not.")
|
||||||
@@ -549,7 +538,6 @@ func (p *AdventurePlugin) processMortgagePayments() {
|
|||||||
freshChar.HouseLoanBalance = 0
|
freshChar.HouseLoanBalance = 0
|
||||||
freshChar.HouseMissedPayments = 0
|
freshChar.HouseMissedPayments = 0
|
||||||
_ = saveAdvCharacter(freshChar)
|
_ = saveAdvCharacter(freshChar)
|
||||||
_ = upsertPlayerMetaHouseState(freshChar.UserID, houseStateFromAdvChar(freshChar))
|
|
||||||
userMu.Unlock()
|
userMu.Unlock()
|
||||||
// Paid off!
|
// Paid off!
|
||||||
if freshChar.HasPet() {
|
if freshChar.HasPet() {
|
||||||
@@ -561,7 +549,6 @@ func (p *AdventurePlugin) processMortgagePayments() {
|
|||||||
}
|
}
|
||||||
freshChar.HouseMissedPayments = 0
|
freshChar.HouseMissedPayments = 0
|
||||||
_ = saveAdvCharacter(freshChar)
|
_ = saveAdvCharacter(freshChar)
|
||||||
_ = upsertPlayerMetaHouseState(freshChar.UserID, houseStateFromAdvChar(freshChar))
|
|
||||||
userMu.Unlock()
|
userMu.Unlock()
|
||||||
} else {
|
} else {
|
||||||
// Missed payment
|
// Missed payment
|
||||||
@@ -572,7 +559,6 @@ func (p *AdventurePlugin) processMortgagePayments() {
|
|||||||
if freshChar.HouseMissedPayments >= 10 {
|
if freshChar.HouseMissedPayments >= 10 {
|
||||||
freshChar.HouseLoanFrozen = true
|
freshChar.HouseLoanFrozen = true
|
||||||
_ = saveAdvCharacter(freshChar)
|
_ = saveAdvCharacter(freshChar)
|
||||||
_ = upsertPlayerMetaHouseState(freshChar.UserID, houseStateFromAdvChar(freshChar))
|
|
||||||
userMu.Unlock()
|
userMu.Unlock()
|
||||||
if freshChar.HasPet() {
|
if freshChar.HasPet() {
|
||||||
_ = p.SendDM(freshChar.UserID, fmt.Sprintf("🏠 %s", fmt.Sprintf(thomFreezePet, freshChar.PetName)))
|
_ = p.SendDM(freshChar.UserID, fmt.Sprintf("🏠 %s", fmt.Sprintf(thomFreezePet, freshChar.PetName)))
|
||||||
@@ -581,7 +567,6 @@ func (p *AdventurePlugin) processMortgagePayments() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_ = saveAdvCharacter(freshChar)
|
_ = saveAdvCharacter(freshChar)
|
||||||
_ = upsertPlayerMetaHouseState(freshChar.UserID, houseStateFromAdvChar(freshChar))
|
|
||||||
userMu.Unlock()
|
userMu.Unlock()
|
||||||
if freshChar.HasPet() {
|
if freshChar.HasPet() {
|
||||||
_ = p.SendDM(freshChar.UserID, fmt.Sprintf("🏠 %s", fmt.Sprintf(thomMissedPaymentPet, freshChar.HouseLoanBalance, freshChar.PetName)))
|
_ = p.SendDM(freshChar.UserID, fmt.Sprintf("🏠 %s", fmt.Sprintf(thomMissedPaymentPet, freshChar.HouseLoanBalance, freshChar.PetName)))
|
||||||
@@ -623,7 +608,6 @@ func (p *AdventurePlugin) sendMortgageRateChangeDMs(oldRate, newRate float64) {
|
|||||||
}
|
}
|
||||||
freshChar.HouseCurrentRate = newRate
|
freshChar.HouseCurrentRate = newRate
|
||||||
_ = saveAdvCharacter(freshChar)
|
_ = saveAdvCharacter(freshChar)
|
||||||
_ = upsertPlayerMetaHouseState(freshChar.UserID, houseStateFromAdvChar(freshChar))
|
|
||||||
userMu.Unlock()
|
userMu.Unlock()
|
||||||
|
|
||||||
newWeekly := houseWeeklyPayment(freshChar.HouseLoanBalance, newRate)
|
newWeekly := houseWeeklyPayment(freshChar.HouseLoanBalance, newRate)
|
||||||
|
|||||||
@@ -193,24 +193,17 @@ func (p *AdventurePlugin) checkMasterworkDrop(userID id.UserID, equip map[Equipm
|
|||||||
autoEquip = true // genuinely better than what they have
|
autoEquip = true // genuinely better than what they have
|
||||||
}
|
}
|
||||||
|
|
||||||
// First-drop detection. Read via player_meta (L4c migration); during
|
// First-drop detection. Counter persists via saveAdvCharacter fan-out.
|
||||||
// soak it falls back to AdvCharacter for users whose row hasn't been
|
|
||||||
// dual-written yet.
|
|
||||||
priorDrops, err := loadMasterworkDrops(userID)
|
priorDrops, err := loadMasterworkDrops(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("adventure: failed to load masterwork counter", "user", userID, "err", err)
|
slog.Error("adventure: failed to load masterwork counter", "user", userID, "err", err)
|
||||||
}
|
}
|
||||||
isFirstDrop := priorDrops == 0
|
isFirstDrop := priorDrops == 0
|
||||||
newDrops := priorDrops + 1
|
newDrops := priorDrops + 1
|
||||||
if err := upsertPlayerMetaMasterworkDrops(userID, newDrops); err != nil {
|
|
||||||
slog.Error("adventure: failed to save masterwork counter (player_meta)", "user", userID, "err", err)
|
|
||||||
}
|
|
||||||
// Dual-write to AdvCharacter during soak. Best-effort: if the row is
|
|
||||||
// missing the player_meta write above is canonical.
|
|
||||||
if char, lerr := loadAdvCharacter(userID); lerr == nil {
|
if char, lerr := loadAdvCharacter(userID); lerr == nil {
|
||||||
char.MasterworkDropsReceived = newDrops
|
char.MasterworkDropsReceived = newDrops
|
||||||
if err := saveAdvCharacter(char); err != nil {
|
if err := saveAdvCharacter(char); err != nil {
|
||||||
slog.Error("adventure: failed to save masterwork counter (legacy)", "user", userID, "err", err)
|
slog.Error("adventure: failed to save masterwork counter", "user", userID, "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,16 +89,12 @@ func (p *AdventurePlugin) checkRivalPoolUnlock(char *AdventureCharacter) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Promote to pool + send unlock DM if not yet announced.
|
// Promote to pool + send unlock DM if not yet announced.
|
||||||
|
// Persistence rides the caller's saveAdvCharacter → fan-out.
|
||||||
char.RivalPool = 1
|
char.RivalPool = 1
|
||||||
pool = 1
|
|
||||||
if !notified {
|
if !notified {
|
||||||
char.RivalUnlockedNotified = true
|
char.RivalUnlockedNotified = true
|
||||||
notified = true
|
|
||||||
p.SendDM(char.UserID, rivalUnlockDM)
|
p.SendDM(char.UserID, rivalUnlockDM)
|
||||||
}
|
}
|
||||||
if err := upsertPlayerMetaRivalState(char.UserID, pool, notified); err != nil {
|
|
||||||
slog.Error("player_meta: rival unlock dual-write failed", "user", char.UserID, "err", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── DB CRUD ──────────────────────────────────────────────────────────────────
|
// ── DB CRUD ──────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ func (p *AdventurePlugin) sendMorningDMs() {
|
|||||||
if err := saveAdvCharacter(&char); err != nil {
|
if err := saveAdvCharacter(&char); err != nil {
|
||||||
slog.Error("babysit: failed to save after daily trickle", "user", char.UserID, "err", err)
|
slog.Error("babysit: failed to save after daily trickle", "user", char.UserID, "err", err)
|
||||||
}
|
}
|
||||||
_ = upsertPlayerMetaPetState(char.UserID, petStateFromAdvChar(&char))
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +126,6 @@ func (p *AdventurePlugin) sendMorningDMs() {
|
|||||||
if petEvent != "" {
|
if petEvent != "" {
|
||||||
char.PetMorningDefense = true
|
char.PetMorningDefense = true
|
||||||
_ = saveAdvCharacter(&char)
|
_ = saveAdvCharacter(&char)
|
||||||
_ = upsertPlayerMetaPetState(char.UserID, petStateFromAdvChar(&char))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send morning DM with choices
|
// Send morning DM with choices
|
||||||
@@ -388,7 +386,6 @@ func (p *AdventurePlugin) midnightReset() error {
|
|||||||
text += " — not all is lost."
|
text += " — not all is lost."
|
||||||
}
|
}
|
||||||
_ = saveAdvCharacter(&char)
|
_ = saveAdvCharacter(&char)
|
||||||
_ = upsertPlayerMetaLifecycleState(char.UserID, lifecycleStateFromAdvChar(&char))
|
|
||||||
}
|
}
|
||||||
if err := p.SendDM(char.UserID, text); err != nil {
|
if err := p.SendDM(char.UserID, text); err != nil {
|
||||||
slog.Error("adventure: failed to send idle shame DM", "user", char.UserID, "err", err)
|
slog.Error("adventure: failed to send idle shame DM", "user", char.UserID, "err", err)
|
||||||
@@ -405,7 +402,6 @@ func (p *AdventurePlugin) midnightReset() error {
|
|||||||
char.BestStreak = char.CurrentStreak
|
char.BestStreak = char.CurrentStreak
|
||||||
}
|
}
|
||||||
_ = saveAdvCharacter(&char)
|
_ = saveAdvCharacter(&char)
|
||||||
_ = upsertPlayerMetaLifecycleState(char.UserID, lifecycleStateFromAdvChar(&char))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -497,11 +497,11 @@ func dndHPSnapshot(userID id.UserID) (cur, max int) {
|
|||||||
return c.HPCurrent, c.HPMax
|
return c.HPCurrent, c.HPMax
|
||||||
}
|
}
|
||||||
|
|
||||||
// markAdventureDead flips the legacy adventure_characters.alive flag and
|
// markAdventureDead flips Alive=false in player_meta (via saveAdvCharacter
|
||||||
// starts the 6h respawn timer for a player who went down in a D&D-layer
|
// fan-out) and starts the 6h respawn timer for a player who went down in a
|
||||||
// combat. Without this, hp_current persists as 0 but the legacy alive
|
// D&D-layer combat. Without this, hp_current persists as 0 but Alive stays
|
||||||
// flag stays true — the "zombie" state where !hospital says "you're
|
// true — the "zombie" state where !hospital says "you're alive!" while the
|
||||||
// alive!" while the sheet shows 0/33. Idempotent: bails if already dead.
|
// sheet shows 0/33. Idempotent: bails if already dead.
|
||||||
//
|
//
|
||||||
// source is "zone" / "expedition" / "patrol"; location is human-readable
|
// source is "zone" / "expedition" / "patrol"; location is human-readable
|
||||||
// (e.g. "Forest of Shadows") and surfaces in the daily report and
|
// (e.g. "Forest of Shadows") and surfaces in the daily report and
|
||||||
|
|||||||
@@ -126,9 +126,7 @@ func (p *AdventurePlugin) handleDnDLongRest(ctx MessageContext) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eligibility: housing OR pay inn fee. Housing read flips to
|
// Eligibility: housing OR pay inn fee.
|
||||||
// player_meta via loadHouseState (L4e reader flip); falls back to
|
|
||||||
// adventure_characters during the soak window.
|
|
||||||
house, _ := loadHouseState(ctx.Sender)
|
house, _ := loadHouseState(ctx.Sender)
|
||||||
hasHousing := house.Tier > 0
|
hasHousing := house.Tier > 0
|
||||||
innPaid := false
|
innPaid := false
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ func (p *AdventurePlugin) handleDnDAbilitiesCmd(ctx MessageContext) error {
|
|||||||
|
|
||||||
// !sheet — read-only D&D character sheet renderer.
|
// !sheet — read-only D&D character sheet renderer.
|
||||||
//
|
//
|
||||||
// Joins:
|
// Sources:
|
||||||
// dnd_character (D&D layer)
|
// dnd_character (D&D layer)
|
||||||
// adventure_characters (legacy skills, pet, housing — for at-a-glance context)
|
// player_meta (skills, pet, housing — for at-a-glance context)
|
||||||
// adventure_equipment (current gear; renders with legacy fields until Phase 4)
|
// adventure_equipment (current gear)
|
||||||
// adventure_treasures (= attunement substrate per v1.1 §7.4)
|
// adventure_treasures (= attunement substrate per v1.1 §7.4)
|
||||||
|
|
||||||
func (p *AdventurePlugin) handleDnDSheetCmd(ctx MessageContext) error {
|
func (p *AdventurePlugin) handleDnDSheetCmd(ctx MessageContext) error {
|
||||||
|
|||||||
@@ -419,12 +419,7 @@ type SkillState struct {
|
|||||||
FishingXP int
|
FishingXP int
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSkills returns true when any skill or XP field is non-zero. Used as
|
// HasSkills returns true when any skill or XP field is non-zero.
|
||||||
// the "row is migrated" marker in loadSkillState — an all-zero row is
|
|
||||||
// indistinguishable from "no row yet" so it falls through to the legacy
|
|
||||||
// table during the soak window. CombatLevel starts at 1 for any created
|
|
||||||
// character (see createAdvCharacter), so the only fully-zero state is a
|
|
||||||
// truly unmigrated row.
|
|
||||||
func (s SkillState) HasSkills() bool {
|
func (s SkillState) HasSkills() bool {
|
||||||
return s.CombatLevel > 0 || s.MiningSkill > 0 || s.ForagingSkill > 0 || s.FishingSkill > 0 ||
|
return s.CombatLevel > 0 || s.MiningSkill > 0 || s.ForagingSkill > 0 || s.FishingSkill > 0 ||
|
||||||
s.CombatXP > 0 || s.MiningXP > 0 || s.ForagingXP > 0 || s.FishingXP > 0
|
s.CombatXP > 0 || s.MiningXP > 0 || s.ForagingXP > 0 || s.FishingXP > 0
|
||||||
@@ -458,9 +453,7 @@ func upsertPlayerMetaSkillState(userID id.UserID, s SkillState) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadSkillState returns the player's skill state from player_meta when
|
// loadSkillState returns the player's skill state from player_meta.
|
||||||
// populated (HasSkills true), falling back to adventure_characters during
|
|
||||||
// the L5a soak window.
|
|
||||||
func loadSkillState(userID id.UserID) (SkillState, error) {
|
func loadSkillState(userID id.UserID) (SkillState, error) {
|
||||||
var s SkillState
|
var s SkillState
|
||||||
err := db.Get().QueryRow(
|
err := db.Get().QueryRow(
|
||||||
@@ -483,10 +476,8 @@ func loadSkillState(userID id.UserID) (SkillState, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skillStateFromAdvChar projects the skill-related fields off an
|
// skillStateFromAdvChar projects the skill-related fields off an
|
||||||
// AdventureCharacter into a SkillState. Used by the dual-write path: every
|
// AdventureCharacter into a SkillState. Used by saveAdvCharacter's fan-out
|
||||||
// site that mutates AdvCharacter skill or XP fields then calls
|
// to mirror skill columns into player_meta.
|
||||||
// upsertPlayerMetaSkillState with this projection so the columns move
|
|
||||||
// cleanly during the L5a soak window.
|
|
||||||
func skillStateFromAdvChar(c *AdventureCharacter) SkillState {
|
func skillStateFromAdvChar(c *AdventureCharacter) SkillState {
|
||||||
return SkillState{
|
return SkillState{
|
||||||
CombatLevel: c.CombatLevel,
|
CombatLevel: c.CombatLevel,
|
||||||
@@ -683,8 +674,7 @@ func upsertPlayerMetaNPCState(userID id.UserID, s NPCState) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadNPCState returns the NPC state from player_meta when populated,
|
// loadNPCState returns the NPC state from player_meta.
|
||||||
// otherwise falls back to adventure_characters during the L5c soak window.
|
|
||||||
func loadNPCState(userID id.UserID) (NPCState, error) {
|
func loadNPCState(userID id.UserID) (NPCState, error) {
|
||||||
var (
|
var (
|
||||||
s NPCState
|
s NPCState
|
||||||
@@ -837,9 +827,7 @@ func upsertPlayerMetaLifecycleState(userID id.UserID, s LifecycleState) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadLifecycleState returns lifecycle state from player_meta when
|
// loadLifecycleState returns lifecycle state from player_meta.
|
||||||
// populated, otherwise falls back to adventure_characters during the L5d
|
|
||||||
// soak window.
|
|
||||||
func loadLifecycleState(userID id.UserID) (LifecycleState, error) {
|
func loadLifecycleState(userID id.UserID) (LifecycleState, error) {
|
||||||
var (
|
var (
|
||||||
s LifecycleState
|
s LifecycleState
|
||||||
@@ -1087,15 +1075,12 @@ func miscStateFromAdvChar(c *AdventureCharacter) MiscState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyPlayerMetaOverlay overlays every player_meta-mirrored field onto an
|
// applyPlayerMetaOverlay populates an AdventureCharacter from player_meta.
|
||||||
// AdventureCharacter struct that was just SELECTed from adventure_characters.
|
// Post-L5 close-out: loadAdvCharacter no longer SELECTs from
|
||||||
// Phase L5h: saveAdvCharacter no longer writes the adventure_characters
|
// adventure_characters — it default-inits the struct and overlays every
|
||||||
// columns, so the legacy row freezes at its pre-L5h state. The overlay
|
// migrated subsystem's value from player_meta here. Errors are skipped — a
|
||||||
// re-sources every migrated subsystem's value from player_meta (with
|
// partial overlay is safer than a failed load (each field falls back to its
|
||||||
// fallback-to-AdvCharacter inside each load helper for the fields where
|
// Go zero value, except Alive which is pre-set true).
|
||||||
// player_meta is still empty), so callers continue to use char.X readers
|
|
||||||
// transparently. Errors are logged and skipped — a partial overlay is safer
|
|
||||||
// than a failed load.
|
|
||||||
func applyPlayerMetaOverlay(c *AdventureCharacter) {
|
func applyPlayerMetaOverlay(c *AdventureCharacter) {
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user