mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
N4/E1: T3 housing payoffs + a home-rest "well-rested" buff
Turn the dead top housing tiers into something worth buying. All three
land without a schema bump, and TestCombatCharacterization stays
byte-identical (the balance corpus never sets the new fields).
T3 trophy room: treasure cap 3->4 at HouseTier>=3 (maxTreasuresForTier).
Enforced at the save gate only -- HouseTier is never written downward,
so a held 4th treasure below tier 3 is unreachable and a load-time cap in
computeAdvBonuses would just add a query for an impossible state. The
all-irreplaceable manual discard prompt now lists the 4th slot too.
T3 workshop: +5% craft success at HouseTier>=3, lifting the cap 0.95->0.98
so a maxed forager still gains. craftingSuccessRate takes a workshopBonus,
threaded through autoCraftConsumables and renderRecipesKnown.
Well-rested buff (replaces the plan's T4 "inn-quality rest", a verified
no-op -- home rest already equals inn rest). Home-only (the inn and a
tier-1 shack grant nothing), starts at T2 and grows through T4, expires at
the next long rest:
- Temp HP cushion (8/12/16% of MaxHP). TempHP was a dormant field; wired
into applyDnDHPScaling as MaxHP headroom -- additive and golden-safe.
- Bonus spell slots (+1/+2/+3 at the caster's highest slot level), the
real reward, lifting casters who trail on spell-pool richness.
applyLongRestSpellSlots resets the pool to base then folds in the
bonus; expiry is stateless (next rest's reset drops it).
Magnitudes are tunable defaults; revisit against the post-parties
re-baseline.
Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
This commit is contained in:
110
internal/plugin/adventure_well_rested.go
Normal file
110
internal/plugin/adventure_well_rested.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Well-rested — a long rest taken at a home of tier 2+ grants a temporary buff
|
||||
// that lasts until the next long rest. Renting a room at Thom Krooke's inn does
|
||||
// not: the perk exists to give owning (and upgrading) a house a payoff the inn
|
||||
// can't match, and it turns the dead T3/T4 prestige tiers into something worth
|
||||
// buying.
|
||||
//
|
||||
// Two halves, both scaling with house tier (2 < 3 < 4):
|
||||
// - wellRestedTempHP: a temporary hit-point cushion, layered above MaxHP for
|
||||
// the day's fights via applyDnDHPScaling; and
|
||||
// - wellRestedSlotBonus: bonus spell slots folded into the caster's pool at
|
||||
// their highest available slot level.
|
||||
//
|
||||
// The tuning below is deliberately generous toward casters, who trail on
|
||||
// spell-pool richness — the bonus slots are the substantive reward and the
|
||||
// temp HP is a lighter feel-good cushion. All values are tunable.
|
||||
//
|
||||
// Nothing here is a secret discovery mechanic (cf. Misty/Arina), so the rest
|
||||
// message surfaces exactly what was granted.
|
||||
|
||||
// homeRestTempHPPct maps house tier → temp-HP fraction of MaxHP.
|
||||
func homeRestTempHPPct(houseTier int) float64 {
|
||||
switch {
|
||||
case houseTier >= 4:
|
||||
return 0.16
|
||||
case houseTier >= 3:
|
||||
return 0.12
|
||||
case houseTier >= 2:
|
||||
return 0.08
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// homeRestBonusSlots maps house tier → number of bonus spell slots.
|
||||
func homeRestBonusSlots(houseTier int) int {
|
||||
switch {
|
||||
case houseTier >= 4:
|
||||
return 3
|
||||
case houseTier >= 3:
|
||||
return 2
|
||||
case houseTier >= 2:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// wellRestedTempHP returns the temporary HP granted by a home long rest at the
|
||||
// given house tier. Tier 1 and the inn grant none. Always at least 1 when the
|
||||
// tier qualifies, so a low-HP character still sees the buff.
|
||||
func wellRestedTempHP(hpMax, houseTier int) int {
|
||||
pct := homeRestTempHPPct(houseTier)
|
||||
if pct <= 0 {
|
||||
return 0
|
||||
}
|
||||
hp := int(float64(hpMax) * pct)
|
||||
if hp < 1 {
|
||||
hp = 1
|
||||
}
|
||||
return hp
|
||||
}
|
||||
|
||||
// wellRestedSlotBonus returns the temporary bonus spell slots granted by a home
|
||||
// long rest, placed entirely at the caster's highest available slot level (the
|
||||
// most valuable, and one a caster is guaranteed to have). Tier 1, the inn, and
|
||||
// non-casters (empty pool) grant none.
|
||||
func wellRestedSlotBonus(houseTier int, pool map[int]int) map[int]int {
|
||||
n := homeRestBonusSlots(houseTier)
|
||||
if n == 0 || len(pool) == 0 {
|
||||
return nil
|
||||
}
|
||||
highest := 0
|
||||
for lvl := range pool {
|
||||
if lvl > highest {
|
||||
highest = lvl
|
||||
}
|
||||
}
|
||||
if highest == 0 {
|
||||
return nil
|
||||
}
|
||||
return map[int]int{highest: n}
|
||||
}
|
||||
|
||||
// wellRestedSummary renders the player-facing description of a granted buff,
|
||||
// e.g. "+18 temp HP, +2 level-5 spell slots". Returns "" when nothing was
|
||||
// granted (tier 1, the inn, or a non-caster with no temp HP).
|
||||
func wellRestedSummary(tempHP int, slotBonus map[int]int) string {
|
||||
var bits []string
|
||||
if tempHP > 0 {
|
||||
bits = append(bits, fmt.Sprintf("+%d temp HP", tempHP))
|
||||
}
|
||||
for lvl, n := range slotBonus {
|
||||
if n <= 0 {
|
||||
continue
|
||||
}
|
||||
plural := ""
|
||||
if n != 1 {
|
||||
plural = "s"
|
||||
}
|
||||
bits = append(bits, fmt.Sprintf("+%d level-%d spell slot%s", n, lvl, plural))
|
||||
}
|
||||
return strings.Join(bits, ", ")
|
||||
}
|
||||
Reference in New Issue
Block a user