Files
devplacepy/devplacepy/services/game/store/infrastructure.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

52 lines
1.7 KiB
Python

# retoor <retoor@molodetz.nl>
from __future__ import annotations
import random
from .. import economy
from .common import GameError, _lvl, conditional_update_farm
from .farm import ensure_farm, get_farm
INFRA_COLUMN = {i.key: f"infra_{i.key}" for i in economy.INFRASTRUCTURE}
def buy_infrastructure(user: dict, key: str) -> dict:
infra = economy.infra_for(key)
if not infra:
raise GameError("Unknown infrastructure.")
farm = ensure_farm(user["uid"])
column = INFRA_COLUMN[infra.key]
rows = conditional_update_farm(
farm["uid"],
set_clause=f"coins = coins - :cost, {column} = 1",
where_clause=(
f"COALESCE({column}, 0) = 0 AND coins >= :cost "
f"AND COALESCE(prestige, 0) >= :min_prestige"
),
params={"cost": infra.cost, "min_prestige": infra.min_prestige},
)
if rows == 0:
farm = get_farm(user["uid"])
if _lvl(farm, column):
raise GameError("You already own that infrastructure.")
if _lvl(farm, "prestige") < infra.min_prestige:
raise GameError(f"{infra.name} requires prestige {infra.min_prestige}.")
raise GameError("Not enough coins for that infrastructure.")
return {"key": infra.key, "spent": infra.cost}
def owns_infrastructure(farm: dict, key: str) -> bool:
column = INFRA_COLUMN.get(key)
return bool(column and _lvl(farm, column))
def roll_canary(coins_gain: int, plant_cost: int) -> int:
roll = random.random()
if roll < economy.CANARY_DOUBLE_CHANCE:
return coins_gain * 2
if roll < economy.CANARY_DOUBLE_CHANCE + economy.CANARY_FAIL_CHANCE:
return min(coins_gain, plant_cost)
return coins_gain