Files
devplacepy/devplacepy/services/game/store/treasury.py
T
2026-07-23 01:15:04 +02:00

159 lines
4.8 KiB
Python

# retoor <retoor@molodetz.nl>
from __future__ import annotations
from datetime import datetime
from devplacepy.database import get_table
from .. import economy
from .common import GameError, _iso, _iso_week, _lvl, _now, conditional_update_farm
from .farm import ensure_farm, get_farm
TREASURY_UID = "treasury-main"
def _treasury():
return get_table("game_treasury")
def ensure_treasury() -> dict:
row = _treasury().find_one(uid=TREASURY_UID)
if row:
return row
_treasury().insert(
{
"uid": TREASURY_UID,
"balance": 0,
"collected_total": 0,
"granted_total": 0,
"updated_at": _iso(_now()),
}
)
return _treasury().find_one(uid=TREASURY_UID)
def treasury_balance() -> int:
row = _treasury().find_one(uid=TREASURY_UID)
return int(row.get("balance") or 0) if row else 0
def _treasury_update(set_clause: str, where_clause: str, params: dict) -> int:
from sqlalchemy import text
from devplacepy.database import db
sql = (
f"UPDATE game_treasury SET {set_clause}, updated_at = :updated_at "
f"WHERE uid = :treasury_uid AND ({where_clause})"
)
bind = {**params, "updated_at": _iso(_now()), "treasury_uid": TREASURY_UID}
with db:
result = db.executable.execute(text(sql), bind)
return result.rowcount
def credit_treasury(amount: int) -> None:
if amount <= 0:
return
ensure_treasury()
_treasury_update(
set_clause=(
"balance = COALESCE(balance, 0) + :amount, "
"collected_total = COALESCE(collected_total, 0) + :amount"
),
where_clause="1 = 1",
params={"amount": amount},
)
def _debit_treasury(amount: int) -> bool:
ensure_treasury()
rows = _treasury_update(
set_clause=(
"balance = COALESCE(balance, 0) - :amount, "
"granted_total = COALESCE(granted_total, 0) + :amount"
),
where_clause="COALESCE(balance, 0) >= :amount",
params={"amount": amount},
)
return rows > 0
def _refund_treasury(amount: int) -> None:
if amount <= 0:
return
_treasury_update(
set_clause=(
"balance = COALESCE(balance, 0) + :amount, "
"granted_total = COALESCE(granted_total, 0) - :amount"
),
where_clause="1 = 1",
params={"amount": amount},
)
def grant_status(farm: dict, now: datetime) -> dict:
week = _iso_week(now)
if farm.get("last_grant_week") == week:
return {"available": False, "amount": 0, "reason": "Grant already claimed this week."}
if _lvl(farm, "prestige") > economy.GRANT_MAX_PRESTIGE:
return {
"available": False,
"amount": 0,
"reason": f"Grants support farms up to prestige {economy.GRANT_MAX_PRESTIGE}.",
}
if int(farm.get("coins", 0)) >= economy.GRANT_WEALTH_CEILING:
return {
"available": False,
"amount": 0,
"reason": f"Grants support farms below {economy.GRANT_WEALTH_CEILING} coins.",
}
if _lvl(farm, "harvests_week") < economy.GRANT_MIN_WEEK_HARVESTS:
return {
"available": False,
"amount": 0,
"reason": (
f"Harvest at least {economy.GRANT_MIN_WEEK_HARVESTS} builds this week to qualify."
),
}
amount = economy.grant_amount(treasury_balance())
if amount <= 0:
return {
"available": False,
"amount": 0,
"reason": "The treasury is empty - refactor fees fill it.",
}
return {"available": True, "amount": amount, "reason": ""}
def claim_grant(user: dict) -> dict:
farm = ensure_farm(user["uid"])
now = _now()
status = grant_status(farm, now)
if not status["available"]:
raise GameError(status["reason"])
amount = status["amount"]
week = _iso_week(now)
if not _debit_treasury(amount):
raise GameError("The treasury is empty - refactor fees fill it.")
rows = conditional_update_farm(
farm["uid"],
set_clause=(
"coins = coins + :amount, "
"last_grant_week = :week, "
"lifetime_coins_earned = COALESCE(lifetime_coins_earned, 0) + :amount"
),
where_clause=(
"(last_grant_week IS NULL OR last_grant_week != :week) AND coins < :ceiling"
),
params={"amount": amount, "week": week, "ceiling": economy.GRANT_WEALTH_CEILING},
)
if rows == 0:
_refund_treasury(amount)
farm = get_farm(user["uid"])
if farm and farm.get("last_grant_week") == week:
raise GameError("Grant already claimed this week.")
raise GameError("Your farm changed - refresh and try again.")
return {"amount": amount, "week": week}