|
# retoor <retoor@molodetz.nl>
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from .. import economy
|
|
from .common import GameError, _iso, _lvl, _parse, conditional_update_farm
|
|
from .farm import ensure_farm, get_farm
|
|
|
|
|
|
def upgrade_defense(user: dict) -> dict:
|
|
farm = ensure_farm(user["uid"])
|
|
level = _lvl(farm, "defense_level")
|
|
next_tier = economy.next_defense_tier(level)
|
|
if not next_tier:
|
|
raise GameError("Defense is already at the top tier.")
|
|
rows = conditional_update_farm(
|
|
farm["uid"],
|
|
set_clause="coins = coins - :cost, defense_level = :new_level",
|
|
where_clause="COALESCE(defense_level, 0) = :current_level AND coins >= :cost",
|
|
params={"cost": next_tier.upgrade_cost, "new_level": next_tier.level, "current_level": level},
|
|
)
|
|
if rows == 0:
|
|
farm = get_farm(user["uid"])
|
|
current_level = _lvl(farm, "defense_level")
|
|
if current_level != level:
|
|
raise GameError("Defense already changed - refresh and try again.")
|
|
raise GameError("Not enough coins to upgrade defense.")
|
|
return {"defense_level": next_tier.level, "spent": next_tier.upgrade_cost}
|
|
|
|
|
|
def charge_upkeep(farm: dict, now: datetime) -> dict:
|
|
level = _lvl(farm, "defense_level")
|
|
if level <= 0:
|
|
return farm
|
|
last_raw = farm.get("defense_last_upkeep_at") or ""
|
|
last = _parse(last_raw)
|
|
if not last:
|
|
rows = conditional_update_farm(
|
|
farm["uid"],
|
|
set_clause="defense_last_upkeep_at = :ts",
|
|
where_clause="(defense_last_upkeep_at IS NULL OR defense_last_upkeep_at = '') AND COALESCE(defense_level, 0) = :level",
|
|
params={"ts": _iso(now), "level": level},
|
|
)
|
|
if rows:
|
|
return {**farm, "defense_last_upkeep_at": _iso(now)}
|
|
return get_farm(farm["user_uid"]) or farm
|
|
elapsed_days = int((now - last).total_seconds() // 86400)
|
|
if elapsed_days < 1:
|
|
return farm
|
|
days_due = min(elapsed_days, economy.UPKEEP_GRACE_DAYS)
|
|
tier = economy.defense_tier(level)
|
|
coins = int(farm.get("coins", 0))
|
|
total_due = economy.daily_upkeep(tier, coins) * days_due
|
|
new_timestamp = _iso(last + timedelta(days=elapsed_days))
|
|
if coins >= total_due:
|
|
rows = conditional_update_farm(
|
|
farm["uid"],
|
|
set_clause="coins = coins - :total_due, defense_last_upkeep_at = :new_ts",
|
|
where_clause="defense_last_upkeep_at = :expected_last AND COALESCE(defense_level, 0) = :expected_level AND coins >= :total_due",
|
|
params={
|
|
"total_due": total_due,
|
|
"new_ts": new_timestamp,
|
|
"expected_last": last_raw,
|
|
"expected_level": level,
|
|
},
|
|
)
|
|
if rows == 0:
|
|
return get_farm(farm["user_uid"]) or farm
|
|
return {**farm, "coins": coins - total_due, "defense_last_upkeep_at": new_timestamp}
|
|
new_level = max(0, level - 1)
|
|
rows = conditional_update_farm(
|
|
farm["uid"],
|
|
set_clause="coins = 0, defense_level = :new_level, defense_last_upkeep_at = :new_ts",
|
|
where_clause="defense_last_upkeep_at = :expected_last AND COALESCE(defense_level, 0) = :expected_level",
|
|
params={"new_level": new_level, "new_ts": new_timestamp, "expected_last": last_raw, "expected_level": level},
|
|
)
|
|
if rows == 0:
|
|
return get_farm(farm["user_uid"]) or farm
|
|
return {
|
|
**farm,
|
|
"coins": 0,
|
|
"defense_level": new_level,
|
|
"defense_last_upkeep_at": new_timestamp,
|
|
}
|