feat(economy): add /achievements milestone badges
15 one-time achievements over the lifetime stat counters (work/wealth/gambling/ crime/heists/fishing/streaks/prestige), each paying a modest one-time coin reward when unlocked. Detection is lazy - opening /achievements claims any newly earned (like quests roll on view) - so no per-command hook is needed, and rewards are a bounded, one-time coin source. - New achievements.py: ACHIEVEMENTS table, pure newly_earned/achievements_view, and locked do_check_achievements. New achievements_earned schema field. - /achievements command (own view claims; viewing others is read-only) with progress bars, db_error handling, and an "unlocked" banner. 8 tests: threshold detection, one-time claim (no double pay), multi-unlock, view progress capping. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013VbAVsrZuYesea99mPMmPT
This commit is contained in:
@@ -10,6 +10,8 @@ from discord import app_commands
|
||||
from core import economy
|
||||
import strings as S
|
||||
|
||||
from ._replies import reply_db_error
|
||||
|
||||
|
||||
def register_economy_profile_commands(
|
||||
tree: app_commands.CommandTree,
|
||||
@@ -342,6 +344,53 @@ def register_economy_profile_commands(
|
||||
data = await economy.get_user(target.id)
|
||||
await interaction.response.send_message(embed=_balance_embed(target, data))
|
||||
|
||||
@tree.command(name="achievements", description=S.CMD["achievements"])
|
||||
async def cmd_achievements(
|
||||
interaction: discord.Interaction, kasutaja: discord.Member | None = None
|
||||
):
|
||||
target = kasutaja or interaction.user
|
||||
note = ""
|
||||
# Only the invoker's own view claims newly-earned achievements.
|
||||
if target.id == interaction.user.id:
|
||||
res = await economy.do_check_achievements(interaction.user.id)
|
||||
if not res["ok"]:
|
||||
await reply_db_error(interaction)
|
||||
return
|
||||
if res["new"]:
|
||||
names = ", ".join(
|
||||
f"{economy.ACHIEVEMENTS[a]['emoji']} {economy.ACHIEVEMENTS[a]['name']}"
|
||||
for a in res["new"]
|
||||
)
|
||||
note = S.ACHIEVEMENTS_UI["unlocked_note"].format(
|
||||
names=names, reward=coin(res["reward"])
|
||||
) + "\n\n"
|
||||
|
||||
data = await economy.get_user(target.id)
|
||||
rows = economy.achievements_view(data)
|
||||
earned_count = sum(1 for r in rows if r["earned"])
|
||||
lines = [
|
||||
S.ACHIEVEMENTS_UI["row_earned"].format(
|
||||
emoji=r["emoji"], name=r["name"], reward=coin(r["reward"])
|
||||
)
|
||||
if r["earned"]
|
||||
else S.ACHIEVEMENTS_UI["row_locked"].format(
|
||||
emoji=r["emoji"], name=r["name"], progress=r["progress"],
|
||||
goal=r["goal"], reward=coin(r["reward"]),
|
||||
)
|
||||
for r in rows
|
||||
]
|
||||
title = S.ACHIEVEMENTS_UI["title"]
|
||||
if target.id != interaction.user.id:
|
||||
title += f" · {target.display_name}"
|
||||
embed = discord.Embed(
|
||||
title=title,
|
||||
description=note
|
||||
+ S.ACHIEVEMENTS_UI["desc"].format(earned=earned_count, total=len(rows))
|
||||
+ "\n\n" + "\n".join(lines),
|
||||
color=0xF4C430,
|
||||
)
|
||||
await interaction.response.send_message(embed=embed)
|
||||
|
||||
@tree.command(name="cooldowns", description=S.CMD["cooldowns"])
|
||||
async def cmd_cooldowns(interaction: discord.Interaction):
|
||||
data = await economy.get_user(interaction.user.id)
|
||||
|
||||
Reference in New Issue
Block a user