diff --git a/backend/engine.py b/backend/engine.py
index 819fb45..e098db0 100644
--- a/backend/engine.py
+++ b/backend/engine.py
@@ -116,8 +116,25 @@ def process_offline_and_ticks(state: dict) -> dict:
# Heat decay
state['heat'] = max(0.0, state.get('heat', 0.0) - (0.5 * delta))
+ check_server_achievements(state)
+
return sanitize_state(state)
+def check_server_achievements(state: dict):
+ ach = state.setdefault('achievements', {})
+ if state.get('totalEarned', 0) > 0:
+ ach['first_hit'] = True
+ if state.get('drugs', {}).get('weed', {}).get('workers', 0) >= 10:
+ ach['grower_op'] = True
+ if state.get('drugs', {}).get('meth', {}).get('unlocked'):
+ ach['blue_sky'] = True
+ if state.get('drugs', {}).get('cocaine', {}).get('unlocked'):
+ ach['cartel_tier'] = True
+ if state.get('cash', 0) >= 1000000:
+ ach['millionaire'] = True
+ if state.get('cleanBonds', 0) >= 1:
+ ach['laundered'] = True
+
def apply_actions(state: dict, actions: list) -> dict:
"""Applies and authorizes player actions sequentially against server authority."""
prod_m, sell_m, raw_m, dealer_m = get_multipliers(state)
diff --git a/frontend/js/game.js b/frontend/js/game.js
index 392a74c..0928d63 100644
--- a/frontend/js/game.js
+++ b/frontend/js/game.js
@@ -186,7 +186,7 @@ function renderLabView() {
-
${d.name}
-
${d.workerName}s: ${data.workers}
+
+ ${d.workerName}s: ${data.workers}
+
+ (${fmtNum(data.workers * d.produceRate * getProductionMultiplier())}g/s)
+
+
+
+ Auto-Refines
+ 1 Raw ➔ 1g
+
- ${d.rawName}
+ RAW SUPPLY (${d.rawName.toUpperCase()})
${fmtNum(data.raw)}
- ${d.productName}
+ READY PRODUCT (${d.productName.toUpperCase()})
${fmtNum(data.product)}g
@@ -780,6 +789,16 @@ function updateLiveInventory() {
const streetProdEl = document.getElementById(`val-prod-street-${d.id}`);
if (streetProdEl) streetProdEl.textContent = `${fmtNum(drugData.product)}g`;
+ const workerCountEl = document.getElementById(`val-worker-count-${d.id}`);
+ if (workerCountEl) workerCountEl.textContent = `${d.workerName}s: ${drugData.workers}`;
+
+ const autoProdEl = document.getElementById(`val-auto-prod-${d.id}`);
+ if (autoProdEl) {
+ const currentSpeed = drugData.workers * d.produceRate * getProductionMultiplier();
+ autoProdEl.textContent = `(${fmtNum(currentSpeed)}g/s)`;
+ autoProdEl.style.color = (drugData.workers > 0 && drugData.raw > 0) ? '#4ade80' : '#8b949e';
+ }
+
// Live button state updates (enable/disable as resources change)
const rawCost = Math.floor(d.rawCost * rawCostMulti);
const workerCost = getWorkerCost(d.id);
@@ -802,6 +821,9 @@ function updateLiveInventory() {
const btnSell10x = document.getElementById(`btn-sell-10x-${d.id}`);
if (btnSell10x) btnSell10x.disabled = (drugData.product < 10);
+
+ const btnUnlock = document.getElementById(`btn-unlock-${d.id}`);
+ if (btnUnlock) btnUnlock.disabled = (G.cash < getUnlockCost(d.id));
});
if (dom.quickStash) {