Commit Graph

3 Commits

Author SHA1 Message Date
Rene Arumetsa
a2e1601d0e feat(economy): add /consumables shop of repeatable, expiring boosts
The economy had many coin faucets but almost no sinks: the /shop is
one-time ownership, and gambling/rob fines route to the house (which
players drain back via jackpots and heists), so they recirculate rather
than destroy coins. Result: steady inflation.

Add a consumables shop as a true recurring sink - buying destroys the
coins and grants a temporary boost, so there's always something to spend
on after gear is maxed:

  - Energiajook XL (500) - 1h of 2x earnings on /work, /beg, /crime
  - XP jook (500)        - 1h of 2x EXP
  - Kohv (300)           - instantly clears all cooldowns

Timed buffs live in a new active_buffs field ({kind: expiry_iso}), pruned
on read; rebuying extends the timer. Effects hook where they belong:
earn_mult in income.do_work/do_beg/do_crime, exp_buff_mult in
levels.award_exp; kohv is self-contained. New /consumables command browses
the menu (with active buffs) or buys a boost. Covered by 9 tests.

Note: active_buffs is a new PocketBase field - run
scripts/sync_pb_schema.py before deploying or buffs won't persist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-19 20:32:36 +03:00
Rene Arumetsa
3b35f82d80 Fix RequestView over-funding reentrancy (do_give double-transfer)
A follow-up reentrancy sweep of the five remaining money-moving views found
one more instance of the same bug class. The other four (quests, prestige,
fish, shop) verified clean - their underlying do_* functions are idempotent.

RequestView / FundModal (commands/economy_support_commands.py):
- on_submit read self._view.remaining, then awaited do_give, then decremented
  remaining. Because discord.py dispatches each modal submit as its own task
  and do_give is a plain non-idempotent transfer, a funder could open two
  modals and submit both before the first resolved: both read the same
  pre-decrement remaining, both passed the range check, and both transferred
  `amount` - over-funding the request (remaining goes negative) and moving up
  to the funder's whole balance.
- Fix: reserve the amount synchronously (decrement remaining BEFORE the do_give
  await, with no await in between - atomic under asyncio), and roll the
  reservation back if the transfer fails. The second concurrent submit now
  sees the reduced remaining and is rejected. Added a cheap _fund guard
  (remaining<=0 / is_finished) so a click on a funded request doesn't open a
  dead modal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-10 18:49:25 +03:00
Rene Arumetsa
77a3badd41 Feature: Clean up the codebase 2026-04-20 23:01:51 +03:00