Files
gogobee/internal/plugin/wordle_render.go
prosolis 0a1359de46 Add masterwork rework, arena combat log, Death's Reprieve fix, and wordle improvements
Adventure — Masterwork Rework:
- Expand from 3 generic items to 15 tiered masterwork items across
  mining (weapon), fishing (armor), and foraging (boots) with per-tier
  drop rates (5%/4%/3%/2%/1.5%) and location gating
- Skill-specific cross-skill bonuses replace flat masterwork check
- Auto-equip if better, silent discard for duplicates, inventory for rest
- Add !adventure equip command for manual masterwork equipping
- Tiered room announcements: T1-2 DM only, T3 quiet, T4-5 full
- First-drop detection with special message
- Character sheet shows /⚔️ markers for masterwork/arena gear
- Sell protection for special gear in shop

Adventure — Arena Combat Log:
- Turn-based Dragon Quest style narrative for arena fights
- Outcome-first design: roll determines win/loss, log assembled backward
- No-repeat flavor text via actionPicker with per-pool tracking
- 60 participation XP on arena loss

Adventure — Death's Reprieve Fix:
- All equipment set to 1 condition instead of death-level degradation

Wordle:
- Refactored word sourcing and expanded fallback word list
- Simplified Wordnik integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 16:24:59 -07:00

246 lines
5.9 KiB
Go

package plugin
import (
"fmt"
"strings"
)
// letterEmoji returns the colored emoji for a LetterResult.
func letterEmoji(r LetterResult) string {
switch r {
case LetterCorrect:
return "🟩"
case LetterPresent:
return "🟨"
default:
return "⬛"
}
}
// renderGrid renders the full emoji grid for the current puzzle state.
func renderWordleGrid(puzzle *WordlePuzzle) string {
var sb strings.Builder
status := fmt.Sprintf("%d/6", len(puzzle.Guesses))
if puzzle.Solved {
status += " 🟩🟩🟩🟩🟩"
}
sb.WriteString(fmt.Sprintf("🟩 **Wordle #%d** — %s\n\n", puzzle.PuzzleNumber, status))
for _, g := range puzzle.Guesses {
// Emoji tiles.
for _, r := range g.Results {
sb.WriteString(letterEmoji(r))
}
// Word + player name.
sb.WriteString(fmt.Sprintf(" %s (%s)", g.Word, g.PlayerName))
if g.Word == puzzle.Answer {
sb.WriteString(" ✅")
}
sb.WriteString("\n")
}
// Keyboard view.
sb.WriteString("\n")
sb.WriteString(renderKeyboard(puzzle.LetterStates))
return sb.String()
}
// renderKeyboard renders the QWERTY keyboard with color-coded letter states.
func renderKeyboard(states map[rune]LetterResult) string {
rows := []string{
"QWERTYUIOP",
"ASDFGHJKL",
"ZXCVBNM",
}
var sb strings.Builder
for _, row := range rows {
for i, ch := range row {
if i > 0 {
sb.WriteString(" ")
}
if state, ok := states[ch]; ok {
sb.WriteString(letterEmoji(state))
}
sb.WriteRune(ch)
}
sb.WriteString("\n")
}
return sb.String()
}
// renderWordleStartAnnouncement renders the puzzle start message.
func renderWordleStartAnnouncement(puzzleNumber, wordLength int, hint string) string {
base := fmt.Sprintf(
"🟩 **Daily Wordle #%d**\nA new %d-letter puzzle is ready! Work together — 6 guesses shared.",
puzzleNumber, wordLength,
)
if hint != "" {
base += fmt.Sprintf("\n💡 **Hint:** %s", hint)
}
base += "\n\nGuess with: `!wordle <word>`"
return base
}
// renderSolvedAnnouncement renders the solved puzzle message.
func renderSolvedAnnouncement(puzzle *WordlePuzzle, definition string, payouts []WordlePayout) string {
var sb strings.Builder
// Find the solver.
lastGuess := puzzle.Guesses[len(puzzle.Guesses)-1]
sb.WriteString(fmt.Sprintf("🎉 **Solved in %d/6!**\n", len(puzzle.Guesses)))
sb.WriteString(fmt.Sprintf("The word was **%s** — solved by %s on guess %d.\n",
puzzle.Answer, lastGuess.PlayerName, len(puzzle.Guesses)))
if definition != "" {
sb.WriteString(fmt.Sprintf("\n📖 *%s*\n", definition))
}
// Full grid.
sb.WriteString("\n")
for _, g := range puzzle.Guesses {
for _, r := range g.Results {
sb.WriteString(letterEmoji(r))
}
sb.WriteString(fmt.Sprintf(" %s (%s)", g.Word, g.PlayerName))
if g.Word == puzzle.Answer {
sb.WriteString(" ✅")
}
sb.WriteString("\n")
}
// Contributors with payouts.
if len(payouts) > 0 {
sb.WriteString("\n💰 **Payouts:**\n")
for _, pay := range payouts {
bonus := ""
if pay.Solver {
bonus = " 🏆 (solver bonus!)"
}
sb.WriteString(fmt.Sprintf(" **%s**: +€%d%s\n", pay.Name, pay.Amount, bonus))
}
} else {
sb.WriteString("\n🏆 Today's contributors:\n")
contributors := wordleContributors(puzzle)
for _, c := range contributors {
line := fmt.Sprintf(" %s — %d guess", c.name, c.guesses)
if c.guesses != 1 {
line += "es"
}
if c.solved {
line += " 🏆"
}
sb.WriteString(line + "\n")
}
}
return sb.String()
}
// renderFailedAnnouncement renders the failed puzzle message.
func renderFailedAnnouncement(puzzle *WordlePuzzle, definition string) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("💀 **Puzzle failed — 6/6**\nThe word was **%s**.\n", puzzle.Answer))
if definition != "" {
sb.WriteString(fmt.Sprintf("\n📖 *%s*\n", definition))
}
// Full grid.
sb.WriteString("\n")
for _, g := range puzzle.Guesses {
for _, r := range g.Results {
sb.WriteString(letterEmoji(r))
}
sb.WriteString(fmt.Sprintf(" %s (%s)\n", g.Word, g.PlayerName))
}
sb.WriteString("\nBetter luck tomorrow. Contributors:\n")
contributors := wordleContributors(puzzle)
for _, c := range contributors {
line := fmt.Sprintf(" %s — %d guess", c.name, c.guesses)
if c.guesses != 1 {
line += "es"
}
sb.WriteString(line + "\n")
}
return sb.String()
}
type wordleContributor struct {
name string
guesses int
solved bool
}
// wordleContributors tallies guess counts per player.
func wordleContributors(puzzle *WordlePuzzle) []wordleContributor {
type entry struct {
name string
guesses int
solved bool
}
seen := map[string]*entry{}
var order []string
for i, g := range puzzle.Guesses {
key := string(g.PlayerID)
if e, ok := seen[key]; ok {
e.guesses++
if i == len(puzzle.Guesses)-1 && puzzle.Solved {
e.solved = true
}
} else {
e := &entry{name: g.PlayerName, guesses: 1}
if i == len(puzzle.Guesses)-1 && puzzle.Solved {
e.solved = true
}
seen[key] = e
order = append(order, key)
}
}
result := make([]wordleContributor, 0, len(order))
for _, key := range order {
e := seen[key]
result = append(result, wordleContributor{name: e.name, guesses: e.guesses, solved: e.solved})
}
return result
}
// renderLeaderboard renders the all-time Wordle leaderboard.
func renderWordleLeaderboard(stats []WordlePlayerStat, streak int) string {
var sb strings.Builder
sb.WriteString("📊 **Wordle Leaderboard**\n")
for i, s := range stats {
line := fmt.Sprintf(" %d. %s — %d solve", i+1, s.DisplayName, s.PuzzlesSolved)
if s.PuzzlesSolved != 1 {
line += "s"
}
if s.WinningGuesses > 0 {
line += fmt.Sprintf(" | %d winning guess", s.WinningGuesses)
if s.WinningGuesses != 1 {
line += "es"
}
line += " 🏆"
}
sb.WriteString(line + "\n")
}
if streak > 0 {
sb.WriteString(fmt.Sprintf("\nCommunity streak: %d day", streak))
if streak != 1 {
sb.WriteString("s")
}
sb.WriteString(" solved in a row 🔥\n")
}
return sb.String()
}