mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Add housing/pets/Thom Krooke, wordle overhaul, boost system, backup strategy
Part 3 (Housing, Thom Krooke & Pets): - Housing system with tiered upgrades, mortgage loans, FRED API rates - Thom Krooke realtor NPC with !thom commands and !thom pay extra principal - Pet system with arrival, naming, leveling, combat, morning defense - Pet ditch recovery scales with level, name validation Wordle overhaul: - Remove daily expiration — puzzles persist until solved/failed - Auto-start new puzzle after solve, fail, or skip - Sequential puzzle IDs instead of date-based - Auto-create puzzle on first guess if none active Adventure fixes: - Double XP/money boost toggle (!adv boost, admin only) - Fix ensureCharacter wiping existing data on query errors - Fix rival RPS format string bug - Fix daily summary empty data (load today+yesterday logs) - Fix holdem DM-to-room reply routing - Fix Robbie payout to 25% of item value - Add fishing to leaderboard and TwinBee calculations - Add Thom to morning menu - Persist mortgage rate across restarts via db.CacheGet/Set Infrastructure: - Daily database backup via VACUUM INTO with 7-day retention - Admin DM notification on backup failure (async) - Daily NPC house balance reset for holdem Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -148,21 +148,47 @@ func (p *AdventurePlugin) handleArenaFight(ctx MessageContext) error {
|
||||
return p.resolveArenaSurvival(ctx, run, char, tier, monster, combatLog, npcResult)
|
||||
}
|
||||
|
||||
// Normal combat roll
|
||||
// Pet combat actions
|
||||
petResult := petRollCombatActions(char, monster.Name)
|
||||
|
||||
// Normal combat roll — pet deflect reduces effective death chance
|
||||
deathChance := arenaDeathChance(monster, char, equip)
|
||||
if petResult != nil && petResult.Deflected {
|
||||
deathChance *= 0.5 // deflect halves death chance for this round
|
||||
}
|
||||
roll := rand.Float64()
|
||||
died := roll < deathChance
|
||||
|
||||
closeness := 1.0 - math.Abs(roll-deathChance)/math.Max(deathChance, 1-deathChance)
|
||||
combatLog := generateArenaCombatLog(!died, closeness)
|
||||
|
||||
// Append pet text to combat log
|
||||
var petText string
|
||||
if petResult != nil {
|
||||
if petResult.Attacked {
|
||||
petText += "\n\n" + petResult.AttackText
|
||||
}
|
||||
if petResult.Deflected {
|
||||
petText += "\n\n" + petResult.DeflectText
|
||||
}
|
||||
}
|
||||
|
||||
if died {
|
||||
if npcResult != nil && npcResult.Text != "" {
|
||||
combatLog.NPCText = npcResult.Text
|
||||
}
|
||||
combatLog.NPCText += petText
|
||||
if petResult != nil && (petResult.Attacked || petResult.Deflected) {
|
||||
combatLog.NPCText += "\n\n" + petDeathText(char)
|
||||
}
|
||||
return p.resolveArenaDeath(ctx, run, char, tier, monster, combatLog)
|
||||
}
|
||||
|
||||
combatLog.NPCText += petText
|
||||
if petResult != nil && (petResult.Attacked || petResult.Deflected) {
|
||||
combatLog.NPCText += "\n\n" + petVictoryText(char)
|
||||
}
|
||||
|
||||
return p.resolveArenaSurvival(ctx, run, char, tier, monster, combatLog, npcResult)
|
||||
}
|
||||
|
||||
@@ -212,20 +238,44 @@ func (p *AdventurePlugin) confirmAndStartArenaRun(ctx MessageContext) error {
|
||||
return p.resolveArenaSurvival(ctx, run, char, tier, monster, combatLog, npcResult)
|
||||
}
|
||||
|
||||
petResult := petRollCombatActions(char, monster.Name)
|
||||
|
||||
deathChance := arenaDeathChance(monster, char, equip)
|
||||
if petResult != nil && petResult.Deflected {
|
||||
deathChance *= 0.5
|
||||
}
|
||||
roll := rand.Float64()
|
||||
died := roll < deathChance
|
||||
|
||||
closeness := 1.0 - math.Abs(roll-deathChance)/math.Max(deathChance, 1-deathChance)
|
||||
combatLog := generateArenaCombatLog(!died, closeness)
|
||||
|
||||
var petText string
|
||||
if petResult != nil {
|
||||
if petResult.Attacked {
|
||||
petText += "\n\n" + petResult.AttackText
|
||||
}
|
||||
if petResult.Deflected {
|
||||
petText += "\n\n" + petResult.DeflectText
|
||||
}
|
||||
}
|
||||
|
||||
if died {
|
||||
if npcResult != nil && npcResult.Text != "" {
|
||||
combatLog.NPCText = npcResult.Text
|
||||
}
|
||||
combatLog.NPCText += petText
|
||||
if petResult != nil && (petResult.Attacked || petResult.Deflected) {
|
||||
combatLog.NPCText += "\n\n" + petDeathText(char)
|
||||
}
|
||||
return p.resolveArenaDeath(ctx, run, char, tier, monster, combatLog)
|
||||
}
|
||||
|
||||
combatLog.NPCText += petText
|
||||
if petResult != nil && (petResult.Attacked || petResult.Deflected) {
|
||||
combatLog.NPCText += "\n\n" + petVictoryText(char)
|
||||
}
|
||||
|
||||
return p.resolveArenaSurvival(ctx, run, char, tier, monster, combatLog, npcResult)
|
||||
}
|
||||
|
||||
@@ -531,6 +581,51 @@ func (p *AdventurePlugin) resolveArenaDeath(ctx MessageContext, run *ArenaRun, c
|
||||
return p.SendDM(ctx.Sender, text)
|
||||
}
|
||||
|
||||
// ── Pet ditch recovery — reduced death penalty ──
|
||||
petRecovery := petRollDitchRecovery(char)
|
||||
if petRecovery {
|
||||
// Pet intervenes — player still dies but respawn timer is reduced
|
||||
char.Kill()
|
||||
if char.DeadUntil != nil {
|
||||
reduced := time.Now().UTC().Add(petDitchRecoveryTime(char.PetLevel))
|
||||
char.DeadUntil = &reduced
|
||||
}
|
||||
|
||||
char.ArenaLosses++
|
||||
char.CombatXP += arenaParticipationXP
|
||||
if leveled, _ := checkAdvLevelUp(char, "combat"); leveled {
|
||||
p.checkRivalPoolUnlock(char)
|
||||
}
|
||||
if err := saveAdvCharacter(char); err != nil {
|
||||
slog.Error("arena: failed to save after pet ditch recovery", "user", ctx.Sender, "err", err)
|
||||
}
|
||||
|
||||
run.Status = "dead"
|
||||
run.Earnings = 0
|
||||
run.TierEarnings = 0
|
||||
run.XPAccumulated = 0
|
||||
endNow := time.Now().UTC()
|
||||
run.EndedAt = &endNow
|
||||
_ = saveArenaRun(run)
|
||||
insertArenaHistory(run.UserID, run.StartTier, run.Tier, run.RoundsSurvived, 0, "dead", monster.Name)
|
||||
upsertArenaStats(run.UserID, 0, true, run.Tier)
|
||||
|
||||
closer := arenaLoseCloser(monster.Name, len(combatLog.Rounds))
|
||||
text := renderArenaCombatLog(combatLog, monster, false, 0, arenaParticipationXP, closer)
|
||||
text += "\n\n_oof_"
|
||||
|
||||
_ = p.SendDM(ctx.Sender, text)
|
||||
|
||||
// Game room posts
|
||||
gr := gamesRoom()
|
||||
if gr != "" {
|
||||
_ = p.SendMessage(gr, petDitchRecoveryGameRoom(char.DisplayName, char.PetName, true))
|
||||
}
|
||||
|
||||
p.sendHospitalAd(ctx.Sender, char)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── Actual death — forfeit all session rewards ──
|
||||
lostEarnings := run.Earnings + run.TierEarnings
|
||||
|
||||
@@ -722,7 +817,16 @@ func arenaDeathChance(monster *ArenaMonster, char *AdventureCharacter, equip map
|
||||
}
|
||||
equipMod := avgTier * 0.03 // 0 at tier 0, 0.15 at tier 5
|
||||
|
||||
deathChance := baseDeath + levelMod - equipMod + skillMod
|
||||
// Housing HP bonus reduces death chance
|
||||
houseMod := char.HouseHPBonus() // 0-20% based on house tier
|
||||
|
||||
// Pet morning defense buff (cat offering / dog smothering)
|
||||
petDefMod := 0.0
|
||||
if char.PetMorningDefense {
|
||||
petDefMod = 0.05
|
||||
}
|
||||
|
||||
deathChance := baseDeath + levelMod - equipMod + skillMod - houseMod - petDefMod
|
||||
return math.Max(0.01, math.Min(0.98, deathChance))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user