import math import time # Drug Catalog mirroring config.js # Drug Catalog mirroring config.js DRUG_CONFIGS = { 'weed': {'rawCost': 10, 'sellPrice': 16, 'produceRate': 1.0, 'sellRate': 1.0, 'supplyRate': 1.0, 'supplierCost': 75, 'workerCost': 100, 'dealerCost': 150, 'unlockCost': 0}, 'shrooms': {'rawCost': 35, 'sellPrice': 65, 'produceRate': 0.8, 'sellRate': 0.8, 'supplyRate': 0.8, 'supplierCost': 300, 'workerCost': 450, 'dealerCost': 650, 'unlockCost': 500}, 'adderall': {'rawCost': 120, 'sellPrice': 240, 'produceRate': 0.6, 'sellRate': 0.6, 'supplyRate': 0.6, 'supplierCost': 1500, 'workerCost': 2000, 'dealerCost': 3000, 'unlockCost': 2500}, 'meth': {'rawCost': 400, 'sellPrice': 950, 'produceRate': 0.5, 'sellRate': 0.5, 'supplyRate': 0.5, 'supplierCost': 6000, 'workerCost': 8000, 'dealerCost': 12000, 'unlockCost': 10000}, 'mdma': {'rawCost': 1200, 'sellPrice': 3200, 'produceRate': 0.4, 'sellRate': 0.4, 'supplyRate': 0.4, 'supplierCost': 20000, 'workerCost': 28000, 'dealerCost': 40000, 'unlockCost': 35000}, 'ketamine': {'rawCost': 3800, 'sellPrice': 11000, 'produceRate': 0.35, 'sellRate': 0.35, 'supplyRate': 0.35, 'supplierCost': 75000, 'workerCost': 95000, 'dealerCost': 140000, 'unlockCost': 120000}, 'cocaine': {'rawCost': 12000, 'sellPrice': 38000, 'produceRate': 0.3, 'sellRate': 0.3, 'supplyRate': 0.3, 'supplierCost': 250000, 'workerCost': 320000, 'dealerCost': 480000, 'unlockCost': 400000}, 'crack': {'rawCost': 35000, 'sellPrice': 115000, 'produceRate': 0.25, 'sellRate': 0.25, 'supplyRate': 0.25, 'supplierCost': 700000, 'workerCost': 900000, 'dealerCost': 1400000, 'unlockCost': 1200000}, 'heroin': {'rawCost': 110000, 'sellPrice': 420000, 'produceRate': 0.2, 'sellRate': 0.2, 'supplyRate': 0.2, 'supplierCost': 2800000, 'workerCost': 3500000, 'dealerCost': 5500000, 'unlockCost': 4500000}, 'fentanyl': {'rawCost': 450000, 'sellPrice': 2100000, 'produceRate': 0.15, 'sellRate': 0.15, 'supplyRate': 0.15, 'supplierCost': 12000000, 'workerCost': 15000000, 'dealerCost': 24000000, 'unlockCost': 20000000} } SYNTHESIS_RECIPES = { 'calvin_klein': {'ingredients': {'cocaine': 5, 'ketamine': 5}, 'payout': 95000}, 'speedball': {'ingredients': {'heroin': 3, 'cocaine': 3}, 'payout': 850000}, 'candy_flip': {'ingredients': {'shrooms': 15, 'mdma': 10}, 'payout': 75000} } SKILL_COSTS = { 'chem_1': 500, 'chem_2': 15000, 'chem_3': 250000, 'king_1': 1000, 'king_2': 35000, 'king_3': 600000, 'smug_1': 2000, 'smug_2': 50000, 'smug_3': 1000000 } def clean_round(val: float) -> float: """Rounds float gracefully to 1 decimal place without floating point anomalies.""" if math.isnan(val) or math.isinf(val) or val < 0: return 0.0 return round(float(val), 1) def get_initial_state() -> dict: state = { 'cash': 25.0, 'cleanBonds': 0, 'heat': 0.0, 'totalEarned': 0.0, 'lastTick': time.time(), 'drugs': {}, 'skills': {s: False for s in SKILL_COSTS}, 'achievements': {}, 'synthesis': {r: 0 for r in SYNTHESIS_RECIPES} } for drug_id, cfg in DRUG_CONFIGS.items(): state['drugs'][drug_id] = { 'unlocked': (drug_id == 'weed'), 'raw': 0.0, 'product': 0.0, 'suppliers': 0, 'workers': 0, 'dealers': 0 } return state def get_multipliers(state: dict): bonds = state.get('cleanBonds', 0) skills = state.get('skills', {}) achievements = state.get('achievements', {}) prod_m = 1.0 + (bonds * 0.03) if skills.get('chem_1'): prod_m += 0.15 if achievements.get('millionaire'): prod_m += 0.20 sell_m = 1.0 + (bonds * 0.04) if skills.get('king_2'): sell_m += 0.25 if achievements.get('first_hit'): sell_m += 0.05 if achievements.get('cartel_tier'): sell_m += 0.15 raw_m = 0.85 if skills.get('chem_2') else 1.0 dealer_m = 1.0 if skills.get('king_1'): dealer_m += 0.20 if skills.get('king_3'): dealer_m += 0.50 return prod_m, sell_m, raw_m, dealer_m def process_offline_and_ticks(state: dict) -> dict: now = time.time() last = state.get('lastTick', now) delta = min(max(0.0, now - last), 86400.0) # Cap at 24h offline state['lastTick'] = now if delta <= 0: return sanitize_state(state) prod_m, sell_m, raw_m, dealer_m = get_multipliers(state) for drug_id, data in state.get('drugs', {}).items(): cfg = DRUG_CONFIGS.get(drug_id) if not cfg or not data.get('unlocked'): continue # Suppliers automatic raw cultivation / procurement suppliers = data.get('suppliers', 0) if suppliers > 0: supply_speed = suppliers * cfg.get('supplyRate', 1.0) * prod_m data['raw'] = data.get('raw', 0.0) + (supply_speed * delta) # Workers production (Refining raw -> product) workers = data.get('workers', 0) raw = data.get('raw', 0.0) if workers > 0 and raw > 0: potential_prod = workers * cfg['produceRate'] * prod_m * delta actual_prod = min(raw, potential_prod) data['raw'] = max(0.0, raw - actual_prod) data['product'] = data.get('product', 0.0) + actual_prod # Dealers selling (Product -> Cash) dealers = data.get('dealers', 0) product = data.get('product', 0.0) if dealers > 0 and product > 0: potential_sell = dealers * cfg['sellRate'] * dealer_m * delta actual_sell = min(product, potential_sell) data['product'] = max(0.0, product - actual_sell) rev = actual_sell * cfg['sellPrice'] * sell_m state['cash'] = state.get('cash', 0.0) + rev state['totalEarned'] = state.get('totalEarned', 0.0) + rev # 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) for act in actions: action_type = act.get('type') drug_id = act.get('drug_id') if action_type == 'buy_raw' and drug_id in DRUG_CONFIGS: cfg = DRUG_CONFIGS[drug_id] cost = cfg['rawCost'] * raw_m amount = max(1, int(act.get('amount', 1))) total_cost = cost * amount if state['cash'] >= total_cost: state['cash'] -= total_cost state['drugs'][drug_id]['raw'] = state['drugs'][drug_id].get('raw', 0.0) + amount elif action_type == 'manufacture' and drug_id in DRUG_CONFIGS: data = state['drugs'][drug_id] amount = max(1, int(act.get('amount', 1))) actual = min(amount, int(data.get('raw', 0))) if actual > 0: data['raw'] -= actual yield_count = actual if state.get('skills', {}).get('chem_3'): # Purity proc bonus yield_count *= 1.3 data['product'] = data.get('product', 0.0) + yield_count state['heat'] = min(100.0, state.get('heat', 0.0) + (0.5 * actual)) elif action_type == 'sell_manual' and drug_id in DRUG_CONFIGS: data = state['drugs'][drug_id] cfg = DRUG_CONFIGS[drug_id] amount = max(1, int(act.get('amount', 1))) actual = min(amount, int(data.get('product', 0))) if actual > 0: data['product'] -= actual profit = actual * cfg['sellPrice'] * sell_m state['cash'] += profit state['totalEarned'] += profit state['heat'] = min(100.0, state.get('heat', 0.0) + (0.8 * actual)) elif action_type == 'hire_supplier' and drug_id in DRUG_CONFIGS: cfg = DRUG_CONFIGS[drug_id] count = state['drugs'][drug_id].get('suppliers', 0) cost = math.floor(cfg.get('supplierCost', 75) * math.pow(1.15, count)) if state['cash'] >= cost: state['cash'] -= cost state['drugs'][drug_id]['suppliers'] = count + 1 elif action_type == 'hire_worker' and drug_id in DRUG_CONFIGS: cfg = DRUG_CONFIGS[drug_id] count = state['drugs'][drug_id].get('workers', 0) cost = math.floor(cfg['workerCost'] * math.pow(1.15, count)) if state['cash'] >= cost: state['cash'] -= cost state['drugs'][drug_id]['workers'] = count + 1 elif action_type == 'hire_dealer' and drug_id in DRUG_CONFIGS: cfg = DRUG_CONFIGS[drug_id] count = state['drugs'][drug_id].get('dealers', 0) cost = math.floor(cfg['dealerCost'] * math.pow(1.15, count)) if state['cash'] >= cost: state['cash'] -= cost state['drugs'][drug_id]['dealers'] = count + 1 elif action_type == 'unlock_drug' and drug_id in DRUG_CONFIGS: cfg = DRUG_CONFIGS[drug_id] cost = cfg['unlockCost'] if state['cash'] >= cost and not state['drugs'][drug_id].get('unlocked'): state['cash'] -= cost state['drugs'][drug_id]['unlocked'] = True elif action_type == 'blend_recipe': recipe_id = act.get('recipe_id') if recipe_id in SYNTHESIS_RECIPES: r = SYNTHESIS_RECIPES[recipe_id] can_blend = True for ingr_id, needed in r['ingredients'].items(): if state['drugs'].get(ingr_id, {}).get('product', 0) < needed: can_blend = False break if can_blend: for ingr_id, needed in r['ingredients'].items(): state['drugs'][ingr_id]['product'] -= needed state['cash'] += r['payout'] state['totalEarned'] += r['payout'] state['heat'] = min(100.0, state.get('heat', 0.0) + 5.0) elif action_type == 'buy_skill': skill_id = act.get('skill_id') if skill_id in SKILL_COSTS and not state.get('skills', {}).get(skill_id): cost = SKILL_COSTS[skill_id] if state['cash'] >= cost: state['cash'] -= cost state.setdefault('skills', {})[skill_id] = True elif action_type == 'bribe_police': bribe_cost = max(100.0, state['cash'] * 0.15) if state['cash'] >= bribe_cost: state['cash'] -= bribe_cost state['heat'] = 0.0 elif action_type == 'flush_stash': state['heat'] = 20.0 elif action_type == 'launder_prestige': bonds_to_earn = math.floor(math.sqrt(max(0.0, state.get('totalEarned', 0.0)) / 25000.0)) gain = max(0, bonds_to_earn - state.get('cleanBonds', 0)) if gain >= 1: state['cleanBonds'] += gain state['cash'] = 50.0 state['heat'] = 0.0 for d_id, d_data in state['drugs'].items(): d_data['unlocked'] = (d_id == 'weed') d_data['raw'] = 0.0 d_data['product'] = 0.0 d_data['suppliers'] = 0 d_data['workers'] = 0 d_data['dealers'] = 0 return sanitize_state(state) def sanitize_state(state: dict) -> dict: """Enforces max 1 decimal and prevents negative or runaway numbers.""" state['cash'] = clean_round(state.get('cash', 0.0)) state['heat'] = clean_round(min(100.0, max(0.0, state.get('heat', 0.0)))) state['totalEarned'] = clean_round(state.get('totalEarned', 0.0)) state['cleanBonds'] = int(state.get('cleanBonds', 0)) for d_id, data in state.get('drugs', {}).items(): data['raw'] = clean_round(data.get('raw', 0.0)) data['product'] = clean_round(data.get('product', 0.0)) data['suppliers'] = int(data.get('suppliers', 0)) data['workers'] = int(data.get('workers', 0)) data['dealers'] = int(data.get('dealers', 0)) return state