Adventure: !adventure recipes — list known crafting recipes

Players had no way to see what they could craft without grinding
ingredients and watching combat narrative. New command lists every recipe
unlocked at the player's current Foraging level, grouped by tier, with:

- Result name + ingredient list (so players know what to gather)
- Per-recipe success rate at the player's current level
- Locked-recipe count + next-unlock threshold
- Max auto-crafts per combat (1 + (level-10)/10)

Sub-Foraging-10 players see only the unlock hint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-04-28 23:23:53 -07:00
parent 8e3b8377c0
commit 0bebcb56cd
2 changed files with 68 additions and 0 deletions

View File

@@ -1,8 +1,10 @@
package plugin
import (
"fmt"
"log/slog"
"math/rand/v2"
"strings"
"maunium.net/go/mautrix/id"
)
@@ -339,6 +341,61 @@ type CraftResult struct {
Success bool
}
// renderRecipesKnown returns a player-facing list of recipes available at
// their current foraging level, plus a teaser line for the next unlock
// threshold. Hides exact ingredient lists for recipes the player hasn't
// unlocked — only the count of locked recipes is shown.
func renderRecipesKnown(foragingLevel int) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("🧪 **Crafting Recipes** — Foraging Lv.%d\n\n", foragingLevel))
if foragingLevel < 10 {
sb.WriteString("_Auto-crafting unlocks at Foraging Lv.10. Keep gathering._")
return sb.String()
}
// Group recipes by tier, list those known.
known := map[int][]CraftingRecipe{}
totalKnown := 0
totalLocked := 0
nextUnlock := 0
for _, r := range craftingRecipes {
if r.MinForaging <= foragingLevel {
known[r.Tier] = append(known[r.Tier], r)
totalKnown++
} else {
totalLocked++
if nextUnlock == 0 || r.MinForaging < nextUnlock {
nextUnlock = r.MinForaging
}
}
}
for tier := 1; tier <= 5; tier++ {
recipes := known[tier]
if len(recipes) == 0 {
continue
}
sb.WriteString(fmt.Sprintf("**Tier %d** (Foraging %d+)\n", tier, recipes[0].MinForaging))
for _, r := range recipes {
rate := craftingSuccessRate(foragingLevel, r.MinForaging) * 100
sb.WriteString(fmt.Sprintf(" • %s — %s (%.0f%% success)\n",
r.Result, strings.Join(r.Ingredients, " + "), rate))
}
}
sb.WriteString(fmt.Sprintf("\nKnown: %d · ", totalKnown))
if totalLocked == 0 {
sb.WriteString("All recipes unlocked. ⭐")
} else {
sb.WriteString(fmt.Sprintf("Locked: %d (next unlock at Foraging Lv.%d)", totalLocked, nextUnlock))
}
maxCrafts := 1 + (foragingLevel-10)/10
sb.WriteString(fmt.Sprintf("\nMax auto-crafts per combat: %d.", maxCrafts))
return sb.String()
}
// craftXPRewards: per-tier foraging XP granted on successful (and tiny on
// failed) auto-crafts. Successful T1 ≈ 30% of a Foraging Success haul, scaling
// up so T5 grants are meaningful. Failures get a token consolation grant —