Files
devplacepy/devplacepy/services/game/store/cosmetics.py
T
retoorandClaude Sonnet 5 34f76aad65 Code Farm economy rebalance: market saturation, infrastructure/defense/cosmetics, mastery track, secondary leaderboards, admin eras, underdog bonus and weekly contracts
Every purchase/upgrade path (new and pre-existing) is now race-safe against concurrent requests via atomic conditional SQL updates.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 23:55:46 +02:00

63 lines
1.8 KiB
Python

# retoor <retoor@molodetz.nl>
from __future__ import annotations
from sqlalchemy.exc import IntegrityError
from devplacepy.database import get_table
from devplacepy.utils import generate_uid
from .. import economy
from .common import GameError, _iso, _now, _update_farm, conditional_update_farm
from .farm import ensure_farm
def _cosmetics():
return get_table("game_cosmetics")
def owned_cosmetic_keys(user_uid: str) -> set[str]:
return {row["cosmetic_key"] for row in _cosmetics().find(user_uid=user_uid)}
def buy_cosmetic(user: dict, key: str) -> dict:
cosmetic = economy.cosmetic_for(key)
if not cosmetic:
raise GameError("Unknown cosmetic.")
farm = ensure_farm(user["uid"])
now = _iso(_now())
row_uid = generate_uid()
try:
_cosmetics().insert(
{
"uid": row_uid,
"user_uid": user["uid"],
"cosmetic_key": key,
"purchased_at": now,
"created_at": now,
}
)
except IntegrityError:
raise GameError("You already own that cosmetic.")
rows = conditional_update_farm(
farm["uid"],
set_clause="coins = coins - :cost",
where_clause="coins >= :cost",
params={"cost": cosmetic.cost_coins},
)
if rows == 0:
_cosmetics().delete(uid=row_uid)
raise GameError("Not enough coins for that cosmetic.")
return {"key": key, "spent": cosmetic.cost_coins}
def equip_title(user: dict, key: str) -> dict:
cosmetic = economy.cosmetic_for(key)
if not cosmetic or cosmetic.kind != "title":
raise GameError("Unknown title.")
if key not in owned_cosmetic_keys(user["uid"]):
raise GameError("You do not own that title.")
farm = ensure_farm(user["uid"])
_update_farm(farm["uid"], {"active_title": key})
return {"active_title": key}