Code Farm docs: Continuous Delivery for bots, exact formulas, accuracy fixes
DevPlace CI / test (push) Failing after 46m24s

Document auto-replant and POST /game/plot/stop in detail for automation authors
(exact conditions, quest/audit differences, race behavior, rotation recipe),
add an Exact formulas section (build time, rewards, raid payout, quests,
contracts, market supply), document the remaining crop/perk/legacy state fields.
Fix: Golden Parachute carry-over is up to 60%, crops reward_xp is base XP, and
stale game CLAUDE.md notes (Mastery step 5, raid notification names the raider).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 03:30:31 +02:00
co-authored by Claude Sonnet 5.5
parent d326c6b3f1
commit d84e4cf3d3
4 changed files with 76 additions and 11 deletions
+1 -1
View File
@@ -275,7 +275,7 @@ client are on the [Code Farm guide](/docs/code-farm.html).
method="POST",
path="/game/prestige",
title="Refactor (prestige)",
summary="Reset the farm at level 10+ for a permanent +25% coin bonus and earn Stars to spend on Legacy upgrades. Refactoring costs a coin fee that scales with prestige and current wealth (the farm state's refactor_cost); the fee funds the community treasury and a fraction of the remaining coins (10% base, up to 35% with the Golden Parachute Legacy upgrade) carries over. From prestige 50 onward, every 10 more prestige also earns a permanent Mastery point.",
summary="Reset the farm at level 10+ for a permanent +25% coin bonus and earn Stars to spend on Legacy upgrades. Refactoring costs a coin fee that scales with prestige and current wealth (the farm state's refactor_cost); the fee funds the community treasury and a fraction of the remaining coins (10% base, up to 60% with the Golden Parachute Legacy upgrade at level 10) carries over. From prestige 50 onward, every 10 more prestige also earns a permanent Mastery point.",
auth="user",
destructive=True,
sample_response={"ok": True, "farm": {"prestige": 1, "coins": 6550}},
+3 -3
View File
@@ -32,7 +32,7 @@ Devii plays via the `game_*` `http` actions in `actions/catalog/game.py` (state/
## Stealing (competitive loop, backwards compatible)
`store.steal(thief, owner, slot)` mirrors `water`: it requires the owner plot to be `ready` AND past the protection window `economy.effective_steal_grace(owner_defense_level)` (base `STEAL_GRACE_SECONDS` 60s, +30s per owner Branch Protection level) measured from `ready_at`, AND that the thief is **off cooldown for this victim** (`steal_cooldown_remaining(thief, owner, now) == 0`, i.e. no row in `game_steals` for the pair within `STEAL_COOLDOWN_SECONDS` = 3600 - you can raid a given neighbour only once per hour); it takes a **share** of the build rather than destroying it: `conditional_update_row` adds the share to the plot's `raided_fraction` (new `game_plots` REAL column, default 0, `COALESCE`d everywhere) and the crop stays in place, so the owner still harvests `1 - raided_fraction` of its value (`_harvest_crop` takes a `raided_fraction` argument). A build is raidable until the shares reach 1.0, after which `steal_reason` is `"stripped"`. The thief's payout is `realizable_harvest_coins(...) * share`, **clamped to the un-raided remainder** so repeated raids can never mint coins (a property sweep proved the un-clamped `max(1, round(...))` overshot by 1 coin on a fully stripped build). Raids are additionally capped at `STEAL_MAX_PER_VICTIM_PER_DAY` (3) per victim per day via `raids_against_today` over the new `idx_game_steals_owner_time` index, so an offline player cannot be stripped by an unlimited queue of raiders. Payout is **coins only** - no XP, no `total_harvests`, so the leaderboard stays earned by real farming - then inserts a `game_steals` row stamping the cooldown. The route `POST /game/farm/{username}/steal` (reusing `GameSlotForm`) then `track_action`s both sides, fires `create_notification(owner, "harvest_stolen", "Someone raided your Code Farm...", thief_uid, "/game")` (the message never names the thief; `related_uid` is internal), and `await notify_farm`s **both** the owner and the thief usernames so both farms refresh live. The new `harvest_stolen` notification type rides the existing in-app relay (live toast, no new wiring). No DB migration: grace + payout are computed from existing `ready_at`/perk/legacy columns, the `game_steals` table is created by the ensure-block (absent rows -> cooldown 0 -> first steal always allowed), and the new `GamePlotOut.can_steal`/`steal_coins`/`steal_cooldown_seconds`/`steal_reason` + `GameFarmOut.steal_cooldown_seconds` + `GameFarmViewOut.stole_coins` schema fields default safe. The per-pair cooldown is computed **once per farm** in `serialize_farm` (when the viewer is not the owner) and threaded into every `serialize_plot` as `steal_locked_until`, so `can_steal` is false and `steal_reason` is `"cooldown"`/`"protected"` accordingly. **Display must equal enforcement:** `serialize_farm` also threads the owner's Defense building into every `serialize_plot` (`steal_grace_bonus` = the tier's `grace_bonus`, `steal_floor` = the tier's `steal_fraction_floor` raised to `OBSERVABILITY_STEAL_FLOOR` when the owner owns Observability), so the serialized `can_steal`/`steal_coins` match exactly what `store/actions.py::steal()` will enforce and pay - a client acting on `can_steal=true` can never get a "still protected" 400, and `steal_coins` is the exact payout. Never let `serialize_plot` and `steal()` compute grace or payout from different inputs. Frontend: the ready/not-owner branch of `_game_grid.html` and `GameFarm.js._plotHtml` render a `btn-danger` Steal button (`data-game-action="steal"`, `data-confirm` gated like prestige) carrying `steal_coins`, or a disabled "Raid again in <countdown>" label when on cooldown; on success `GameFarm.js._submit` toasts the payout from `data.stole_coins`.
`store.steal(thief, owner, slot)` mirrors `water`: it requires the owner plot to be `ready` AND past the protection window `economy.effective_steal_grace(owner_defense_level)` (base `STEAL_GRACE_SECONDS` 60s, +30s per owner Branch Protection level) measured from `ready_at`, AND that the thief is **off cooldown for this victim** (`steal_cooldown_remaining(thief, owner, now) == 0`, i.e. no row in `game_steals` for the pair within `STEAL_COOLDOWN_SECONDS` = 3600 - you can raid a given neighbour only once per hour); it takes a **share** of the build rather than destroying it: `conditional_update_row` adds the share to the plot's `raided_fraction` (new `game_plots` REAL column, default 0, `COALESCE`d everywhere) and the crop stays in place, so the owner still harvests `1 - raided_fraction` of its value (`_harvest_crop` takes a `raided_fraction` argument). A build is raidable until the shares reach 1.0, after which `steal_reason` is `"stripped"`. The thief's payout is `realizable_harvest_coins(...) * share`, **clamped to the un-raided remainder** so repeated raids can never mint coins (a property sweep proved the un-clamped `max(1, round(...))` overshot by 1 coin on a fully stripped build). Raids are additionally capped at `STEAL_MAX_PER_VICTIM_PER_DAY` (3) per victim per day via `raids_against_today` over the new `idx_game_steals_owner_time` index, so an offline player cannot be stripped by an unlimited queue of raiders. Payout is **coins only** - no XP, no `total_harvests`, so the leaderboard stays earned by real farming - then inserts a `game_steals` row stamping the cooldown. The route `POST /game/farm/{username}/steal` (reusing `GameSlotForm`) then `track_action`s both sides, fires `create_notification(owner, "harvest_stolen", "<thief> raided your Code Farm and took N coins (P%) from your <crop> build...", thief_uid, "/game")` (the message names the raider, the crop, the coins and the percentage), and `await notify_farm`s **both** the owner and the thief usernames so both farms refresh live. The new `harvest_stolen` notification type rides the existing in-app relay (live toast, no new wiring). No DB migration: grace + payout are computed from existing `ready_at`/perk/legacy columns, the `game_steals` table is created by the ensure-block (absent rows -> cooldown 0 -> first steal always allowed), and the new `GamePlotOut.can_steal`/`steal_coins`/`steal_cooldown_seconds`/`steal_reason` + `GameFarmOut.steal_cooldown_seconds` + `GameFarmViewOut.stole_coins` schema fields default safe. The per-pair cooldown is computed **once per farm** in `serialize_farm` (when the viewer is not the owner) and threaded into every `serialize_plot` as `steal_locked_until`, so `can_steal` is false and `steal_reason` is `"cooldown"`/`"protected"` accordingly. **Display must equal enforcement:** `serialize_farm` also threads the owner's Defense building into every `serialize_plot` (`steal_grace_bonus` = the tier's `grace_bonus`, `steal_floor` = the tier's `steal_fraction_floor` raised to `OBSERVABILITY_STEAL_FLOOR` when the owner owns Observability), so the serialized `can_steal`/`steal_coins` match exactly what `store/actions.py::steal()` will enforce and pay - a client acting on `can_steal=true` can never get a "still protected" 400, and `steal_coins` is the exact payout. Never let `serialize_plot` and `steal()` compute grace or payout from different inputs. Frontend: the ready/not-owner branch of `_game_grid.html` and `GameFarm.js._plotHtml` render a `btn-danger` Steal button (`data-game-action="steal"`, `data-confirm` gated like prestige) carrying `steal_coins`, or a disabled "Raid again in <countdown>" label when on cooldown; on success `GameFarm.js._submit` toasts the payout from `data.stole_coins`.
## Extended mechanics (all backwards compatible)
@@ -96,7 +96,7 @@ A new, genuinely new table `game_market_ticks` (`crop_key`, `hour_bucket` = UTC
### 3. Mastery track and new crop families (`economy.py` prestige/mastery/crops sections + `store/mastery.py`)
Orthogonal to prestige, modeled directly on the existing Legacy shape (leveled, its own currency, survives prestige). **Two-counter design, load-bearing:** `mastery_points` (spendable, decreases on spend) vs `mastery_points_earned_total` (never decreases) - crop unlocks gate on the earned total so spending points can never re-lock content. `economy.mastery_points_awarded(old_prestige, new_prestige)` awards the FIRST Mastery point the moment prestige crosses `MASTERY_UNLOCK_PRESTIGE` (50) and one more every `MASTERY_PRESTIGE_STEP` (10) thereafter (`_mastery_total_for(p) = 1 + (p-50)//10` for `p>=50`, difference of the before/after totals - correct even if prestige ever jumps by more than 1). `store/actions.py::prestige()` adds the award into the same reset dict, alongside `stars`, as a field that is **not** zeroed. `POST /game/mastery` (`store.upgrade_mastery`) spends `mastery_points` on `economy.MASTERY_UPGRADES`: `autoreplant` (Continuous Delivery - wired via a shared `_try_autoreplant` helper called from both `harvest()` and `_auto_harvest()` after crediting, replants the same crop immediately if still affordable/unlocked), `analytics` (Farm Analytics - `GameFarmOut.mastery_analytics_unlocked` gates the `lifetime_coins_earned`/`lifetime_harvests` stats panel, rendered by `_game_analytics.html`/`data-analytics-host` inside the Mastery section - both counters are accumulated by `credit_farm`, so they are exact from the moment the upgrade is bought, not backfilled for prior activity), `contracts` (Legacy Contracts - unlocks the weekly contract slot, see section 6; this deliberately consolidates the critique's "Legacy Contracts" and "weekly contracts" into ONE mechanism rather than two redundant long-goal systems).
Orthogonal to prestige, modeled directly on the existing Legacy shape (leveled, its own currency, survives prestige). **Two-counter design, load-bearing:** `mastery_points` (spendable, decreases on spend) vs `mastery_points_earned_total` (never decreases) - crop unlocks gate on the earned total so spending points can never re-lock content. `economy.mastery_points_awarded(old_prestige, new_prestige)` awards the FIRST Mastery point the moment prestige crosses `MASTERY_UNLOCK_PRESTIGE` (50) and one more every `MASTERY_PRESTIGE_STEP` (5) thereafter (`_mastery_total_for(p) = 1 + (p-50)//5` for `p>=50`, difference of the before/after totals - correct even if prestige ever jumps by more than 1). `store/actions.py::prestige()` adds the award into the same reset dict, alongside `stars`, as a field that is **not** zeroed. `POST /game/mastery` (`store.upgrade_mastery`) spends `mastery_points` on `economy.MASTERY_UPGRADES`: `autoreplant` (Continuous Delivery - wired via a shared `_try_autoreplant` helper called from both `harvest()` and `_auto_harvest()` after crediting, replants the same crop immediately if still affordable/unlocked), `analytics` (Farm Analytics - `GameFarmOut.mastery_analytics_unlocked` gates the `lifetime_coins_earned`/`lifetime_harvests` stats panel, rendered by `_game_analytics.html`/`data-analytics-host` inside the Mastery section - both counters are accumulated by `credit_farm`, so they are exact from the moment the upgrade is bought, not backfilled for prior activity), `contracts` (Legacy Contracts - unlocks the weekly contract slot, see section 6; this deliberately consolidates the critique's "Legacy Contracts" and "weekly contracts" into ONE mechanism rather than two redundant long-goal systems).
Three new high-tier crops (`distsys`/`mlpipe`/`secfort` in `economy.CROPS`) extend the `Crop` dataclass with three new **trailing, defaulted** fields (`min_mastery: int = 0`, `steal_immune: bool = False`, `era_key: str | None = None`) - safe because every existing `Crop(...)` construction is positional over the original 8 non-defaulted fields. They set `min_level=MAX_LEVEL` (so, like every other crop, they re-lock after each refactor until the player re-levels to 20 - consistent with how Kernel etc. already behave post-prestige) and `min_mastery=1` (gated on `mastery_points_earned_total`, checked both in `store/actions.py::plant()` - real enforcement - and in `economy.crop_payload`'s `locked` flag - display). `secfort` (Security Fortress) is `steal_immune=True`: `serialize_plot` forces `can_steal=False`/`steal_reason="immune"` and `store/actions.py::steal()` raises immediately, before any grace/cooldown check, when the target crop is immune.
@@ -131,7 +131,7 @@ Continuous Delivery used to be a one-way lock: the harvested crop was replanted
Refactoring is no longer free at level 10 - it is the economy's progressive wealth sink, and the sink funds a redistribution loop. All backwards compatible: new `game_farms` columns (`legacy_carryover`, `last_grant_week`) default NULL and are `COALESCE`d, and the `game_treasury` table starts empty (lazily seeded by `ensure_treasury`).
- **Dynamic fee** (`economy.refactor_cost(prestige, coins)` = `REFACTOR_BASE_COST` (20k) `* (1 + prestige) * prestige_multiplier(prestige) + REFACTOR_WEALTH_PCT` (15%) `* coins`): the polynomial prestige term makes time-to-afford grow linearly with prestige (a soft cap on prestige cycling - income grows only linearly, so an exponential fee would wall off Mastery), and the wealth term is a progressive tax a hoarder cannot dodge. At prestige 0 the fee means a level-10 farm must keep farming (~23.5k coins minimum) before its first refactor - instant level-10 cycling is dead.
- **Carry-over** (`economy.refactor_carryover(coins, fee, carryover_level)`): after the fee, `REFACTOR_CARRYOVER_BASE` (10%) of the remainder survives the reset, raised +5%/level by the new **Golden Parachute** Legacy upgrade (`legacy_carryover`, max 5 -> 35%). Always strictly below 100% so a refactor can never be net-profitable (same invariant class as the fertilize fix). `prestige()` pins the balance in the atomic WHERE (`AND coins = :coins_now`) so a concurrent harvest yields a "balance just changed - refresh" `GameError` instead of a wrong charge, then credits the fee to the treasury.
- **Carry-over** (`economy.refactor_carryover(coins, fee, carryover_level)`): after the fee, `REFACTOR_CARRYOVER_BASE` (10%) of the remainder survives the reset, raised +5%/level by the new **Golden Parachute** Legacy upgrade (`legacy_carryover`, max 10 -> 60%). Always strictly below 100% so a refactor can never be net-profitable (same invariant class as the fertilize fix). `prestige()` pins the balance in the atomic WHERE (`AND coins = :coins_now`) so a concurrent harvest yields a "balance just changed - refresh" `GameError` instead of a wrong charge, then credits the fee to the treasury.
- **Treasury** (`game_treasury`, single row `uid="treasury-main"`, `balance`/`collected_total`/`granted_total`): every refactor fee is credited atomically (`credit_treasury`, raw conditional SQL like `conditional_update_farm`). The ledger invariant `balance == collected_total - granted_total` is fuzz-verified.
- **Community grant** (`POST /game/grant`, `store.claim_grant`): once per ISO week (`last_grant_week`), an **active low-wealth, low-prestige** farm (coins < `GRANT_WEALTH_CEILING` 10k, `harvests_week >= GRANT_MIN_WEEK_HARVESTS` 5 - the activity gate blocks idle alts, prestige <= `GRANT_MAX_PRESTIGE` 5 - so post-refactor whales with a briefly low balance never draw welfare) claims `economy.grant_amount(balance)` (capped `GRANT_CAP` 2,500). Order of operations: atomic treasury debit first (`WHERE balance >= :amount`), then the atomic farm credit+week-stamp; a losing farm update refunds the treasury. Race-verified with real OS processes: concurrent claims by one farm pay exactly once, and two farms racing an underfunded treasury never overdraw it.
- **Fan-out:** `serialize_farm` exposes `refactor_cost`/`refactor_affordable`/`refactor_carryover_pct`/`refactor_carryover_preview` plus the owner-only `grant_*`/`treasury_balance`; the shop row and `GameFarm.js._shopHtml` show the fee and hide the button until affordable; the sidebar **Community grant** section is `_game_grant.html`/`data-grant-host`/`_grantHtml`. Devii: `game_prestige` is now in `CONFIRM_REQUIRED` (large irreversible spend) with a declared `confirm` param; `game_claim_grant` is a plain auth tool. Docs: `game-prestige` updated + `game-grant` added.
+71 -6
View File
@@ -53,9 +53,11 @@ never be stolen.
Build time is the base time divided by the farm build speed (CI tier, the Build Cache perk, the
Bare-Metal Legacy upgrade, and the Private Registry for its three crops), so upgrades finish
everything proportionally faster. Coin and experience rewards shown in the API already include
everything proportionally faster. Coin rewards shown in the API already include
your perks, refactor bonus, Legacy multiplier, and the current Market Saturation factor, so read
them from the live state rather than from this table. Each crop in the state also carries a
them from the live state rather than from this table. The `reward_xp` in `crops` is the crop's base
experience: your actual experience is that value times `1 + 0.05 * Mentorship level` (see Exact
formulas). Each crop in the state also carries a
`market_state` of `normal`, `saturated`, or `boosted`.
## Plots
@@ -327,8 +329,8 @@ fee = round(20000 * (1 + prestige) * (1 + 0.25 * prestige) + 0.15 * coins)
so it grows with both your refactor count and your current balance (a wealth tax), and each
refactor takes longer to afford than the last. The live state reports the exact `refactor_cost`
and whether you can pay it right now (`refactor_affordable`). After the fee, **10%** of your
remaining coins carry over into the new run - up to **35%** with the Golden Parachute Legacy
upgrade - previewed exactly as `refactor_carryover_preview` (and `refactor_carryover_pct`). The
remaining coins carry over into the new run - `10% + 5%` per Golden Parachute Legacy level, so
up to **60%** at its maximum level 10 - previewed exactly as `refactor_carryover_preview` (and `refactor_carryover_pct`). The
new run starts with the 50 starting coins plus that carry-over.
Each refactor also awards **Stars**: `1 + level / 5 + prestige` (rounded down, using the level
@@ -409,6 +411,65 @@ an Era-exclusive cosmetic. Your own state reports `era_active`, `era_name`, `era
`era_harvests` while one is running; an Era can also make Era-exclusive crops plantable for its
duration.
## Exact formulas (for calculators and bots)
Every number the game shows comes from one formula, and the same formula is used when a client
reads it and when the server pays it, so a bot can reproduce any figure below exactly. `P` is your
prestige count and `L` the level of the named upgrade. All results are rounded with ordinary
half-to-even rounding to whole units, the way the live state reports them.
| Quantity | Formula |
|----------|---------|
| Prestige multiplier | `1 + 0.25 * P` |
| Legacy multiplier | `1 + 0.10 * L(multiplier)` |
| Income scale (social rewards, quest goals) | `prestige multiplier * legacy multiplier` |
| Build speed | `CI speed * (1 + 0.04 * L(growth)) * (1 + 0.05 * L(speed))`, times `1.15` for Rust, Compiler and Kernel with the Private Registry |
| Build seconds | `max(1, round(base seconds / build speed))` |
| Planting cost | `max(1, round(cost * max(0, 1 - 0.03 * L(discount))))` |
| Harvest coins | `round(base coins * (1 + 0.05 * L(yield)) * prestige multiplier * legacy multiplier * market factor)`, times `1.25` more with an active Underdog boost |
| Realized harvest coins | the harvest coins above, then times `5` if golden, then times `1.2` with an active weekly-contract boost, then times `1.12` for the Canary upside when owned (the Canary roll itself is random at harvest time) |
| Harvest experience | `round(base xp * (1 + 0.05 * L(xp)))` |
| Site experience from a harvest | `floor(farm experience / 25)`, so one Kernel harvest (400) is 16 site XP |
| Raid payout | `min(int(realized * (1 - raided so far)), max(1, round(realized * taken)))`, where `realized` is the **owner's** realized harvest coins for that build (the owner's perks, prestige, Legacy, boosts and golden flag, plus the current market factor, not yours), `taken = min(1 - raided so far, min(cap, max(floor, (0.50 - 0.05 * L(defense)) * (1 - tier reduction))))` and `L(defense)` is the owner's Branch Protection level. A raid is refused when nothing worth at least 1 coin is left |
| Watering reward | `max(6, round(6 * income scale))` coins and 3 experience |
| Daily bonus | `max(base, round(base * max(1, income scale)))` with `base = 20 + 12 * (min(streak, 7) - 1)` |
| Refactor fee | `round(20000 * (1 + P) * (1 + 0.25 * P) + 0.15 * coins)` |
| Refactor Stars | `1 + floor(level / 5) + P` |
| Grant | `min(2500, floor(treasury / eligible farms))`, zero when below 250 |
| Build seconds saved by one watering | `max(1, round(build seconds * 0.08))` |
| Fertilize cost | `ceil(realized coins * skipped seconds / full build seconds * 1.05)` |
**What a raid and a harvest do to your numbers.** A harvest pays coins and experience, advances the
`harvest` and `earn` quests, counts toward `total_harvests` and the weekly harvest count, awards
site XP, and adds one tick to that crop's Market Saturation supply. A **raid pays the raider coins
only**: no experience, no harvest count, no site XP, and it adds **no** Market Saturation supply,
because a raid moves value that was already produced instead of printing new supply. The owner is
paid the remaining share when they harvest.
**What counts as "active" for Market Saturation.** Supply is
`harvests in the last 48 hours * base build seconds / 86400 / active farms`, where an active farm is
any farm with at least one harvest this ISO week (at least 1). Only harvests by the owner, manual or
by the CI Bot, are counted.
**Daily quests, exactly.** Each day three of the four kinds below are given, taken in the order
`plant`, `harvest`, `water`, `earn` starting at a position chosen from your user and the date. The
goal is picked inside the range shown, and `scale` is your income scale (never below 1):
| Kind | Goal range | Coins | Experience |
|------|-----------|-------|------------|
| `plant` | 3 to 6 | `round(goal * 8 * scale)` | `goal * 2` |
| `harvest` | 3 to 5 | `round(goal * 14 * scale)` | `goal * 4` |
| `water` | 2 to 4 | `round(goal * 16 * scale)` | `goal * 5` |
| `earn` | 150 to 400, then `floor(goal * scale / 10) * 10` (never below 150) | `round(goal * 0.15)` | `max(1, round(goal / 30 / scale))` |
**Weekly contract, exactly.** One kind per ISO week. Goal is `4 * (min + max)` of the kind's range
above (36 plant, 32 harvest, 24 water, 2200 earn), and the `earn` goal is then scaled by your
income scale and rounded down to tens. It pays Stars and experience that do **not** scale: 3 Stars
and 40 XP for `plant` and `harvest`, 3 Stars and 30 XP for `water`, 5 Stars and 60 XP for `earn`.
**Not counted.** Auto-replant from Continuous Delivery does not advance the `plant` quest (see the
Continuous Delivery section). Quest progress is capped at the goal.
## Live updates
A farm page subscribes to the pub/sub topic `public.game.farm.{username}` and refreshes whenever
@@ -534,7 +595,8 @@ lists you act on. A plot looks like this:
```
A plot's `state` is `empty`, `growing`, or `ready`. `reward_coins`/`reward_xp` on a plot are the
crop's **base** rewards; the multiplied live values are on the matching entry in `crops`. An
crop's **base** rewards; the multiplied live coin value is on the matching entry in `crops` (its
`reward_xp` is still base experience, see Exact formulas). An
entry in `crops` carries the live `cost`, `reward_coins`, `reward_xp`, `grow_seconds`,
`min_level`, `market_state`, and a `locked` flag, so a client can decide what is both unlocked
and affordable without hard-coding the tables above.
@@ -555,7 +617,10 @@ read your **own** farm - on someone else's farm the lists are empty and the flag
**Lists:** `plots`, `crops`, plus *(owner)* `perks`, `quests` (daily entries, and the weekly
contract when unlocked), `legacy`, `mastery`, `infrastructure`, `cosmetics`. Each purchasable
entry carries `key`, `name`, `icon`, `description`, its current `level`/`owned` state, the exact
next `cost`, and a `maxed` flag where applicable.
next `cost`, and a `maxed` flag where applicable. Perks and Legacy upgrades also carry
`max_level`. Each entry in `crops` carries `locked` plus the reason: `locked_reason` is `level`,
`mastery` or `era` (empty when plantable), `locked_text` is the human sentence, `min_level` is the
unlock level and `min_mastery` is the Mastery points that must have been earned (0 or 1).
**Refactor:** `prestige`, `prestige_multiplier`, `prestige_min_level`, `prestige_available`
*(owner)*, `refactor_cost`, `refactor_affordable` *(owner)*, `refactor_carryover_pct`,
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "devplacepy"
version = "1.1.6"
version = "1.1.7"
description = "DevPlace - The Developer Social Network"
requires-python = ">=3.12"
dependencies = [