Code Farm docs: Continuous Delivery for bots, exact formulas, accuracy fixes
DevPlace CI / test (push) Failing after 46m24s
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:
@@ -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}},
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user