forked from retoor/devplacepy
feat: add steal mechanic to Code Farm with 60s protection window and half-coin reward
This commit is contained in:
@@ -15,6 +15,9 @@ MAX_WATERS_PER_PLOT = 3
|
||||
WATER_REWARD_COINS = 6
|
||||
WATER_REWARD_XP = 3
|
||||
|
||||
STEAL_GRACE_SECONDS = 60
|
||||
STEAL_FRACTION = 0.5
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Crop:
|
||||
@@ -220,6 +223,10 @@ def effective_reward_xp(crop: Crop, xp_level: int = 0) -> int:
|
||||
return round(crop.reward_xp * (1 + PERK_BY_KEY["xp"].step * xp_level))
|
||||
|
||||
|
||||
def steal_reward_coins(crop: Crop, yield_level: int = 0, prestige: int = 0) -> int:
|
||||
return max(1, round(effective_reward_coins(crop, yield_level, prestige) * STEAL_FRACTION))
|
||||
|
||||
|
||||
def daily_reward(streak: int) -> int:
|
||||
effective = min(max(streak, 1), DAILY_STREAK_CAP)
|
||||
return DAILY_BASE + DAILY_STREAK_STEP * (effective - 1)
|
||||
|
||||
@@ -133,7 +133,15 @@ def _plot_state(plot: dict, now: datetime) -> str:
|
||||
return "growing"
|
||||
|
||||
|
||||
def serialize_plot(plot: dict, *, viewer_uid: str, owner_uid: str, now: datetime) -> dict:
|
||||
def serialize_plot(
|
||||
plot: dict,
|
||||
*,
|
||||
viewer_uid: str,
|
||||
owner_uid: str,
|
||||
now: datetime,
|
||||
yield_level: int = 0,
|
||||
prestige: int = 0,
|
||||
) -> dict:
|
||||
state = _plot_state(plot, now)
|
||||
crop = economy.crop_for(plot.get("crop_key", ""))
|
||||
ready_at = _parse(plot.get("ready_at", ""))
|
||||
@@ -149,6 +157,17 @@ def serialize_plot(plot: dict, *, viewer_uid: str, owner_uid: str, now: datetime
|
||||
and viewer_uid not in watered
|
||||
and len(watered) < economy.MAX_WATERS_PER_PLOT
|
||||
)
|
||||
can_steal = (
|
||||
state == "ready"
|
||||
and not is_owner
|
||||
and bool(viewer_uid)
|
||||
and bool(crop)
|
||||
and ready_at is not None
|
||||
and now >= ready_at + timedelta(seconds=economy.STEAL_GRACE_SECONDS)
|
||||
)
|
||||
steal_coins = (
|
||||
economy.steal_reward_coins(crop, yield_level, prestige) if can_steal else 0
|
||||
)
|
||||
return {
|
||||
"slot": plot.get("slot_index", 0),
|
||||
"state": state,
|
||||
@@ -162,6 +181,8 @@ def serialize_plot(plot: dict, *, viewer_uid: str, owner_uid: str, now: datetime
|
||||
"watered_count": len(watered),
|
||||
"max_waters": economy.MAX_WATERS_PER_PLOT,
|
||||
"can_water": can_water,
|
||||
"can_steal": can_steal,
|
||||
"steal_coins": steal_coins,
|
||||
"fertilize_cost": (
|
||||
economy.fertilize_cost(remaining) if state == "growing" and is_owner else 0
|
||||
),
|
||||
@@ -174,9 +195,20 @@ def serialize_farm(
|
||||
now = now or _now()
|
||||
viewer_uid = viewer["uid"] if viewer else ""
|
||||
owner_uid = owner["uid"]
|
||||
prestige = _lvl(farm, "prestige")
|
||||
growth_level = _lvl(farm, "perk_growth")
|
||||
discount_level = _lvl(farm, "perk_discount")
|
||||
yield_level = _lvl(farm, "perk_yield")
|
||||
plots = get_plots(farm["uid"])
|
||||
serialized_plots = [
|
||||
serialize_plot(plot, viewer_uid=viewer_uid, owner_uid=owner_uid, now=now)
|
||||
serialize_plot(
|
||||
plot,
|
||||
viewer_uid=viewer_uid,
|
||||
owner_uid=owner_uid,
|
||||
now=now,
|
||||
yield_level=yield_level,
|
||||
prestige=prestige,
|
||||
)
|
||||
for plot in plots
|
||||
]
|
||||
progress = economy.level_progress(int(farm.get("xp", 0)))
|
||||
@@ -185,10 +217,6 @@ def serialize_farm(
|
||||
next_tier = economy.next_ci_tier(ci_tier)
|
||||
ci_entry = economy.CI_BY_TIER.get(ci_tier)
|
||||
is_owner = viewer_uid == owner_uid
|
||||
prestige = _lvl(farm, "prestige")
|
||||
growth_level = _lvl(farm, "perk_growth")
|
||||
discount_level = _lvl(farm, "perk_discount")
|
||||
yield_level = _lvl(farm, "perk_yield")
|
||||
streak = _lvl(farm, "streak")
|
||||
daily_available = is_owner and _daily_available(farm, now)
|
||||
return {
|
||||
@@ -401,6 +429,49 @@ def water(visitor: dict, owner: dict, slot: int) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def steal(thief: dict, owner: dict, slot: int) -> dict:
|
||||
if thief["uid"] == owner["uid"]:
|
||||
raise GameError("You cannot steal from your own farm.")
|
||||
farm = ensure_farm(owner["uid"])
|
||||
plot = _plot_at(farm["uid"], slot)
|
||||
if not plot or not plot.get("crop_key"):
|
||||
raise GameError("That plot is empty.")
|
||||
now = _now()
|
||||
if _plot_state(plot, now) != "ready":
|
||||
raise GameError("That build is not ready.")
|
||||
crop = economy.crop_for(plot.get("crop_key", ""))
|
||||
if not crop:
|
||||
raise GameError("Unknown crop type.")
|
||||
ready_at = _parse(plot.get("ready_at", "")) or now
|
||||
if now < ready_at + timedelta(seconds=economy.STEAL_GRACE_SECONDS):
|
||||
raise GameError("That harvest is still protected.")
|
||||
_plots().update(
|
||||
{
|
||||
"uid": plot["uid"],
|
||||
"crop_key": "",
|
||||
"planted_at": "",
|
||||
"ready_at": "",
|
||||
"watered_by": "[]",
|
||||
"updated_at": _iso(now),
|
||||
},
|
||||
["uid"],
|
||||
)
|
||||
coins_gain = economy.steal_reward_coins(
|
||||
crop, _lvl(farm, "perk_yield"), _lvl(farm, "prestige")
|
||||
)
|
||||
thief_farm = ensure_farm(thief["uid"])
|
||||
_update_farm(
|
||||
thief_farm["uid"],
|
||||
{"coins": int(thief_farm.get("coins", 0)) + coins_gain},
|
||||
)
|
||||
return {
|
||||
"slot": slot,
|
||||
"crop": crop.key,
|
||||
"coins": coins_gain,
|
||||
"owner_uid": owner["uid"],
|
||||
}
|
||||
|
||||
|
||||
def leaderboard(limit: int = 25) -> list[dict]:
|
||||
farms = sorted(
|
||||
_farms().find(),
|
||||
|
||||
Reference in New Issue
Block a user