import time from pyscript import document, window from pyodide.ffi import create_proxy as _raw_create_proxy _event_proxies = [] def create_proxy(fn): p = _raw_create_proxy(fn) _event_proxies.append(p) return p from src.core.engine import GameEngine from src.systems.achievements import AchievementSystem, WebAchievementProvider, ACHIEVEMENT_DETAILS from src.systems import save from src.core import resources import json # Apply real-time custom balance data from browser storage if present try: _raw_bal = window.localStorage.getItem("HIT_BALANCE_DATA") if _raw_bal: _bal_dict = json.loads(str(_raw_bal)) if _bal_dict and isinstance(_bal_dict, dict): resources._apply_balance_dict(_bal_dict) print("[HIT] Successfully loaded real-time custom balance from browser storage!") except Exception as _bal_err: pass # Initialize Game Objects engine = GameEngine() # Ensure runtime methods are present regardless of browser caching if not hasattr(GameEngine, "refresh_candidate_pool"): def _refresh_candidate_pool(self, manual: bool = False) -> bool: if manual: if self.cash < 50.0: return False self.cash -= 50.0 if hasattr(self, "generate_candidate_pool"): self.generate_candidate_pool() self.candidate_refresh_timer = 60.0 return True GameEngine.refresh_candidate_pool = _refresh_candidate_pool if not hasattr(GameEngine, "get_resource_cap"): def _get_resource_cap(self, res_key: str) -> float: tier_map = { "quartz_sand": (0, 2000.0), "copper_ore": (0, 2000.0), "crude_polymer": (0, 2000.0), "iron_ore": (0, 2000.0), "silicon_ingot": (1, 1000.0), "copper_wire": (1, 1000.0), "poly_substrate": (1, 1000.0), "chassis_frame": (1, 1000.0), "wafer_3nm": (2, 500.0), "optic_fiber": (2, 500.0), "logic_gate": (2, 500.0), "liquid_coolant": (2, 500.0), "tensor_asic": (3, 100.0), "hbm_memory": (3, 100.0), "optic_fabric": (3, 100.0), "cooling_unit": (3, 100.0), "rack_matrix": (3, 100.0), "ai_supercluster": (3, 20.0) } if res_key not in tier_map: return 2000.0 tier, base = tier_map[res_key] mults = [1.0, 2.0, 3.0, 5.0, 8.0] cur_lvl = getattr(self, "storage_upgrades", {}).get(tier, 0) mult = mults[cur_lvl] if cur_lvl < len(mults) else 8.0 return base * mult GameEngine.get_resource_cap = _get_resource_cap def format_currency_compact(val: float) -> str: """Formats large monetary numbers compactly: $12.4k, $5.8M, $1.2B, $4.5T, $8.2Qa, $3.1Qi...""" if val is None: return "$0.00" v = float(val) if v < 0: return "-" + format_currency_compact(-v) if v < 10000.0: return f"${v:,.2f}" suffixes = [ (1e24, "Sp"), (1e21, "Sx"), (1e18, "Qi"), (1e15, "Qa"), (1e12, "T"), (1e9, "B"), (1e6, "M"), (1e3, "k") ] for threshold, suffix in suffixes: if v >= threshold: num = v / threshold return f"${num:.2f}{suffix}" if num < 100 else f"${num:.1f}{suffix}" return f"${v:.2e}" def format_number_compact(val: float) -> str: """Formats large quantities compactly: 12.4k, 5.8M, 1.2B, 4.5T...""" if val is None: return "0" v = float(val) if v < 0: return "-" + format_number_compact(-v) if v < 10000.0: return f"{v:,.0f}" if v.is_integer() else f"{v:,.1f}" suffixes = [ (1e24, "Sp"), (1e21, "Sx"), (1e18, "Qi"), (1e15, "Qa"), (1e12, "T"), (1e9, "B"), (1e6, "M"), (1e3, "k") ] for threshold, suffix in suffixes: if v >= threshold: num = v / threshold return f"{num:.2f}{suffix}" if num < 100 else f"{num:.1f}{suffix}" return f"{v:.2e}" if not hasattr(GameEngine, "upgrade_storage_facility"): def _upgrade_storage_facility(self, tier: int) -> bool: costs = [50000.0, 250000.0, 1000000.0, 5000000.0] if tier not in [0, 1, 2, 3]: return False if not hasattr(self, "storage_upgrades"): self.storage_upgrades = {0: 0, 1: 0, 2: 0, 3: 0} cur_lvl = self.storage_upgrades.get(tier, 0) if cur_lvl >= 4: return False cost = costs[cur_lvl] if self.cash < cost: return False self.cash -= cost self.storage_upgrades[tier] = cur_lvl + 1 return True GameEngine.upgrade_storage_facility = _upgrade_storage_facility if not hasattr(GameEngine, "debug_simulate_jump_to_phase"): def _debug_simulate_jump_to_phase(self, phase_num: int) -> bool: if phase_num not in [1, 2, 3, 4]: return False self.phase = phase_num self.sla = 100.0 self.csi = 100.0 self.burnout = False self.pc_crashed = False self.is_rebooting = False self.stress = 0.0 self.cpu_temp = 35.0 self.active_incidents = [] # 1. Simulate Phase 1 Mastery (applied for Phase 2+) if phase_num >= 2: p1_max_upgrades = { "dual_monitors": 5, "keurig": 5, "ram_module": 5, "file_cabinets": 5, "usb_fan": 5, "liquid_cooler": 3, "auto_responder": 3, "sop_database": 3, "advanced_scripting": 2, "wiki_page": 5, "script_snippets": 5, "overclock_cpu": 3, "ai_documenter": 3, "stress_ball_quality": 3, "coffee_thermos": 3, "ticket_payout_boost": 3, "stress_rate_reduction": 3, "customer_service_training": 3 } for k, lvl in p1_max_upgrades.items(): self.upgrades[k] = max(self.upgrades.get(k, 0), lvl) self.career_track = "dev" self.cash = max(self.cash, 5000.0) self.tickets_solved = max(self.tickets_solved, 1500) self.solved_tickets_inventory = max(self.solved_tickets_inventory, 350) self.knowledge_points = max(self.knowledge_points, 2500.0) self.auto_dispatch = True self.department_morale = 100.0 self.upgrades["server_racks"] = max(4, self.upgrades.get("server_racks", 0)) # Setup experienced Phase 2 Team Roster self.tech_roster = [ { "id": "tech_beth_computerperson", "role": "l1_tech", "name": "Beth Computerperson", "role_title": "Level 1 Support Tech", "icon": "🎧", "speed_mult": 1.35, "stress_rate_mult": 0.70, "base_wage": 0.05, "wage_per_sec": 0.07, "hire_cost": 0.0, "server_u": 1, "status": "idle", "stress": 0.0, "break_timer": 0.0, "assigned_incident_id": None, "lifetime_solved": 45, "xp": 250, "level": 3, "trait_name": "🀝 Loyal Coworker", "trait_icon": "🀝", "trait_desc": "Your former helpdesk peer. Reliable, fast, and stays cool under pressure." }, { "id": "tech_alex_coder", "role": "l1_tech", "name": "Alex Coder", "role_title": "Level 1 Support Tech", "icon": "πŸ’»", "speed_mult": 1.25, "stress_rate_mult": 0.80, "base_wage": 0.05, "wage_per_sec": 0.06, "hire_cost": 0.0, "server_u": 1, "status": "idle", "stress": 0.0, "break_timer": 0.0, "assigned_incident_id": None, "lifetime_solved": 30, "xp": 180, "level": 2, "trait_name": "⚑ Fast Typer", "trait_icon": "⚑", "trait_desc": "+25% Ticket speed." }, { "id": "tech_jordan_zen", "role": "l2_tech", "name": "Jordan Zen", "role_title": "Level 2 Support Tech", "icon": "πŸ›‘οΈ", "speed_mult": 1.40, "stress_rate_mult": 0.60, "base_wage": 0.12, "wage_per_sec": 0.15, "hire_cost": 0.0, "server_u": 2, "status": "idle", "stress": 0.0, "break_timer": 0.0, "assigned_incident_id": None, "lifetime_solved": 40, "xp": 280, "level": 3, "trait_name": "πŸ›‘οΈ Zen Master", "trait_icon": "πŸ›‘οΈ", "trait_desc": "-40% Stress accumulation." }, { "id": "tech_sam_sys", "role": "sysadmin", "name": "Sam Thorne", "role_title": "Systems Administrator", "icon": "πŸ§™β€β™‚οΈ", "speed_mult": 1.60, "stress_rate_mult": 0.70, "base_wage": 0.25, "wage_per_sec": 0.30, "hire_cost": 0.0, "server_u": 4, "status": "idle", "stress": 0.0, "break_timer": 0.0, "assigned_incident_id": None, "lifetime_solved": 25, "xp": 350, "level": 3, "trait_name": "πŸ’» Script Wizard", "trait_icon": "πŸ’»", "trait_desc": "Automates routine tasks with shell scripts." } ] self.upgrades["l1_techs"] = 2 self.upgrades["l2_techs"] = 1 self.upgrades["sysadmins"] = 1 # 2. Simulate Phase 2 Mastery & Phase 3 Facilities (applied for Phase 3+) if phase_num >= 3: if hasattr(self, "lock_in_phase2_hardware_upgrades"): self.lock_in_phase2_hardware_upgrades() self.cash = max(self.cash, 125000.0) self.tickets_solved = max(self.tickets_solved, 15000) self.solved_tickets_inventory = max(self.solved_tickets_inventory, 1500) self.knowledge_points = max(self.knowledge_points, 25000.0) self.research_points = max(getattr(self, "research_points", 0.0), 750.0) self.leads = max(self.leads, 15.0) # Phase 3 Facilities self.phase3_facilities["call_center"] = max(2, self.phase3_facilities.get("call_center", 0)) self.phase3_facilities["helpdesk"] = max(2, self.phase3_facilities.get("helpdesk", 0)) self.phase3_facilities["datacenter"] = max(2, self.phase3_facilities.get("datacenter", 0)) self.phase3_facilities["corporate"] = max(1, self.phase3_facilities.get("corporate", 0)) # Infrastructure Upgrades self.upgrades["blade_servers"] = max(10, self.upgrades.get("blade_servers", 0)) self.upgrades["fiber_pipeline"] = max(5, self.upgrades.get("fiber_pipeline", 0)) self.upgrades["sales_agents"] = max(3, self.upgrades.get("sales_agents", 0)) self.upgrades["procurement_specialists"] = max(2, self.upgrades.get("procurement_specialists", 0)) # Corporate Leadership Requisitions self.cto_hired_leadership["ops_director"] = max(1, self.cto_hired_leadership.get("ops_director", 0)) self.cto_hired_leadership["dc_manager"] = max(1, self.cto_hired_leadership.get("dc_manager", 0)) self.cto_hired_leadership["hr_lead"] = max(1, self.cto_hired_leadership.get("hr_lead", 0)) # Foundational R&D Tech Tree Patents starter_patents = ["ai_prescreen", "self_healing_nodes", "predictive_noc", "sla_optimizer"] for pat in starter_patents: if isinstance(self.rd_techs_unlocked, set): self.rd_techs_unlocked.add(pat) elif pat not in self.rd_techs_unlocked: self.rd_techs_unlocked.append(pat) # Active Client Contracts self.contracts_bronze = max(self.contracts_bronze, 3) self.contracts_silver = max(self.contracts_silver, 2) self.contracts_gold = max(self.contracts_gold, 1) # 3. Simulate Phase 3 Mastery & Phase 4 Enterprise (applied for Phase 4) if phase_num == 4: if hasattr(self, "lock_in_phase2_hardware_upgrades"): self.lock_in_phase2_hardware_upgrades() self.cash = max(self.cash, 10000000.0) self.tickets_solved = max(self.tickets_solved, 100000) self.solved_tickets_inventory = max(self.solved_tickets_inventory, 10000) self.knowledge_points = max(self.knowledge_points, 500000.0) self.research_points = max(getattr(self, "research_points", 0.0), 5000.0) self.leads = max(self.leads, 50.0) # Max Phase 3 Facilities for fac in ["call_center", "helpdesk", "datacenter", "corporate"]: self.phase3_facilities[fac] = max(5, self.phase3_facilities.get(fac, 0)) # Full Leadership Directors for l_key in ["ops_director", "dc_manager", "hr_lead", "training_director", "security_director"]: self.cto_hired_leadership[l_key] = max(2, self.cto_hired_leadership.get(l_key, 0)) # All 9 R&D Tech Tree Patents all_patents = [ "ai_prescreen", "self_healing_nodes", "predictive_noc", "sla_optimizer", "trunk_bonding", "high_density_blade", "quantum_telemetry", "ai_cluster_mesh", "quantum_hypervisor" ] for pat in all_patents: if isinstance(self.rd_techs_unlocked, set): self.rd_techs_unlocked.add(pat) elif pat not in self.rd_techs_unlocked: self.rd_techs_unlocked.append(pat) # Active Contracts self.contracts_bronze = max(self.contracts_bronze, 5) self.contracts_silver = max(self.contracts_silver, 4) self.contracts_gold = max(self.contracts_gold, 3) # Enterprise Facilities self.facilities["call_centers"] = max(5, self.facilities.get("call_centers", 0)) self.facilities["datacenters"] = max(3, self.facilities.get("datacenters", 0)) self.facilities["fabs"] = max(2, self.facilities.get("fabs", 0)) self.facilities["ai_campuses"] = max(1, self.facilities.get("ai_campuses", 0)) # Supply Inventory & Manufacturing if hasattr(self, "supply_inventory") and isinstance(self.supply_inventory, dict): for k in ["raw_silicon", "silicon_wafers", "hbm_gpus", "server_blades"]: if k in self.supply_inventory: self.supply_inventory[k] = max(self.supply_inventory.get(k, 0), 50) if hasattr(self, "storage_upgrades") and isinstance(self.storage_upgrades, dict): for tier in [0, 1, 2, 3]: self.storage_upgrades[tier] = max(self.storage_upgrades.get(tier, 0), 2) if hasattr(self, "c_suite") and isinstance(self.c_suite, dict): for exec_role in ["cto", "coo", "cro", "cfo"]: self.c_suite[exec_role] = True if hasattr(self, "ai_incubator") and isinstance(self.ai_incubator, dict): self.ai_incubator["stage"] = max(1, self.ai_incubator.get("stage", 1)) if getattr(self, "achievement_system", None): self.achievement_system.check_achievements(self) return True GameEngine.debug_simulate_jump_to_phase = _debug_simulate_jump_to_phase current_ach_filter = "all" achievements_ui_initialized = False current_stocks_sector_filter = "all" last_limit_orders_sig = None last_sparkline_sigs = {} last_catalysts_sig = None last_candidate_pool_sig = None last_roster_sig = None current_cashflow_mode = "real" current_cashflow_timeframe = "sec" mechanic_unlock_queue = [] is_mechanic_modal_open = False def parse_allowed_phases(phases_str: str) -> set: if not phases_str: return {1, 2, 3, 4, 5} allowed = set() for part in phases_str.split(","): part = part.strip() if part.endswith("+"): base_num = part[:-1].strip() if base_num.isdigit(): for p in range(int(base_num), 6): allowed.add(p) elif part.isdigit(): allowed.add(int(part)) return allowed def queue_mechanic_unlock(key: str, custom_title: str = None, custom_desc: str = None, target_tab: str = None): global mechanic_unlock_queue if not hasattr(engine, "unlocked_mechanic_tutorials"): engine.unlocked_mechanic_tutorials = set() if isinstance(engine.unlocked_mechanic_tutorials, list): engine.unlocked_mechanic_tutorials = set(engine.unlocked_mechanic_tutorials) engine.unlocked_mechanic_tutorials.add(key) guide = getattr(resources, "MECHANIC_GUIDES", {}).get(key, {}) title = custom_title or guide.get("title", key.replace("_", " ").title()) icon = guide.get("icon", "✨") category = guide.get("category", "Operations") phase = guide.get("phase", engine.phase) summary = custom_desc or guide.get("summary", "A new operational department and capability has been unlocked.") bullets = guide.get("key_mechanics", []) pro_tip = guide.get("pro_tips", "") # Log to IT Syslog Console if hasattr(engine, "event_log") and engine.event_log: engine.event_log.log( f"MILESTONE: {title} - {summary}", category="MILESTONE", level="SUCCESS", link_tab=target_tab or "helpdesk", link_label="View" ) # Check player preference: If Discreet Toasts is enabled in Settings, show toast alert only if hasattr(engine, "settings") and isinstance(engine.settings, dict) and engine.settings.get("milestone_toasts_only", False): show_feature_discovery_toast(key, title=title, desc=summary, icon=icon) return # Avoid duplicate modal in queue for q in mechanic_unlock_queue: if q.get("key") == key: return payload = { "key": key, "title": title, "icon": icon, "category": category, "phase": phase, "summary": summary, "bullets": bullets, "pro_tip": pro_tip, "target_tab": target_tab } mechanic_unlock_queue.append(payload) process_mechanic_unlock_queue() def process_mechanic_unlock_queue(): global is_mechanic_modal_open, mechanic_unlock_queue if is_mechanic_modal_open or not mechanic_unlock_queue: return modal_el = document.getElementById("modalMechanicUnlock") if not modal_el: return item = mechanic_unlock_queue[0] # Populate modal fields el_icon = document.getElementById("unlock-modal-icon-badge") if el_icon: el_icon.innerText = item.get("icon", "✨") el_cat = document.getElementById("unlock-modal-category") if el_cat: el_cat.innerText = item.get("category", "OPERATIONS").upper() el_phase = document.getElementById("unlock-modal-phase-badge") if el_phase: el_phase.innerText = f"PHASE {item.get('phase', engine.phase)}" el_title = document.getElementById("unlock-modal-title") if el_title: el_title.innerText = item.get("title", "New System Unlocked") el_trig = document.getElementById("unlock-modal-trigger") if el_trig: el_trig.innerText = f"Progression Milestone Reached β€’ Phase {engine.phase}" el_desc = document.getElementById("unlock-modal-desc") if el_desc: el_desc.innerText = item.get("summary", "") el_bullets = document.getElementById("unlock-modal-bullets") if el_bullets: bullets = item.get("bullets", []) if bullets: b_html = "" for b in bullets: b_html += f'
  • βœ”{b}
  • ' el_bullets.innerHTML = b_html else: el_bullets.innerHTML = '
  • βœ”Operational capability ready for immediate use.
  • ' el_tip_box = document.getElementById("unlock-modal-pro-tip-box") el_tip = document.getElementById("unlock-modal-pro-tip") if el_tip_box and el_tip: if item.get("pro_tip"): el_tip_box.style.display = "flex" el_tip.innerText = item.get("pro_tip") else: el_tip_box.style.display = "none" el_queue = document.getElementById("unlock-modal-queue-indicator") if el_queue: if len(mechanic_unlock_queue) > 1: el_queue.style.display = "inline-block" el_queue.innerText = f"1 of {len(mechanic_unlock_queue)}" else: el_queue.style.display = "none" btn_tab = document.getElementById("btn-unlock-modal-view-tab") if btn_tab: t_tab = item.get("target_tab") if t_tab: btn_tab.style.display = "inline-block" btn_tab.onclick = create_proxy(lambda e: (dismiss_mechanic_unlock_modal(), switch_to_tab(t_tab))) else: btn_tab.style.display = "none" btn_ack = document.getElementById("btn-unlock-modal-acknowledge") if btn_ack: btn_ack.innerText = "Next Milestone βž”" if len(mechanic_unlock_queue) > 1 else "Acknowledge & Continue βž”" btn_ack.onclick = create_proxy(lambda e: dismiss_mechanic_unlock_modal()) try: window.eval('var el = document.getElementById("modalMechanicUnlock"); if (el) { var m = bootstrap.Modal.getOrCreateInstance(el); if (m) m.show(); }') is_mechanic_modal_open = True except Exception as e: print("Error displaying mechanic unlock modal:", e) def dismiss_mechanic_unlock_modal(): global is_mechanic_modal_open, mechanic_unlock_queue try: window.eval('var el = document.getElementById("modalMechanicUnlock"); if (el) { var m = bootstrap.Modal.getOrCreateInstance(el); if (m) m.hide(); }') except Exception: pass is_mechanic_modal_open = False if mechanic_unlock_queue: mechanic_unlock_queue.pop(0) if mechanic_unlock_queue: window.setTimeout(create_proxy(process_mechanic_unlock_queue), 350) def switch_to_tab(tab_id: str): try: btn = document.getElementById(tab_id) if btn: btn.click() except Exception as e: print("Error switching tab:", e) def update_declarative_visibility(): """Dynamically evaluate declarative milestone data attributes across DOM elements.""" if not hasattr(engine, "unlocked_milestones"): engine.unlocked_milestones = set() if isinstance(engine.unlocked_milestones, list): engine.unlocked_milestones = set(engine.unlocked_milestones) if not hasattr(engine, "unlocked_mechanic_tutorials"): engine.unlocked_mechanic_tutorials = set() if isinstance(engine.unlocked_mechanic_tutorials, list): engine.unlocked_mechanic_tutorials = set(engine.unlocked_mechanic_tutorials) elements = document.querySelectorAll("[data-phases], [data-phase], [data-min-cash], [data-min-tickets], [data-min-kp], [data-min-facilities], [data-unlock-var]") for i in range(len(elements)): el = elements[i] # Skip primary navigation tab items managed exclusively by PHASE_TAB_CONFIG to prevent click flickering if el.parentElement and getattr(el.parentElement, "id", "") == "gameTabs": continue if hasattr(el, "closest") and el.closest("#gameTabs"): continue # 1. Phase Check phases_attr = el.getAttribute("data-phases") or el.getAttribute("data-phase") or "" allowed_phases = parse_allowed_phases(phases_attr) if engine.phase not in allowed_phases: el.style.display = "none" continue # Determine milestone key tut_key = el.getAttribute("data-tutorial-key") milestone_key = el.getAttribute("data-milestone-key") or tut_key or el.id # If this milestone was already achieved during this run/save, keep it permanently unlocked! already_unlocked = bool(milestone_key and milestone_key in engine.unlocked_milestones) if not already_unlocked: # Check threshold requirements # 2. Min Cash (checks current cash OR lifetime cash earned) min_cash_attr = el.getAttribute("data-min-cash") if min_cash_attr: try: req_cash = float(min_cash_attr) if engine.cash < req_cash and getattr(engine, "total_cash_earned", 0.0) < req_cash: el.style.display = "none" continue except Exception: pass # 3. Min Tickets min_tix_attr = el.getAttribute("data-min-tickets") if min_tix_attr: try: if engine.tickets_solved < int(min_tix_attr): el.style.display = "none" continue except Exception: pass # 4. Min KP min_kp_attr = el.getAttribute("data-min-kp") if min_kp_attr: try: if engine.knowledge_points < float(min_kp_attr): el.style.display = "none" continue except Exception: pass # 5. Min Facilities (e.g. data-min-facilities="corporate:1") min_fac_attr = el.getAttribute("data-min-facilities") if min_fac_attr: fac_met = True for fac_item in min_fac_attr.split(","): if ":" in fac_item: f_k, f_c = fac_item.strip().split(":", 1) if hasattr(engine, "phase3_facilities") and isinstance(engine.phase3_facilities, dict): if engine.phase3_facilities.get(f_k, 0) < int(f_c): fac_met = False break elif hasattr(engine, "facilities") and isinstance(engine.facilities, dict): if engine.facilities.get(f_k, 0) < int(f_c): fac_met = False break if not fac_met: el.style.display = "none" continue # 6. Unlock Variable (e.g. data-unlock-var="career_choice" or data-unlock-var="ai_documenter") unlock_var_attr = el.getAttribute("data-unlock-var") if unlock_var_attr: val = getattr(engine, unlock_var_attr, None) if val is None and hasattr(engine, "upgrades") and isinstance(engine.upgrades, dict): val = engine.upgrades.get(unlock_var_attr, None) unlock_amt_attr = el.getAttribute("data-unlock-amt") req_amt = int(unlock_amt_attr) if unlock_amt_attr else 1 if val is None or (isinstance(val, (int, float)) and val < req_amt) or (isinstance(val, bool) and not val): el.style.display = "none" continue # All conditions met for the first time -> Register Permanent Unlock! if milestone_key: engine.unlocked_milestones.add(milestone_key) # Check tutorial discovery trigger if tut_key and tut_key not in engine.unlocked_mechanic_tutorials: tut_title = el.getAttribute("data-tutorial-title") tut_desc = el.getAttribute("data-tutorial-desc") queue_mechanic_unlock(tut_key, tut_title, tut_desc) # All conditions met (or already permanently unlocked) -> Reveal element! el.style.display = "" def handle_secret_click(event=None): engine.register_secret_click() update_ui() def update_phase_promotions(engine): """Universal promotion check: updates requirement labels and enables/disables the button.""" config = engine.get_promotion_config() if not config: return all_satisfied = True for req in config.get("requirements", []): el = document.getElementById(req["id"]) if not el: continue is_met = req["check"](engine) text_content = req["text"](engine) if is_met: el.className = "mb-1 text-success fw-bold text-center" el.innerHTML = f"βœ”οΈ {text_content}" else: el.className = "mb-1 text-muted text-center" el.innerHTML = f"❌ {text_content}" all_satisfied = False # Toggle the promotion button for the current phase btn_id = config.get("button_id") if btn_id: btn = document.getElementById(btn_id) if btn: if all_satisfied: btn.removeAttribute("disabled") else: btn.setAttribute("disabled", "true") def generate_svg_sparkline(history: list, is_up: bool) -> str: """Generate a clean mini SVG sparkline chart from price history points.""" if not history or len(history) < 2: return '' min_p = min(history) max_p = max(history) rng = max_p - min_p if max_p > min_p else 1.0 n = len(history) pts = [] for i, val in enumerate(history): x = (i / (n - 1)) * 100.0 y = 21.0 - ((val - min_p) / rng) * 18.0 pts.append(f"{x:.1f},{y:.1f}") poly_pts = " ".join(pts) color = "#00ffcc" if is_up else "#ff3366" return f'' def show_achievement_toast(achievement_id): """Dynamically spawn a beautiful Bootstrap 5 toast notification for unlocks.""" defs = achievement_system.definitions or ACHIEVEMENT_DETAILS details = defs.get(achievement_id, {}) name = details.get("name", achievement_id.replace("_", " ").title()) desc = details.get("description", "Milestone reached!") icon = details.get("icon", "πŸ†") toast_container = document.getElementById("toast-container") if toast_container: toast_html = f""" """ div = document.createElement("div") div.innerHTML = toast_html.strip() toast_element = div.firstElementChild if hasattr(div, "firstElementChild") and div.firstElementChild else div.firstChild toast_container.appendChild(toast_element) # Remove the toast after 6 seconds def remove_toast(): try: if toast_element and toast_element in toast_container.children: toast_container.removeChild(toast_element) except Exception: pass window.setTimeout(create_proxy(remove_toast), 6000) update_achievements_ui() def show_feature_discovery_toast(key, title=None, desc=None, icon="✨"): """Dynamically spawn a discovery toast notification when a new game system unlocks.""" toast_container = document.getElementById("toast-container") if not toast_container: return t_title = title or key.replace("_", " ").title() t_desc = desc or "New system feature unlocked. Click to open full operational briefing." toast_html = f""" """ div = document.createElement("div") div.innerHTML = toast_html.strip() toast_element = div.firstElementChild if hasattr(div, "firstElementChild") and div.firstElementChild else div.firstChild toast_container.appendChild(toast_element) def remove_toast(): try: if toast_element and toast_element in toast_container.children: toast_container.removeChild(toast_element) except Exception: pass window.setTimeout(create_proxy(remove_toast), 7000) current_syslog_filter = "ALL" def show_event_log_js(): """Render and open the IT Syslog Console modal.""" render_syslog_entries() try: window.eval('var el = document.getElementById("systemEventLogModal"); if (el) { var m = bootstrap.Modal.getOrCreateInstance(el); if (m) m.show(); }') except Exception as e: print("Error showing syslog modal:", e) def set_syslog_filter_js(filter_cat: str): global current_syslog_filter current_syslog_filter = filter_cat group = document.getElementById("syslog-filter-group") if group: for btn in group.querySelectorAll("button"): onclick_attr = btn.getAttribute("onclick") or "" if f"'{filter_cat}'" in onclick_attr: btn.className = "btn btn-outline-info active" if filter_cat == "ALL" else ( "btn btn-outline-warning active" if filter_cat == "EVENT" else ( "btn btn-outline-danger active" if filter_cat == "HARDWARE" else ( "btn btn-outline-success active" if filter_cat == "MILESTONE" else "btn btn-outline-primary active" ) ) ) else: btn.className = "btn btn-outline-secondary" render_syslog_entries() def clear_syslog_js(): if hasattr(engine, "event_log") and engine.event_log: engine.event_log.clear() render_syslog_entries() def render_syslog_entries(): feed = document.getElementById("syslog-console-feed") count_badge = document.getElementById("syslog-entry-count") if not feed: return if not hasattr(engine, "event_log") or not engine.event_log: feed.innerHTML = '
    No syslog events recorded yet.
    ' return cat_filt = None if current_syslog_filter == "ALL" else current_syslog_filter entries = engine.event_log.get_entries(limit=50, category_filter=cat_filt) if count_badge: count_badge.innerText = f"{len(engine.event_log.entries)} entries" if not entries: feed.innerHTML = f'
    No entries matching filter [{current_syslog_filter}].
    ' return html = "" for e in entries: lvl = e.get("level", "INFO") cat = e.get("category", "SYSTEM") ts = e.get("timestamp", "--:--:--") msg = e.get("message", "") tab = e.get("link_tab") lbl = e.get("link_label") lvl_color = "#38bdf8" if lvl == "INFO" else ("#4ade80" if lvl == "SUCCESS" else ("#fbbf24" if lvl == "WARN" else "#f87171")) cat_badge = f'[{cat}]' lvl_badge = f'{lvl}' link_btn = "" if tab: link_btn = f' ' html += f"""
    [{ts}] {cat_badge} {lvl_badge} {msg}
    {link_btn}
    """ feed.innerHTML = html def toggle_milestone_toasts_js(val: bool = None): global is_mechanic_modal_open, mechanic_unlock_queue if not hasattr(engine, "settings") or not isinstance(engine.settings, dict): engine.settings = {} if val is None: curr = engine.settings.get("milestone_toasts_only", False) engine.settings["milestone_toasts_only"] = not curr else: engine.settings["milestone_toasts_only"] = bool(val) chk = document.getElementById("toggle-milestone-toasts") if chk: chk.checked = bool(engine.settings["milestone_toasts_only"]) mode_text = "Discreet Toasts" if engine.settings["milestone_toasts_only"] else "Full Modal Dialogs" print(f"[Settings] Milestone notification mode set to: {mode_text}") # If activated via the modal button, dismiss modal immediately and provide confirmation feedback if is_mechanic_modal_open or val is True: try: window.eval('var el = document.getElementById("modalMechanicUnlock"); if (el) { var m = bootstrap.Modal.getOrCreateInstance(el); if (m) m.hide(); }') except Exception: pass is_mechanic_modal_open = False # Flush any remaining queued milestone modals into toast alerts if mechanic_unlock_queue: mechanic_unlock_queue.pop(0) while mechanic_unlock_queue: q_item = mechanic_unlock_queue.pop(0) show_feature_discovery_toast( q_item.get("key", "milestone"), title=q_item.get("title"), desc=q_item.get("summary"), icon=q_item.get("icon", "✨") ) # Show confirmation feedback toast show_feature_discovery_toast( "milestone_toast_preference", title="πŸ”• Toast Alerts Enabled", desc="Milestone unlocks will now appear as corner toasts. You can re-enable full modals in Settings at any time.", icon="πŸ”•" ) _active_event_modal_info = None def show_random_event_modal(ev: dict): """Populate and display the Random Event modal.""" global _active_event_modal_info el_icon = document.getElementById("event-modal-icon") el_cat = document.getElementById("event-modal-category") el_title = document.getElementById("event-modal-title") el_desc = document.getElementById("event-modal-desc") el_dur = document.getElementById("event-modal-duration") el_effects = document.getElementById("event-modal-effects-list") dur = float(ev.get("duration", 30)) _active_event_modal_info = { "id": ev.get("id"), "name": ev.get("name"), "end_time": time.time() + dur, "duration": dur, "active": True } if el_icon: el_icon.innerText = ev.get("icon", "⚑") if el_cat: el_cat.innerText = f"{ev.get('category', 'Incident').upper()} EVENT" if el_title: el_title.innerText = ev.get("name", "Random Incident") if el_desc: el_desc.innerText = ev.get("description", "A random operational incident has occurred.") if el_dur: el_dur.innerText = f"{dur:.0f}s" if el_effects: effs = ev.get("effects", {}) eff_html = "" for k, v in effs.items(): if k.endswith("_fallback"): continue k_name = k.replace("_", " ").title() val_str = f"{v}x" if "mult" in k else (f"+{v}%" if "pct" in k else str(v)) eff_html += f'
  • β€’ {k_name}: {val_str}
  • ' el_effects.innerHTML = eff_html or '
  • No immediate adverse effects.
  • ' try: window.eval('var el = document.getElementById("randomEventModal"); if (el) { var m = bootstrap.Modal.getOrCreateInstance(el); if (m) m.show(); }') except Exception as e: print("Error displaying random event modal:", e) def set_achievements_filter(filter_name): global current_ach_filter current_ach_filter = filter_name filter_container = document.getElementById("ach-filter-buttons") if filter_container: for btn in filter_container.querySelectorAll(".ach-filter-btn"): f_val = btn.getAttribute("data-filter") if f_val == filter_name: btn.className = "btn btn-sm btn-warning active ach-filter-btn" else: if f_val == "unlocked": btn.className = "btn btn-sm btn-outline-success ach-filter-btn" elif f_val == "locked": btn.className = "btn btn-sm btn-outline-secondary ach-filter-btn" else: btn.className = "btn btn-sm btn-outline-light ach-filter-btn" update_achievements_ui() def init_achievements_ui(): global achievements_ui_initialized grid = document.getElementById("achievements-grid") if not grid: return defs = achievement_system.definitions or ACHIEVEMENT_DETAILS if not defs: return html_parts = [] for ach_id, ach in defs.items(): icon = ach.get("icon", "πŸ†") name = ach.get("name", ach_id) desc = ach.get("description", "") phase = ach.get("phase_unlock", 1) card_html = f"""
    {icon}
    {name}
    πŸ”’ Locked

    {desc}

    Phase {phase}
    """ html_parts.append(card_html) grid.innerHTML = "".join(html_parts) # Filter button listeners filter_container = document.getElementById("ach-filter-buttons") if filter_container: for btn in filter_container.querySelectorAll(".ach-filter-btn"): f_val = btn.getAttribute("data-filter") btn.addEventListener("click", create_proxy(lambda e, f=f_val: set_achievements_filter(f))) achievements_ui_initialized = True update_achievements_ui() def update_achievements_ui(): global current_ach_filter grid = document.getElementById("achievements-grid") if not grid: return defs = achievement_system.definitions or ACHIEVEMENT_DETAILS if not defs: return total_count = len(defs) unlocked_count = sum(1 for k, v in engine.achievements.items() if v and k in defs) pct = round((unlocked_count / total_count * 100.0), 1) if total_count > 0 else 0.0 count_el = document.getElementById("ach-unlocked-count") total_el = document.getElementById("ach-total-count") pct_el = document.getElementById("ach-pct") bar_el = document.getElementById("ach-progress-bar") if count_el: count_el.innerText = str(unlocked_count) if total_el: total_el.innerText = str(total_count) if pct_el: pct_el.innerText = f"{pct:.0f}%" if bar_el: bar_el.style.width = f"{pct}%" bar_el.setAttribute("aria-valuenow", str(int(pct))) if pct >= 100.0: bar_el.className = "progress-bar bg-success progress-bar-striped" else: bar_el.className = "progress-bar bg-warning progress-bar-striped progress-bar-animated" # Filter buttons phase visibility (Hide future phase buttons) filter_container = document.getElementById("ach-filter-buttons") if filter_container: for btn in filter_container.querySelectorAll(".ach-filter-btn"): f_val = btn.getAttribute("data-filter") if f_val and f_val.startswith("phase-"): try: p_num = int(f_val.split("-")[1]) if p_num > engine.phase: btn.style.display = "none" if current_ach_filter == f_val: current_ach_filter = "all" else: btn.style.display = "inline-block" except Exception: pass for ach_id, ach in defs.items(): card_el = document.getElementById(f"card-ach-{ach_id}") if not card_el: continue is_unlocked = bool(engine.achievements.get(ach_id, False)) phase = ach.get("phase_unlock", 1) is_future = (phase > engine.phase) # Check filter visibility visible = True if current_ach_filter == "all": visible = True elif current_ach_filter == "phase-1": visible = (phase == 1) elif current_ach_filter == "phase-2": visible = (phase == 2) elif current_ach_filter == "phase-3": visible = (phase == 3) elif current_ach_filter == "phase-4": visible = (phase == 4) elif current_ach_filter == "unlocked": visible = is_unlocked elif current_ach_filter == "locked": visible = not is_unlocked card_el.style.display = "block" if visible else "none" # Update styling, content & badge badge_el = document.getElementById(f"badge-ach-{ach_id}") card_inner = document.getElementById(f"card-inner-ach-{ach_id}") icon_box = document.getElementById(f"icon-box-ach-{ach_id}") title_el = document.getElementById(f"title-ach-{ach_id}") desc_el = document.getElementById(f"desc-ach-{ach_id}") if is_future: # Grayed out without details (Fog of War) if title_el: title_el.innerText = "πŸ”’ Classified Milestone" title_el.className = "mb-0 fw-bold text-muted ach-title" if desc_el: desc_el.innerText = f"Milestone objective classified until unlocking Phase {phase}." desc_el.className = "text-muted small mb-2 fst-italic" if badge_el: badge_el.innerHTML = 'πŸ”’ Classified' if card_inner: card_inner.className = "card h-100 bg-dark bg-opacity-50 text-muted border-secondary opacity-50 p-3 rounded shadow-sm" card_inner.style.background = "" if icon_box: icon_box.innerText = "❓" icon_box.className = "fs-1 me-3 p-2 rounded bg-black bg-opacity-25 border border-secondary text-muted text-center" elif is_unlocked: if title_el: title_el.innerText = ach.get("name", ach_id) title_el.className = "mb-0 fw-bold text-white ach-title" if desc_el: desc_el.innerText = ach.get("description", "") desc_el.className = "text-light text-opacity-75 small mb-2" if badge_el: badge_el.innerHTML = '🟒 Unlocked' if card_inner: card_inner.className = "card h-100 bg-dark text-white border-success shadow p-3 rounded" card_inner.style.background = "linear-gradient(135deg, rgba(20,40,30,0.85), rgba(15,25,20,0.95))" if icon_box: icon_box.innerText = ach.get("icon", "πŸ†") icon_box.className = "fs-1 me-3 p-2 rounded bg-success bg-opacity-25 border border-success text-center" else: if title_el: title_el.innerText = ach.get("name", ach_id) title_el.className = "mb-0 fw-bold text-white ach-title" if desc_el: desc_el.innerText = ach.get("description", "") desc_el.className = "text-muted small mb-2" if badge_el: badge_el.innerHTML = 'πŸ”’ Locked' if card_inner: card_inner.className = "card h-100 bg-dark bg-opacity-75 text-white border-secondary opacity-75 p-3 rounded shadow-sm" card_inner.style.background = "" if icon_box: icon_box.innerText = ach.get("icon", "πŸ†") icon_box.className = "fs-1 me-3 p-2 rounded bg-black bg-opacity-50 border border-secondary text-center" # Bind the Achievements System provider = WebAchievementProvider(toast_callback=show_achievement_toast) achievement_system = AchievementSystem(provider) engine.achievement_system = achievement_system # Autosave / UI Message flash helpers def flash_save_status(): status_el = document.getElementById("settings-message") status_el.innerText = "Progress saved to LocalStorage!" window.setTimeout(create_proxy(lambda: setattr(status_el, "innerText", "")), 3000) def flash_autosave_status(): autosave_indicator = document.getElementById("autosave-indicator") if autosave_indicator: autosave_indicator.style.opacity = "1" window.setTimeout(create_proxy(lambda: setattr(autosave_indicator.style, "opacity", "0")), 2000) def confirm_and_reset(): if window.confirm("Are you sure you want to completely wipe all progress? This cannot be undone."): save.clear_localstorage() engine.reset() update_ui() status_el = document.getElementById("settings-message") status_el.innerText = "Game reset successful!" window.setTimeout(create_proxy(lambda: setattr(status_el, "innerText", "")), 3000) def export_save_text(): save_str = save.export_save(engine) area = document.getElementById("save-text-area") area.value = save_str window.navigator.clipboard.writeText(save_str) area.select() status_el = document.getElementById("settings-message") status_el.innerText = "Save string copied to clipboard!" window.setTimeout(create_proxy(lambda: setattr(status_el, "innerText", "")), 3000) def import_save_text(): area = document.getElementById("save-text-area") save_str = area.value.strip() if not save_str: return success = save.import_save(engine, save_str) status_el = document.getElementById("settings-message") if success: save.save_to_localstorage(engine) area.value = "" status_el.innerText = "Save imported successfully!" update_ui() else: status_el.innerText = "Invalid save string!" window.setTimeout(create_proxy(lambda: setattr(status_el, "innerText", "")), 3000) def toggle_auto_research(e): engine.auto_research_active = e.target.checked update_ui() def toggle_auto_kb(e): engine.auto_kb_active = e.target.checked update_ui() def toggle_auto_dispatch(e): engine.auto_dispatch = e.target.checked update_ui() def assign_worker_js(incident_id, worker_type): if worker_type == "none": engine.assign_incident(incident_id, None) else: engine.assign_incident(incident_id, worker_type) update_ui() def hire_branch_manager_js(branch_id): engine.hire_branch_manager(branch_id) update_ui() def set_branch_directive_js(branch_id, directive): engine.set_branch_directive(branch_id, directive) update_ui() def toggle_it_policy_js(policy_id): engine.toggle_it_policy(policy_id) update_ui() def start_rd_project_js(project_id): engine.start_rd_project(project_id) update_ui() def hire_cto_leader_js(role_id): engine.hire_cto_leader(role_id) update_ui() def set_cto_staffing_policy_js(policy_id): engine.set_cto_staffing_policy(policy_id) update_ui() def set_cto_procedure_policy_js(policy_id): engine.set_cto_procedure_policy(policy_id) update_ui() def build_facility_js(facility_type): if engine.build_phase3_facility(str(facility_type)): update_ui() def research_tech_js(tech_id): if engine.select_research_project(str(tech_id)): update_ui() def synthesize_kp_js(amount): if engine.synthesize_knowledge(float(amount)): update_ui() def toggle_auto_synthesis_js(e): engine.auto_synthesis_active = bool(e.target.checked) update_ui() def hire_candidate_js(candidate_id): if engine.hire_candidate(str(candidate_id)): update_ui() def fire_tech_js(tech_id): if engine.fire_tech(str(tech_id)): update_ui() def trigger_hr_ability_js(ability_id): if engine.trigger_hr_ability(str(ability_id)): update_ui() def show_guide_js(guide_id): guide = resources.MECHANIC_GUIDES.get(str(guide_id)) if not guide: return el_icon = document.getElementById("guide-modal-icon") if el_icon: el_icon.innerText = guide.get("icon", "πŸ’‘") el_title = document.getElementById("guide-modal-title") if el_title: el_title.innerText = guide.get("title", "Mechanic Overview") el_cat = document.getElementById("guide-modal-category") if el_cat: el_cat.innerText = guide.get("category", "Operations") el_sum = document.getElementById("guide-modal-summary") if el_sum: el_sum.innerText = guide.get("summary", "") el_mech = document.getElementById("guide-modal-mechanics") if el_mech: items_html = [] for m in guide.get("key_mechanics", []): items_html.append(f'
  • {m}
  • ') el_mech.innerHTML = "".join(items_html) el_res = document.getElementById("guide-modal-resources") if el_res: pills_html = [] for r in guide.get("resources_involved", []): pills_html.append(f'{r}') el_res.innerHTML = "".join(pills_html) el_tips = document.getElementById("guide-modal-tips") if el_tips: el_tips.innerText = guide.get("pro_tips", "") try: window.eval(""" (function() { const modalEl = document.getElementById('mechanicGuideModal'); if (modalEl) { if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { const inst = bootstrap.Modal.getOrCreateInstance(modalEl); inst.show(); } else { modalEl.classList.add('show'); modalEl.style.display = 'block'; modalEl.removeAttribute('aria-hidden'); modalEl.setAttribute('aria-modal', 'true'); } } })(); """) except Exception as ex: print(f"Error opening guide modal: {ex}") def show_orientation_js(phase_or_key): """Populates and opens the Phase Corporate Orientation Packet Modal.""" try: p_num = int(phase_or_key) except (ValueError, TypeError): p_num = 1 packet = getattr(resources, "ORIENTATION_PACKETS", {}).get(p_num) if not packet: return el_icon = document.getElementById("orientation-modal-icon") if el_icon: el_icon.innerText = packet.get("icon", "πŸ“‹") el_title = document.getElementById("orientation-modal-title") if el_title: el_title.innerText = packet.get("title", "ORIENTATION PACKET") el_sub = document.getElementById("orientation-modal-subtitle") if el_sub: el_sub.innerText = packet.get("subtitle", "") el_memo = document.getElementById("orientation-modal-memo") if el_memo: el_memo.innerText = packet.get("sender_memo", "") el_badge = document.getElementById("orientation-modal-badge") if el_badge: el_badge.innerText = packet.get("theme_badge", "ORIENTATION") color = packet.get("theme_color", "info") el_badge.className = f"badge bg-{color} text-dark fw-bold font-monospace px-2 py-1" el_fantasy = document.getElementById("orientation-modal-fantasy") if el_fantasy: el_fantasy.innerText = packet.get("fantasy_overview", "") el_tabs = document.getElementById("orientation-modal-tabs") if el_tabs: tabs_html = [] for t in packet.get("unlocked_tabs", []): tabs_html.append(f'''
    {t.get("icon", "πŸ“")} {t.get("name", "")}
    {t.get("desc", "")}
    ''') el_tabs.innerHTML = "".join(tabs_html) el_mechs = document.getElementById("orientation-modal-mechanics") if el_mechs: mechs_html = [] for m in packet.get("key_mechanics", []): mechs_html.append(f'''
    {m.get("icon", "βš™οΈ")} {m.get("title", "")}
    {m.get("desc", "")}
    ''') el_mechs.innerHTML = "".join(mechs_html) el_chk = document.getElementById("orientation-modal-checklist") if el_chk: chk_html = [] for item in packet.get("first_day_checklist", []): chk_html.append(f'
  • {item}
  • ') el_chk.innerHTML = "".join(chk_html) el_protip = document.getElementById("orientation-modal-protip") if el_protip: el_protip.innerText = packet.get("pro_tip", "") try: window.eval(""" (function() { const modalEl = document.getElementById('orientationPacketModal'); if (modalEl) { if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { const inst = bootstrap.Modal.getOrCreateInstance(modalEl); inst.show(); } else { modalEl.classList.add('show'); modalEl.style.display = 'block'; modalEl.removeAttribute('aria-hidden'); modalEl.setAttribute('aria-modal', 'true'); } } })(); """) except Exception as ex: print(f"Error opening orientation modal: {ex}") _event_proxies = [] window.show_guide_js = create_proxy(show_guide_js) window.show_orientation_js = create_proxy(show_orientation_js) window.trigger_hr_ability_js = create_proxy(trigger_hr_ability_js) window.hire_candidate_js = create_proxy(hire_candidate_js) window.fire_tech_js = create_proxy(fire_tech_js) window.assign_worker_js = create_proxy(assign_worker_js) window.hire_branch_manager_js = create_proxy(hire_branch_manager_js) window.set_branch_directive_js = create_proxy(set_branch_directive_js) window.toggle_it_policy_js = create_proxy(toggle_it_policy_js) window.start_rd_project_js = create_proxy(start_rd_project_js) window.hire_cto_leader_js = create_proxy(hire_cto_leader_js) window.set_cto_staffing_policy_js = create_proxy(set_cto_staffing_policy_js) window.set_cto_procedure_policy_js = create_proxy(set_cto_procedure_policy_js) window.build_facility_js = create_proxy(build_facility_js) window.research_tech_js = create_proxy(research_tech_js) window.synthesize_kp_js = create_proxy(synthesize_kp_js) window.toggle_auto_synthesis_js = create_proxy(toggle_auto_synthesis_js) window.buy_prestige_upgrade_js = create_proxy(lambda up_id: (engine.buy_prestige_upgrade(str(up_id)), update_ui())) window.claim_offline_progress_js = create_proxy(lambda: claim_offline_progress_js()) window.show_event_log_js = create_proxy(show_event_log_js) window.set_syslog_filter_js = create_proxy(set_syslog_filter_js) window.clear_syslog_js = create_proxy(clear_syslog_js) window.toggle_milestone_toasts_js = create_proxy(toggle_milestone_toasts_js) window.toggle_stress_cruise_control_js = create_proxy(lambda checked: (engine.toggle_stress_cruise_control(checked), update_ui())) window.toggle_auto_research_js = create_proxy(lambda checked: (engine.toggle_auto_research(checked), update_ui())) window.toggle_auto_kb_js = create_proxy(lambda checked: (engine.toggle_auto_kb(checked), update_ui())) window.toggle_auto_manuals_js = create_proxy(lambda checked: (engine.toggle_auto_manuals(checked), update_ui())) window.toggle_auto_courses_js = create_proxy(lambda checked: (engine.toggle_auto_courses(checked), update_ui())) window.toggle_auto_dispatch_js = create_proxy(lambda checked: (engine.toggle_auto_dispatch(checked), update_ui())) window.set_rack_governor_js = create_proxy(lambda mode: (engine.set_rack_governor_mode(str(mode)), update_ui())) window.format_currency_compact_js = create_proxy(format_currency_compact) window.format_number_compact_js = create_proxy(format_number_compact) def setup_listeners(): """Register Python event handlers to HTML DOM elements.""" def bind_click(el_id, fn): el = document.getElementById(el_id) if el: p = create_proxy(fn) _event_proxies.append(p) el.addEventListener("click", p) def bind_change(el_id, fn): el = document.getElementById(el_id) if el: p = create_proxy(fn) _event_proxies.append(p) el.addEventListener("change", p) def bind_input(el_id, fn): el = document.getElementById(el_id) if el: p = create_proxy(fn) _event_proxies.append(p) el.addEventListener("input", p) bind_click("btn-answer", lambda e: (engine.answer_call(), update_ui())) bind_click("btn-resolve-low", lambda e: (engine.resolve_ticket_low(), update_ui())) bind_click("btn-resolve-medium", lambda e: (engine.start_resolve_ticket_medium(), update_ui())) bind_click("btn-resolve-high", lambda e: (engine.start_resolve_ticket_high(), update_ui())) bind_click("btn-research", lambda e: (engine.research_issue(), update_ui())) bind_click("btn-write-kb", lambda e: (engine.start_write_kb_article(), update_ui())) bind_click("btn-reboot", lambda e: (engine.start_reboot(), update_ui())) bind_click("btn-reboot-tactical", lambda e: (engine.start_reboot(), update_ui())) bind_click("btn-brew", lambda e: (engine.start_brew_coffee(), update_ui())) bind_click("btn-drink", lambda e: (engine.drink_coffee(), update_ui())) bind_click("btn-stress", lambda e: (engine.click_stress_ball(), update_ui())) # Mobile Top & Bottom Dock Listeners bind_click("btn-brew-mobile", lambda e: (engine.start_brew_coffee(), update_ui())) bind_click("btn-drink-mobile", lambda e: (engine.drink_coffee(), update_ui())) bind_click("btn-stress-mobile", lambda e: (engine.click_stress_ball(), update_ui())) # Phase 2 Manager Workbench Actions bind_click("btn-answer-p2", lambda e: (engine.answer_call(), update_ui())) bind_click("btn-resolve-p2-low", lambda e: (engine.resolve_ticket_low(), update_ui())) bind_click("btn-resolve-p2-medium", lambda e: (engine.start_resolve_ticket_medium(), update_ui())) bind_click("btn-resolve-p2-high", lambda e: (engine.start_resolve_ticket_high(), update_ui())) bind_click("btn-brew-team-coffee", lambda e: (engine.brew_team_coffee(3), update_ui())) bind_click("btn-brew-team-coffee-bench", lambda e: (engine.brew_team_coffee(3), update_ui())) # Phase 3 CTO Downtime Executive Clickers bind_click("btn-cto-commit-prototype", lambda e: (engine.commit_architecture_prototype(), update_ui())) bind_click("btn-cto-executive-pitch", lambda e: (engine.conduct_executive_pitch(), update_ui())) bind_click("btn-cto-stress-relief", lambda e: (engine.executive_stress_relief(), update_ui())) # Phase 3 Global Infrastructure Governor & Maintenance Listeners bind_click("btn-infra-maintenance", lambda e: (engine.trigger_maintenance_window(), update_ui())) bind_click("btn-infra-mode-eco", lambda e: (engine.set_infra_governor_mode("eco"), update_ui())) bind_click("btn-infra-mode-nominal", lambda e: (engine.set_infra_governor_mode("nominal"), update_ui())) bind_click("btn-infra-mode-overclocked", lambda e: (engine.set_infra_governor_mode("overclocked"), update_ui())) bind_click("btn-infra-mode-ludicrous", lambda e: (engine.set_infra_governor_mode("ludicrous"), update_ui())) # Phase 3 Knowledge Synthesis Auto-Switch Listener bind_change("switch-auto-synthesis", lambda e: (setattr(engine, "auto_synthesis_active", bool(e.target.checked)), update_ui())) # Phase 3 Branch Manager Fleet Hires & Directives for b_id in getattr(resources, "CALL_CENTER_BRANCHES", {}): bind_click(f"btn-branch-hire-{b_id}", lambda e, b=b_id: (engine.hire_branch_manager(b), update_ui())) for d in ["speed", "quality", "budget"]: bind_click(f"btn-branch-dir-{b_id}-{d}", lambda e, b=b_id, dir_k=d: (engine.set_branch_directive(b, dir_k), update_ui())) # Secret click listener for the span_me = document.getElementById("meeeee") if span_me: _secret_proxy = create_proxy(lambda e: ( engine.register_secret_click(), update_ui() )) span_me.addEventListener("click", _secret_proxy) # Upgrades (Phase 1 + Expansion + Evolutionary + Phase 2 Hires) bind_click("btn-buy-dual_monitors", lambda e: (engine.buy_upgrade("dual_monitors"), update_ui())) bind_click("btn-buy-keurig", lambda e: (engine.buy_upgrade("keurig"), update_ui())) bind_click("btn-buy-auto_responder", lambda e: (engine.buy_upgrade("auto_responder"), update_ui())) bind_click("btn-buy-ergonomic_chair", lambda e: (engine.buy_upgrade("ergonomic_chair"), update_ui())) bind_click("btn-buy-coffee_tier", lambda e: (engine.buy_upgrade("coffee_tier"), update_ui())) bind_click("btn-buy-usb_fan", lambda e: (engine.buy_upgrade("usb_fan"), update_ui())) bind_click("btn-buy-liquid_cooler", lambda e: (engine.buy_upgrade("liquid_cooler"), update_ui())) bind_click("btn-buy-sop_database", lambda e: (engine.buy_upgrade("sop_database"), update_ui())) bind_click("btn-buy-advanced_scripting", lambda e: (engine.buy_upgrade("advanced_scripting"), update_ui())) bind_click("btn-buy-ram_module", lambda e: (engine.buy_upgrade("ram_module"), update_ui())) bind_click("btn-buy-file_cabinets", lambda e: (engine.buy_upgrade("file_cabinets"), update_ui())) bind_click("btn-buy-wiki_page", lambda e: (engine.buy_upgrade("wiki_page"), update_ui())) bind_click("btn-buy-script_snippets", lambda e: (engine.buy_upgrade("script_snippets"), update_ui())) bind_click("btn-buy-call_agents", lambda e: (engine.buy_upgrade("call_agents"), update_ui())) bind_click("btn-buy-overclock_cpu", lambda e: (engine.buy_upgrade("overclock_cpu"), update_ui())) bind_click("btn-buy-server_racks", lambda e: (engine.buy_upgrade("server_racks"), update_ui())) bind_click("btn-buy-l1_techs", lambda e: (engine.buy_upgrade("l1_techs"), update_ui())) bind_click("btn-buy-l2_techs", lambda e: (engine.buy_upgrade("l2_techs"), update_ui())) bind_click("btn-buy-sysadmins", lambda e: (engine.buy_upgrade("sysadmins"), update_ui())) # Career Tracks Selectors bind_click("btn-select-dev", lambda e: (engine.select_career_track("dev"), update_ui())) bind_click("btn-select-sysadmin", lambda e: (engine.select_career_track("sysadmin"), update_ui())) bind_click("btn-select-net", lambda e: (engine.select_career_track("net"), update_ui())) # Career / Promotion def handle_promote_phase2(e): if engine.request_promotion(): update_ui() show_orientation_js(2) else: update_ui() bind_click("btn-promote", handle_promote_phase2) def handle_promote_phase3(e): if engine.incorporate_msp(): update_ui() show_orientation_js(3) else: update_ui() bind_click("btn-promote-msp", handle_promote_phase3) bind_click("btn-prestige", lambda e: (engine.prestige_resume_update(), update_ui())) # Phase 3 MSP Client Contracts & Proposal Station bind_click("btn-pitch-bronze", lambda e: (engine.pitch_contract("bronze"), update_ui())) bind_click("btn-pitch-silver", lambda e: (engine.pitch_contract("silver"), update_ui())) bind_click("btn-pitch-gold", lambda e: (engine.pitch_contract("gold"), update_ui())) bind_click("btn-sales-pitch-bronze", lambda e: (engine.pitch_contract("bronze"), update_ui())) bind_click("btn-sales-pitch-silver", lambda e: (engine.pitch_contract("silver"), update_ui())) bind_click("btn-sales-pitch-gold", lambda e: (engine.pitch_contract("gold"), update_ui())) bind_click("btn-cancel-bronze", lambda e: (engine.cancel_contract("bronze"), update_ui())) bind_click("btn-cancel-silver", lambda e: (engine.cancel_contract("silver"), update_ui())) bind_click("btn-cancel-gold", lambda e: (engine.cancel_contract("gold"), update_ui())) # Phase 3 Business Upgrades bind_click("btn-buy-fiber_pipeline", lambda e: (engine.buy_upgrade("fiber_pipeline"), update_ui())) bind_click("btn-buy-blade_servers", lambda e: (engine.buy_upgrade("blade_servers"), update_ui())) bind_click("btn-buy-sales_agents", lambda e: (engine.buy_upgrade("sales_agents"), update_ui())) bind_click("btn-buy-procurement_specialists", lambda e: (engine.buy_upgrade("procurement_specialists"), update_ui())) bind_click("btn-buy-sales_training", lambda e: (engine.buy_upgrade("sales_training"), update_ui())) # Phase 2 dispatch and HR upgrades bind_click("btn-buy-auto_dispatcher_software", lambda e: (engine.buy_upgrade("auto_dispatcher_software"), update_ui())) bind_click("btn-buy-dispatch_optimizer", lambda e: (engine.buy_upgrade("dispatch_optimizer"), update_ui())) bind_click("btn-buy-stress_cruise_control", lambda e: (engine.buy_upgrade("stress_cruise_control"), update_ui())) # Phase 1 AI Documenter and New Upgrades bind_click("btn-buy-ai_documenter", lambda e: (engine.buy_upgrade("ai_documenter"), update_ui())) bind_click("btn-buy-stress_ball_quality", lambda e: (engine.buy_upgrade("stress_ball_quality"), update_ui())) bind_click("btn-buy-coffee_thermos", lambda e: (engine.buy_upgrade("coffee_thermos"), update_ui())) bind_click("btn-buy-ticket_payout_boost", lambda e: (engine.buy_upgrade("ticket_payout_boost"), update_ui())) bind_click("btn-buy-stress_rate_reduction", lambda e: (engine.buy_upgrade("stress_rate_reduction"), update_ui())) bind_click("btn-buy-customer_service_training", lambda e: (engine.buy_upgrade("customer_service_training"), update_ui())) # New Active Actions bind_click("btn-stress-ball", lambda e: (engine.click_stress_ball(), update_ui())) bind_click("btn-coolant-vent", lambda e: (engine.trigger_coolant_vent(), update_ui())) bind_click("btn-publish-manual", lambda e: (engine.publish_tech_manual(), update_ui())) bind_click("btn-publish-course", lambda e: (engine.publish_certification_course(), update_ui())) # Slider listener bind_input("slider-overclock", lambda e: (setattr(engine, "overclock_setting", float(e.target.value)), update_ui())) # Auto-publish manuals listener bind_change("switch-auto-manuals", lambda e: (setattr(engine, "auto_manuals_active", bool(e.target.checked)), update_ui())) # Auto Automation Switches bind_change("switch-auto-research", lambda e: toggle_auto_research(e)) bind_change("switch-auto-kb", lambda e: toggle_auto_kb(e)) bind_change("switch-auto-dispatch", lambda e: toggle_auto_dispatch(e)) # Phase 2 Manager Emergency Workbench Listeners def handle_refresh_candidate_pool_click(): if hasattr(engine, "refresh_candidate_pool"): engine.refresh_candidate_pool(manual=True) else: if engine.cash >= 50.0: engine.cash -= 50.0 if hasattr(engine, "generate_candidate_pool"): engine.generate_candidate_pool() engine.candidate_refresh_timer = 60.0 update_ui() bind_click("btn-refresh-candidate-pool", lambda e: handle_refresh_candidate_pool_click()) # Sidebar Brew Button & Team Coffee Pot Brew bind_click("btn-sidebar-brew", lambda e: (engine.start_brew_coffee(), update_ui())) bind_click("btn-brew-team-coffee", lambda e: (engine.brew_team_coffee(3), update_ui())) # Repeatable stress dump and DC coolant flush bind_click("btn-kp-stress-dump", lambda e: (engine.automated_stress_release(), update_ui())) bind_click("btn-dc-flush", lambda e: (engine.emergency_dc_flush(), update_ui())) # Rack Overclock Governor mode selectors bind_click("btn-gov-normal", lambda e: (engine.set_rack_governor_mode("normal"), update_ui())) bind_click("btn-gov-turbo", lambda e: (engine.set_rack_governor_mode("turbo"), update_ui())) bind_click("btn-gov-overdrive", lambda e: (engine.set_rack_governor_mode("overdrive"), update_ui())) bind_click("btn-sidebar-gov-normal", lambda e: (engine.set_rack_governor_mode("normal"), update_ui())) bind_click("btn-sidebar-gov-turbo", lambda e: (engine.set_rack_governor_mode("turbo"), update_ui())) bind_click("btn-sidebar-gov-overdrive", lambda e: (engine.set_rack_governor_mode("overdrive"), update_ui())) # Data Center upgrades bind_click("btn-dc-buy-server_racks", lambda e: (engine.buy_upgrade("server_racks"), update_ui())) bind_click("btn-dc-buy-blade_servers", lambda e: (engine.buy_upgrade("blade_servers"), update_ui())) bind_click("btn-dc-buy-rack_overclock_governor", lambda e: (engine.buy_upgrade("rack_overclock_governor"), update_ui())) bind_click("btn-buy-dc_ac", lambda e: (engine.buy_upgrade("dc_ac"), update_ui())) bind_click("btn-dc-buy-dc_ac", lambda e: (engine.buy_upgrade("dc_ac"), update_ui())) bind_click("btn-buy-dc_ups", lambda e: (engine.buy_upgrade("dc_ups"), update_ui())) bind_click("btn-dc-buy-dc_ups", lambda e: (engine.buy_upgrade("dc_ups"), update_ui())) bind_click("btn-buy-dc_speeds", lambda e: (engine.buy_upgrade("dc_speeds"), update_ui())) bind_click("btn-dc-buy-dc_speeds", lambda e: (engine.buy_upgrade("dc_speeds"), update_ui())) # Permanent personnel upgrades in Staff tab (fallback compatibility) bind_click("btn-staff-buy-l1_techs", lambda e: (engine.buy_upgrade("l1_techs"), update_ui())) bind_click("btn-staff-buy-l2_techs", lambda e: (engine.buy_upgrade("l2_techs"), update_ui())) bind_click("btn-staff-buy-sysadmins", lambda e: (engine.buy_upgrade("sysadmins"), update_ui())) # HR Console active actions and upgrades bind_click("btn-team-building", lambda e: (engine.trigger_hr_ability("team_building"), update_ui())) bind_click("btn-catered-lunch", lambda e: (engine.trigger_hr_ability("catered_lunch"), update_ui())) bind_click("btn-perf-review", lambda e: (engine.trigger_hr_ability("performance_review"), update_ui())) bind_click("btn-stress_seminar", lambda e: (engine.trigger_hr_ability("stress_seminar"), update_ui())) bind_click("btn-ac_window", lambda e: (engine.trigger_hr_ability("ac_window"), update_ui())) # HR Upgrades levels bind_click("btn-upgrade-team_building", lambda e: (engine.upgrade_hr_ability("team_building"), update_ui())) bind_click("btn-upgrade-catered_lunch", lambda e: (engine.upgrade_hr_ability("catered_lunch"), update_ui())) bind_click("btn-upgrade-performance_review", lambda e: (engine.upgrade_hr_ability("performance_review"), update_ui())) bind_click("btn-upgrade-stress_seminar", lambda e: (engine.upgrade_hr_ability("stress_seminar"), update_ui())) bind_click("btn-upgrade-ac_window", lambda e: (engine.upgrade_hr_ability("ac_window"), update_ui())) # HR Facility Upgrades bind_click("btn-buy-hr-water_cooler", lambda e: (engine.buy_hr_upgrade("water_cooler"), update_ui())) bind_click("btn-buy-hr-snack_fridge", lambda e: (engine.buy_hr_upgrade("snack_fridge"), update_ui())) bind_click("btn-buy-hr-office_plants", lambda e: (engine.buy_hr_upgrade("office_plants"), update_ui())) bind_click("btn-buy-hr-hr_coordinator", lambda e: (engine.buy_hr_upgrade("hr_coordinator"), update_ui())) bind_click("btn-buy-hr_water_cooler", lambda e: (engine.buy_hr_upgrade("water_cooler"), update_ui())) bind_click("btn-buy-hr_snack_fridge", lambda e: (engine.buy_hr_upgrade("snack_fridge"), update_ui())) bind_click("btn-buy-hr_office_plants", lambda e: (engine.buy_hr_upgrade("office_plants"), update_ui())) bind_click("btn-buy-hr_hr_coordinator", lambda e: (engine.buy_hr_upgrade("hr_coordinator"), update_ui())) # Offline Progress Claim Button bind_click("btn-claim-offline", lambda e: claim_offline_progress_js()) # Settings bind_click("btn-save", lambda e: (save.save_to_localstorage(engine), flash_save_status())) bind_click("btn-reset", lambda e: confirm_and_reset()) bind_click("btn-export", lambda e: export_save_text()) bind_click("btn-import", lambda e: import_save_text()) # Commercial Banking & Loans Listeners bind_click("btn-take-loan-micro", lambda e: (engine.take_loan("micro_credit"), update_ui())) bind_click("btn-take-loan-infra", lambda e: (engine.take_loan("infra_expansion"), update_ui())) bind_click("btn-take-loan-corp", lambda e: (engine.take_loan("corporate_facility"), update_ui())) bind_click("btn-take-loan-syndicated", lambda e: (engine.take_loan("syndicated_facility"), update_ui())) bind_click("btn-take-loan-sovereign", lambda e: (engine.take_loan("sovereign_bond"), update_ui())) bind_click("btn-take-loan-max", lambda e: (engine.take_loan("max"), update_ui())) bind_click("btn-repay-500", lambda e: (engine.repay_debt(500.0), update_ui())) bind_click("btn-repay-2500", lambda e: (engine.repay_debt(2500.0), update_ui())) bind_click("btn-repay-50k", lambda e: (engine.repay_debt(50000.0), update_ui())) bind_click("btn-repay-1m", lambda e: (engine.repay_debt(1000000.0), update_ui())) bind_click("btn-repay-10m", lambda e: (engine.repay_debt(10000000.0), update_ui())) bind_click("btn-repay-100m", lambda e: (engine.repay_debt(100000000.0), update_ui())) bind_click("btn-repay-all", lambda e: (engine.repay_all_debt(), update_ui())) def toggle_auto_loan_repayment(e): engine.auto_loan_repayment = e.target.checked update_ui() bind_change("switch-auto-loan-repayment", toggle_auto_loan_repayment) # Phase 3 Sales & Lead Gen Tab Listeners bind_click("btn-sales-pitch-bronze", lambda e: (engine.pitch_contract("bronze"), update_ui())) bind_click("btn-sales-pitch-silver", lambda e: (engine.pitch_contract("silver"), update_ui())) bind_click("btn-sales-pitch-gold", lambda e: (engine.pitch_contract("gold"), update_ui())) bind_click("btn-sales-buy-sales_agents", lambda e: (engine.buy_upgrade("sales_agents"), update_ui())) bind_click("btn-sales-buy-fiber_pipeline", lambda e: (engine.buy_upgrade("fiber_pipeline"), update_ui())) # Phase 3 Stock Market & Corporate IPO Listeners bind_click("btn-launch-ipo", lambda e: (engine.conduct_hit_ipo(), update_ui())) bind_click("btn-hit-buyback", lambda e: (engine.hit_stock_buyback(1000), update_ui())) bind_click("btn-hit-dividend", lambda e: (engine.hit_pay_dividend(0.25), update_ui())) def liquidate_all_portfolio(e): for ticker in list(engine.portfolio.keys()): sh = engine.portfolio[ticker]["shares"] if sh > 0: engine.sell_stock(ticker, sh) update_ui() document.getElementById("btn-liquidate-all").addEventListener("click", create_proxy(liquidate_all_portfolio)) # Multi-Sector Equities Filter Listeners def handle_sector_filter(e): global current_stocks_sector_filter btn = e.target.closest("button") if btn and btn.hasAttribute("data-sector"): current_stocks_sector_filter = btn.getAttribute("data-sector") # Update active class on filter buttons btn_group = document.getElementById("stocks-sector-filters") if btn_group: for b in btn_group.querySelectorAll("button"): if b.getAttribute("data-sector") == current_stocks_sector_filter: b.className = "btn btn-outline-info active" else: b.className = "btn btn-outline-info" update_ui() btn_filters = document.getElementById("stocks-sector-filters") if btn_filters: btn_filters.addEventListener("click", create_proxy(handle_sector_filter)) # Quantitative AI Broker Unlock Listener btn_unlock_broker = document.getElementById("btn-buy-algo-broker") if btn_unlock_broker: btn_unlock_broker.addEventListener("click", create_proxy(lambda e: (engine.buy_upgrade("algo_broker"), update_ui()))) # Create Limit Order Listener def handle_create_limit_order(e): ticker = document.getElementById("select-limit-ticker").value order_type = document.getElementById("select-limit-type").value p_val = document.getElementById("input-limit-price").value s_val = document.getElementById("input-limit-shares").value if p_val and s_val: try: target_p = float(p_val) qty = int(s_val) if engine.add_limit_order(ticker, order_type, target_p, qty): document.getElementById("input-limit-price").value = "" document.getElementById("input-limit-shares").value = "" update_ui() except Exception: pass btn_create_order = document.getElementById("btn-create-limit-order") if btn_create_order: btn_create_order.addEventListener("click", create_proxy(handle_create_limit_order)) # Active Limit Orders Action Delegation def handle_limit_orders_table_click(e): btn = e.target.closest("button") if not btn: return act = btn.getAttribute("data-order-act") ord_id = btn.getAttribute("data-order-id") if act == "cancel" and ord_id: engine.cancel_limit_order(ord_id) update_ui() elif act == "toggle" and ord_id: engine.toggle_limit_order(ord_id) update_ui() tbl_orders = document.getElementById("table-limit-orders-body") if tbl_orders: tbl_orders.addEventListener("click", create_proxy(handle_limit_orders_table_click)) # Multi-Sector Trading Grid Delegated Click Listener (Works for all 14 stocks) def handle_stock_grid_trade(e): btn = e.target.closest("button") if not btn or not btn.hasAttribute("data-stock-act"): return act = btn.getAttribute("data-stock-act") ticker = btn.getAttribute("data-ticker") qty_str = btn.getAttribute("data-qty") if not ticker or not act: return curr_price = engine.stock_market_prices.get(ticker, 1.0) if act == "buy": if qty_str == "max": qty = int(engine.cash // curr_price) if curr_price > 0 else 0 else: qty = int(qty_str) if qty > 0: engine.buy_stock(ticker, qty) update_ui() elif act == "sell": owned = engine.portfolio.get(ticker, {}).get("shares", 0) if qty_str == "all": qty = owned else: qty = int(qty_str) if qty > 0: engine.sell_stock(ticker, qty) update_ui() grid_trading = document.getElementById("stocks-trading-grid") if grid_trading: grid_trading.addEventListener("click", create_proxy(handle_stock_grid_trade)) # Debug Panel Actions def debug_set_cash(e): val = document.getElementById("input-debug-cash").value if val: engine.cash = float(val) update_ui() bind_click("btn-debug-set-cash", debug_set_cash) bind_click("btn-debug-add-cash-10k", lambda e: (setattr(engine, "cash", engine.cash + 10000.0), update_ui())) bind_click("btn-debug-add-cash-100k", lambda e: (setattr(engine, "cash", engine.cash + 100000.0), update_ui())) bind_click("btn-debug-add-cash-1m", lambda e: (setattr(engine, "cash", engine.cash + 1000000.0), update_ui())) def debug_set_tickets(e): val = document.getElementById("input-debug-tickets").value if val: v_int = int(val) delta = v_int - engine.solved_tickets_inventory engine.solved_tickets_inventory = v_int if delta > 0: engine.tickets_solved += delta update_ui() bind_click("btn-debug-set-tickets", debug_set_tickets) bind_click("btn-debug-add-tickets-100", lambda e: (setattr(engine, "solved_tickets_inventory", engine.solved_tickets_inventory + 100), setattr(engine, "tickets_solved", engine.tickets_solved + 100), update_ui())) bind_click("btn-debug-add-tickets-500", lambda e: (setattr(engine, "solved_tickets_inventory", engine.solved_tickets_inventory + 500), setattr(engine, "tickets_solved", engine.tickets_solved + 500), update_ui())) bind_click("btn-debug-add-tickets-5000", lambda e: (setattr(engine, "solved_tickets_inventory", engine.solved_tickets_inventory + 5000), setattr(engine, "tickets_solved", engine.tickets_solved + 5000), update_ui())) def debug_set_kp(e): val = document.getElementById("input-debug-kp").value if val: engine.knowledge_points = float(val) update_ui() bind_click("btn-debug-set-kp", debug_set_kp) bind_click("btn-debug-add-kp-1k", lambda e: (setattr(engine, "knowledge_points", engine.knowledge_points + 1000.0), update_ui())) bind_click("btn-debug-add-kp-10k", lambda e: (setattr(engine, "knowledge_points", engine.knowledge_points + 10000.0), update_ui())) def debug_set_rp(e): val = document.getElementById("input-debug-rp").value if val: engine.research_points = float(val) update_ui() bind_click("btn-debug-set-rp", debug_set_rp) bind_click("btn-debug-add-rp-100", lambda e: (setattr(engine, "research_points", engine.research_points + 100.0), update_ui())) bind_click("btn-debug-add-rp-500", lambda e: (setattr(engine, "research_points", engine.research_points + 500.0), update_ui())) def debug_set_leads(e): val = document.getElementById("input-debug-leads").value if val: engine.leads = float(val) update_ui() bind_click("btn-debug-set-leads", debug_set_leads) bind_click("btn-debug-add-leads-10", lambda e: (setattr(engine, "leads", engine.leads + 10.0), update_ui())) bind_click("btn-debug-add-leads-50", lambda e: (setattr(engine, "leads", engine.leads + 50.0), update_ui())) def debug_set_csi(e): val = document.getElementById("input-debug-csi").value if val: engine.csi = max(0.0, min(100.0, float(val))) update_ui() bind_click("btn-debug-set-csi", debug_set_csi) def debug_set_sla(e): val = document.getElementById("input-debug-sla").value if val: engine.sla = max(0.0, min(100.0, float(val))) update_ui() bind_click("btn-debug-set-sla", debug_set_sla) def debug_clear_queue_sla(e): engine._tickets_queue_low = 0.0 engine._tickets_queue_medium = 0.0 engine._tickets_queue_high = 0.0 if hasattr(engine, "active_incidents"): engine.active_incidents.clear() engine.sla = 100.0 update_ui() bind_click("btn-debug-clear-queue-sla", debug_clear_queue_sla) def debug_cool_systems(e): engine.cpu_temp = 35.0 engine.dc_temp = 35.0 engine.pc_crashed = False engine.is_rebooting = False update_ui() bind_click("btn-debug-cool-systems", debug_cool_systems) def debug_reset_cooldowns(e): engine.coffee_cooldown_timer = 0.0 engine.emergency_coolant_cooldown = 0.0 engine.exec_espresso_cooldown = 0.0 engine.exec_hotfix_cooldown = 0.0 engine.exec_deal_cooldown = 0.0 engine.exec_supply_cooldown = 0.0 if hasattr(engine, "hr_cooldowns"): for k in engine.hr_cooldowns: engine.hr_cooldowns[k] = 0.0 if hasattr(engine, "ceo_lifestyle_cooldowns"): for k in engine.ceo_lifestyle_cooldowns: engine.ceo_lifestyle_cooldowns[k] = 0.0 update_ui() bind_click("btn-debug-reset-cooldowns", debug_reset_cooldowns) def debug_zero_stress(e): engine.stress = 0.0 engine.burnout = False update_ui() bind_click("btn-debug-zero-stress", debug_zero_stress) # Phase 3 Facilities cheats bind_click("btn-debug-fac-cc-1", lambda e: (engine.phase3_facilities.__setitem__("call_center", engine.phase3_facilities.get("call_center", 0) + 1), update_ui())) bind_click("btn-debug-fac-cc-5", lambda e: (engine.phase3_facilities.__setitem__("call_center", engine.phase3_facilities.get("call_center", 0) + 5), update_ui())) bind_click("btn-debug-fac-hd-1", lambda e: (engine.phase3_facilities.__setitem__("helpdesk", engine.phase3_facilities.get("helpdesk", 0) + 1), update_ui())) bind_click("btn-debug-fac-hd-5", lambda e: (engine.phase3_facilities.__setitem__("helpdesk", engine.phase3_facilities.get("helpdesk", 0) + 5), update_ui())) bind_click("btn-debug-fac-dc-1", lambda e: (engine.phase3_facilities.__setitem__("datacenter", engine.phase3_facilities.get("datacenter", 0) + 1), update_ui())) bind_click("btn-debug-fac-dc-5", lambda e: (engine.phase3_facilities.__setitem__("datacenter", engine.phase3_facilities.get("datacenter", 0) + 5), update_ui())) bind_click("btn-debug-fac-corp-1", lambda e: (engine.phase3_facilities.__setitem__("corporate", engine.phase3_facilities.get("corporate", 0) + 1), update_ui())) bind_click("btn-debug-fac-corp-5", lambda e: (engine.phase3_facilities.__setitem__("corporate", engine.phase3_facilities.get("corporate", 0) + 5), update_ui())) # R&D and Leaders cheats def debug_unlock_all_rd(e): for node_id in getattr(resources, "RD_TECH_TREE_REGISTRY", {}): if node_id not in engine.rd_techs_unlocked: engine.rd_techs_unlocked.append(node_id) update_ui() bind_click("btn-debug-unlock-all-rd", debug_unlock_all_rd) def debug_hire_all_leaders(e): for r in getattr(resources, "CTO_LEADERSHIP_ROLES", {}): engine.cto_hired_leadership[r] = engine.cto_hired_leadership.get(r, 0) + 1 update_ui() bind_click("btn-debug-hire-all-leaders", debug_hire_all_leaders) def debug_add_all_contracts(e): engine.contracts_bronze = getattr(engine, "contracts_bronze", 0) + 1 engine.contracts_silver = getattr(engine, "contracts_silver", 0) + 1 engine.contracts_gold = getattr(engine, "contracts_gold", 0) + 1 update_ui() bind_click("btn-debug-add-all-contracts", debug_add_all_contracts) # Manufacturing and Battery cheats def debug_add_mfg_mats(e): for m_id in getattr(resources, "MANUFACTURING_RESOURCES", {}): engine.manufacturing_inventory[m_id] = engine.manufacturing_inventory.get(m_id, 0.0) + 1000.0 update_ui() bind_click("btn-debug-add-mfg-mats", debug_add_mfg_mats) def debug_full_battery(e): engine.grid_battery_charge = max(10000.0, getattr(engine, "grid_battery_capacity", 10000.0)) update_ui() bind_click("btn-debug-full-battery", debug_full_battery) def debug_set_upgrade_lvl(e): up_id = document.getElementById("select-debug-upgrade").value val = document.getElementById("input-debug-upgrade-level").value if up_id and val: engine.upgrades[up_id] = int(val) update_ui() bind_click("btn-debug-set-upgrade", debug_set_upgrade_lvl) bind_click("btn-debug-phase1", lambda e: (engine.debug_jump_to_phase(1), update_ui(), show_orientation_js(1))) bind_click("btn-debug-phase2", lambda e: (engine.debug_jump_to_phase(2), update_ui(), show_orientation_js(2))) bind_click("btn-debug-phase3", lambda e: (engine.debug_jump_to_phase(3), update_ui(), show_orientation_js(3))) bind_click("btn-debug-phase4", lambda e: (engine.debug_jump_to_phase(4), update_ui(), show_orientation_js(4))) bind_click("btn-debug-sim-phase2", lambda e: (engine.debug_simulate_jump_to_phase(2), update_ui(), show_orientation_js(2))) bind_click("btn-debug-sim-phase3", lambda e: (engine.debug_simulate_jump_to_phase(3), update_ui(), show_orientation_js(3))) bind_click("btn-debug-sim-phase4", lambda e: (engine.debug_simulate_jump_to_phase(4), update_ui(), show_orientation_js(4))) # Fast-Forward Multi-Timeframe Simulated Progression Jumps def _do_ff(sec: float, label: str): res = engine.debug_fast_forward(sec) update_ui() if hasattr(window, "showToast"): try: window.showToast(f"⏩ Fast-Forward {label} Complete", f"Simulated {sec:,.0f}s: +${res['cash_delta']:,.2f}, +{res['tickets_delta']:,} tickets") except Exception: pass bind_click("btn-debug-ff-1h", lambda e: _do_ff(3600.0, "1 Hour")) bind_click("btn-debug-ff-1d", lambda e: _do_ff(86400.0, "1 Day (24h)")) bind_click("btn-debug-ff-1w", lambda e: _do_ff(604800.0, "1 Week (7d)")) bind_click("btn-debug-ff-1m", lambda e: _do_ff(2592000.0, "1 Month (30d)")) bind_click("btn-debug-ff-1y", lambda e: _do_ff(31536000.0, "1 Year (365d)")) # Milestone Toast Preference Switch bind_change("toggle-milestone-toasts", lambda e: toggle_milestone_toasts_js(bool(e.target.checked))) # Debug Random Events Selection & Manual Triggering sel_ev = document.getElementById("select-debug-trigger-event") if sel_ev and hasattr(engine, "random_events") and engine.random_events: all_evs = engine.random_events.get_all_events() opts = [] for ev in all_evs: e_id = ev.get("id", "") e_name = ev.get("name", e_id) e_phases = ",".join(str(p) for p in ev.get("phases", [1,2,3,4])) opts.append(f'') sel_ev.innerHTML = "".join(opts) def debug_trigger_selected_event(e): sel = document.getElementById("select-debug-trigger-event") if sel and sel.value and hasattr(engine, "random_events") and engine.random_events: engine.random_events.trigger_event(sel.value, force=True) update_ui() bind_click("btn-debug-trigger-selected-event", debug_trigger_selected_event) def debug_roll_random_event(e): if hasattr(engine, "random_events") and engine.random_events: res = engine.random_events.roll_for_event(engine.phase, force=True) update_ui() bind_click("btn-debug-roll-random-event", debug_roll_random_event) # Diagnostic Snapshot & AI Session Exporter Bindings def update_diagnostic_modal_content(): diag_str = engine.generate_diagnostic_snapshot() textarea = document.getElementById("diag-report-textarea") counter = document.getElementById("diag-byte-counter") if textarea: textarea.value = diag_str if counter: counter.innerText = f"{len(diag_str):,} characters" return diag_str def copy_text_to_clipboard(text: str): try: if hasattr(window, "navigator") and hasattr(window.navigator, "clipboard") and window.navigator.clipboard: window.navigator.clipboard.writeText(text) else: window.eval(f""" (function() {{ const temp = document.createElement('textarea'); temp.value = {json.dumps(text)}; document.body.appendChild(temp); temp.select(); document.execCommand('copy'); document.body.removeChild(temp); }})(); """) except Exception as err: print("Clipboard error:", err) def debug_open_diag_modal(e): update_diagnostic_modal_content() bind_click("btn-debug-open-diag-modal", debug_open_diag_modal) def debug_copy_diag_quick(e): diag_str = update_diagnostic_modal_content() copy_text_to_clipboard(diag_str) btn = document.getElementById("btn-debug-copy-diag-quick") if btn: orig = btn.innerText btn.innerText = "βœ“ Copied!" def reset_btn(): btn.innerText = orig window.setTimeout(create_proxy(reset_btn), 1500) if hasattr(engine, "event_log") and engine.event_log: engine.event_log.add_entry("DIAGNOSTIC", "πŸ“‹ Complete Session Diagnostic Snapshot copied to clipboard.") bind_click("btn-debug-copy-diag-quick", debug_copy_diag_quick) def diag_copy_from_modal(e): textarea = document.getElementById("diag-report-textarea") txt = textarea.value if textarea else engine.generate_diagnostic_snapshot() copy_text_to_clipboard(txt) btn = document.getElementById("btn-diag-copy-clipboard") if btn: orig = btn.innerText btn.innerText = "βœ“ Copied!" def reset_btn(): btn.innerText = orig window.setTimeout(create_proxy(reset_btn), 1500) bind_click("btn-diag-copy-clipboard", diag_copy_from_modal) def diag_download_md(e): diag_str = update_diagnostic_modal_content() try: day_val = getattr(engine, "game_day", 1) filename = f"HIT_Diagnostic_Report_Phase_{engine.phase}_Day_{day_val}.md" window.eval(f""" (function() {{ const blob = new Blob([{json.dumps(diag_str)}], {{type: 'text/markdown'}}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = '{filename}'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }})(); """) except Exception as ex: print("Download error:", ex) bind_click("btn-diag-download-md", diag_download_md) def diag_load_from_textarea(e): textarea = document.getElementById("diag-report-textarea") if textarea and textarea.value: res = engine.load_diagnostic_snapshot(textarea.value) if res: update_ui() if hasattr(engine, "event_log") and engine.event_log: engine.event_log.add_entry("DIAGNOSTIC", f"⚑ Restored gameplay state to Phase {engine.phase} from diagnostic snapshot payload.") btn = document.getElementById("btn-diag-load-from-textarea") if btn: orig = btn.innerText btn.innerText = "βœ“ Restored!" def reset_btn(): btn.innerText = orig window.setTimeout(create_proxy(reset_btn), 1500) bind_click("btn-diag-load-from-textarea", diag_load_from_textarea) # Static On-Site Slot Listeners (Safe guard) for s_idx in range(4): def make_unlock(idx): return lambda e: (engine.unlock_onsite_slot(), update_ui()) btn_u = document.getElementById(f"btn-unlock-slot-{s_idx}") if btn_u: btn_u.addEventListener("click", create_proxy(make_unlock(s_idx))) def make_fire(idx): return lambda e: ( len(engine.hired_onsite_techs) > idx and engine.fire_onsite_tech(engine.hired_onsite_techs[idx]["id"]), update_ui() ) btn_f = document.getElementById(f"btn-fire-slot-{s_idx}") if btn_f: btn_f.addEventListener("click", create_proxy(make_fire(s_idx))) # Phase 4 Promotion Listener btn_prom = document.getElementById("btn-promote-enterprise") if btn_prom: def handle_promote_phase4(e): if engine.incorporate_global_enterprise(): update_ui() show_orientation_js(4) else: update_ui() btn_prom.addEventListener("click", create_proxy(handle_promote_phase4)) # Phase 4 Executive Action Clickers btn_ex_esp = document.getElementById("btn-exec-espresso") if btn_ex_esp: btn_ex_esp.addEventListener("click", create_proxy(lambda e: (engine.execute_executive_espresso(), update_ui()))) btn_ex_hot = document.getElementById("btn-exec-hotfix") if btn_ex_hot: btn_ex_hot.addEventListener("click", create_proxy(lambda e: (engine.execute_executive_hotfix(), update_ui()))) btn_ex_deal = document.getElementById("btn-exec-deal") if btn_ex_deal: btn_ex_deal.addEventListener("click", create_proxy(lambda e: (engine.sign_enterprise_deal(), update_ui()))) btn_ex_sup = document.getElementById("btn-exec-supply") if btn_ex_sup: btn_ex_sup.addEventListener("click", create_proxy(lambda e: (engine.squeeze_supply_chain(), update_ui()))) # Phase 4 Operations Deck Hotkeys btn_ops_esp = document.getElementById("btn-ops-exec-espresso") if btn_ops_esp: btn_ops_esp.addEventListener("click", create_proxy(lambda e: (engine.execute_executive_espresso(), update_ui()))) btn_ops_hot = document.getElementById("btn-ops-exec-hotfix") if btn_ops_hot: btn_ops_hot.addEventListener("click", create_proxy(lambda e: (engine.execute_executive_hotfix(), update_ui()))) btn_ops_deal = document.getElementById("btn-ops-exec-deal") if btn_ops_deal: btn_ops_deal.addEventListener("click", create_proxy(lambda e: (engine.sign_enterprise_deal(), update_ui()))) btn_ops_sup = document.getElementById("btn-ops-exec-supply") if btn_ops_sup: btn_ops_sup.addEventListener("click", create_proxy(lambda e: (engine.squeeze_supply_chain(), update_ui()))) # Phase 4 Manual Fabrication Workbench Listeners for r_key in ["slice_wafers", "etch_gpus", "assemble_blades"]: def make_manual_craft(r): return lambda e: (engine.craft_recipe_batch(r), update_ui()) btn_cr = document.getElementById(f"btn-craft-{r_key}") if btn_cr: btn_cr.addEventListener("click", create_proxy(make_manual_craft(r_key))) # Phase 4 Automated Silicon Arc Furnaces Upgrade btn_sf = document.getElementById("btn-buy-silicon-furnace") if btn_sf: btn_sf.addEventListener("click", create_proxy(lambda e: (engine.buy_upgrade("silicon_furnaces"), update_ui()))) # Phase 4 Anno Manufacturing Facility Listeners for b_key in resources.MANUFACTURING_FACILITIES: def make_bld_fn(b): return lambda e: (engine.build_manufacturing_facility(b), update_ui()) def make_demo_fn(b): return lambda e: (engine.demolish_manufacturing_facility(b), update_ui()) def make_oc_fn(b): return lambda e: (engine.toggle_facility_overclock(b), update_ui()) def make_pause_fn(b): return lambda e: (engine.toggle_facility_pause(b), update_ui()) b_bld = document.getElementById(f"btn-mfg-build-{b_key}") if b_bld: b_bld.addEventListener("click", create_proxy(make_bld_fn(b_key))) b_demo = document.getElementById(f"btn-mfg-demo-{b_key}") if b_demo: b_demo.addEventListener("click", create_proxy(make_demo_fn(b_key))) b_oc = document.getElementById(f"btn-mfg-oc-{b_key}") if b_oc: b_oc.addEventListener("click", create_proxy(make_oc_fn(b_key))) b_ps = document.getElementById(f"btn-mfg-pause-{b_key}") if b_ps: b_ps.addEventListener("click", create_proxy(make_pause_fn(b_key))) # Phase 4 Manufacturing Resource Sell & Auto-Sell Listeners for r_key in resources.MANUFACTURING_RESOURCES: def make_sell_fn(r): return lambda e: (engine.sell_manufacturing_resource(r, 10.0), update_ui()) def make_autosell_fn(r): def toggle_as(e): curr = engine.auto_sell_thresholds.get(r, -1) cap = resources.MANUFACTURING_RESOURCES[r]["base_cap"] # Toggle through disabled (-1) -> 80% cap -> 50% cap if curr < 0: engine.set_auto_sell_threshold(r, int(cap * 0.8)) elif curr >= int(cap * 0.8): engine.set_auto_sell_threshold(r, int(cap * 0.5)) else: engine.set_auto_sell_threshold(r, -1) update_ui() return toggle_as b_sell = document.getElementById(f"btn-mfg-sell-{r_key}") if b_sell: b_sell.addEventListener("click", create_proxy(make_sell_fn(r_key))) b_as = document.getElementById(f"btn-mfg-autosell-{r_key}") if b_as: b_as.addEventListener("click", create_proxy(make_autosell_fn(r_key))) # Phase 4 Storage Capacity Upgrade Listeners for t_idx in range(4): def make_storage_upgrade_fn(t): return lambda e: (engine.upgrade_storage_facility(t), update_ui()) btn_st = document.getElementById(f"btn-upgrade-storage-{t_idx}") if btn_st: btn_st.addEventListener("click", create_proxy(make_storage_upgrade_fn(t_idx))) # Phase 4 Project Singularity Delivery Listeners for sing_item in ["tensor_asic", "hbm_memory", "optic_fabric", "cooling_unit", "rack_matrix"]: def make_sing_del_fn(item_id, amount): return lambda e: (engine.deliver_to_project_singularity(item_id, amount), update_ui()) b_del = document.getElementById(f"btn-sing-deliver-{sing_item}") if b_del: amt = 5 if sing_item in ["cooling_unit", "rack_matrix"] else 10 b_del.addEventListener("click", create_proxy(make_sing_del_fn(sing_item, amt))) def deliver_all_singularity(e): for item_id in ["tensor_asic", "hbm_memory", "optic_fabric", "cooling_unit", "rack_matrix"]: engine.deliver_to_project_singularity(item_id, 1000) update_ui() b_del_all = document.getElementById("btn-sing-deliver-all") if b_del_all: b_del_all.addEventListener("click", create_proxy(deliver_all_singularity)) # Phase 4 Facilities Acquisition Listeners for fac_key in ["call_centers", "datacenters", "fabs", "ai_campuses"]: def make_buy_fac(f): return lambda e: (engine.buy_enterprise_facility(f), update_ui()) b_fac = document.getElementById(f"btn-buy-facility-{fac_key}") if b_fac: b_fac.addEventListener("click", create_proxy(make_buy_fac(fac_key))) b_ops_fac = document.getElementById(f"btn-ops-buy-facility-{fac_key}") if b_ops_fac: b_ops_fac.addEventListener("click", create_proxy(make_buy_fac(fac_key))) # Phase 4 C-Suite & VP Appointment Listeners for ex_key in ["cto", "coo", "cro", "cfo", "vp_hr", "vp_infra", "vp_supply", "caio"]: def make_hire_csuite(ex): return lambda e: (engine.hire_c_suite_executive(ex), update_ui()) btn_c = document.getElementById(f"btn-hire-csuite-{ex_key}") if btn_c: btn_c.addEventListener("click", create_proxy(make_hire_csuite(ex_key))) # Phase 4 Corporate Directives & Macro Policy Listeners for ot_key in ["40h", "50h", "60h", "war_mode"]: def make_ot_policy(p): return lambda e: (engine.set_overtime_policy(p), update_ui()) btn_ot = document.getElementById(f"btn-policy-overtime-{ot_key}") if btn_ot: btn_ot.addEventListener("click", create_proxy(make_ot_policy(ot_key))) for pr_key in ["frugal", "market", "premium", "elite"]: def make_pr_policy(s): return lambda e: (engine.set_payroll_scale(s), update_ui()) btn_pr = document.getElementById(f"btn-policy-payroll-{pr_key}") if btn_pr: btn_pr.addEventListener("click", create_proxy(make_pr_policy(pr_key))) for rd_key in ["off", "modest", "aggressive", "unlimited"]: def make_rd_policy(b): return lambda e: (engine.set_rd_budget(b), update_ui()) btn_rd = document.getElementById(f"btn-policy-rd-{rd_key}") if btn_rd: btn_rd.addEventListener("click", create_proxy(make_rd_policy(rd_key))) # Phase 4 AI Incubator Funding btn_ai_100k = document.getElementById("btn-fund-ai-100k") if btn_ai_100k: btn_ai_100k.addEventListener("click", create_proxy(lambda e: (engine.fund_ai_incubator(100000.0), update_ui()))) btn_ai_1m = document.getElementById("btn-fund-ai-1m") if btn_ai_1m: btn_ai_1m.addEventListener("click", create_proxy(lambda e: (engine.fund_ai_incubator(1000000.0), update_ui()))) btn_ai_10m = document.getElementById("btn-fund-ai-10m") if btn_ai_10m: btn_ai_10m.addEventListener("click", create_proxy(lambda e: (engine.fund_ai_incubator(10000000.0), update_ui()))) # Phase 3 Stock Exchange Corporate Action Listeners btn_ipo = document.getElementById("btn-hit-ipo") if btn_ipo: btn_ipo.addEventListener("click", create_proxy(lambda e: (engine.conduct_hit_ipo(), update_ui()))) btn_sec_offering = document.getElementById("btn-hit-secondary-offering") if btn_sec_offering: btn_sec_offering.addEventListener("click", create_proxy(lambda e: (engine.conduct_hit_secondary_offering(), update_ui()))) btn_buyback = document.getElementById("btn-hit-buyback") if btn_buyback: btn_buyback.addEventListener("click", create_proxy(lambda e: (engine.conduct_hit_share_buyback(), update_ui()))) btn_div = document.getElementById("btn-hit-dividend") if btn_div: btn_div.addEventListener("click", create_proxy(lambda e: (engine.distribute_hit_dividend(), update_ui()))) btn_liq = document.getElementById("btn-liquidate-all") if btn_liq: btn_liq.addEventListener("click", create_proxy(lambda e: (engine.liquidate_all_stocks(), update_ui()))) btn_algo = document.getElementById("btn-buy-algo-broker") if btn_algo: btn_algo.addEventListener("click", create_proxy(lambda e: (engine.buy_upgrade("algo_broker"), update_ui()))) # AI Limit Order Deployment btn_deploy_limit = document.getElementById("btn-deploy-limit-order") if btn_deploy_limit: def on_deploy_limit(e): sel_tick = document.getElementById("select-limit-ticker") sel_type = document.getElementById("select-limit-type") inp_p = document.getElementById("input-limit-price") inp_sh = document.getElementById("input-limit-shares") if sel_tick and sel_type and inp_p and inp_sh: t = sel_tick.value ot = sel_type.value try: p = float(inp_p.value) sh = int(inp_sh.value) if p > 0 and sh > 0: engine.add_limit_order(t, ot, p, sh) except Exception: pass update_ui() btn_deploy_limit.addEventListener("click", create_proxy(on_deploy_limit)) # Sector Filter Button Listeners filters_container = document.getElementById("stocks-sector-filters") if filters_container: def on_sector_filter_click(e): global current_stocks_sector_filter btn = e.target.closest("button") if btn and btn.hasAttribute("data-sector"): sec = btn.getAttribute("data-sector") current_stocks_sector_filter = sec # Toggle active class for b in filters_container.querySelectorAll("button"): b.classList.remove("active") btn.classList.add("active") update_ui() filters_container.addEventListener("click", create_proxy(on_sector_filter_click)) # Delegated Stock Card Trading Listeners (Buy 1/10/100/Max, Sell 1/10/100/All) stocks_grid = document.getElementById("stocks-trading-grid") if stocks_grid: def on_stock_trade_click(e): btn = e.target.closest("button") if not btn or not btn.hasAttribute("data-stock-act"): return act = btn.getAttribute("data-stock-act") ticker = btn.getAttribute("data-ticker") qty_str = btn.getAttribute("data-qty") if not ticker or ticker not in resources.STOCKS_REGISTRY: return curr_p = engine.stock_market_prices.get(ticker, resources.STOCKS_REGISTRY[ticker]["base_price"]) if act == "buy": if qty_str == "max": max_sh = int(engine.cash // curr_p) if curr_p > 0 else 0 if max_sh > 0: engine.buy_stock(ticker, max_sh) else: try: sh = int(qty_str) if sh > 0: engine.buy_stock(ticker, sh) except Exception: pass elif act == "sell": owned = engine.portfolio.get(ticker, {}).get("shares", 0) if qty_str == "all": if owned > 0: engine.sell_stock(ticker, owned) else: try: sh = int(qty_str) if sh > 0 and owned >= sh: engine.sell_stock(ticker, sh) except Exception: pass update_ui() stocks_grid.addEventListener("click", create_proxy(on_stock_trade_click)) # Delegated Limit Orders Table Listeners (Toggle Pause / Cancel) orders_table = document.getElementById("table-limit-orders-body") if orders_table: def on_order_action_click(e): btn = e.target.closest("button") if not btn or not btn.hasAttribute("data-order-act"): return act = btn.getAttribute("data-order-act") ord_id = btn.getAttribute("data-order-id") if ord_id: if act == "toggle": engine.toggle_limit_order(ord_id) elif act == "cancel": engine.cancel_limit_order(ord_id) # Phase 4 Regional Operations Listeners btn_reg_hub = document.getElementById("btn-reg-buy-hub") if btn_reg_hub: btn_reg_hub.addEventListener("click", create_proxy(lambda e: (engine.buy_enterprise_facility("call_centers"), update_ui()))) for p_type, prefix, vals in [("payroll", "pay", ["standard", "generous", "executive"]), ("infra", "infra", ["standard", "enterprise", "hyperscale"]), ("training", "train", ["basic", "certified", "architect"])]: for v in vals: def make_reg_pol_fn(pt, pv): return lambda e: (engine.set_regional_policy(pt, pv), update_ui()) b_pol = document.getElementById(f"btn-reg-policy-{prefix}-{v}") if b_pol: b_pol.addEventListener("click", create_proxy(make_reg_pol_fn(p_type, v))) for up_k in ["hub_ai_dispatcher", "hub_optical_backbone", "hub_specialist_centers", "hub_microgrid_power"]: def make_reg_upg_fn(uk): return lambda e: (engine.buy_regional_fleet_upgrade(uk), update_ui()) b_upg = document.getElementById(f"btn-reg-upgrade-{up_k}") if b_upg: b_upg.addEventListener("click", create_proxy(make_reg_upg_fn(up_k))) # Phase 4 CEO Lifestyle Event Listeners btn_meditate = document.getElementById("btn-ceo-meditate") if btn_meditate: btn_meditate.addEventListener("click", create_proxy(lambda e: (engine.execute_ceo_meditation(), update_ui()))) btn_yacht_cruise = document.getElementById("btn-ceo-yacht-cruise") if btn_yacht_cruise: btn_yacht_cruise.addEventListener("click", create_proxy(lambda e: (engine.execute_ceo_yacht_cruise(), update_ui()))) btn_yacht_upg = document.getElementById("btn-ceo-yacht-upgrade") if btn_yacht_upg: btn_yacht_upg.addEventListener("click", create_proxy(lambda e: (engine.buy_ceo_yacht_upgrade(), update_ui()))) btn_isl_upg = document.getElementById("btn-ceo-island-upgrade") if btn_isl_upg: btn_isl_upg.addEventListener("click", create_proxy(lambda e: (engine.buy_ceo_island_upgrade(), update_ui()))) for act_k in ["supercar", "gala", "cinema", "vineyard"]: def make_ceo_act_fn(ak): return lambda e: (engine.trigger_ceo_lifestyle_action(ak), update_ui()) b_act = document.getElementById(f"btn-ceo-act-{act_k}") # Interface & Tutorial Preferences Toggle Listeners def on_toggle_help_icons(e): engine.settings["show_help_icons"] = bool(e.target.checked) update_ui() def on_toggle_tooltips(e): engine.settings["show_tooltips"] = bool(e.target.checked) update_ui() toggle_help = document.getElementById("toggle-help-icons") if toggle_help: toggle_help.addEventListener("change", create_proxy(on_toggle_help_icons)) toggle_tt = document.getElementById("toggle-button-tooltips") if toggle_tt: toggle_tt.addEventListener("change", create_proxy(on_toggle_tooltips)) # Initialize Achievements Dynamic UI init_achievements_ui() last_recruits_sig = "" last_prestige_tree_sig = "" active_offline_report = None def render_prestige_tree_ui(): global last_prestige_tree_sig # Update XP headers disp_unspent = document.getElementById("display-prestige-unspent-xp") if disp_unspent: disp_unspent.innerText = f"{engine.unspent_xp:,} XP" disp_lifetime = document.getElementById("display-prestige-lifetime-xp") if disp_lifetime: payout_bonus = engine.xp * 2 stress_relief = engine.xp * 1 disp_lifetime.innerText = f"{engine.xp:,} XP (+{payout_bonus}% Payout, -{stress_relief}% Stress)" sig = f"{engine.unspent_xp}_{engine.spent_xp}_{getattr(engine, 'prestige_upgrades', {})}" if sig == last_prestige_tree_sig: return last_prestige_tree_sig = sig branch_map = { "autonomous": document.getElementById("prestige-branch-autonomous"), "early_game": document.getElementById("prestige-branch-early_game"), "workforce": document.getElementById("prestige-branch-workforce"), "enterprise": document.getElementById("prestige-branch-enterprise") } upgrades = getattr(resources, "PRESTIGE_UPGRADES", {}) branch_htmls = {k: [] for k in branch_map} for up_id, spec in upgrades.items(): branch = spec.get("branch", "autonomous") if branch not in branch_htmls: continue curr_lvl = engine.prestige_upgrades.get(up_id, 0) max_lvl = spec.get("max_level", 1) cost = engine.get_prestige_upgrade_cost(up_id) is_max = curr_lvl >= max_lvl can_afford = engine.unspent_xp >= cost and not is_max btn_class = "btn-outline-secondary opacity-50" if is_max else ("btn-outline-warning" if can_afford else "btn-outline-secondary") btn_disabled = "disabled" if (is_max or not can_afford) else "" btn_text = "MAX LEVEL" if is_max else f"Upgrade ({cost} XP)" badge_text = f"MAX" if is_max else f"Lvl {curr_lvl}/{max_lvl}" badge_class = "bg-success" if is_max else ("bg-warning text-dark" if curr_lvl > 0 else "bg-secondary") node_html = f'''
    {spec.get("icon", "⭐")} {spec.get("name", up_id)} {badge_text}
    {spec.get("desc", "")}
    ''' branch_htmls[branch].append(node_html) for b_key, el in branch_map.items(): if el: el.innerHTML = "".join(branch_htmls[b_key]) def check_offline_progress(): global active_offline_report last_saved = getattr(engine, "last_save_time", None) if not last_saved: return now = time.time() elapsed = now - last_saved # If away for at least 30 seconds if elapsed >= 30.0: report = engine.calculate_offline_progress(elapsed) active_offline_report = report # Populate modal fields h = int(report["offline_seconds"] // 3600) m = int((report["offline_seconds"] % 3600) // 60) s = int(report["offline_seconds"] % 60) el_time = document.getElementById("offline-disp-time-away") if el_time: el_time.innerText = f"{h}h {m}m {s}s" el_eff = document.getElementById("offline-disp-efficiency") if el_eff: el_eff.innerText = f"{int(report['efficiency'] * 100)}% Efficiency" el_gross = document.getElementById("offline-disp-gross") if el_gross: el_gross.innerText = f"+${report['gross_cash']:,.2f}" el_opex = document.getElementById("offline-disp-opex") if el_opex: el_opex.innerText = f"-${report['opex_cash']:,.2f}" el_net = document.getElementById("offline-disp-net") if el_net: el_net.innerText = f"+${report['net_cash']:,.2f}" el_tix = document.getElementById("offline-disp-tickets") if el_tix: el_tix.innerText = f"+{report['tickets_solved']:,}" el_kp = document.getElementById("offline-disp-kp") if el_kp: el_kp.innerText = f"+{report['kp_gained']:,.1f} KP" el_rp = document.getElementById("offline-disp-rp") if el_rp: el_rp.innerText = f"+{report['rp_gained']:,.1f} RP" el_sxp = document.getElementById("offline-disp-staff-xp") if el_sxp: el_sxp.innerText = f"+{report['staff_xp_gained']:,} XP" # Phase 4 MFG Container mfg_cont = document.getElementById("offline-container-mfg") mfg_items = document.getElementById("offline-disp-mfg-items") if mfg_cont and mfg_items: mfg_dict = report.get("mfg_produced", {}) if mfg_dict and any(v > 0 for v in mfg_dict.values()): mfg_cont.style.display = "block" chips = [] for k, v in mfg_dict.items(): if v > 0: name_pretty = k.replace("_", " ").title() chips.append(f'πŸ“¦ {name_pretty}: +{v:,}') if report.get("overflow_cash", 0) > 0: chips.append(f'πŸ’° Overflow Liquidated: +${report["overflow_cash"]:,.2f}') mfg_items.innerHTML = "".join(chips) else: mfg_cont.style.display = "none" # Show modal try: modal_el = document.getElementById("offlineShiftModal") if modal_el: if hasattr(window, "bootstrap") and hasattr(window.bootstrap, "Modal"): modal_inst = window.bootstrap.Modal.getOrCreateInstance(modal_el) modal_inst.show() else: modal_el.classList.add("show") modal_el.style.display = "block" except Exception as ex: print(f"Error opening offline modal: {ex}") def claim_offline_progress_js(): global active_offline_report if active_offline_report: engine.apply_offline_progress(active_offline_report) active_offline_report = None update_ui() def render_active_effects_hud(): bar = document.getElementById("active-effects-hud-bar") container = document.getElementById("active-effects-container") if not bar or not container: return items = [] # 1. Random Events if hasattr(engine, "random_events") and engine.random_events: for ev in engine.random_events.get_active_events_list(): effects_desc = [] eff = ev.get("effects", {}) for k, v in eff.items(): if "fallback" in k.lower(): # If in Phase 4 and it's phase4_fallback, or Phase 3 and phase3_fallback, unpack it if (k == "phase4_fallback" and engine.phase >= 4) or (k == "phase3_fallback" and engine.phase == 3): if isinstance(v, dict): for sub_k, sub_v in v.items(): sub_str = f"{sub_v}x" if "_mult" in sub_k else (f"{sub_v:+.0f}" if isinstance(sub_v, (int, float)) else str(sub_v)) sub_clean = sub_k.replace("_mult", "").replace("_delta", "").replace("_boost", "").replace("_", " ").title() effects_desc.append(f"{sub_clean}: {sub_str}") continue if isinstance(v, dict): continue val_str = f"{v}x" if "_mult" in k else (f"{v:+.0f}" if isinstance(v, (int, float)) and ("delta" in k or "boost" in k) else (f"{v}x" if isinstance(v, (int, float)) else str(v))) k_clean = k.replace("_mult", "").replace("_", " ").title() effects_desc.append(f"{k_clean}: {val_str}") eff_text = ", ".join(effects_desc) if effects_desc else ev.get("description", "") items.append({ "type": "event", "icon": ev.get("icon", "⚑"), "name": ev.get("name", "Event"), "effects_summary": eff_text, "time_remaining": ev.get("time_remaining", 0), "badge_class": "bg-primary text-white border border-info" }) # 2. HR Active Abilities if hasattr(engine, "hr_active_effects"): lunch_timer = engine.hr_active_effects.get("lunch_boost", 0.0) if lunch_timer > 0.0: items.append({ "type": "hr_buff", "icon": "πŸ•", "name": "Catered Lunch", "effects_summary": "+30% Tech Speed", "time_remaining": round(lunch_timer, 1), "badge_class": "bg-success text-dark border border-light" }) review_timer = engine.hr_active_effects.get("review_boost", 0.0) if review_timer > 0.0: items.append({ "type": "hr_buff", "icon": "πŸ“", "name": "Performance Review", "effects_summary": "+30% Tech Speed", "time_remaining": round(review_timer, 1), "badge_class": "bg-info text-dark border border-light" }) # 3. Critical Debuffs if getattr(engine, "burnout", False): items.append({ "type": "debuff", "icon": "πŸ”₯", "name": "CRITICAL BURNOUT", "effects_summary": "Actions blocked", "time_remaining": round(getattr(engine, "burnout_timer", 0.0), 1), "badge_class": "bg-danger text-white border border-warning" }) if getattr(engine, "pc_crashed", False): items.append({ "type": "debuff", "icon": "πŸ’₯", "name": "BSOD SYSTEM CRASH", "effects_summary": "Rebooting PC required", "time_remaining": round(getattr(engine, "crash_timer", 0.0), 1), "badge_class": "bg-danger text-white border border-warning" }) if not items: bar.style.display = "none" container.innerHTML = "" return bar.style.display = "block" html = "" for it in items: html += f"""
    {it['icon']} {it['name']} ({it['effects_summary']}) ⏱️ {it['time_remaining']}s
    """ container.innerHTML = html _last_observed_phase = None def update_ui(): global _last_observed_phase update_declarative_visibility() render_active_effects_hud() # Check for triggered random event modal popup if hasattr(engine, "pending_event_modal") and engine.pending_event_modal: ev_modal = engine.pending_event_modal engine.pending_event_modal = None show_random_event_modal(ev_modal) # Update active random event modal timers and handle graceful auto-dismiss (+30s grace period) global _active_event_modal_info if _active_event_modal_info and _active_event_modal_info.get("active"): el_dur = document.getElementById("event-modal-duration") el_prog = document.getElementById("event-modal-progress") rem = _active_event_modal_info["end_time"] - time.time() dur = max(1.0, _active_event_modal_info["duration"]) if rem > 0: if el_dur: el_dur.innerText = f"{rem:.1f}s remaining" if el_prog: pct = max(0.0, min(100.0, (rem / dur) * 100.0)) el_prog.style.width = f"{pct:.1f}%" el_prog.className = "progress-bar bg-warning progress-bar-striped progress-bar-animated" else: grace_left = 30.0 + rem if grace_left > 0: if el_dur: el_dur.innerText = f"Expired (Auto-closing in {grace_left:.0f}s)" if el_prog: el_prog.style.width = "0%" el_prog.className = "progress-bar bg-secondary" else: _active_event_modal_info["active"] = False _active_event_modal_info = None try: window.eval('var el = document.getElementById("randomEventModal"); if (el) { var m = bootstrap.Modal.getInstance(el); if (m) m.hide(); }') except Exception: pass try: window.active_game_engine_phase = engine.phase if _last_observed_phase is None: _last_observed_phase = engine.phase elif engine.phase > _last_observed_phase: _last_observed_phase = engine.phase show_orientation_js(engine.phase) elif engine.phase < _last_observed_phase: _last_observed_phase = engine.phase except Exception: pass debug_tab_li = document.getElementById("debug-tab-li") if debug_tab_li: if getattr(engine, "debugger", False): debug_tab_li.style.display = "inline-block" # or "block" else: debug_tab_li.style.display = "none" # Show/Hide Help (?) Guide buttons based on settings show_help = engine.settings.get("show_help_icons", True) guide_buttons = document.querySelectorAll(".guide-btn") for i in range(len(guide_buttons)): btn = guide_buttons[i] btn.style.display = "inline-block" if show_help else "none" # Populate and toggle action button hover tooltips based on settings show_tooltips = engine.settings.get("show_tooltips", True) for btn_id, tt_text in resources.BUTTON_TOOLTIPS.items(): el = document.getElementById(btn_id) if el: if show_tooltips: el.setAttribute("title", tt_text) else: el.removeAttribute("title") # Also handle elements with data-tooltip-key attribute tt_elements = document.querySelectorAll("[data-tooltip-key]") for i in range(len(tt_elements)): el = tt_elements[i] key = el.getAttribute("data-tooltip-key") if key in resources.BUTTON_TOOLTIPS: if show_tooltips: el.setAttribute("title", resources.BUTTON_TOOLTIPS[key]) else: el.removeAttribute("title") # Update settings checkboxes if present toggle_help = document.getElementById("toggle-help-icons") if toggle_help: toggle_help.checked = show_help toggle_tt = document.getElementById("toggle-button-tooltips") if toggle_tt: toggle_tt.checked = show_tooltips """Update every visual DOM element based on current engine state.""" # Time Calendar and Clock Display month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] m_name = month_names[min(11, engine.game_month - 1)] document.getElementById("display-game-time").innerText = f"Yr {engine.game_year}, {m_name} (Q{engine.game_quarter}) - Day {engine.game_day} ({engine.game_hour:02d}:{engine.game_minute:02d})" # Show/Hide sidebar stress dump container stress_dump_el = document.getElementById("sidebar-stress-dump-container") if stress_dump_el: if engine.get_progression_tier() >= 2: stress_dump_el.style.display = "block" btn_dump = document.getElementById("btn-kp-stress-dump") if btn_dump: if engine.solved_tickets_inventory >= 5 and engine.knowledge_points >= 10.0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_dump.removeAttribute("disabled") else: btn_dump.setAttribute("disabled", "true") else: stress_dump_el.style.display = "none" # Peak Status Badge hour = engine.game_hour day = engine.game_day quarter = engine.game_quarter badge_el = document.getElementById("display-peak-badge") if quarter == 4 and day >= 28: badge_el.innerText = "Holiday Shutdown πŸ–οΈ" badge_el.className = "badge bg-success" elif hour >= 22 or hour < 6: badge_el.innerText = "Overnight Standby πŸ’€" badge_el.className = "badge bg-secondary" elif hour >= 6 and hour < 9: badge_el.innerText = "Early Morning πŸŒ…" badge_el.className = "badge bg-info text-dark" elif hour >= 9 and hour < 12: badge_el.innerText = "Morning Peak πŸ“ˆ" badge_el.className = "badge bg-danger animate__animated animate__pulse animate__infinite" elif hour >= 12 and hour < 14: badge_el.innerText = "Lunch Lull πŸ“‰" badge_el.className = "badge bg-warning text-dark" elif hour >= 14 and hour < 17: badge_el.innerText = "Afternoon Peak πŸ“ˆ" badge_el.className = "badge bg-danger animate__animated animate__pulse animate__infinite" else: badge_el.innerText = "Evening Standby πŸŒ†" badge_el.className = "badge bg-info text-dark" # Resources HUD (Top and Sidebar) document.getElementById("display-cash").innerText = f"${engine.cash:,.2f}" solved_count = engine.global_tickets_resolved_total if engine.phase >= 4 else engine.tickets_solved document.getElementById("display-solved").innerText = f"{solved_count:,}" # CSI display updates csi_val = getattr(engine, "csi", 100.0) document.getElementById("display-csi").innerText = f"{csi_val:.1f}" csi_emoji_el = document.getElementById("display-csi-emoji") if csi_val >= 80.0: csi_emoji_el.innerText = "🟒 πŸ˜„" document.getElementById("display-csi").className = "fs-4 fw-bold text-success" elif csi_val >= 50.0: csi_emoji_el.innerText = "🟑 😐" document.getElementById("display-csi").className = "fs-4 fw-bold text-warning" else: csi_emoji_el.innerText = "πŸ”΄ 😠" document.getElementById("display-csi").className = "fs-4 fw-bold text-danger animate__animated animate__flash animate__infinite" document.getElementById("display-sidebar-cash").innerText = f"${engine.cash:,.2f}" document.getElementById("display-sidebar-solved").innerText = f"{solved_count:,}" # Mobile Header Minified Stats Strip m_cash = document.getElementById("mobile-disp-cash") if m_cash: m_cash.innerText = f"${engine.cash:,.2f}" m_solved = document.getElementById("mobile-disp-solved") if m_solved: m_solved.innerText = f"{solved_count:,}" m_csi = document.getElementById("mobile-disp-csi") if m_csi: m_csi.innerText = f"{csi_val:.0f}%" if csi_val >= 80.0: m_csi.className = "text-success font-monospace fw-bold" elif csi_val >= 50.0: m_csi.className = "text-warning font-monospace fw-bold" else: m_csi.className = "text-danger font-monospace fw-bold" m_queue = document.getElementById("mobile-disp-queue") if m_queue: if engine.phase >= 4: in_flight = engine.global_tickets_in_flight max_q = max(1000, engine.facilities.get("call_centers", 0) * 500) m_queue.innerText = f"{in_flight:,} / {max_q:,}" else: m_queue.innerText = f"{int(engine.tickets_queue)} / {engine.get_max_tickets_queue()}" m_kp = document.getElementById("mobile-disp-kp") if m_kp: if engine.phase >= 4: m_kp.innerText = f"{engine.research_points:,.0f} RP" else: m_kp.innerText = f"{engine.knowledge_points:,.0f}" # Mobile Header CPU Temp Sync m_temp_box = document.getElementById("mobile-stat-temp-box") m_temp = document.getElementById("mobile-disp-temp") if m_temp_box and m_temp: if engine.phase in [1, 2] and (engine.upgrades.get("overclock_cpu", 0) > 0 or engine.cpu_temp > 35.0): m_temp_box.style.display = "block" m_temp.innerText = f"{engine.cpu_temp:.1f}Β°C" if engine.cpu_temp >= 55.0: m_temp.className = "text-danger font-monospace fw-bold animate__animated animate__flash animate__infinite" elif engine.cpu_temp >= 45.0: m_temp.className = "text-warning font-monospace fw-bold" else: m_temp.className = "text-info font-monospace fw-bold" else: m_temp_box.style.display = "none" # Intake Rate and Stress Rate displays in Sidebar time_mult = resources.get_time_of_day_multiplier(engine.game_hour) quarter_mult = resources.get_quarter_multiplier(engine.game_quarter, engine.game_day) if engine.phase >= 4: corp_metrics = engine.get_corporate_health_metrics() intake_rate = corp_metrics["tickets_inflow"] document.getElementById("display-intake-rate").innerText = f"Rate: {intake_rate:.1f}/s" elif engine.phase == 3: client_rate = 0.0 client_rate += engine.contracts_bronze * resources.CONTRACT_SPECS["bronze"]["tickets"] client_rate += engine.contracts_silver * resources.CONTRACT_SPECS["silver"]["tickets"] client_rate += engine.contracts_gold * resources.CONTRACT_SPECS["gold"]["tickets"] intake_rate = client_rate * time_mult * quarter_mult document.getElementById("display-intake-rate").innerText = f"Rate: {intake_rate:.2f}/s" else: macro_tix = resources.get_macro_phase_multiplier(engine.phase, "ticket_mult") intake_rate = resources.get_passive_ticket_rate(engine.upgrades.get("call_agents", 0), engine.upgrades.get("auto_responder", 0)) * time_mult * quarter_mult * macro_tix document.getElementById("display-intake-rate").innerText = f"Rate: {intake_rate:.2f}/s" stress_rate = resources.get_passive_stress_rate(engine.upgrades["ergonomic_chair"], engine.xp, engine.kb_articles, engine.game_quarter) document.getElementById("display-stress-rate").innerText = f"Rate: +{stress_rate:.2f}/s" # Dynamic header displays container_sla = document.getElementById("container-sla") container_server = document.getElementById("container-server") card_promotion = document.getElementById("card-promotion") card_promotion_msp = document.getElementById("card-promotion-msp") card_prestige = document.getElementById("card-prestige") container_xp = document.getElementById("container-xp") # XP Prestige Indicator (Visible in Phase 2+ or whenever XP has been earned) if getattr(engine, "xp", 0) > 0 or engine.phase >= 2: container_xp.style.display = "block" document.getElementById("display-xp").innerText = f"{getattr(engine, 'xp', 0):,} XP" else: container_xp.style.display = "none" # Phase Specific Toggles # ========================================================================= # PHASE SPECIFIC TOGGLES & HUD # ========================================================================= # ------------------------------------------------------------------------- # PHASE 1: Lowly Helpdesk Tech # ------------------------------------------------------------------------- if engine.phase == 1: container_sla.style.display = "none" container_server.style.display = "none" card_prestige.style.display = "none" card_promotion.style.display = "block" card_promotion_msp.style.display = "none" card_p_ent = document.getElementById("card-promotion-enterprise") if card_p_ent: card_p_ent.style.display = "none" hud_p3 = document.getElementById("sidebar-phase3-hud") if hud_p3: hud_p3.style.display = "none" msp_sec = document.getElementById("msp-contracts-section") if msp_sec: msp_sec.style.display = "none" # Dynamic Requirements from resources.py via engine.check_promotion_eligibility eval_p1 = engine.check_promotion_eligibility(1) req_t_el = document.getElementById("req-tickets") req_c_el = document.getElementById("req-cash") req_s_el = document.getElementById("req-specialization") btn_prom = document.getElementById("btn-promote") if req_t_el and "tickets" in eval_p1["checks"]: chk = eval_p1["checks"]["tickets"] req_t_el.className = "mb-1 text-success fw-bold text-center" if chk["met"] else "mb-1 text-muted text-center" req_t_el.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if req_c_el and "cash" in eval_p1["checks"]: chk = eval_p1["checks"]["cash"] req_c_el.className = "mb-1 text-success fw-bold text-center" if chk["met"] else "mb-1 text-muted text-center" req_c_el.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if req_s_el and "specialization" in eval_p1["checks"]: chk = eval_p1["checks"]["specialization"] req_s_el.className = "text-success fw-bold text-center" if chk["met"] else "text-muted text-center" req_s_el.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if btn_prom: fee = eval_p1["fee"] btn_prom.innerText = f"Submit Promotion Application (Costs ${fee:,.2f})" if eval_p1["eligible"]: btn_prom.removeAttribute("disabled") else: btn_prom.setAttribute("disabled", "true") # Career Fork Display Management fork_card = document.getElementById("card-specialization-fork") status_card = document.getElementById("card-specialization-status") if engine.career_track == "": if getattr(engine, "get_progression_tier", lambda: 1)() >= 5: if fork_card: fork_card.style.display = "block" else: if fork_card: fork_card.style.display = "none" if status_card: status_card.style.display = "none" else: if fork_card: fork_card.style.display = "none" if status_card: status_card.style.display = "block" track_names = {"dev": "Software Developer πŸ’»", "sysadmin": "Sysadmin πŸ›‘οΈ", "net": "Network Engineer πŸ“‘"} disp_spec = document.getElementById("display-selected-specialization") if disp_spec: disp_spec.innerText = track_names.get(engine.career_track, engine.career_track) # ------------------------------------------------------------------------- # PHASE 2: Helpdesk Manager # ------------------------------------------------------------------------- elif engine.phase == 2: container_sla.style.display = "block" container_server.style.display = "block" card_prestige.style.display = "block" card_promotion.style.display = "none" card_promotion_msp.style.display = "block" card_p_ent = document.getElementById("card-promotion-enterprise") if card_p_ent: card_p_ent.style.display = "none" hud_p3 = document.getElementById("sidebar-phase3-hud") if hud_p3: hud_p3.style.display = "none" msp_sec = document.getElementById("msp-contracts-section") if msp_sec: msp_sec.style.display = "none" fork_c = document.getElementById("card-specialization-fork") if fork_c: fork_c.style.display = "none" status_c = document.getElementById("card-specialization-status") if status_c: status_c.style.display = "none" # SLA text and color sla_pct = round(getattr(engine, "sla", 100.0), 1) sla_el = document.getElementById("display-sla") if sla_el: sla_el.innerText = f"{sla_pct}%" if sla_pct >= 90.0: sla_el.className = "fs-4 fw-bold text-success" elif sla_pct >= 70.0: sla_el.className = "fs-4 fw-bold text-warning" else: sla_el.className = "fs-4 fw-bold text-danger animate__animated animate__flash animate__infinite" # Server Load text disp_srv = document.getElementById("display-server-load") if disp_srv: disp_srv.innerText = f"{engine.get_server_usage()} / {engine.get_max_server_capacity()}U" # Prestige XP details xp_estimate = int(engine.cash // 500.0) disp_xp = document.getElementById("display-prestige-xp") if disp_xp: disp_xp.innerText = f"{xp_estimate:,} XP" btn_prest = document.getElementById("btn-prestige") if btn_prest: if engine.cash >= 5000.0: btn_prest.removeAttribute("disabled") else: btn_prest.setAttribute("disabled", "true") # Dynamic Promotion Requirements (Phase 2 -> 3) eval_p2 = engine.check_promotion_eligibility(2) req_msp_t = document.getElementById("req-msp-tickets") req_msp_c = document.getElementById("req-msp-cash") req_msp_techs = document.getElementById("req-msp-techs") btn_prom_msp = document.getElementById("btn-promote-msp") if req_msp_t and "tickets" in eval_p2["checks"]: chk = eval_p2["checks"]["tickets"] req_msp_t.className = "mb-1 text-success fw-bold text-center" if chk["met"] else "mb-1 text-muted text-center" req_msp_t.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if req_msp_c and "cash" in eval_p2["checks"]: chk = eval_p2["checks"]["cash"] req_msp_c.className = "mb-1 text-success fw-bold text-center" if chk["met"] else "mb-1 text-muted text-center" req_msp_c.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if req_msp_techs and "staff" in eval_p2["checks"]: chk = eval_p2["checks"]["staff"] req_msp_techs.className = "text-success fw-bold text-center" if chk["met"] else "text-muted text-center" req_msp_techs.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if btn_prom_msp: fee = eval_p2["fee"] btn_prom_msp.innerText = f"Incorporate Company (Costs ${fee:,.2f})" if eval_p2["eligible"]: btn_prom_msp.removeAttribute("disabled") else: btn_prom_msp.setAttribute("disabled", "true") # ------------------------------------------------------------------------- # PHASE 3: Regional MSP Owner # ------------------------------------------------------------------------- elif engine.phase == 3: container_sla.style.display = "block" container_server.style.display = "block" card_prestige.style.display = "block" card_promotion.style.display = "none" card_promotion_msp.style.display = "none" card_promotion_enterprise = document.getElementById("card-promotion-enterprise") if card_promotion_enterprise: card_promotion_enterprise.style.display = "block" hud_p3 = document.getElementById("sidebar-phase3-hud") if hud_p3: hud_p3.style.display = "block" msp_sec = document.getElementById("msp-contracts-section") if msp_sec: msp_sec.style.display = "block" fork_c = document.getElementById("card-specialization-fork") if fork_c: fork_c.style.display = "none" status_c = document.getElementById("card-specialization-status") if status_c: status_c.style.display = "none" # SLA text and color sla_pct = round(getattr(engine, "sla", 100.0), 1) sla_el = document.getElementById("display-sla") if sla_el: sla_el.innerText = f"{sla_pct}%" if sla_pct >= 90.0: sla_el.className = "fs-4 fw-bold text-success" elif sla_pct >= 70.0: sla_el.className = "fs-4 fw-bold text-warning" else: sla_el.className = "fs-4 fw-bold text-danger animate__animated animate__flash animate__infinite" # Server Load text disp_srv = document.getElementById("display-server-load") if disp_srv: disp_srv.innerText = f"{engine.get_server_usage()} / {engine.get_max_server_capacity()}U" # Prestige XP details xp_estimate = int(engine.cash // 500.0) disp_xp = document.getElementById("display-prestige-xp") if disp_xp: disp_xp.innerText = f"{xp_estimate:,} XP" btn_prest = document.getElementById("btn-prestige") if btn_prest: if engine.cash >= 5000.0: btn_prest.removeAttribute("disabled") else: btn_prest.setAttribute("disabled", "true") # Sidebar Business variables update disp_leads = document.getElementById("display-leads") if disp_leads: disp_leads.innerText = f"{engine.leads:.1f} / {engine.get_max_leads()}" disp_bw = document.getElementById("display-bandwidth") if disp_bw: disp_bw.innerText = f"{engine.get_bandwidth_used()} / {engine.get_max_bandwidth()} Mbps" # Client contract proposal stats cb = document.getElementById("count-contracts-bronze") if cb: cb.innerText = f"{getattr(engine, 'contracts_bronze', 0)} Bronze" cs = document.getElementById("count-contracts-silver") if cs: cs.innerText = f"{getattr(engine, 'contracts_silver', 0)} Silver" cg = document.getElementById("count-contracts-gold") if cg: cg.innerText = f"{getattr(engine, 'contracts_gold', 0)} Gold" # Pitch buttons enabled rules free_compute = engine.get_free_server_capacity() bandwidth_used = engine.get_bandwidth_used() bandwidth_max = engine.get_max_bandwidth() btn_pitch_b = document.getElementById("btn-pitch-bronze") if btn_pitch_b: if engine.leads >= 1.0 and bandwidth_used + 2 <= bandwidth_max and free_compute >= 1: btn_pitch_b.removeAttribute("disabled") else: btn_pitch_b.setAttribute("disabled", "true") btn_pitch_s = document.getElementById("btn-pitch-silver") if btn_pitch_s: if engine.leads >= 3.0 and bandwidth_used + 15 <= bandwidth_max and free_compute >= 4: btn_pitch_s.removeAttribute("disabled") else: btn_pitch_s.setAttribute("disabled", "true") btn_pitch_g = document.getElementById("btn-pitch-gold") if btn_pitch_g: if engine.leads >= 8.0 and bandwidth_used + 80 <= bandwidth_max and free_compute >= 15: btn_pitch_g.removeAttribute("disabled") else: btn_pitch_g.setAttribute("disabled", "true") # Cancel buttons btn_c_b = document.getElementById("btn-cancel-bronze") if btn_c_b: btn_c_b.disabled = (getattr(engine, "contracts_bronze", 0) == 0) btn_c_s = document.getElementById("btn-cancel-silver") if btn_c_s: btn_c_s.disabled = (getattr(engine, "contracts_silver", 0) == 0) btn_c_g = document.getElementById("btn-cancel-gold") if btn_c_g: btn_c_g.disabled = (getattr(engine, "contracts_gold", 0) == 0) # Dynamic Promotion to Phase 4 Requirements Check eval_p3 = engine.check_promotion_eligibility(3) req_ent_cash = document.getElementById("req-enterprise-cash") req_ent_contracts = document.getElementById("req-enterprise-contracts") req_ent_credit = document.getElementById("req-enterprise-credit") btn_promote_ent = document.getElementById("btn-promote-enterprise") if req_ent_cash and "cash" in eval_p3["checks"]: chk = eval_p3["checks"]["cash"] req_ent_cash.className = "mb-1 text-success fw-bold text-center" if chk["met"] else "mb-1 text-muted text-center" req_ent_cash.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if req_ent_contracts and "contracts" in eval_p3["checks"]: chk = eval_p3["checks"]["contracts"] req_ent_contracts.className = "mb-1 text-success fw-bold text-center" if chk["met"] else "mb-1 text-muted text-center" req_ent_contracts.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if req_ent_credit and "credit" in eval_p3["checks"]: chk = eval_p3["checks"]["credit"] req_ent_credit.className = "text-success fw-bold text-center" if chk["met"] else "text-muted text-center" req_ent_credit.innerHTML = f"{'βœ”οΈ' if chk['met'] else '❌'} {chk['label']}" if btn_promote_ent: fee = eval_p3["fee"] btn_promote_ent.innerText = f"Incorporate Global Enterprise (Costs ${fee:,.2f})" if eval_p3["eligible"]: btn_promote_ent.removeAttribute("disabled") else: btn_promote_ent.setAttribute("disabled", "true") # ------------------------------------------------------------------------- # PHASE 4: Global IT Conglomerate # ------------------------------------------------------------------------- else: container_sla.style.display = "block" container_server.style.display = "block" card_prestige.style.display = "block" card_promotion.style.display = "none" card_promotion_msp.style.display = "none" card_promotion_enterprise = document.getElementById("card-promotion-enterprise") if card_promotion_enterprise: card_promotion_enterprise.style.display = "none" hud_p3 = document.getElementById("sidebar-phase3-hud") if hud_p3: hud_p3.style.display = "block" msp_sec = document.getElementById("msp-contracts-section") if msp_sec: msp_sec.style.display = "block" fork_c = document.getElementById("card-specialization-fork") if fork_c: fork_c.style.display = "none" status_c = document.getElementById("card-specialization-status") if status_c: status_c.style.display = "none" # Unhide Enterprise Navigation tab tab_enterprise_li = document.getElementById("enterprise-tab-li") if tab_enterprise_li: tab_enterprise_li.style.display = "block" # SLA text and color sla_pct = round(getattr(engine, "sla", 100.0), 1) sla_el = document.getElementById("display-sla") if sla_el: sla_el.innerText = f"{sla_pct}%" if sla_pct >= 90.0: sla_el.className = "fs-4 fw-bold text-success" elif sla_pct >= 70.0: sla_el.className = "fs-4 fw-bold text-warning" else: sla_el.className = "fs-4 fw-bold text-danger animate__animated animate__flash animate__infinite" # Server Load text disp_srv = document.getElementById("display-server-load") if disp_srv: disp_srv.innerText = f"{engine.get_server_usage()} / {engine.get_max_server_capacity()}U" # Prestige XP details xp_estimate = int(engine.cash // 500.0) disp_xp = document.getElementById("display-prestige-xp") if disp_xp: disp_xp.innerText = f"{xp_estimate:,} XP" btn_prest = document.getElementById("btn-prestige") if btn_prest: if engine.cash >= 5000.0: btn_prest.removeAttribute("disabled") else: btn_prest.setAttribute("disabled", "true") # Sidebar Business variables update disp_leads = document.getElementById("display-leads") if disp_leads: disp_leads.innerText = f"{engine.leads:.1f} / {engine.get_max_leads()}" disp_bw = document.getElementById("display-bandwidth") if disp_bw: disp_bw.innerText = f"{engine.get_bandwidth_used()} / {engine.get_max_bandwidth()} Mbps" cb = document.getElementById("count-contracts-bronze") if cb: cb.innerText = f"{getattr(engine, 'contracts_bronze', 0)} Bronze" cs = document.getElementById("count-contracts-silver") if cs: cs.innerText = f"{getattr(engine, 'contracts_silver', 0)} Silver" cg = document.getElementById("count-contracts-gold") if cg: cg.innerText = f"{getattr(engine, 'contracts_gold', 0)} Gold" # END phase toggles # System Crash Pane Toggle crash_pane = document.getElementById("crash-pane") reboot_bar = document.getElementById("bar-reboot") reboot_time = resources.get_reboot_duration(engine.career_track) if engine.pc_crashed: crash_pane.style.display = "block" if engine.is_rebooting: reboot_pct = min(100.0, round((engine.reboot_progress / reboot_time) * 100.0, 1)) reboot_bar.style.width = f"{reboot_pct}%" reboot_bar.className = "progress-bar bg-danger progress-bar-striped progress-bar-animated" reboot_bar.innerText = f"Rebooting... {reboot_pct}%" document.getElementById("btn-reboot").setAttribute("disabled", "true") else: reboot_bar.style.width = "0%" reboot_bar.className = "progress-bar bg-secondary" reboot_bar.innerText = "Reboot Required" document.getElementById("btn-reboot").removeAttribute("disabled") else: crash_pane.style.display = "none" # Tactical Reboot button states btn_reboot_tactical = document.getElementById("btn-reboot-tactical") if engine.is_rebooting: btn_reboot_tactical.setAttribute("disabled", "true") reboot_pct = min(100.0, round((engine.reboot_progress / reboot_time) * 100.0, 1)) btn_reboot_tactical.innerText = f"πŸ”„ Rebooting... {reboot_pct}%" elif engine.pc_crashed or engine.cpu_temp <= 40.0: btn_reboot_tactical.setAttribute("disabled", "true") btn_reboot_tactical.innerText = "πŸ”„ Preventative Reboot" else: btn_reboot_tactical.removeAttribute("disabled") btn_reboot_tactical.innerText = "πŸ”„ Preventative Reboot (Cooldown)" # Status badges status_badge = document.getElementById("status-badge") status_badge_m = document.getElementById("status-badge-mobile") if engine.pc_crashed: msg = "🚨 SYSTEM OFFLINE! Reboot PC" m_msg = "🚨 CRASHED" cls = "badge bg-danger py-2 px-3 fw-bold animate__animated animate__flash animate__infinite" m_cls = "badge bg-danger py-1 px-2 fw-bold animate__animated animate__flash animate__infinite" elif engine.burnout: msg = "🚨 BURNT OUT! Drink Coffee to Recover" m_msg = "🚨 BURNOUT" cls = "badge bg-danger py-2 px-3 fw-bold animate__animated animate__flash animate__infinite" m_cls = "badge bg-danger py-1 px-2 fw-bold animate__animated animate__flash animate__infinite" else: role_title = "πŸ‘¨β€πŸ’» Active Support Tech" if engine.phase == 1 else ("πŸ‘” Helpdesk Manager" if engine.phase == 2 else ("πŸš€ Chief Technology Officer" if engine.phase == 3 else "🌐 Global Conglomerate CEO")) m_role = "Support Tech" if engine.phase == 1 else ("Manager" if engine.phase == 2 else ("CTO" if engine.phase == 3 else "CEO")) msg = role_title m_msg = m_role cls = "badge bg-success py-2 px-3 fw-bold" m_cls = "badge bg-success py-1 px-2 fw-bold" if status_badge: status_badge.className = cls status_badge.innerText = msg if status_badge_m: status_badge_m.className = m_cls status_badge_m.innerText = m_msg # Button Enables/Disables is_busy = engine.is_resolving_medium or engine.is_resolving_high or engine.is_writing_kb or engine.is_rebooting if engine.burnout or engine.pc_crashed or engine.is_rebooting: document.getElementById("btn-answer").setAttribute("disabled", "true") document.getElementById("btn-resolve-low").setAttribute("disabled", "true") document.getElementById("btn-resolve-medium").setAttribute("disabled", "true") document.getElementById("btn-resolve-high").setAttribute("disabled", "true") document.getElementById("btn-write-kb").setAttribute("disabled", "true") document.getElementById("btn-brew").setAttribute("disabled", "true") else: # Answer Call if engine.tickets_queue >= engine.get_max_tickets_queue(): document.getElementById("btn-answer").setAttribute("disabled", "true") else: document.getElementById("btn-answer").removeAttribute("disabled") # Resolve Low if engine.tickets_queue_low >= 1.0 and not is_busy: document.getElementById("btn-resolve-low").removeAttribute("disabled") else: document.getElementById("btn-resolve-low").setAttribute("disabled", "true") # Resolve Med if engine.tickets_queue_medium >= 1.0 and not is_busy: document.getElementById("btn-resolve-medium").removeAttribute("disabled") else: document.getElementById("btn-resolve-medium").setAttribute("disabled", "true") # Resolve High if engine.tickets_queue_high >= 1.0 and not is_busy: document.getElementById("btn-resolve-high").removeAttribute("disabled") else: document.getElementById("btn-resolve-high").setAttribute("disabled", "true") # Write KB Article (costs 5 solved tickets) if not is_busy and engine.solved_tickets_inventory >= 5: document.getElementById("btn-write-kb").removeAttribute("disabled") else: document.getElementById("btn-write-kb").setAttribute("disabled", "true") # Brew Coffee if engine.is_brewing or engine.coffee >= engine.get_max_coffee(): document.getElementById("btn-brew").setAttribute("disabled", "true") else: document.getElementById("btn-brew").removeAttribute("disabled") # Mobile Brew Button btn_brew_m = document.getElementById("btn-brew-mobile") if btn_brew_m: if engine.is_brewing or engine.coffee >= engine.get_max_coffee(): btn_brew_m.setAttribute("disabled", "true") else: btn_brew_m.removeAttribute("disabled") # Drink Coffee (available even in burnout/crash, as long as coffee exists and not on cooldown!) btn_drink = document.getElementById("btn-drink") lbl_drink = document.getElementById("label-drink-coffee") btn_drink_m = document.getElementById("btn-drink-mobile") lbl_drink_m = document.getElementById("label-drink-coffee-mobile") cd_drink = getattr(engine, "coffee_cooldown_timer", 0.0) if btn_drink: if cd_drink > 0.0: if lbl_drink: lbl_drink.innerText = f"🍡 Cooldown ({cd_drink:.1f}s)" btn_drink.setAttribute("disabled", "true") else: if lbl_drink: lbl_drink.innerText = "🍡 Drink Cup" if engine.coffee >= 1.0: btn_drink.removeAttribute("disabled") else: btn_drink.setAttribute("disabled", "true") if btn_drink_m: if cd_drink > 0.0: if lbl_drink_m: lbl_drink_m.innerText = f"🍡 ({cd_drink:.1f}s)" btn_drink_m.setAttribute("disabled", "true") else: if lbl_drink_m: lbl_drink_m.innerText = "🍡 Drink" if engine.coffee >= 1.0: btn_drink_m.removeAttribute("disabled") else: btn_drink_m.setAttribute("disabled", "true") # Severity Queue Counters & Dynamic Real-Time Reward Texts document.getElementById("queue-low-count").innerText = f"{int(engine.tickets_queue_low)} Tickets" document.getElementById("queue-medium-count").innerText = f"{int(engine.tickets_queue_medium)} Tickets" document.getElementById("queue-high-count").innerText = f"{int(engine.tickets_queue_high)} Tickets" val_low = resources.get_ticket_cash_value("low", engine.upgrades.get("dual_monitors", 0), engine.xp, engine.sla, engine.kb_articles, engine.upgrades.get("ticket_payout_boost", 0), engine.csi) val_med = resources.get_ticket_cash_value("medium", engine.upgrades.get("dual_monitors", 0), engine.xp, engine.sla, engine.kb_articles, engine.upgrades.get("ticket_payout_boost", 0), engine.csi) val_high = resources.get_ticket_cash_value("high", engine.upgrades.get("dual_monitors", 0), engine.xp, engine.sla, engine.kb_articles, engine.upgrades.get("ticket_payout_boost", 0), engine.csi) el_r_low = document.getElementById("reward-low-text") if el_r_low: el_r_low.innerText = f"Reward: ${val_low:.2f} (Base $5.00 + Multipliers)" el_r_med = document.getElementById("reward-medium-text") if el_r_med: el_r_med.innerText = f"Reward: ${val_med:.2f} (Base $15.00 + Multipliers)" el_r_high = document.getElementById("reward-high-text") if el_r_high: el_r_high.innerText = f"Reward: ${val_high:.2f} (Base $40.00 + Multipliers)" # Coffee Count document.getElementById("display-coffee").innerText = f"{int(engine.coffee)} / {engine.get_max_coffee()}" disp_coffee_m = document.getElementById("display-coffee-mobile") if disp_coffee_m: disp_coffee_m.innerText = f"{int(engine.coffee)} / {engine.get_max_coffee()}" # KB Counter document.getElementById("display-kb-count").innerText = f"{engine.kb_articles}" # Stress bar max_stress = engine.get_max_stress() if hasattr(engine, "get_max_stress") else 100.0 stress_pct = round(min(100.0, max(0.0, (engine.stress / max_stress) * 100.0)), 1) stress_bar = document.getElementById("bar-stress") stress_bar_m = document.getElementById("bar-stress-mobile") text_stress_m = document.getElementById("text-stress-pct-mobile") if text_stress_m: text_stress_m.innerText = f"{engine.stress:.0f} / {max_stress:.0f}" if stress_bar: stress_bar.style.width = f"{stress_pct}%" stress_bar.innerText = f"{engine.stress:.0f} / {max_stress:.0f} ({stress_pct}%)" if stress_bar_m: stress_bar_m.style.width = f"{stress_pct}%" if engine.burnout: stress_bar.className = "progress-bar bg-danger progress-bar-striped progress-bar-animated font-weight-bold" if stress_bar_m: stress_bar_m.className = "progress-bar bg-danger progress-bar-striped progress-bar-animated font-weight-bold" elif stress_pct >= 85: stress_bar.className = "progress-bar bg-danger progress-bar-striped" if stress_bar_m: stress_bar_m.className = "progress-bar bg-danger progress-bar-striped" elif stress_pct >= 60: stress_bar.className = "progress-bar bg-warning text-dark font-weight-bold" if stress_bar_m: stress_bar_m.className = "progress-bar bg-warning text-dark font-weight-bold" else: stress_bar.className = "progress-bar bg-info" if stress_bar_m: stress_bar_m.className = "progress-bar bg-info" disp_temp_m = document.getElementById("display-temp-mobile") if disp_temp_m: if engine.phase == 1: disp_temp_m.innerText = f"{engine.cpu_temp:.1f}Β°C" else: disp_temp_m.innerText = f"{engine.dc_temp:.1f}Β°C" # Top Navbar Thermal Telemetry Indicator (Phase 1 CPU vs Phase 2+ Network Closet) disp_temp_header = document.getElementById("display-cpu-temp-header") disp_temp_emoji = document.getElementById("display-cpu-temp-header-emoji") container_temp_header = document.getElementById("container-cpu-temp-header") label_temp_header = container_temp_header.querySelector(".text-warning") if container_temp_header else None if container_temp_header: container_temp_header.style.display = "block" if engine.phase <= 3 else "none" if engine.phase == 1: if label_temp_header: label_temp_header.innerText = "CPU HEAT" if container_temp_header: container_temp_header.title = "Workstation CPU Core Temperature (Max 90.0Β°C)" if disp_temp_header: temp_val = engine.cpu_temp disp_temp_header.innerText = f"{temp_val:.1f}Β°C" if temp_val >= 80.0 or engine.pc_crashed: if disp_temp_emoji: disp_temp_emoji.innerText = "πŸ”₯" disp_temp_header.className = "text-danger fw-bold animate__animated animate__pulse animate__infinite" elif temp_val >= 60.0: if disp_temp_emoji: disp_temp_emoji.innerText = "⚠️" disp_temp_header.className = "text-warning fw-bold" else: if disp_temp_emoji: disp_temp_emoji.innerText = "❄️" disp_temp_header.className = "text-info fw-bold" else: # Phase 2+ Network Closet Temperature if label_temp_header: label_temp_header.innerText = "CLOSET TEMP" if container_temp_header: container_temp_header.title = "Network Closet Environmental Temperature (Critical: 90.0Β°C)" if disp_temp_header: dc_val = engine.dc_temp disp_temp_header.innerText = f"{dc_val:.1f}Β°C" if not engine.dc_online or dc_val >= 70.0: if disp_temp_emoji: disp_temp_emoji.innerText = "πŸ”₯" disp_temp_header.className = "text-danger fw-bold animate__animated animate__pulse animate__infinite" elif dc_val >= 45.0: if disp_temp_emoji: disp_temp_emoji.innerText = "⚠️" disp_temp_header.className = "text-warning fw-bold" else: if disp_temp_emoji: disp_temp_emoji.innerText = "❄️" disp_temp_header.className = "text-info fw-bold" # Header Telemetry Filtering for Phase 2 (Hide XP, SLA, CSI, Credit in Phase 2) if engine.phase == 2: for chip_id in ["container-xp", "container-sla", "container-csi", "container-credit", "container-debt"]: el_chip = document.getElementById(chip_id) if el_chip: el_chip.style.display = "none" # Total Queue bar if engine.phase >= 4: in_flight = engine.global_tickets_in_flight max_q = max(1000, engine.facilities.get("call_centers", 0) * 500) q_pct = min(100.0, round((in_flight / max_q) * 100.0, 1)) q_bar = document.getElementById("bar-queue") q_bar.style.width = f"{q_pct}%" q_bar.innerText = f"{in_flight:,} / {max_q:,} In-Flight" if q_pct >= 90: q_bar.className = "progress-bar bg-danger text-white font-weight-bold animate__animated animate__headShake animate__infinite" elif q_pct >= 70: q_bar.className = "progress-bar bg-warning text-dark" else: q_bar.className = "progress-bar bg-info" else: max_q = engine.get_max_tickets_queue() q_pct = round((engine.tickets_queue / max_q) * 100.0, 1) q_bar = document.getElementById("bar-queue") q_bar.style.width = f"{q_pct}%" q_bar.innerText = f"{int(engine.tickets_queue)} / {max_q} Tickets" if q_pct >= 100: q_bar.className = "progress-bar bg-danger text-white font-weight-bold animate__animated animate__headShake animate__infinite" elif q_pct >= 80: q_bar.className = "progress-bar bg-warning text-dark" else: q_bar.className = "progress-bar bg-info" # Sidebar Thermal Widget Switching (Phase 1 CPU vs Phase 2+ Network Closet) sidebar_cpu_pane = document.getElementById("sidebar-cpu-pane") sidebar_closet_pane = document.getElementById("sidebar-closet-pane") if engine.phase == 1: if sidebar_cpu_pane: sidebar_cpu_pane.style.display = "block" if sidebar_closet_pane: sidebar_closet_pane.style.display = "none" # CPU Temp Bar temp_bar = document.getElementById("bar-cpu-temp") if temp_bar: temp_bar.style.width = f"{engine.cpu_temp}%" temp_bar.innerText = f"{engine.cpu_temp:.1f}Β°C" if engine.pc_crashed: temp_bar.className = "progress-bar bg-danger progress-bar-striped progress-bar-animated font-weight-bold" elif engine.cpu_temp >= 85.0: temp_bar.className = "progress-bar bg-danger progress-bar-striped animate__animated animate__flash animate__infinite" elif engine.cpu_temp >= 65.0: temp_bar.className = "progress-bar bg-warning text-dark font-weight-bold" else: temp_bar.className = "progress-bar bg-success" else: if sidebar_cpu_pane: sidebar_cpu_pane.style.display = "none" if sidebar_closet_pane: sidebar_closet_pane.style.display = "block" # Network Closet Temp Bar closet_bar = document.getElementById("bar-closet-temp") if closet_bar: pct = min(100.0, max(20.0, engine.dc_temp)) closet_bar.style.width = f"{pct}%" closet_bar.innerText = f"{engine.dc_temp:.1f}Β°C" if not engine.dc_online: closet_bar.className = "progress-bar bg-danger progress-bar-striped progress-bar-animated font-weight-bold" elif engine.dc_temp >= 70.0: closet_bar.className = "progress-bar bg-danger progress-bar-striped animate__animated animate__flash animate__infinite" elif engine.dc_temp >= 45.0: closet_bar.className = "progress-bar bg-warning text-dark font-weight-bold" else: closet_bar.className = "progress-bar bg-info text-dark font-weight-bold" disp_cooling = document.getElementById("display-closet-cooling-rate") if disp_cooling: disp_cooling.innerText = f"AC: -{0.30 * engine.upgrades.get('dc_ac', 0):.2f}Β°C/s" disp_gov_mode = document.getElementById("display-sidebar-governor-mode") curr_gov = getattr(engine, "rack_governor_mode", "normal") gov_lvl = engine.upgrades.get("rack_overclock_governor", 0) if disp_gov_mode: disp_gov_mode.innerText = f"{curr_gov.capitalize()} (1.0x)" if curr_gov == "normal" else (f"{curr_gov.capitalize()} (1.35x)" if curr_gov == "turbo" else f"{curr_gov.capitalize()} (1.75x)") disp_gov_mode.className = "badge bg-success font-monospace" if curr_gov == "normal" else ("badge bg-warning text-dark font-monospace" if curr_gov == "turbo" else "badge bg-danger font-monospace") for g_mode in ["normal", "turbo", "overdrive"]: for prefix in ["btn-gov-", "btn-sidebar-gov-"]: btn_g = document.getElementById(f"{prefix}{g_mode}") if btn_g: if g_mode == "turbo" and gov_lvl < 1: btn_g.setAttribute("disabled", "true") elif g_mode == "overdrive" and gov_lvl < 2: btn_g.setAttribute("disabled", "true") else: btn_g.removeAttribute("disabled") if curr_gov == g_mode: btn_g.className = f"btn btn-xs btn-{ 'success' if g_mode == 'normal' else ('warning text-dark' if g_mode == 'turbo' else 'danger') } py-1 px-1 font-monospace text-start fw-bold" else: btn_g.className = f"btn btn-xs btn-outline-{ 'success' if g_mode == 'normal' else ('warning' if g_mode == 'turbo' else 'danger') } py-1 px-1 font-monospace text-start fw-bold" btn_flush = document.getElementById("btn-dc-flush") if btn_flush: cd = engine.hr_cooldowns.get("dc_flush", 0.0) if cd > 0.0: btn_flush.innerText = f"❄️ Emergency DC Flush ({cd:.0f}s)" btn_flush.setAttribute("disabled", "true") elif engine.cash < 150.0: btn_flush.innerText = "❄️ Emergency DC Flush ($150.00)" btn_flush.setAttribute("disabled", "true") else: btn_flush.innerText = "❄️ Emergency DC Flush ($150.00)" btn_flush.removeAttribute("disabled") btn_gov_buy = document.getElementById("btn-dc-buy-rack_overclock_governor") if btn_gov_buy: gov_cost = engine.get_upgrade_cost_dict("rack_overclock_governor") if gov_lvl >= 3: btn_gov_buy.innerText = "MAX LEVEL (3/3)" btn_gov_buy.setAttribute("disabled", "true") else: c_cash = gov_cost.get("cash", 0.0) c_kp = gov_cost.get("kp", 0.0) btn_gov_buy.innerText = f"Unlock ({format_currency_compact(c_cash)} + {int(c_kp)} KP)" if engine.cash >= c_cash and engine.knowledge_points >= c_kp: btn_gov_buy.removeAttribute("disabled") else: btn_gov_buy.setAttribute("disabled", "true") dc_shut = document.getElementById("sidebar-dc-shutdown-pane") if dc_shut: dc_shut.style.display = "none" if engine.dc_online else "block" cooling_rate = resources.get_passive_cooling_rate(engine.upgrades["usb_fan"], engine.upgrades["liquid_cooler"], engine.career_track) document.getElementById("display-cooling-rate").innerText = f"Cooling: -{cooling_rate:.1f}Β°C/sec" # Upgrades Loop upgrades_list = [ "dual_monitors", "keurig", "auto_responder", "ergonomic_chair", "coffee_tier", "usb_fan", "liquid_cooler", "sop_database", "advanced_scripting", "ram_module", "file_cabinets", "wiki_page", "script_snippets", "call_agents", "overclock_cpu", "ai_documenter", "server_racks", "l1_techs", "l2_techs", "sysadmins", "fiber_pipeline", "blade_servers", "sales_agents", "procurement_specialists", "stress_ball_quality", "coffee_thermos", "sales_training", "auto_dispatcher_software", "dispatch_optimizer", "ticket_payout_boost", "stress_rate_reduction", "customer_service_training" ] tier = engine.get_progression_tier() completed_upgrades = [] for up_id in upgrades_list: lvl = engine.upgrades.get(up_id, 0) cost_dict = engine.get_upgrade_cost_dict(up_id) # Display level and cost lvl_el = document.getElementById(f"level-{up_id}") if lvl_el: if up_id == "coffee_tier": coffee_names = ["Instant Coffee", "French Press", "Espresso Maker", "Premium Roast", "Cafe Latte", "Premium Mocha Cappacino"] curr_name = coffee_names[min(5, lvl)] lvl_el.innerText = f"Tier {lvl + 1} ({curr_name})" else: lvl_el.innerText = f"Lvl {lvl}" # Dynamic live calculation descriptions from resources.py desc_el = document.getElementById(f"desc-{up_id}") if desc_el: desc_el.innerText = resources.get_upgrade_description(up_id, lvl) # Format composite cost label & check maxed is_maxed = False spec = resources.UPGRADES_REGISTRY.get(up_id, {}) max_l = spec.get("max_lvl") if up_id == "coffee_tier": max_l = 5 elif up_id in ["sop_database", "advanced_scripting"]: max_l = 1 if max_l is not None and lvl >= max_l: is_maxed = True completed_upgrades.append({ "id": up_id, "name": spec.get("name", up_id.replace("_", " ").title()), "level": lvl, "max_lvl": max_l, "desc": resources.get_upgrade_description(up_id, lvl) }) if is_maxed: cost_text = "MAX" time_text = "" else: parts = [] if "solved_tickets" in cost_dict: parts.append(f"{cost_dict['solved_tickets']} Tickets") if "kp" in cost_dict: parts.append(f"{int(cost_dict['kp'])} KP") if "cash" in cost_dict: parts.append(f"${cost_dict['cash']:,.2f}") cost_text = " + ".join(parts) if parts else "$0.00" # Estimated time to purchase calculation time_needed = 0.0 infinite = False if "cash" in cost_dict and engine.cash < cost_dict["cash"]: needed = cost_dict["cash"] - engine.cash if cash_rate > 0.0: time_needed = max(time_needed, needed / cash_rate) else: infinite = True if "solved_tickets" in cost_dict and engine.solved_tickets_inventory < cost_dict["solved_tickets"]: needed = cost_dict["solved_tickets"] - engine.solved_tickets_inventory if solved_tickets_rate > 0.0: time_needed = max(time_needed, needed / solved_tickets_rate) else: infinite = True if "kp" in cost_dict and engine.knowledge_points < cost_dict["kp"]: needed = cost_dict["kp"] - engine.knowledge_points if kp_rate > 0.0: time_needed = max(time_needed, needed / kp_rate) else: infinite = True if engine.cash >= cost_dict.get("cash", 0.0) and engine.solved_tickets_inventory >= cost_dict.get("solved_tickets", 0) and engine.knowledge_points >= cost_dict.get("kp", 0.0): time_text = "" elif infinite: time_text = " (Infinite)" else: if time_needed < 60.0: time_text = f" ({time_needed:.0f}s)" elif time_needed < 3600.0: mins = time_needed // 60 secs = time_needed % 60 time_text = f" ({mins:.0f}m {secs:.0f}s)" else: hrs = time_needed // 3600 mins = (time_needed % 3600) // 60 time_text = f" ({hrs:.0f}h {mins:.0f}m)" cost_el = document.getElementById(f"cost-{up_id}") if cost_el: cost_el.innerText = cost_text time_span = document.getElementById(f"time-{up_id}") if time_span: time_span.innerText = time_text # Show/Hide card container based on evolutionary tier and phase gating card = document.getElementById(f"card-{up_id}") if card: visible = False # Maxed upgrades are moved down to Completed Upgrades if is_maxed: visible = False elif up_id in ["server_racks", "blade_servers", "l1_techs", "l2_techs", "sysadmins"] and engine.phase == 1: visible = False elif engine.phase == 1: if up_id in ["dual_monitors", "keurig", "ergonomic_chair", "coffee_tier", "usb_fan"]: visible = True elif up_id in ["ram_module", "liquid_cooler", "file_cabinets", "wiki_page", "script_snippets", "stress_ball_quality", "coffee_thermos", "ticket_payout_boost", "stress_rate_reduction", "customer_service_training"]: visible = (tier >= 2) elif up_id in ["call_agents", "ai_documenter", "overclock_cpu"]: visible = (tier >= 3) elif up_id in ["auto_responder", "sop_database", "advanced_scripting"]: visible = (tier >= 4) elif engine.phase >= 2: # Phase 2+ retires dual_monitors and file_cabinets from active grid if up_id in ["dual_monitors", "file_cabinets"]: visible = False elif up_id in [ "keurig", "auto_responder", "ergonomic_chair", "coffee_tier", "usb_fan", "liquid_cooler", "sop_database", "advanced_scripting", "ram_module", "wiki_page", "script_snippets", "call_agents", "overclock_cpu", "ai_documenter", "stress_ball_quality", "coffee_thermos", "server_racks", "l1_techs", "l2_techs", "sysadmins", "auto_dispatcher_software", "dispatch_optimizer", "stress_cruise_control", "ticket_payout_boost", "stress_rate_reduction", "customer_service_training" ]: visible = True elif up_id in ["fiber_pipeline", "blade_servers", "sales_agents", "procurement_specialists", "sales_training"] and engine.phase >= 3: visible = True card.style.display = "block" if visible else "none" # Button enable check btn = document.getElementById(f"btn-buy-{up_id}") if btn: has_cash = engine.cash >= cost_dict.get("cash", 0.0) has_tickets = engine.solved_tickets_inventory >= cost_dict.get("solved_tickets", 0) has_kp = engine.knowledge_points >= cost_dict.get("kp", 0.0) has_funds = has_cash and has_tickets and has_kp has_capacity = True if up_id in resources.STAFF_SERVER_USAGE: needed_u = resources.STAFF_SERVER_USAGE[up_id] has_capacity = engine.get_free_server_capacity() >= needed_u if has_funds and has_capacity and not is_maxed and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn.removeAttribute("disabled") else: btn.setAttribute("disabled", "true") # Render Completed Upgrades Accordion comp_list_el = document.getElementById("completed-upgrades-list") comp_count_el = document.getElementById("completed-upgrades-count") comp_acc_el = document.getElementById("accordion-item-completed-upgrades") if comp_list_el and comp_count_el: comp_count_el.innerText = str(len(completed_upgrades)) if comp_acc_el: comp_acc_el.style.display = "block" if completed_upgrades else "none" items_html = [] for cu in completed_upgrades: items_html.append( f'
    ' f'
    ' f'{cu["name"]}' f'βœ… Maxed (Lvl {cu["level"]}/{cu["max_lvl"]})' f'
    ' f'
    {cu["desc"]}
    ' f'
    ' ) comp_list_el.innerHTML = "".join(items_html) if items_html else '
    No completed upgrades yet.
    ' # Brewing Coffee Progress brew_bar = document.getElementById("bar-brew") brew_bar_m = document.getElementById("bar-brew-mobile") if engine.is_brewing: tier_spec = resources.get_coffee_tier_spec(engine.upgrades.get("coffee_tier", 0) + 1) brew_time = tier_spec.get("brew_time", 5.0) brew_pct = round((engine.brew_progress / max(0.1, brew_time)) * 100.0, 1) if brew_bar: brew_bar.style.width = f"{brew_pct}%" brew_bar.className = "progress-bar bg-success progress-bar-striped progress-bar-animated" brew_bar.innerText = f"Brewing... {brew_pct}%" if brew_bar_m: brew_bar_m.style.width = f"{brew_pct}%" else: if brew_bar: brew_bar.style.width = "0%" brew_bar.innerText = "Idle" brew_bar.className = "progress-bar bg-secondary" if brew_bar_m: brew_bar_m.style.width = "0%" # Auto-Responder progress bar and telemetry labels ar_bar = document.getElementById("bar-responder") ar_status_lbl = document.getElementById("label-responder-status") ar_target_lbl = document.getElementById("label-responder-target") ar_rate_lbl = document.getElementById("label-responder-rate") ar_lvl = engine.upgrades.get("auto_responder", 0) if ar_lvl > 0: if ar_bar: if engine.phase == 1: ar_pct = round(engine.auto_responder_progress * 100.0, 1) ar_bar.style.width = f"{ar_pct}%" ar_bar.className = "progress-bar bg-primary progress-bar-striped progress-bar-animated" ar_bar.innerText = f"Processing... {ar_pct}%" else: ar_bar.style.width = "100%" ar_bar.className = "progress-bar bg-success progress-bar-striped progress-bar-animated" ar_bar.innerText = f"Passive Revenue: +${0.50 * ar_lvl:.2f}/s" if ar_status_lbl: ar_status_lbl.className = "badge bg-success font-monospace" ar_status_lbl.innerText = f"Active (Lvl {ar_lvl})" if ar_target_lbl: if engine.upgrades.get("advanced_scripting", 0) > 0: ar_target_lbl.className = "text-warning fw-bold" ar_target_lbl.innerText = "Low, Medium & High Tickets" elif engine.upgrades.get("sop_database", 0) > 0: ar_target_lbl.className = "text-info fw-bold" ar_target_lbl.innerText = "Low & Medium Tickets" else: ar_target_lbl.className = "text-info fw-bold" ar_target_lbl.innerText = "Low Tickets Only" if ar_rate_lbl: rate_val = engine.get_auto_responder_rate() ar_rate_lbl.innerText = f"{rate_val:.2f} tix/sec" else: if ar_bar: ar_bar.style.width = "0%" ar_bar.innerText = "Not Installed" ar_bar.className = "progress-bar bg-secondary" if ar_status_lbl: ar_status_lbl.className = "badge bg-secondary font-monospace" ar_status_lbl.innerText = "Not Installed" if ar_target_lbl: ar_target_lbl.className = "text-muted" ar_target_lbl.innerText = "Low Tickets Only" if ar_rate_lbl: ar_rate_lbl.innerText = "0.0 tix/sec" # Phase 2 Coffee sidebar and HR Operations Consolidation sidebar_brew_sec = document.getElementById("sidebar-brew-section") card_coffee_station = document.getElementById("card-coffee-station") card_hr_station = document.getElementById("card-hr-station") panel_console_col = document.getElementById("panel-console-col") panel_breakroom_col = document.getElementById("panel-breakroom-col") if engine.phase >= 2: sidebar_brew_sec.style.display = "block" card_coffee_station.style.display = "none" card_hr_station.style.display = "block" panel_breakroom_col.style.display = "none" panel_console_col.className = "col-lg-12 col-12" # Update sidebar brew button states sidebar_brew_btn = document.getElementById("btn-sidebar-brew") sidebar_brew_bar = document.getElementById("bar-sidebar-brew") sidebar_brew_status = document.getElementById("sidebar-brew-status") if engine.is_brewing: tier_spec = resources.get_coffee_tier_spec(engine.upgrades.get("coffee_tier", 0) + 1) brew_time = tier_spec.get("brew_time", 5.0) brew_pct = round((engine.brew_progress / max(0.1, brew_time)) * 100.0, 1) sidebar_brew_bar.style.width = f"{brew_pct}%" sidebar_brew_bar.className = "progress-bar bg-info progress-bar-striped progress-bar-animated" sidebar_brew_status.innerText = f"Brewing... {brew_pct}%" sidebar_brew_btn.setAttribute("disabled", "true") else: sidebar_brew_bar.style.width = "0%" sidebar_brew_bar.className = "progress-bar bg-secondary" sidebar_brew_status.innerText = "Idle" # Enable brew button if not max coffee and not pc crashed/burnout if engine.coffee >= engine.get_max_coffee() or engine.burnout or engine.pc_crashed or engine.is_rebooting: sidebar_brew_btn.setAttribute("disabled", "true") else: sidebar_brew_btn.removeAttribute("disabled") # Update HR Console active effects and cooldowns # 1. Active HR Boosts Badges lunch_badge = document.getElementById("badge-lunch-boost") if lunch_badge: lunch_time = engine.hr_active_effects.get("lunch_boost", 0.0) if lunch_time > 0.0: lunch_badge.innerText = f"🍱 Catered Lunch: Active ({lunch_time:.1f}s)" lunch_badge.className = "badge bg-success p-2 text-white" else: lunch_badge.innerText = "🍱 Catered Lunch: Inactive" lunch_badge.className = "badge bg-secondary p-2" review_badge = document.getElementById("badge-review-boost") if review_badge: review_time = engine.hr_active_effects.get("review_boost", 0.0) if review_time > 0.0: review_badge.innerText = f"πŸ“Š Perf Review: Active ({review_time:.1f}s)" review_badge.className = "badge bg-success p-2 text-white" else: review_badge.innerText = "πŸ“Š Perf Review: Inactive" review_badge.className = "badge bg-secondary p-2" seminar_badge = document.getElementById("badge-seminar-boost") if seminar_badge: seminar_time = engine.hr_durations.get("stress_seminar", 0.0) if seminar_time > 0.0: seminar_badge.innerText = f"🧘 Stress Seminar: Active ({seminar_time:.1f}s)" seminar_badge.className = "badge bg-success p-2 text-white" else: seminar_badge.innerText = "🧘 Stress Seminar: Inactive" seminar_badge.className = "badge bg-secondary p-2" vent_badge = document.getElementById("badge-vent-boost") if vent_badge: vent_time = engine.hr_durations.get("ac_window", 0.0) if vent_time > 0.0: vent_badge.innerText = f"❄️ AC Vent: Active ({vent_time:.1f}s)" vent_badge.className = "badge bg-success p-2 text-white" else: vent_badge.innerText = "❄️ AC Vent: Inactive" vent_badge.className = "badge bg-secondary p-2" # 2. Active HR Actions Button states & Cooldown text all_abilities = ["team_building", "catered_lunch", "performance_review", "stress_seminar", "ac_window"] for ability in all_abilities: lvl = max(1, engine.hr_ability_levels.get(ability, 1)) lbl_lvl = document.getElementById(f"level-{ability}") if lbl_lvl: lbl_lvl.innerText = f"Lvl {lvl}" lbl_lvl.style.display = "inline" if lvl > 0 else "none" btn_id = "btn-team-building" if ability == "team_building" else ("btn-catered-lunch" if ability == "catered_lunch" else ("btn-perf-review" if ability == "performance_review" else f"btn-{ability}")) btn_el = document.getElementById(btn_id) lock_el = document.getElementById(f"lock-{ability}") if lock_el: lock_el.style.display = "none" if lvl > 0 else "inline" if btn_el: btn_el.style.display = "inline" if lvl > 0 else "none" cd_id = f"cooldown-{ability.replace('_', '-')}" cd_el = document.getElementById(cd_id) if cd_el: cd_el.style.display = "inline" if lvl > 0 else "none" cd_val = engine.hr_cooldowns.get(ability, 0.0) if cd_val > 0.0: cd_el.innerText = f"CD: {cd_val:.1f}s" else: cd_el.innerText = "Ready" desc_el = document.getElementById(f"desc-{ability}") if desc_el: specs = resources.get_hr_ability_specs(ability, max(1, lvl)) if ability == "team_building": desc_el.innerText = f"Costs ${specs.get('cash', 100.0):.2f}. Instantly relieves {specs.get('stress_relief', 30.0):.0f} stress. Cooldown {specs.get('cooldown', 60.0):.0f}s." elif ability == "catered_lunch": desc_el.innerText = f"Costs ${specs.get('cash', 200.0):.2f}. Boosts tech speed by +{specs.get('speed_bonus', 0.3)*100:.0f}% for {specs.get('duration', 30.0):.0f}s. Cooldown {specs.get('cooldown', 90.0):.0f}s." elif ability == "performance_review": desc_el.innerText = f"Costs ${specs.get('cash', 150.0):.2f}. Boosts tech speed by +{specs.get('speed_bonus', 0.3)*100:.0f}% for {specs.get('duration', 30.0):.0f}s. Cooldown {specs.get('cooldown', 60.0):.0f}s." elif ability == "stress_seminar": desc_el.innerText = f"Costs ${specs.get('cash', 300.0):.0f} + {specs.get('kp', 40.0):.0f} KP. Passive stress relief of {abs(specs.get('stress_rate', -1.0)):.1f}/s for {specs.get('duration', 60.0):.0f}s. Cooldown {specs.get('cooldown', 180.0):.0f}s." elif ability == "ac_window": desc_el.innerText = f"Costs ${specs.get('cash', 400.0):.0f} + {specs.get('kp', 60.0):.0f} KP. Extra cooling of -{specs.get('cooling_rate', 1.5):.1f}Β°C/s to CPU & DC for {specs.get('duration', 45.0):.0f}s. Cooldown {specs.get('cooldown', 240.0):.0f}s." if btn_el and lvl > 0: specs = resources.get_hr_ability_specs(ability, lvl) cash_cost = specs.get("cash", 0.0) kp_cost = specs.get("kp", 0.0) cd_val = engine.hr_cooldowns.get(ability, 0.0) if cd_val > 0.0: btn_el.setAttribute("disabled", "true") else: if engine.cash >= cash_cost and engine.knowledge_points >= kp_cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_el.removeAttribute("disabled") else: btn_el.setAttribute("disabled", "true") up_btn = document.getElementById(f"btn-upgrade-{ability}") if up_btn: cost_kp = resources.get_hr_ability_upgrade_cost(ability, lvl) if cost_kp >= 999999: up_btn.innerText = "MAX" up_btn.setAttribute("disabled", "true") else: btn_txt = f"Unlock ({cost_kp} KP)" if lvl == 0 else f"Upgrade ({cost_kp} KP)" up_btn.innerText = btn_txt if engine.knowledge_points >= cost_kp and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: up_btn.removeAttribute("disabled") else: up_btn.setAttribute("disabled", "true") # 3. HR Facility Upgrades Level and Cost mapping (in Upgrades tab) for up in ["water_cooler", "snack_fridge", "office_plants", "hr_coordinator"]: lvl = engine.hr_upgrades.get(up, 0) cost = resources.get_hr_upgrade_cost(up, lvl) # Card visibility in Upgrades tab c_el = document.getElementById(f"card-hr_{up}") if c_el: c_el.style.display = "block" if engine.phase >= 2 else "none" el_lvl = document.getElementById(f"level-hr_{up}") or document.getElementById(f"level-hr-{up}") if el_lvl: el_lvl.innerText = f"Lvl {lvl}" el_cost = document.getElementById(f"cost-hr_{up}") if el_cost: el_cost.innerText = f"${cost:.2f}" if lvl < 3 else "MAX" up_btn = document.getElementById(f"btn-buy-hr_{up}") or document.getElementById(f"btn-buy-hr-{up}") if up_btn: if lvl >= 3: up_btn.innerText = "MAX" up_btn.setAttribute("disabled", "true") else: up_btn.innerText = f"Purchase (${cost:.0f})" if engine.cash >= cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: up_btn.removeAttribute("disabled") else: up_btn.setAttribute("disabled", "true") # Stress Cruise Control UI in HR Station scc_lvl = engine.upgrades.get("stress_cruise_control", 0) scc_status_badge = document.getElementById("badge-stress-cruise-status") scc_desc = document.getElementById("desc-stress-cruise-control") scc_cost = document.getElementById("cost-stress_cruise_control") scc_btn = document.getElementById("btn-buy-stress_cruise_control") scc_switch_container = document.getElementById("container-switch-stress-cruise") scc_switch = document.getElementById("switch-stress-cruise-control") scc_drain_lbl = document.getElementById("lbl-stress-cruise-drain") if scc_lvl > 0: if scc_status_badge: if getattr(engine, "stress_cruise_control_active", False): scc_status_badge.className = "badge bg-success font-monospace" scc_status_badge.innerText = f"ACTIVE (Lvl {scc_lvl})" else: scc_status_badge.className = "badge bg-warning text-dark font-monospace" scc_status_badge.innerText = f"STANDBY (Lvl {scc_lvl})" if scc_switch_container: scc_switch_container.style.display = "block" if scc_switch: scc_switch.checked = getattr(engine, "stress_cruise_control_active", False) if scc_drain_lbl: cfg = resources.STRESS_CRUISE_CONTROL_CONFIG.get(scc_lvl, resources.STRESS_CRUISE_CONTROL_CONFIG[1]) scc_drain_lbl.innerText = f"-{cfg['stress_relief_per_sec']:.2f} stress/s" # Upgrade button state if scc_lvl >= 3: if scc_cost: scc_cost.innerText = "MAX TIER" if scc_btn: scc_btn.innerText = "MAX" scc_btn.setAttribute("disabled", "true") else: next_cost_dict = engine.get_upgrade_cost_dict("stress_cruise_control") next_cash = next_cost_dict.get("cash", 0.0) next_kp = next_cost_dict.get("kp", 0.0) if scc_cost: scc_cost.innerText = f"${next_cash:,.0f} + {next_kp:.0f} KP" if scc_btn: scc_btn.innerText = f"Upgrade (Lvl {scc_lvl + 1})" if engine.cash >= next_cash and engine.knowledge_points >= next_kp: scc_btn.removeAttribute("disabled") else: scc_btn.setAttribute("disabled", "true") else: if scc_status_badge: scc_status_badge.className = "badge bg-secondary font-monospace" scc_status_badge.innerText = "LOCKED" if scc_switch_container: scc_switch_container.style.display = "none" cost_dict = engine.get_upgrade_cost_dict("stress_cruise_control") c_cash = cost_dict.get("cash", 1200.0) c_kp = cost_dict.get("kp", 40.0) if scc_cost: scc_cost.innerText = f"${c_cash:,.0f} + {c_kp:.0f} KP" if scc_btn: scc_btn.innerText = "Unlock Concierge" if engine.cash >= c_cash and engine.knowledge_points >= c_kp: scc_btn.removeAttribute("disabled") else: scc_btn.setAttribute("disabled", "true") # 4. Phase 2+ Network Closet / Data Center pane render # Adapt text dynamically: Phase 2 = Network Closet, Phase 3+ = Data Center is_phase2_closet = (engine.phase == 2) el_dc_heading = document.getElementById("dc-main-heading") if el_dc_heading: el_dc_heading.innerText = "πŸ”Œ Network Closet Management" if is_phase2_closet else "πŸ–₯️ Data Center Management" el_dc_sub = document.getElementById("dc-sub-heading") if el_dc_sub: el_dc_sub.innerText = "Monitor patch panel ports, switch thermals, and network routing speeds." if is_phase2_closet else "Monitor server racks thermal levels, power backups, and core processing speeds." el_dc_therm_head = document.getElementById("dc-thermals-heading") if el_dc_therm_head: el_dc_therm_head.innerText = "Closet Thermals & Environment" if is_phase2_closet else "Thermals & Environment" el_dc_temp_lbl = document.getElementById("dc-temp-label") if el_dc_temp_lbl: el_dc_temp_lbl.innerText = "Network Closet Temp:" if is_phase2_closet else "Data Center Temp:" el_dc_heat_lbl = document.getElementById("dc-heat-label") if el_dc_heat_lbl: el_dc_heat_lbl.innerText = "Heat Gen (Switches & Panels):" if is_phase2_closet else "Heat Gen (Racks & Blades):" el_dc_cool_lbl = document.getElementById("dc-cooling-label") if el_dc_cool_lbl: el_dc_cool_lbl.innerText = "Closet Exhaust Cooling:" if is_phase2_closet else "AC Cooling Rate:" el_dc_over_head = document.getElementById("dc-overview-heading") if el_dc_over_head: el_dc_over_head.innerText = "Closet Overview" if is_phase2_closet else "Facility Overview" el_dc_racks_lbl = document.getElementById("dc-racks-label") if el_dc_racks_lbl: el_dc_racks_lbl.innerText = "Total Patch Panel Units:" if is_phase2_closet else "Total Server Racks:" el_dc_blades_lbl = document.getElementById("dc-blades-label") if el_dc_blades_lbl: el_dc_blades_lbl.innerText = "Total Gigabit Switches:" if is_phase2_closet else "Total Blade Servers:" el_dc_upg_head = document.getElementById("dc-upgrades-heading") if el_dc_upg_head: el_dc_upg_head.innerText = "Network Closet Upgrades" if is_phase2_closet else "Infrastructure Upgrades" document.getElementById("dc-racks-count").innerText = str(engine.upgrades.get("server_racks", 0)) document.getElementById("dc-blades-count").innerText = str(engine.upgrades.get("blade_servers", 0)) document.getElementById("dc-temp-text").innerText = f"{engine.dc_temp:.1f}Β°C" dc_bar = document.getElementById("dc-temp-bar") dc_pct = max(0, min(100, int((engine.dc_temp - 20) / 70.0 * 100.0))) dc_bar.style.width = f"{dc_pct}%" dc_bar.setAttribute("aria-valuenow", str(engine.dc_temp)) if engine.dc_temp >= 75.0: dc_bar.className = "progress-bar progress-bar-striped progress-bar-animated bg-danger" elif engine.dc_temp >= 50.0: dc_bar.className = "progress-bar progress-bar-striped progress-bar-animated bg-warning text-dark" else: dc_bar.className = "progress-bar progress-bar-striped progress-bar-animated bg-info" dc_heat_gen = 0.10 * engine.upgrades.get("server_racks", 0) + 0.25 * engine.upgrades.get("blade_servers", 0) document.getElementById("dc-heat-gen-text").innerText = f"+{dc_heat_gen:.2f}Β°C/s" dc_ac_cool = 0.30 * engine.upgrades.get("dc_ac", 0) if engine.hr_durations.get("ac_window", 0.0) > 0.0: specs = resources.get_hr_ability_specs("ac_window", engine.hr_ability_levels.get("ac_window", 0)) dc_ac_cool += specs.get("cooling_rate", 1.5) document.getElementById("dc-ac-cool-text").innerText = f"-{dc_ac_cool:.2f}Β°C/s" document.getElementById("dc-ac-cost-text").innerText = f"-${0.05 * engine.upgrades.get('dc_ac', 0):.2f}/s" status_badge = document.getElementById("dc-status-badge") if engine.dc_online: status_badge.innerHTML = '🟒 ONLINE' document.getElementById("dc-shutdown-alert").style.display = "none" else: status_badge.innerHTML = 'πŸ”΄ OFFLINE - OVERHEATED' document.getElementById("dc-shutdown-alert").style.display = "block" btn_flush = document.getElementById("btn-dc-flush") cd_flush = engine.hr_cooldowns.get("dc_flush", 0.0) flush_name = "❄️ Emergency Closet Purge" if is_phase2_closet else "❄️ Emergency DC Flush" if cd_flush > 0.0: btn_flush.innerText = f"❄️ Purge ({cd_flush:.0f}s)" if is_phase2_closet else f"❄️ Coolant Flush ({cd_flush:.0f}s)" btn_flush.setAttribute("disabled", "true") else: btn_flush.innerText = flush_name if engine.cash >= 150.0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_flush.removeAttribute("disabled") else: btn_flush.setAttribute("disabled", "true") # Dynamic Network Closet 2D Visual Blueprint Update racks_lvl = engine.upgrades.get("server_racks", 0) blades_lvl = engine.upgrades.get("blade_servers", 0) gov_lvl = engine.upgrades.get("rack_overclock_governor", 0) curr_gov = getattr(engine, "rack_governor_mode", "normal") el_racks_cnt = document.getElementById("dc-racks-count") el_blades_cnt = document.getElementById("dc-blades-count") if el_racks_cnt: el_racks_cnt.innerText = str(racks_lvl) if el_blades_cnt: el_blades_cnt.innerText = str(blades_lvl) el_bp_load = document.getElementById("blueprint-rack-load-badge") if el_bp_load: used_u = engine.get_server_usage() if hasattr(engine, "get_server_usage") else 0 max_u = engine.get_max_server_capacity() el_bp_load.innerText = f"LOAD: {used_u} / {max_u} U" el_bp_kp = document.getElementById("blueprint-kp-cap-label") if el_bp_kp: el_bp_kp.innerText = f"+{engine.get_max_kp()} KP" el_bp_chip = document.getElementById("dc-blueprint-status-chip") if el_bp_chip: if not engine.dc_online: el_bp_chip.className = "badge bg-danger font-monospace animate__animated animate__flash animate__infinite" el_bp_chip.innerText = "πŸ”΄ OVERHEAT TRIP" elif engine.dc_temp >= 45.0: el_bp_chip.className = "badge bg-warning text-dark font-monospace" el_bp_chip.innerText = "⚠️ HIGH THERMAL" else: el_bp_chip.className = "badge bg-success font-monospace" el_bp_chip.innerText = "❄️ AC NOMINAL" el_bp_ac = document.getElementById("blueprint-ac-cooling-badge") if el_bp_ac: el_bp_ac.innerText = f"-{0.30 * engine.upgrades.get('dc_ac', 0):.2f}Β°C/s Active" # Governor Buttons on Blueprint for g_mode in ["normal", "turbo", "overdrive"]: btn_g = document.getElementById(f"btn-gov-{g_mode}") if btn_g: if g_mode == "turbo" and gov_lvl < 1: btn_g.setAttribute("disabled", "true") btn_g.className = "btn btn-xs btn-outline-secondary py-1 px-1 font-monospace text-start" elif g_mode == "overdrive" and gov_lvl < 2: btn_g.setAttribute("disabled", "true") btn_g.className = "btn btn-xs btn-outline-secondary py-1 px-1 font-monospace text-start" else: btn_g.removeAttribute("disabled") if curr_gov == g_mode: btn_g.className = f"btn btn-xs btn-{ 'success' if g_mode == 'normal' else ('warning text-dark' if g_mode == 'turbo' else 'danger') } py-1 px-1 font-monospace text-start fw-bold" else: btn_g.className = f"btn btn-xs btn-outline-{ 'success' if g_mode == 'normal' else ('warning' if g_mode == 'turbo' else 'danger') } py-1 px-1 font-monospace text-start fw-bold" # Dynamic Blade Server Slots in 19" Rack Visual Blueprint blade_container = document.getElementById("blueprint-blade-slots-container") if blade_container: num_slots = max(1, min(6, blades_lvl)) slot_html = [] for s_idx in range(num_slots): is_active = (s_idx < blades_lvl) status_cls = "bg-success" if (is_active and engine.dc_online) else ("bg-secondary" if not is_active else "bg-danger animate__animated animate__flash") status_txt = "ONLINE" if (is_active and engine.dc_online) else ("EMPTY BAY" if not is_active else "TRIPPED") led_color = "bg-success animate__animated animate__pulse animate__infinite" if is_active and engine.dc_online else "bg-secondary" slot_html.append(f"""
    β€’ πŸ–₯️ BLADE UNIT #{s_idx+1:02d}
    {status_txt}
    """) blade_container.innerHTML = "".join(slot_html) for dc_up in ["server_racks", "blade_servers", "rack_overclock_governor", "dc_ac", "dc_ups", "dc_speeds"]: spec = resources.UPGRADES_REGISTRY.get(dc_up, {}) if not spec: continue lvl = engine.upgrades.get(dc_up, 0) cost_dict = engine.get_upgrade_cost_dict(dc_up) cost_cash = cost_dict.get("cash", 0.0) cost_kp = cost_dict.get("kp", 0.0) lbl_lvl = document.getElementById(f"lbl-dc-{dc_up}-level") lbl_cost = document.getElementById(f"lbl-dc-{dc_up}-cost") if lbl_lvl: lbl_lvl.innerText = str(lvl) if lbl_cost: if "max_lvl" in spec and lvl >= spec["max_lvl"]: lbl_cost.innerText = "MAX" else: if cost_kp > 0: lbl_cost.innerText = f"${cost_cash:,.2f} + {cost_kp:.0f} KP" else: lbl_cost.innerText = f"${cost_cash:,.2f}" btn_id = f"btn-dc-buy-{dc_up}" if dc_up in ["server_racks", "blade_servers", "rack_overclock_governor", "dc_ac", "dc_ups", "dc_speeds"] else f"btn-buy-{dc_up}" btn_el = document.getElementById(btn_id) if btn_el: is_maxed = "max_lvl" in spec and lvl >= spec["max_lvl"] has_funds = (engine.cash >= cost_cash and engine.knowledge_points >= cost_kp) if has_funds and not is_maxed and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_el.removeAttribute("disabled") else: btn_el.setAttribute("disabled", "true") for staff_up in ["l1_techs", "l2_techs", "sysadmins"]: lvl = engine.upgrades.get(staff_up, 0) cost_dict = engine.get_upgrade_cost_dict(staff_up) cost_cash = cost_dict.get("cash", 0.0) staff_id_key = "l1" if staff_up == "l1_techs" else ("l2" if staff_up == "l2_techs" else "sys") lbl_hired = document.getElementById(f"lbl-staff-{staff_id_key}-hired") lbl_cost = document.getElementById(f"lbl-staff-{staff_id_key}-cost") if lbl_hired: lbl_hired.innerText = str(lvl) if lbl_cost: lbl_cost.innerText = f"${cost_cash:,.2f}" btn_el = document.getElementById(f"btn-staff-buy-{staff_up}") if btn_el: has_capacity = engine.get_free_server_capacity() >= resources.STAFF_SERVER_USAGE[staff_up] has_funds = engine.cash >= cost_cash if has_funds and has_capacity and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_el.removeAttribute("disabled") else: btn_el.setAttribute("disabled", "true") # 4. On-Site Staff Slots in-place updates (Safe guard if present) el_slots_count = document.getElementById("staff-slots-count") if el_slots_count: el_slots_count.innerText = f"Slots Unlocked: {engine.onsite_slots_unlocked}/4" slot_costs = [1500.0, 3500.0, 8000.0, 18000.0] for i in range(4): name_el = document.getElementById(f"slot-name-{i}") quirk_el = document.getElementById(f"slot-quirk-{i}") lvl_el = document.getElementById(f"slot-lvl-{i}") stats_el = document.getElementById(f"slot-stats-{i}") speed_el = document.getElementById(f"slot-speed-{i}") cost_el = document.getElementById(f"slot-cost-{i}") xpbar_el = document.getElementById(f"slot-xpbar-{i}") btn_unlock = document.getElementById(f"btn-unlock-slot-{i}") btn_fire = document.getElementById(f"btn-fire-slot-{i}") if not name_el: continue if i >= engine.onsite_slots_unlocked: cost = slot_costs[i] unlocked_so_far = (i == engine.onsite_slots_unlocked) name_el.innerText = f"πŸ”’ Slot {i+1} (Locked)" quirk_el.innerText = "Unlock this slot to hire an on-site technician." lvl_el.innerText = "Locked" lvl_el.className = "badge bg-secondary" if stats_el: stats_el.style.display = "none" if btn_unlock: btn_unlock.style.display = "block" btn_unlock.innerText = f"Unlock Slot (${cost:,.0f})" btn_unlock.disabled = not (unlocked_so_far and engine.cash >= cost) if btn_fire: btn_fire.style.display = "none" elif i >= len(engine.hired_onsite_techs): name_el.innerText = f"πŸ‘€ Empty Staff Slot {i+1}" quirk_el.innerText = "Slot available. Hire a candidate from the recruitment board below." lvl_el.innerText = "Empty" lvl_el.className = "badge bg-dark border border-secondary text-info" if stats_el: stats_el.style.display = "none" if btn_unlock: btn_unlock.style.display = "none" if btn_fire: btn_fire.style.display = "none" else: tech = engine.hired_onsite_techs[i] xp_pct = min(100.0, (tech["xp"] / (tech["level"] * 100)) * 100.0) name_el.innerText = tech["name"] quirk_el.innerText = f"Quirk: {tech['quirk']}" lvl_el.innerText = f"Lvl {tech['level']}" lvl_el.className = "badge bg-info text-dark" if stats_el: stats_el.style.display = "block" if speed_el: speed_el.innerText = f"{tech['speed_mult']:.2f}x" if cost_el: cost_el.innerText = f"${tech['cost_per_job']:.2f}" if xpbar_el: xpbar_el.style.width = f"{xp_pct}%" if btn_unlock: btn_unlock.style.display = "none" if btn_fire: btn_fire.style.display = "block" btn_fire.disabled = False # Candidate Recruitment Board (Cached to prevent button dropouts) global last_recruits_sig current_sig = ",".join([c["id"] for c in engine.recruits_pool]) + f"_{len(engine.hired_onsite_techs)}_{engine.onsite_slots_unlocked}" if current_sig != last_recruits_sig: last_recruits_sig = current_sig board = document.getElementById("recruitment-board") if board: board.innerHTML = "" if not engine.recruits_pool: board.innerHTML = '

    Waiting for candidates... (Spawns periodically)

    ' else: slots_full = (len(engine.hired_onsite_techs) >= engine.onsite_slots_unlocked) for cand in engine.recruits_pool: col = document.createElement("div") col.className = "col-md-4" col.innerHTML = f'''
    {cand["name"]}
    Base Lvl {cand["level"]} {cand["quirk"]}
    Speed Multiplier: {cand["speed_mult"]:.2f}x
    Cost per Job: ${cand["cost_per_job"]:.2f}
    ''' board.appendChild(col) if not slots_full: btn_el = document.getElementById(f"btn-hire-recruit-{cand['id']}") if btn_el: btn_el.addEventListener("click", create_proxy(lambda e, cid=cand["id"]: (engine.hire_onsite_tech(cid), update_ui()))) # 5. Commercial Banking & Credit UI update (Phase 2+) document.getElementById("finance-tab-li").style.display = "block" document.getElementById("container-credit").style.display = "block" document.getElementById("container-debt").style.display = "block" if engine.total_debt > 0 else "none" tier_info = engine.get_credit_tier() valuation = engine.get_corporate_valuation() apr = engine.get_current_apr() max_borrow = engine.get_max_borrow_limit() avail_borrow = engine.get_available_borrow_capacity() # HUD Navbar document.getElementById("display-credit-score").innerText = f"{int(engine.credit_score)}" badge_hud = document.getElementById("display-credit-tier-badge") badge_hud.innerText = tier_info["tier"] badge_hud.className = f"badge {tier_info['badge_class']} fs-6 py-1 px-2" document.getElementById("display-debt").innerText = f"${engine.total_debt:,.2f}" # Valuation & Credit Profile Card document.getElementById("display-corporate-valuation").innerText = f"${valuation['total_enterprise_value']:,.2f}" document.getElementById("display-finance-credit-score").innerText = f"{int(engine.credit_score)}" badge_profile = document.getElementById("badge-finance-credit-tier") badge_profile.innerText = tier_info["name"] badge_profile.className = f"badge {tier_info['badge_class']} fs-6 py-1 px-3" bar_credit = document.getElementById("bar-credit-score") score_pct = max(0.0, min(100.0, ((engine.credit_score - 300.0) / 550.0) * 100.0)) bar_credit.style.width = f"{score_pct:.1f}%" bar_credit.innerText = f"{int(engine.credit_score)}" bar_credit.className = f"progress-bar {tier_info['badge_class']} fw-bold" document.getElementById("display-finance-apr").innerText = f"{apr * 100.0:.1f}%" document.getElementById("display-finance-limit").innerText = f"${max_borrow:,.2f}" document.getElementById("display-finance-available").innerText = f"${avail_borrow:,.2f}" # Balance Sheet Table document.getElementById("val-sheet-cash").innerText = f"${engine.cash:,.2f}" document.getElementById("val-sheet-hardware").innerText = f"${valuation['hardware_assets']:,.2f}" document.getElementById("val-sheet-mrr").innerText = f"${valuation['mrr_asset_value']:,.2f}" document.getElementById("val-sheet-debt").innerText = f"-${engine.total_debt:,.2f}" document.getElementById("val-sheet-book-value").innerText = f"${valuation['book_value']:,.2f}" # Debt ratio bar debt_ratio = valuation["debt_to_asset_ratio"] bar_ratio = document.getElementById("bar-debt-ratio") lbl_ratio = document.getElementById("lbl-finance-debt-ratio") bar_ratio.style.width = f"{min(100.0, debt_ratio * 100.0):.1f}%" if debt_ratio < 0.35: lbl_ratio.innerText = f"{debt_ratio*100.0:.1f}% (Safe / Prime)" lbl_ratio.className = "fw-bold text-success" bar_ratio.className = "progress-bar bg-success" elif debt_ratio < 0.70: lbl_ratio.innerText = f"{debt_ratio*100.0:.1f}% (Moderate)" lbl_ratio.className = "fw-bold text-warning" bar_ratio.className = "progress-bar bg-warning text-dark" else: lbl_ratio.innerText = f"{debt_ratio*100.0:.1f}% (High Leverage)" lbl_ratio.className = "fw-bold text-danger" bar_ratio.className = "progress-bar bg-danger" # Loan Offers tier_key = tier_info["tier"] # Offer 1: Micro micro_mult = resources.LOAN_OFFERS[0]["amount_multiplier_by_tier"].get(tier_key, 1.0) micro_amount = min(avail_borrow, resources.LOAN_OFFERS[0]["base_amount"] * micro_mult) document.getElementById("lbl-micro-amount").innerText = f"${micro_amount:,.2f}" btn_micro = document.getElementById("btn-take-loan-micro") if avail_borrow >= 10.0 and micro_amount > 0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_micro.removeAttribute("disabled") else: btn_micro.setAttribute("disabled", "true") # Offer 2: Infra infra_mult = resources.LOAN_OFFERS[1]["amount_multiplier_by_tier"].get(tier_key, 0.0) infra_amount = min(avail_borrow, resources.LOAN_OFFERS[1]["base_amount"] * infra_mult) document.getElementById("lbl-infra-amount").innerText = f"${infra_amount:,.2f}" if infra_mult > 0 else "Locked (Fair Tier Req)" btn_infra = document.getElementById("btn-take-loan-infra") if engine.credit_score >= 580.0 and avail_borrow >= 10.0 and infra_amount > 0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_infra.removeAttribute("disabled") else: btn_infra.setAttribute("disabled", "true") # Offer 3: Corp corp_mult = resources.LOAN_OFFERS[2]["amount_multiplier_by_tier"].get(tier_key, 0.0) corp_amount = min(avail_borrow, resources.LOAN_OFFERS[2]["base_amount"] * corp_mult) document.getElementById("lbl-corp-amount").innerText = f"${corp_amount:,.2f}" if corp_mult > 0 else "Locked (Good Tier Req)" btn_corp = document.getElementById("btn-take-loan-corp") if engine.credit_score >= 680.0 and avail_borrow >= 10.0 and corp_amount > 0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_corp.removeAttribute("disabled") else: btn_corp.setAttribute("disabled", "true") # Offer 4: Syndicated Facility (Phase 4 / Prime) lbl_syn = document.getElementById("lbl-syndicated-amount") btn_syn = document.getElementById("btn-take-loan-syndicated") if lbl_syn and len(resources.LOAN_OFFERS) > 3: syn_mult = resources.LOAN_OFFERS[3]["amount_multiplier_by_tier"].get(tier_key, 0.0) syn_amount = min(avail_borrow, resources.LOAN_OFFERS[3]["base_amount"] * syn_mult) lbl_syn.innerText = f"${syn_amount:,.2f}" if syn_mult > 0 else "Locked (Prime A Req)" if btn_syn: if engine.credit_score >= 720.0 and avail_borrow >= 10.0 and syn_amount > 0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_syn.removeAttribute("disabled") else: btn_syn.setAttribute("disabled", "true") # Offer 5: Sovereign Bond (Phase 4 / Elite AAA) lbl_sov = document.getElementById("lbl-sovereign-amount") btn_sov = document.getElementById("btn-take-loan-sovereign") if lbl_sov and len(resources.LOAN_OFFERS) > 4: sov_mult = resources.LOAN_OFFERS[4]["amount_multiplier_by_tier"].get(tier_key, 0.0) sov_amount = min(avail_borrow, resources.LOAN_OFFERS[4]["base_amount"] * sov_mult) lbl_sov.innerText = f"${sov_amount:,.2f}" if sov_mult > 0 else "Locked (Elite AAA Req)" if btn_sov: if engine.credit_score >= 800.0 and avail_borrow >= 10.0 and sov_amount > 0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_sov.removeAttribute("disabled") else: btn_sov.setAttribute("disabled", "true") # Instant Max Draw Button btn_max_loan = document.getElementById("btn-take-loan-max") if btn_max_loan: if avail_borrow >= 10.0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_max_loan.removeAttribute("disabled") btn_max_loan.innerText = f"⚑ Draw Maximum Available Credit Line (${avail_borrow:,.2f})" else: btn_max_loan.setAttribute("disabled", "true") btn_max_loan.innerText = "⚑ Maximum Credit Line Fully Utilized ($0.00)" # Repayment Console document.getElementById("display-console-debt").innerText = f"${engine.total_debt:,.2f}" has_debt = engine.total_debt > 0.0 can_act = not engine.burnout and not engine.pc_crashed and not engine.is_rebooting rep_tiers = [ ("btn-repay-500", 500.0), ("btn-repay-2500", 2500.0), ("btn-repay-50k", 50000.0), ("btn-repay-1m", 1000000.0), ("btn-repay-10m", 10000000.0), ("btn-repay-100m", 100000000.0), ] for btn_id, req_cash in rep_tiers: btn_el = document.getElementById(btn_id) if btn_el: if has_debt and engine.cash >= 1.0 and can_act: btn_el.removeAttribute("disabled") else: btn_el.setAttribute("disabled", "true") btn_pay_all = document.getElementById("btn-repay-all") if btn_pay_all: if has_debt and engine.cash >= 1.0 and can_act: btn_pay_all.removeAttribute("disabled") else: btn_pay_all.setAttribute("disabled", "true") document.getElementById("switch-auto-loan-repayment").checked = engine.auto_loan_repayment # 6. Phase 4 Stock Market, World News Wire & Quantitative AI Broker UI update if engine.phase >= 4: document.getElementById("stocks-tab-li").style.display = "block" # Market Regime & Live News Wire regime_info = resources.MARKET_REGIMES.get(engine.market_regime, resources.MARKET_REGIMES["flat"]) badge_regime = document.getElementById("badge-market-regime") if badge_regime: badge_regime.innerText = regime_info["name"] badge_regime.className = f"badge {regime_info['badge_class']} px-3 py-2 fs-6 fw-bold" headline_el = document.getElementById("ticker-market-headline") if headline_el: headline_el.innerText = engine.market_headline # Active Sector Catalysts Tag Cloud catalysts_container = document.getElementById("active-catalysts-container") if catalysts_container: catalyst_chips = ['ACTIVE SECTOR CATALYSTS:'] active_sector_drifts = {} for ev in engine.active_news_events: for sec, drf in ev.get("sector_impacts", {}).items(): active_sector_drifts[sec] = active_sector_drifts.get(sec, 0.0) + drf if active_sector_drifts: for sec_id, drf_val in active_sector_drifts.items(): sec_info = resources.SECTORS_REGISTRY.get(sec_id, {"name": sec_id.title(), "icon": "πŸ“Š"}) sign = "+" if drf_val >= 0 else "" badge_cls = "bg-success bg-opacity-25 border border-success text-success" if drf_val >= 0 else "bg-danger bg-opacity-25 border border-danger text-danger" rem_sec = int(max(e["remaining_time"] for e in engine.active_news_events if sec_id in e.get("sector_impacts", {}))) chip = f'{sec_info["icon"]} {sec_info["name"]}: {sign}{drf_val*100.0:.0f}% ({rem_sec}s)' catalyst_chips.append(chip) else: catalyst_chips.append('Market in Equilibrium β€’ No Sector Volatility') catalysts_container.innerHTML = " ".join(catalyst_chips) # Recent News Feed List news_list_el = document.getElementById("recent-news-feed-list") if news_list_el and engine.news_feed: feed_items = [] for hl in engine.news_feed[:8]: feed_items.append(f'
  • β€’ {hl}
  • ') news_list_el.innerHTML = "".join(feed_items) # $HIT Corporate Equity Console share_price = engine.get_hit_share_price() if not engine.hit_ipo_completed: document.getElementById("sec-hit-pre-ipo").style.display = "block" document.getElementById("sec-hit-post-ipo").style.display = "none" badge_status = document.getElementById("badge-hit-status") badge_status.innerText = "PRIVATE" badge_status.className = "badge bg-secondary px-2" val_info = engine.get_corporate_valuation() ent_val = val_info["total_enterprise_value"] document.getElementById("display-ipo-valuation").innerText = f"${ent_val:,.2f}" document.getElementById("display-ipo-share-price").innerText = f"${share_price:,.2f}" document.getElementById("display-ipo-proceeds").innerText = f"${25000 * share_price:,.2f}" btn_ipo = document.getElementById("btn-launch-ipo") lbl_qual = document.getElementById("status-ipo-qualification") can_ipo = ent_val >= 25000.0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting if can_ipo: btn_ipo.removeAttribute("disabled") lbl_qual.className = "text-success fw-bold small" lbl_qual.innerText = f"βœ”οΈ Qualified! (${ent_val:,.2f} / $25,000.00)" else: btn_ipo.setAttribute("disabled", "true") lbl_qual.className = "text-muted small" lbl_qual.innerText = f"Requires $25,000.00 Enterprise Valuation (${ent_val:,.2f} / $25,000.00)" else: document.getElementById("sec-hit-pre-ipo").style.display = "none" document.getElementById("sec-hit-post-ipo").style.display = "block" badge_status = document.getElementById("badge-hit-status") badge_status.innerText = "NYSE: HIT!" badge_status.className = "badge bg-success px-2" document.getElementById("display-hit-share-price").innerText = f"${share_price:,.2f}" document.getElementById("display-hit-market-cap").innerText = f"${share_price * engine.hit_total_shares:,.2f}" document.getElementById("display-hit-total-shares").innerText = f"{engine.hit_total_shares:,}" ownership_pct = (engine.hit_player_shares / engine.hit_total_shares) * 100.0 document.getElementById("display-hit-player-shares").innerText = f"{engine.hit_player_shares:,} ({ownership_pct:.1f}%)" # Buyback buyback_cost = 1000 * share_price document.getElementById("lbl-buyback-cost").innerText = f"Cost: ${buyback_cost:,.2f}" btn_bb = document.getElementById("btn-hit-buyback") public_float = engine.hit_total_shares - engine.hit_player_shares if public_float >= 1000 and engine.cash >= buyback_cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_bb.removeAttribute("disabled") else: btn_bb.setAttribute("disabled", "true") # Dividend div_cost = engine.hit_total_shares * 0.25 player_div = engine.hit_player_shares * 0.25 document.getElementById("lbl-dividend-cost").innerText = f"Cost: ${div_cost:,.2f} (You get ${player_div:,.2f})" btn_div = document.getElementById("btn-hit-dividend") if engine.cash >= div_cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_div.removeAttribute("disabled") else: btn_div.setAttribute("disabled", "true") # Portfolio & Returns Tracker summary = engine.get_portfolio_summary() document.getElementById("display-portfolio-val").innerText = f"${summary['total_value']:,.2f}" document.getElementById("display-portfolio-cost").innerText = f"${summary['total_invested']:,.2f}" el_unreal = document.getElementById("display-portfolio-unrealized") pnl_sign = "+" if summary['unrealized_pnl'] >= 0 else "" el_unreal.innerText = f"{pnl_sign}${summary['unrealized_pnl']:,.2f} ({pnl_sign}{summary['unrealized_pct']:.1f}%)" el_unreal.className = "fs-4 fw-bold " + ("text-success" if summary['unrealized_pnl'] >= 0 else "text-danger") el_real = document.getElementById("display-portfolio-realized") real_sign = "+" if summary['realized_pnl'] >= 0 else "" el_real.innerText = f"{real_sign}${summary['realized_pnl']:,.2f}" el_real.className = "text-end fw-bold font-monospace " + ("text-success" if summary['realized_pnl'] >= 0 else "text-danger") document.getElementById("display-portfolio-dividends").innerText = f"${summary['total_dividends']:,.2f}" btn_liq = document.getElementById("btn-liquidate-all") if summary['total_value'] > 0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_liq.removeAttribute("disabled") else: btn_liq.setAttribute("disabled", "true") # Quantitative AI Broker Desk State has_algo = engine.upgrades.get("algo_broker", 0) > 0 badge_broker = document.getElementById("badge-broker-status") if badge_broker: if has_algo: badge_broker.className = "badge bg-success px-3 py-2 fs-6 fw-bold" badge_broker.innerText = f"πŸ€– AI Broker: Active ({len(engine.limit_orders)} Bots)" else: badge_broker.className = "badge bg-dark border border-secondary px-3 py-2 fs-6 fw-bold text-muted" badge_broker.innerText = "πŸ€– AI Broker: Offline" card_algo_locked = document.getElementById("algo-broker-locked") card_algo_unlocked = document.getElementById("algo-broker-unlocked") if card_algo_locked and card_algo_unlocked: if has_algo: card_algo_locked.style.display = "none" card_algo_unlocked.style.display = "block" global last_limit_orders_sig, last_sparkline_sigs # Update active orders table (only rebuild rows if orders list changed) orders_sig = tuple((ord["id"], ord["order_type"], ord["target_price"], ord["shares"], ord.get("enabled", True)) for ord in engine.limit_orders) if last_limit_orders_sig != orders_sig: last_limit_orders_sig = orders_sig badge_orders_count = document.getElementById("badge-limit-orders-count") if badge_orders_count: badge_orders_count.innerText = f"{len(engine.limit_orders)} Active Orders" table_body = document.getElementById("table-limit-orders-body") if table_body: if engine.limit_orders: rows = [] for ord in engine.limit_orders: t = ord["ticker"] curr_p = engine.stock_market_prices.get(t, 0.0) is_buy = ord["order_type"] == "buy_under" type_badge = '🟒 BUY UNDER' if is_buy else 'πŸ”΄ SELL OVER' is_en = ord.get("enabled", True) status_badge = 'ACTIVE' if is_en else 'PAUSED' toggle_btn_txt = "⏸️ Pause" if is_en else "▢️ Resume" row_html = f""" ${t} {type_badge} ${ord['target_price']:,.2f} ${curr_p:,.2f} {ord['shares']:,} sh {status_badge} """ rows.append(row_html) table_body.innerHTML = "".join(rows) else: table_body.innerHTML = 'No active limit orders currently deployed. Set a trigger price above!' else: # Update current prices in limit orders table in-place for ord in engine.limit_orders: p_cell = document.getElementById(f"ord-cur-p-{ord['id']}") if p_cell: curr_p = engine.stock_market_prices.get(ord["ticker"], 0.0) p_cell.innerText = f"${curr_p:,.2f}" else: card_algo_locked.style.display = "block" card_algo_unlocked.style.display = "none" btn_buy_algo = document.getElementById("btn-buy-algo-broker") if btn_buy_algo: can_buy_algo = engine.cash >= 100000.0 and engine.knowledge_points >= 250.0 and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting if can_buy_algo: btn_buy_algo.removeAttribute("disabled") else: btn_buy_algo.setAttribute("disabled", "true") # In-Place Multi-Sector Equities Trading Floor Update (Zero DOM rebuilding, No Flickering!) can_trade = not engine.burnout and not engine.pc_crashed and not engine.is_rebooting for ticker, spec in resources.STOCKS_REGISTRY.items(): card_el = document.getElementById(f"stock-card-{ticker}") sec = spec.get("sector", "tech") if card_el: if current_stocks_sector_filter != "all" and sec != current_stocks_sector_filter: card_el.style.display = "none" else: card_el.style.display = "block" curr_price = engine.stock_market_prices.get(ticker, spec["base_price"]) base_price = spec["base_price"] chg_pct = ((curr_price - base_price) / base_price) * 100.0 chg_sign = "+" if chg_pct >= 0 else "" chg_color = "text-success" if chg_pct >= 0 else "text-danger" pos = engine.portfolio.get(ticker, {"shares": 0, "total_invested": 0.0}) owned_shares = pos["shares"] avg_cost = (pos["total_invested"] / owned_shares) if owned_shares > 0 else 0.0 pos_val = owned_shares * curr_price max_affordable = int(engine.cash // curr_price) if curr_price > 0 else 0 el_p = document.getElementById(f"stock-price-{ticker}") if el_p and el_p.innerText != f"${curr_price:,.2f}": el_p.innerText = f"${curr_price:,.2f}" el_chg = document.getElementById(f"stock-chg-{ticker}") if el_chg: el_chg.innerText = f"{chg_sign}{chg_pct:.2f}%" el_chg.className = f"d-block font-monospace fw-bold {chg_color}" el_own = document.getElementById(f"stock-owned-{ticker}") if el_own and el_own.innerText != f"{owned_shares:,}": el_own.innerText = f"{owned_shares:,}" el_avg = document.getElementById(f"stock-avg-{ticker}") if el_avg: el_avg.innerText = f"${avg_cost:,.2f}" el_val = document.getElementById(f"stock-val-{ticker}") if el_val: el_val.innerText = f"${pos_val:,.2f}" # Update Sparklines efficiently (only when history length or direction updates) hist = engine.stock_market_history.get(ticker, [curr_price]) spark_sig = (len(hist), hist[-1] if hist else 0.0) if last_sparkline_sigs.get(ticker) != spark_sig: last_sparkline_sigs[ticker] = spark_sig el_spark = document.getElementById(f"stock-sparkline-{ticker}") if el_spark: el_spark.innerHTML = generate_svg_sparkline(hist, chg_pct >= 0) def set_stock_btn(b_id, is_active, label=None): b = document.getElementById(b_id) if b: if label and b.innerText != label: b.innerText = label if is_active: b.removeAttribute("disabled") else: b.setAttribute("disabled", "true") set_stock_btn(f"btn-buy-1-{ticker}", can_trade and engine.cash >= curr_price) set_stock_btn(f"btn-buy-10-{ticker}", can_trade and engine.cash >= curr_price * 10) set_stock_btn(f"btn-buy-100-{ticker}", can_trade and engine.cash >= curr_price * 100) set_stock_btn(f"btn-buy-max-{ticker}", can_trade and max_affordable > 0, f"Max ({max_affordable})") set_stock_btn(f"btn-sell-1-{ticker}", can_trade and owned_shares >= 1) set_stock_btn(f"btn-sell-10-{ticker}", can_trade and owned_shares >= 10) set_stock_btn(f"btn-sell-100-{ticker}", can_trade and owned_shares >= 100) set_stock_btn(f"btn-sell-all-{ticker}", can_trade and owned_shares > 0) if engine.phase >= 4: # Render Phase 4 Regional Operations Hub reg_summary = engine.get_regional_fleet_summary() el_reg_hubs = document.getElementById("display-reg-hubs-count") if el_reg_hubs: el_reg_hubs.innerText = f"{reg_summary['hubs_count']} Hubs" el_reg_staff = document.getElementById("display-reg-headcount") if el_reg_staff: el_reg_staff.innerText = f"{reg_summary['total_headcount']:,} Staff" el_reg_tps = document.getElementById("display-reg-throughput") if el_reg_tps: el_reg_tps.innerText = f"{reg_summary['fleet_throughput']:.1f} /s" el_reg_mrr = document.getElementById("display-reg-mrr") if el_reg_mrr: sign_mrr = "+" if reg_summary['fleet_net_mrr'] >= 0 else "-" el_reg_mrr.innerText = f"{sign_mrr}${abs(reg_summary['fleet_net_mrr']):,.2f}/mo" el_reg_mrr.className = "fs-3 font-monospace " + ("text-success" if reg_summary['fleet_net_mrr'] >= 0 else "text-danger") el_reg_gross = document.getElementById("display-reg-gross") if el_reg_gross: el_reg_gross.innerText = f"${reg_summary['fleet_gross_rev_per_sec']:,.2f}/s" el_reg_opex = document.getElementById("display-reg-opex") if el_reg_opex: el_reg_opex.innerText = f"${reg_summary['fleet_cost_per_sec']:,.2f}/s" el_reg_csi = document.getElementById("display-reg-csi-bonus") if el_reg_csi: el_reg_csi.innerText = f"+{reg_summary['csi_bonus']:.1f} CSI" el_reg_mor = document.getElementById("display-reg-morale") if el_reg_mor: el_reg_mor.innerText = f"{engine.corporate_morale:.1f}%" # Hub cost & buy button hub_cost = engine.get_facility_cost("call_centers") el_hub_cost = document.getElementById("display-reg-hub-cost") if el_hub_cost: el_hub_cost.innerText = f"${hub_cost:,.2f}" btn_reg_hub = document.getElementById("btn-reg-buy-hub") if btn_reg_hub: can_buy_hub = engine.cash >= hub_cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting if can_buy_hub: btn_reg_hub.removeAttribute("disabled") else: btn_reg_hub.setAttribute("disabled", "true") # Policies b_pay = document.getElementById("badge-reg-pay") if b_pay: b_pay.innerText = reg_summary["payroll_policy"].title() for pk in ["standard", "generous", "executive"]: b_el = document.getElementById(f"btn-reg-policy-pay-{pk}") if b_el: b_el.className = "btn btn-sm text-start " + ("btn-info text-dark fw-bold" if reg_summary["payroll_policy"] == pk else "btn-outline-info") b_inf = document.getElementById("badge-reg-infra") if b_inf: b_inf.innerText = reg_summary["infra_standard"].title() for ik in ["standard", "enterprise", "hyperscale"]: b_el = document.getElementById(f"btn-reg-policy-infra-{ik}") if b_el: b_el.className = "btn btn-sm text-start " + ("btn-info text-dark fw-bold" if reg_summary["infra_standard"] == ik else "btn-outline-info") b_trn = document.getElementById("badge-reg-train") if b_trn: b_trn.innerText = reg_summary["training_program"].title() for tk in ["basic", "certified", "architect"]: b_el = document.getElementById(f"btn-reg-policy-train-{tk}") if b_el: b_el.className = "btn btn-sm text-start " + ("btn-info text-dark fw-bold" if reg_summary["training_program"] == tk else "btn-outline-info") # Fleet Upgrades for up_id in ["hub_ai_dispatcher", "hub_optical_backbone", "hub_specialist_centers", "hub_microgrid_power"]: lvl = engine.regional_fleet_upgrades.get(up_id, 0) cost_info = engine.get_regional_fleet_upgrade_cost(up_id) lbl_lvl = document.getElementById(f"lbl-reg-lvl-{up_id}") if lbl_lvl: lbl_lvl.innerText = f"Lvl {lvl} / 5" lbl_eff = document.getElementById(f"lbl-reg-eff-{up_id}") if lbl_eff: if up_id == "hub_ai_dispatcher": lbl_eff.innerText = f"+{20 * lvl}% Speed" elif up_id == "hub_optical_backbone": lbl_eff.innerText = f"+{15 * lvl}% MRR" elif up_id == "hub_specialist_centers": lbl_eff.innerText = f"-{25 * lvl}% Escalation Time" elif up_id == "hub_microgrid_power": lbl_eff.innerText = f"-{min(99, 80 * lvl)}% Downtime" lbl_cost = document.getElementById(f"lbl-reg-cost-{up_id}") btn_upg = document.getElementById(f"btn-reg-upgrade-{up_id}") if lvl >= 5: if lbl_cost: lbl_cost.innerText = "MAX LEVEL ACHIEVED" if btn_upg: btn_upg.innerText = "MAXED" btn_upg.setAttribute("disabled", "true") else: if lbl_cost: lbl_cost.innerText = f"Cost: ${cost_info['cash']:,.2f} | {cost_info['kp']:,} KP" if btn_upg: btn_upg.innerText = "Upgrade" can_upg = engine.cash >= cost_info["cash"] and engine.knowledge_points >= cost_info["kp"] and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting if can_upg: btn_upg.removeAttribute("disabled") else: btn_upg.setAttribute("disabled", "true") # Phase 3+ Sales & Lead Gen Tab Updates if engine.phase >= 3: total_mrr = engine.get_mrr() el_tot_mrr = document.getElementById("display-sales-total-mrr") if el_tot_mrr: el_tot_mrr.innerText = f"${total_mrr:,.0f}/mo" el_leads_cnt = document.getElementById("display-sales-leads-count") if el_leads_cnt: el_leads_cnt.innerText = f"{engine.leads:.1f} / {engine.get_max_leads()}" el_lead_rate = document.getElementById("display-sales-lead-rate") if el_lead_rate: lr = engine.get_passive_leads_rate() if hasattr(engine, "get_passive_leads_rate") else resources.get_passive_leads_rate(engine.upgrades.get("sales_agents", 0)) el_lead_rate.innerText = f"+{lr:.2f}/s" el_agents_cnt = document.getElementById("display-sales-agents-count") if el_agents_cnt: el_agents_cnt.innerText = f"{engine.upgrades.get('sales_agents', 0)} Agents" st_spec = resources.UPGRADES_REGISTRY.get("sales_training", {}) pitch_bonus_pct = int(engine.upgrades.get("sales_training", 0) * st_spec.get("win_prob_boost_per_lvl", 0.10) * 100) el_pitch_bonus = document.getElementById("display-sales-pitch-bonus") if el_pitch_bonus: el_pitch_bonus.innerText = f"+{pitch_bonus_pct}%" proc_mult = engine.get_procurement_discount() if hasattr(engine, "get_procurement_discount") else resources.get_procurement_discount(engine.upgrades.get("procurement_specialists", 0)) proc_disc_pct = int(min(50.0, (1.0 - proc_mult) * 100)) el_proc_disc = document.getElementById("display-sales-procurement-discount") if el_proc_disc: el_proc_disc.innerText = f"{proc_disc_pct}%" el_sub_agents = document.getElementById("display-sales-agents-subcount") if el_sub_agents: el_sub_agents.innerText = f"{engine.upgrades.get('sales_agents', 0)} Agents" el_sub_pitch = document.getElementById("display-sales-pitch-bonus-sub") if el_sub_pitch: el_sub_pitch.innerText = f"+{pitch_bonus_pct}%" bw_used = engine.get_bandwidth_used() bw_max = engine.get_max_bandwidth() el_bw_usage = document.getElementById("display-sales-bw-usage") if el_bw_usage: el_bw_usage.innerText = f"{bw_used} / {bw_max} Mbps" bw_pct = min(100.0, round((bw_used / bw_max) * 100.0, 1)) if bw_max > 0 else 0.0 bar_bw = document.getElementById("bar-sales-bw") if bar_bw: bar_bw.style.width = f"{bw_pct}%" bar_bw.innerText = f"{bw_pct}%" bar_bw.className = "progress-bar " + ("bg-danger" if bw_pct >= 90.0 else ("bg-warning" if bw_pct >= 70.0 else "bg-info")) el_brz_cnt = document.getElementById("display-sales-bronze-count") if el_brz_cnt: el_brz_cnt.innerText = str(engine.contracts_bronze) el_sil_cnt = document.getElementById("display-sales-silver-count") if el_sil_cnt: el_sil_cnt.innerText = str(engine.contracts_silver) el_gld_cnt = document.getElementById("display-sales-gold-count") if el_gld_cnt: el_gld_cnt.innerText = str(engine.contracts_gold) # Dynamic Proposal Specs spec_b = resources.CONTRACT_SPECS.get("bronze", {}) spec_s = resources.CONTRACT_SPECS.get("silver", {}) spec_g = resources.CONTRACT_SPECS.get("gold", {}) p_bronze = min(95, int(spec_b.get("probability", 0.8) * 100) + pitch_bonus_pct) p_silver = min(90, int(spec_s.get("probability", 0.5) * 100) + pitch_bonus_pct) p_gold = min(80, int(spec_g.get("probability", 0.25) * 100) + pitch_bonus_pct) el_pc_b = document.getElementById("sales-pitch-chance-bronze") if el_pc_b: el_pc_b.innerText = f"{p_bronze}%" el_pc_s = document.getElementById("sales-pitch-chance-silver") if el_pc_s: el_pc_s.innerText = f"{p_silver}%" el_pc_g = document.getElementById("sales-pitch-chance-gold") if el_pc_g: el_pc_g.innerText = f"{p_gold}%" el_mpc_b = document.getElementById("pitch-chance-bronze") if el_mpc_b: el_mpc_b.innerText = f"Chance: {p_bronze}%" el_mpc_s = document.getElementById("pitch-chance-silver") if el_mpc_s: el_mpc_s.innerText = f"Chance: {p_silver}%" el_mpc_g = document.getElementById("pitch-chance-gold") if el_mpc_g: el_mpc_g.innerText = f"Chance: {p_gold}%" # Dynamic Proposal Card Descriptions (Tab 5 & MSP Panel) # Bronze mrr_b = spec_b.get("mrr", 500.0) rate_b = mrr_b / 10000.0 tix_b = spec_b.get("tickets", 0.20) bw_b = spec_b.get("bandwidth", 2) u_b = spec_b.get("compute", 1) leads_b = spec_b.get("cost_leads", 1) el_mrr_b = document.getElementById("sales-desc-mrr-bronze") if el_mrr_b: el_mrr_b.innerText = f"+${mrr_b:,.0f}/mo" el_rate_b = document.getElementById("sales-desc-rate-bronze") if el_rate_b: el_rate_b.innerText = f"+${rate_b:.2f}/s" el_tix_b = document.getElementById("sales-desc-tickets-bronze") if el_tix_b: el_tix_b.innerText = f"+{tix_b:.2f}" el_req_b = document.getElementById("sales-desc-req-bronze") if el_req_b: el_req_b.innerText = f"{bw_b} Mbps β€’ {u_b}U Space" el_clead_b = document.getElementById("sales-cost-leads-bronze") if el_clead_b: el_clead_b.innerText = f"Cost: {leads_b} Lead{'s' if leads_b != 1 else ''}" el_mdesc_b = document.getElementById("msp-desc-bronze") if el_mdesc_b: el_mdesc_b.innerText = f"+${mrr_b:,.0f} MRR, +${rate_b:.2f}/s" el_mbw_b = document.getElementById("msp-bw-bronze") if el_mbw_b: el_mbw_b.innerText = f"Bandwidth: +{bw_b} Mbps β€’ {u_b}U" el_mcost_b = document.getElementById("msp-cost-bronze") if el_mcost_b: el_mcost_b.innerText = f"(Costs {leads_b} Lead{'s' if leads_b != 1 else ''})" # Silver mrr_s = spec_s.get("mrr", 5250.0) rate_s = mrr_s / 10000.0 tix_s = spec_s.get("tickets", 0.50) bw_s = spec_s.get("bandwidth", 15) u_s = spec_s.get("compute", 4) leads_s = spec_s.get("cost_leads", 3) el_mrr_s = document.getElementById("sales-desc-mrr-silver") if el_mrr_s: el_mrr_s.innerText = f"+${mrr_s:,.0f}/mo" el_rate_s = document.getElementById("sales-desc-rate-silver") if el_rate_s: el_rate_s.innerText = f"+${rate_s:.2f}/s" el_tix_s = document.getElementById("sales-desc-tickets-silver") if el_tix_s: el_tix_s.innerText = f"+{tix_s:.2f}" el_req_s = document.getElementById("sales-desc-req-silver") if el_req_s: el_req_s.innerText = f"{bw_s} Mbps β€’ {u_s}U Space" el_clead_s = document.getElementById("sales-cost-leads-silver") if el_clead_s: el_clead_s.innerText = f"Cost: {leads_s} Leads" el_mdesc_s = document.getElementById("msp-desc-silver") if el_mdesc_s: el_mdesc_s.innerText = f"+${mrr_s:,.0f} MRR, +${rate_s:.2f}/s" el_mbw_s = document.getElementById("msp-bw-silver") if el_mbw_s: el_mbw_s.innerText = f"Bandwidth: +{bw_s} Mbps β€’ {u_s}U" el_mcost_s = document.getElementById("msp-cost-silver") if el_mcost_s: el_mcost_s.innerText = f"(Costs {leads_s} Leads)" # Gold mrr_g = spec_g.get("mrr", 10500.0) rate_g = mrr_g / 10000.0 tix_g = spec_g.get("tickets", 1.20) bw_g = spec_g.get("bandwidth", 80) u_g = spec_g.get("compute", 15) leads_g = spec_g.get("cost_leads", 8) el_mrr_g = document.getElementById("sales-desc-mrr-gold") if el_mrr_g: el_mrr_g.innerText = f"+${mrr_g:,.0f}/mo" el_rate_g = document.getElementById("sales-desc-rate-gold") if el_rate_g: el_rate_g.innerText = f"+${rate_g:.2f}/s" el_tix_g = document.getElementById("sales-desc-tickets-gold") if el_tix_g: el_tix_g.innerText = f"+{tix_g:.2f}" el_req_g = document.getElementById("sales-desc-req-gold") if el_req_g: el_req_g.innerText = f"{bw_g} Mbps β€’ {u_g}U Space" el_clead_g = document.getElementById("sales-cost-leads-gold") if el_clead_g: el_clead_g.innerText = f"Cost: {leads_g} Leads" el_mdesc_g = document.getElementById("msp-desc-gold") if el_mdesc_g: el_mdesc_g.innerText = f"+${mrr_g:,.0f} MRR, +${rate_g:.2f}/s" el_mbw_g = document.getElementById("msp-bw-gold") if el_mbw_g: el_mbw_g.innerText = f"Bandwidth: +{bw_g} Mbps β€’ {u_g}U" el_mcost_g = document.getElementById("msp-cost-gold") if el_mcost_g: el_mcost_g.innerText = f"(Costs {leads_g} Leads)" free_compute = engine.get_free_server_capacity() can_pitch = not engine.burnout and not engine.pc_crashed and not engine.is_rebooting # Dynamic Responsive SLA Proposals Column Layout unlocked_silver = engine.is_contract_tier_unlocked("silver") unlocked_gold = engine.is_contract_tier_unlocked("gold") col_b = document.getElementById("col-proposal-bronze") col_s = document.getElementById("col-proposal-silver") col_g = document.getElementById("col-proposal-gold") if unlocked_gold: if col_b: col_b.className = "col-md-4 col-12" if col_s: col_s.className = "col-md-4 col-12" col_s.style.display = "block" if col_g: col_g.className = "col-md-4 col-12" col_g.style.display = "block" elif unlocked_silver: if col_b: col_b.className = "col-md-6 col-12" if col_s: col_s.className = "col-md-6 col-12" col_s.style.display = "block" if col_g: col_g.style.display = "none" else: if col_b: col_b.className = "col-12" if col_s: col_s.style.display = "none" if col_g: col_g.style.display = "none" btn_pb = document.getElementById("btn-sales-pitch-bronze") or document.getElementById("btn-pitch-bronze") if btn_pb: if can_pitch and engine.leads >= spec_b.get("cost_leads", 1) and bw_used + spec_b.get("bandwidth", 2) <= bw_max and free_compute >= spec_b.get("compute", 1): btn_pb.removeAttribute("disabled") else: btn_pb.setAttribute("disabled", "true") btn_ps = document.getElementById("btn-sales-pitch-silver") or document.getElementById("btn-pitch-silver") if btn_ps: if unlocked_silver and can_pitch and engine.leads >= spec_s.get("cost_leads", 3) and bw_used + spec_s.get("bandwidth", 15) <= bw_max and free_compute >= spec_s.get("compute", 4): btn_ps.removeAttribute("disabled") else: btn_ps.setAttribute("disabled", "true") btn_pg = document.getElementById("btn-sales-pitch-gold") or document.getElementById("btn-pitch-gold") if btn_pg: if unlocked_gold and can_pitch and engine.leads >= spec_g.get("cost_leads", 8) and bw_used + spec_g.get("bandwidth", 80) <= bw_max and free_compute >= spec_g.get("compute", 15): btn_pg.removeAttribute("disabled") else: btn_pg.setAttribute("disabled", "true") # Sales Upgrades inside Sales Tab for s_up in ["sales_agents", "sales_training", "procurement_specialists", "fiber_pipeline"]: s_lvl = engine.upgrades.get(s_up, 0) s_cost_dict = engine.get_upgrade_cost_dict(s_up) s_cash = s_cost_dict.get("cash", 0.0) el_lvl = document.getElementById(f"sales-level-{s_up}") if el_lvl: el_lvl.innerText = f"Lvl {s_lvl}" el_cost = document.getElementById(f"sales-cost-{s_up}") if el_cost: el_cost.innerText = f"${s_cash:,.2f}" btn_sup = document.getElementById(f"btn-sales-buy-{s_up}") if btn_sup: has_cap = True if s_up in resources.STAFF_SERVER_USAGE: has_cap = engine.get_free_server_capacity() >= resources.STAFF_SERVER_USAGE[s_up] if can_pitch and engine.cash >= s_cash and has_cap: btn_sup.removeAttribute("disabled") else: btn_sup.setAttribute("disabled", "true") else: document.getElementById("container-credit").style.display = "none" document.getElementById("container-debt").style.display = "none" sidebar_brew_sec.style.display = "none" card_coffee_station.style.display = "block" card_hr_station.style.display = "none" panel_breakroom_col.style.display = "block" panel_console_col.className = "col-lg-7" # Medium Resolve progress bar bar_med = document.getElementById("bar-resolve-medium") if engine.is_resolving_medium: med_pct = round((engine.resolve_medium_progress / 3.0) * 100.0, 1) bar_med.style.width = f"{med_pct}%" bar_med.className = "progress-bar bg-success progress-bar-striped progress-bar-animated" bar_med.innerText = f"Resolving... {med_pct}%" else: bar_med.style.width = "0%" bar_med.innerText = "" bar_med.className = "progress-bar bg-secondary" # High Resolve progress bar bar_high = document.getElementById("bar-resolve-high") if engine.is_resolving_high: high_pct = round((engine.resolve_high_progress / 8.0) * 100.0, 1) bar_high.style.width = f"{high_pct}%" bar_high.className = "progress-bar bg-success progress-bar-striped progress-bar-animated" bar_high.innerText = f"Resolving... {high_pct}%" else: bar_high.style.width = "0%" bar_high.innerText = "" bar_high.className = "progress-bar bg-secondary" # Phase 2 Manager Workbench progress bars bar_p2_med = document.getElementById("bar-resolve-p2-medium") if bar_p2_med: if engine.is_resolving_medium: med_pct = round((engine.resolve_medium_progress / 3.0) * 100.0, 1) bar_p2_med.style.width = f"{med_pct}%" bar_p2_med.className = "progress-bar bg-warning progress-bar-striped progress-bar-animated" else: bar_p2_med.style.width = "0%" bar_p2_med.className = "progress-bar bg-secondary" bar_p2_high = document.getElementById("bar-resolve-p2-high") if bar_p2_high: if engine.is_resolving_high: high_pct = round((engine.resolve_high_progress / 8.0) * 100.0, 1) bar_p2_high.style.width = f"{high_pct}%" bar_p2_high.className = "progress-bar bg-danger progress-bar-striped progress-bar-animated" else: bar_p2_high.style.width = "0%" bar_p2_high.className = "progress-bar bg-secondary" # Phase 2 Workbench Severity ticket counts p2_low_cnt = sum(1 for inc in engine.active_incidents if inc.get("severity") == "low" and not inc.get("assigned_to")) p2_med_cnt = sum(1 for inc in engine.active_incidents if inc.get("severity") == "medium" and not inc.get("assigned_to")) p2_high_cnt = sum(1 for inc in engine.active_incidents if inc.get("severity") == "high" and not inc.get("assigned_to")) q_p2_low = document.getElementById("queue-p2-low-count") if q_p2_low: q_p2_low.innerText = f"{p2_low_cnt} Low" q_p2_med = document.getElementById("queue-p2-med-count") if q_p2_med: q_p2_med.innerText = f"{p2_med_cnt} Med" q_p2_high = document.getElementById("queue-p2-high-count") if q_p2_high: q_p2_high.innerText = f"{p2_high_cnt} High" # KB Writing progress bar bar_kb = document.getElementById("bar-write-kb") if engine.is_writing_kb: kb_pct = round((engine.kb_write_progress / 10.0) * 100.0, 1) bar_kb.style.width = f"{kb_pct}%" bar_kb.className = "progress-bar bg-info progress-bar-striped progress-bar-animated" bar_kb.innerText = f"Writing KB... {kb_pct}%" else: bar_kb.style.width = "0%" bar_kb.innerText = "" bar_kb.className = "progress-bar bg-secondary" # Evolutionary Resource HUD display (Always visible across all phases) document.getElementById("phase1-hud").style.display = "flex" document.getElementById("display-tickets-inventory").innerText = f"{engine.solved_tickets_inventory} / {engine.get_max_solved_tickets()}" document.getElementById("display-kp").innerText = f"{engine.knowledge_points:.1f} / {engine.get_max_kp()}" # Toggle visibility of progression elements tier = engine.get_progression_tier() if engine.phase == 1: document.getElementById("container-resolve-low").style.display = "block" document.getElementById("container-resolve-medium").style.display = "block" if tier >= 3 else "none" document.getElementById("container-resolve-high").style.display = "block" if tier >= 5 else "none" document.getElementById("documentation-section").style.display = "block" if tier >= 2 else "none" document.getElementById("container-cpu-temp").style.display = "block" document.getElementById("dispatcher-console-pane").style.display = "none" else: # Phase 2+ Manager Workbench preserves manual solving alongside the Dispatcher Board document.getElementById("container-resolve-low").style.display = "block" document.getElementById("container-resolve-medium").style.display = "block" document.getElementById("container-resolve-high").style.display = "block" document.getElementById("documentation-section").style.display = "block" document.getElementById("container-cpu-temp").style.display = "block" document.getElementById("dispatcher-console-pane").style.display = "block" # Sync auto-dispatch toggle checkbox and lock auto_disp_toggle = document.getElementById("switch-auto-dispatch") if engine.upgrades.get("auto_dispatcher_software", 0) > 0: auto_disp_toggle.removeAttribute("disabled") auto_disp_toggle.checked = engine.auto_dispatch else: auto_disp_toggle.setAttribute("disabled", "true") auto_disp_toggle.checked = False # Overclock range slider slider_oc = document.getElementById("slider-overclock") display_oc = document.getElementById("display-overclock-multiplier") if engine.upgrades.get("overclock_cpu", 0) > 0: max_oc = 1.0 + engine.upgrades.get("overclock_cpu", 0) slider_oc.removeAttribute("disabled") slider_oc.setAttribute("max", str(max_oc)) slider_oc.value = str(getattr(engine, "overclock_setting", 1.0)) oc_val = getattr(engine, "overclock_setting", 1.0) display_oc.innerText = f"{oc_val:.2f}x" else: slider_oc.setAttribute("disabled", "true") slider_oc.setAttribute("max", "1.0") slider_oc.value = "1.0" display_oc.innerText = "1.00x" # Stress ball text & enabled state relief = 0.5 + 0.5 * engine.upgrades.get("stress_ball_quality", 0) btn_stress = document.getElementById("btn-stress-ball") btn_stress.innerText = f"🧢 Squeeze Stress Ball (-{relief:.1f} Stress)" if not engine.burnout: btn_stress.removeAttribute("disabled") else: btn_stress.setAttribute("disabled", "true") # Coolant Vent button coolant_cd = getattr(engine, "emergency_coolant_cooldown", 0.0) btn_vent = document.getElementById("btn-coolant-vent") if coolant_cd > 0.0: btn_vent.innerText = f"πŸ’¨ Vent ({engine.emergency_coolant_cooldown:.1f}s)" btn_vent.setAttribute("disabled", "true") else: btn_vent.innerText = "πŸ’¨ Vent Coolant" if not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_vent.removeAttribute("disabled") else: btn_vent.setAttribute("disabled", "true") # Update Team Coffee Pot display (Breakroom) pot_container = document.getElementById("container-team-coffee-pot") if pot_container: pot_container.style.display = "block" if engine.phase in [2, 3] else "none" pot_display = document.getElementById("display-team-coffee") if pot_display: pot_display.innerText = f"{getattr(engine, 'team_coffee_pot', 0)} / {getattr(engine, 'team_coffee_pot_max', 10)} Cups" # Phase 1 vs Phase 2 Operations Top Deck p1_bench = document.getElementById("panel-workbench-p1") p2_bench = document.getElementById("panel-workbench-p2") if p1_bench and p2_bench: if engine.phase >= 2: p1_bench.style.display = "none" p2_bench.style.display = "block" else: p1_bench.style.display = "block" p2_bench.style.display = "none" # Operations Tab Phase 2 Telemetry Deck if engine.phase >= 2: tel_hc = document.getElementById("telemetry-p2-headcount") if tel_hc: used_u = engine.get_server_usage() max_u = engine.get_max_server_capacity() tel_hc.innerText = f"{len(engine.tech_roster)} Techs ({used_u}/{max_u}U)" working_count = sum(1 for t in engine.tech_roster if t.get("status") == "working") idle_count = sum(1 for t in engine.tech_roster if t.get("status") == "idle") break_count = sum(1 for t in engine.tech_roster if t.get("status") == "break") burn_count = sum(1 for t in engine.tech_roster if t.get("status") == "burned_out") tel_w = document.getElementById("telemetry-p2-working") if tel_w: tel_w.innerText = f"🟒 {working_count} Working" tel_i = document.getElementById("telemetry-p2-idle") if tel_i: tel_i.innerText = f"πŸ”΅ {idle_count} Idle" tel_b = document.getElementById("telemetry-p2-break") if tel_b: tel_b.innerText = f"β˜• {break_count} Break" tel_bo = document.getElementById("telemetry-p2-burnout") if tel_bo: tel_bo.innerText = f"πŸ”₯ {burn_count} Burnout" avg_stress = (sum(t.get("stress", 0.0) for t in engine.tech_roster) / len(engine.tech_roster)) if engine.tech_roster else 0.0 morale_pct = max(0.0, min(100.0, 100.0 - avg_stress)) tel_mor_val = document.getElementById("telemetry-p2-morale-val") if tel_mor_val: tel_mor_val.innerText = f"{morale_pct:.0f}%" tel_mor_bar = document.getElementById("telemetry-p2-morale-bar") if tel_mor_bar: tel_mor_bar.style.width = f"{morale_pct}%" tel_mor_bar.className = "progress-bar bg-success" if morale_pct > 60 else ("progress-bar bg-warning" if morale_pct > 30 else "progress-bar bg-danger") tel_pay = document.getElementById("telemetry-p2-payroll") if tel_pay: tel_pay.innerText = f"-${getattr(engine, 'team_wage_burn_rate', 0.0):.2f}/s" # Calculate active resolution velocity tech_vel = sum(resources.STAFF_ROLES_CONFIG.get(t.get("role"), {}).get("base_speed_mult", 1.0) * t.get("speed_mult", 1.0) * 0.25 for t in engine.tech_roster if t.get("status") == "working") tel_vel = document.getElementById("telemetry-p2-velocity") if tel_vel: tel_vel.innerText = f"+{tech_vel:.2f} tix/s" # Candidate Talent Pool Grid (in Staff Tab) cand_grid = document.getElementById("candidate-pool-grid") if cand_grid and engine.phase >= 2: cand_timer = document.getElementById("lbl-candidate-timer") if cand_timer: cand_timer.innerText = f"Auto-refreshes in {getattr(engine, 'candidate_refresh_timer', 60.0):.0f}s" u_badge = document.getElementById("staff-server-u-badge") if u_badge: u_badge.innerText = f"Capacity: {engine.get_server_usage()}/{engine.get_max_server_capacity()}U Used" free_u = engine.get_free_server_capacity() cand_pool = getattr(engine, "candidate_pool", []) cand_ids = [c["id"] for c in cand_pool] cand_afford = [str(engine.cash >= c["hire_cost"] and free_u >= c["server_u"]) for c in cand_pool] cand_sig = f"{','.join(cand_ids)}_{','.join(cand_afford)}" global last_candidate_pool_sig if cand_sig != last_candidate_pool_sig: last_candidate_pool_sig = cand_sig if cand_pool: cand_cards = [] for c in cand_pool: c_id = c["id"] c_name = c["name"] c_role = c["role_title"] c_icon = c["icon"] c_speed = c["speed_mult"] c_wage = c["wage_per_sec"] * 60.0 c_cost = c["hire_cost"] c_u = c["server_u"] c_trait_name = c.get("trait_name", "Hard Worker") c_trait_icon = c.get("trait_icon", "πŸ’Ό") c_trait_desc = c.get("trait_desc", "Standard performance") can_hire = (engine.cash >= c_cost) and (free_u >= c_u) btn_cls = "btn-success fw-bold" if can_hire else "btn-outline-secondary disabled" cand_cards.append(f"""
    {c_icon} {c_name}
    {c_role}
    {c_u}U Space
    {c_trait_icon} {c_trait_name}
    {c_trait_desc}
    Speed:{c_speed:.2f}x
    Wage:${c_wage:.1f}/m
    Fee: ${c_cost:.2f}
    """) cand_grid.innerHTML = "".join(cand_cards) else: cand_grid.innerHTML = '
    No candidates currently available. Click Refresh Pool to recruit!
    ' # Update Live Technician Floor Roster grid (in Staff Tab) roster_grid = document.getElementById("tech-roster-grid") if roster_grid and engine.phase >= 2: roster_items = [f"{t.get('id')}_{t.get('status')}_{int(t.get('stress', 0))}_{t.get('level')}_{t.get('xp')}_{t.get('lifetime_solved')}" for t in engine.tech_roster] roster_sig = ",".join(roster_items) global last_roster_sig if roster_sig != last_roster_sig: last_roster_sig = roster_sig if engine.tech_roster: cards = [] # Check for solo rush pressure by queue unassigned_low = sum(1 for i in engine.active_incidents if i.get("severity") == "low" and not i.get("assigned_to")) unassigned_med = sum(1 for i in engine.active_incidents if i.get("severity") == "medium" and not i.get("assigned_to")) unassigned_high = sum(1 for i in engine.active_incidents if i.get("severity") == "high" and not i.get("assigned_to")) unassigned_onsite = sum(1 for i in engine.active_incidents if i.get("severity") in ["onsite", "critical"] and not i.get("assigned_to")) l1_count = len([t for t in engine.tech_roster if t.get("role") == "l1_tech"]) l2_count = len([t for t in engine.tech_roster if t.get("role") == "l2_tech"]) sys_count = len([t for t in engine.tech_roster if t.get("role") == "sysadmin"]) onsite_count = len([t for t in engine.tech_roster if t.get("role") == "onsite_tech"]) for t in engine.tech_roster: t_id = t["id"] t_name = t["name"] t_role = t["role"] t_status = t.get("status", "idle") t_stress = t.get("stress", 0.0) t_lvl = t.get("level", 1) t_xp = t.get("xp", 0) t_req_xp = t_lvl * 50 t_solved = t.get("lifetime_solved", 0) t_trait_name = t.get("trait_name", "Reliable") t_trait_icon = t.get("trait_icon", "πŸ›‘οΈ") # Solo rush detection for fantasy immersion is_solo_rush = False if t_role == "l1_tech" and l1_count == 1 and unassigned_low >= 2: is_solo_rush = True elif t_role == "l2_tech" and l2_count == 1 and unassigned_med >= 2: is_solo_rush = True elif t_role == "sysadmin" and sys_count == 1 and unassigned_high >= 2: is_solo_rush = True elif t_role == "onsite_tech" and onsite_count == 1 and unassigned_onsite >= 2: is_solo_rush = True rush_badge = '⚠️ Solo Rush' if is_solo_rush else "" role_icon = "πŸ“ž" if t_role == "l1_tech" else ("πŸ’»" if t_role == "l2_tech" else ("πŸ›‘οΈ" if t_role == "sysadmin" else "🚐")) role_title = "L1 Helpdesk" if t_role == "l1_tech" else ("L2 Support" if t_role == "l2_tech" else ("Sysadmin" if t_role == "sysadmin" else "Field On-Site")) if t_status == "working": status_badge = '⚑ Working' elif t_status == "break": status_badge = 'β˜• Break' elif t_status == "burned_out": status_badge = 'πŸ”₯ Burned Out' else: status_badge = '🟒 Idle' stress_bar_color = "bg-success" if t_stress < 50 else ("bg-warning" if t_stress < 80 else "bg-danger") wage_min = t.get("wage_per_sec", resources.STAFF_ROLES_CONFIG.get(t_role, {}).get("wage_per_sec", 0.05)) * 60.0 card_html = f"""
    {role_icon} {t_name} {rush_badge}
    {role_title} Β· Lvl {t_lvl} Β· {t_trait_icon} {t_trait_name}
    {status_badge}
    Fatigue / Stress: {t_stress:.1f}%
    Experience: {t_xp} / {t_req_xp} XP
    Solved: {t_solved} Wage: ${wage_min:.2f}/m
    """ cards.append(card_html) roster_grid.innerHTML = "".join(cards) else: roster_grid.innerHTML = '
    No support technicians hired yet. Recruit candidates from the talent pool above to build your team!
    ' # Update Payroll / Wage burn display staff_burn_el = document.getElementById("staff-burn-rate") if staff_burn_el: staff_burn_el.innerText = f"Payroll: -${getattr(engine, 'team_wage_burn_rate', 0.0):.2f}/s" # Phase 3 Personnel View vs Phase 2 Staff Roster View staff_p2 = document.getElementById("staff-phase2-view") staff_p3 = document.getElementById("staff-phase3-view") if staff_p2 and staff_p3: if engine.phase >= 3: staff_p2.style.display = "none" staff_p3.style.display = "block" else: staff_p2.style.display = "block" staff_p3.style.display = "none" if engine.phase == 3 and staff_p3: health = engine.get_cto_staffing_health() # Staffing Health Badge badge_health = document.getElementById("badge-cto-staffing-health") if badge_health: if health["status"] == "Optimal": badge_health.className = "badge bg-success fs-6 px-3 py-2 fw-bold" badge_health.innerText = "STAFFING: OPTIMAL" elif health["status"] == "Understaffed": badge_health.className = "badge bg-warning text-dark fs-6 px-3 py-2 fw-bold" badge_health.innerText = "STAFFING: UNDERSTAFFED" else: badge_health.className = "badge bg-info text-dark fs-6 px-3 py-2 fw-bold" badge_health.innerText = "STAFFING: OVERSTAFFED" # KPIs el_hired = document.getElementById("display-cto-leaders-hired") if el_hired: el_hired.innerText = f"{health['total_hired']} / {health['total_required']}" el_ratio = document.getElementById("display-cto-staffing-ratio") if el_ratio: el_ratio.innerText = f"{int(health['ratio'] * 100)}%" comp_spec = resources.CTO_COMPENSATION_POLICIES.get(engine.cto_staffing_policy, {}) salary_mult = comp_spec.get("salary_mult", 1.0) leadership_salary = sum(engine.cto_hired_leadership.get(r, 0) * spec["base_salary"] * salary_mult for r, spec in getattr(resources, "CTO_LEADERSHIP_ROLES", {}).items()) el_payroll = document.getElementById("display-cto-leader-payroll") if el_payroll: el_payroll.innerText = f"-${leadership_salary:.2f}/s" el_perk = document.getElementById("display-cto-perk-mult") if el_perk: el_perk.innerText = f"{engine.get_cto_leadership_bonus_mult():.2f}x" # Role cards for role_id, r_info in health["details"].items(): badge_dem = document.getElementById(f"badge-leader-demand-{role_id}") badge_cnt = document.getElementById(f"badge-leader-count-{role_id}") btn_hire = document.getElementById(f"btn-hire-leader-{role_id}") lbl_ratio = document.getElementById(f"lbl-leader-ratio-{role_id}") if lbl_ratio and r_info.get("facility_ratio_desc"): lbl_ratio.innerHTML = f'{r_info["facility_ratio_desc"]} (Target: {r_info["required"]})' if badge_dem: if r_info["hired"] < r_info["required"]: diff = r_info["required"] - r_info["hired"] badge_dem.className = "badge bg-warning text-dark me-2" badge_dem.innerText = f"{diff} Needed" else: badge_dem.className = "badge bg-success me-2" badge_dem.innerText = "Fulfilled" if badge_cnt: badge_cnt.innerText = f"{r_info['hired']} / {r_info['max_slots']}" if btn_hire: if r_info["hired"] >= r_info["max_slots"]: btn_hire.innerText = "Max Appointed" btn_hire.disabled = True btn_hire.className = "btn btn-sm btn-secondary fw-bold" else: btn_hire.innerText = f"Appoint (${r_info['cost']:,.2f})" btn_hire.disabled = (engine.cash < r_info["cost"] or engine.burnout or engine.pc_crashed or engine.is_rebooting) btn_hire.className = "btn btn-sm btn-outline-success fw-bold" # Policy buttons & descriptions for pol_k in ["lean", "market", "premium"]: p_btn = document.getElementById(f"btn-comp-policy-{pol_k}") if p_btn: if engine.cto_staffing_policy == pol_k: p_btn.className = "btn btn-outline-primary btn-sm active" else: p_btn.className = "btn btn-outline-secondary btn-sm" desc_comp = document.getElementById("box-comp-policy-desc") if desc_comp: desc_comp.innerText = comp_spec.get("desc", "") proc_spec = resources.CTO_PROCEDURE_POLICIES.get(engine.cto_procedure_policy, {}) for proc_k in ["agile", "balanced", "iso9001"]: pr_btn = document.getElementById(f"btn-proc-policy-{proc_k}") if pr_btn: if engine.cto_procedure_policy == proc_k: pr_btn.className = "btn btn-outline-primary btn-sm active" else: pr_btn.className = "btn btn-outline-secondary btn-sm" desc_proc = document.getElementById("box-proc-policy-desc") if desc_proc: desc_proc.innerText = proc_spec.get("desc", "") # Update Company-Wide Staffing Telemetry Dashboard wf = engine.get_headcount_breakdown() if hasattr(engine, "get_headcount_breakdown") else { "total": len(engine.tech_roster), "call_center_agents": 0, "support_specialists": 0, "dc_engineers": 0, "corporate_researchers": 0, "branch_leads": 0, "executive_leaders": 0, "core_techs": len(engine.tech_roster) } tps = engine.get_company_ticket_throughput_rate() if hasattr(engine, "get_company_ticket_throughput_rate") else 0.0 badge_tps = document.getElementById("display-corp-tps-badge") if badge_tps: badge_tps.innerText = f"⚑ {tps:.1f} Tickets/sec" el_wf_tot = document.getElementById("display-corp-total-workforce") if el_wf_tot: el_wf_tot.innerText = f"{wf['total']:,}" el_wf_tech = document.getElementById("display-corp-total-techs") if el_wf_tech: el_wf_tech.innerText = f"{wf['support_specialists'] + wf['core_techs']:,}" el_wf_br = document.getElementById("display-corp-branch-staff") if el_wf_br: el_wf_br.innerText = f"{wf['call_center_agents']:,}" el_wf_ld = document.getElementById("display-corp-leaders") if el_wf_ld: el_wf_ld.innerText = f"{wf['branch_leads'] + wf['executive_leaders']:,}" tot_wf = max(1, wf["total"]) def set_headcount_bar(cnt_id, bar_id, count, label_suffix="Staff"): el_c = document.getElementById(cnt_id) el_b = document.getElementById(bar_id) if el_c: el_c.innerText = f"{count:,} {label_suffix}" if el_b: pct = min(100.0, max(5.0 if count > 0 else 0.0, (count / tot_wf) * 100.0)) el_b.style.width = f"{pct:.1f}%" set_headcount_bar("lbl-dept-cc-count", "bar-dept-cc", wf["call_center_agents"], "Agents") set_headcount_bar("lbl-dept-hd-count", "bar-dept-hd", wf["support_specialists"], "Techs") set_headcount_bar("lbl-dept-dc-count", "bar-dept-dc", wf["dc_engineers"], "Engineers") set_headcount_bar("lbl-dept-corp-count", "bar-dept-corp", wf["corporate_researchers"], "Staff") set_headcount_bar("lbl-dept-lead-count", "bar-dept-lead", wf["branch_leads"] + wf["executive_leaders"], "Directors") set_headcount_bar("lbl-dept-core-count", "bar-dept-core", wf["core_techs"], "Techs") # Update Policies & R&D Tab Header & OpEx (Phase 3+) if engine.phase >= 3: badge_rp = document.getElementById("display-policies-rp-badge") if badge_rp: badge_rp.innerText = f"RP: {engine.research_points:.1f} / {engine.get_max_research_points():.0f}" policy_burn = 0.0 if "cloud_migration" in engine.active_it_policies: policy_burn += 0.25 if "on_call_rotation" in engine.active_it_policies: policy_burn += 0.20 if "ai_escalation" in engine.active_it_policies or "ai_auto_escalate" in engine.active_it_policies: policy_burn += 0.75 el_p_burn = document.getElementById("display-cto-policy-burn") if el_p_burn: el_p_burn.innerText = f"-${policy_burn:.2f}/s" # Synchronize Corporate IT Policy Governance Switches & Corporate HQ requirements has_corp_hq = engine.phase3_facilities.get("corporate", 0) >= 1 badge_hq = document.getElementById("badge-policy-hq-req") if badge_hq: if has_corp_hq: badge_hq.className = "badge bg-success bg-opacity-75 py-2 px-3 border border-success" badge_hq.innerText = "πŸ›οΈ Corporate HQ Online (Policies Active)" else: badge_hq.className = "badge bg-warning bg-opacity-75 py-2 px-3 border border-warning text-dark" badge_hq.innerText = "πŸ›οΈ Requires 1 Corporate HQ Facility" policy_switch_map = { "switch-policy-zero_trust_mfa": "zero_trust_mfa", "switch-policy-standard_soe": "standard_soe", "switch-policy-cloud_migration": "cloud_migration", "switch-policy-cicd_automation": "cicd_automation", "switch-policy-on_call_rotation": "on_call_rotation", "switch-policy-ai_escalation": "ai_escalation" } for el_id, p_key in policy_switch_map.items(): sw = document.getElementById(el_id) if sw: is_active = (p_key in engine.active_it_policies) or \ (p_key == "cicd_automation" and "cicd_devops" in engine.active_it_policies) or \ (p_key == "ai_escalation" and "ai_auto_escalate" in engine.active_it_policies) sw.checked = is_active sw.disabled = not has_corp_hq # Update Tech Manuals & Certifications HUD doc_section_visible = (engine.phase > 1 or engine.get_progression_tier() >= 2) if doc_section_visible: document.getElementById("display-manuals-count").innerText = f"{engine.tech_manuals} Manuals" manual_rate = 0.50 * engine.tech_manuals document.getElementById("display-manuals-income").innerText = f"+${manual_rate:.2f}/s passive" manual_cost = engine.get_manual_kb_cost() btn_manual = document.getElementById("btn-publish-manual") btn_manual.innerText = f"πŸ“˜ Publish Manual ({manual_cost} KB)" if engine.kb_articles >= manual_cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_manual.removeAttribute("disabled") else: btn_manual.setAttribute("disabled", "true") # AI Documenter Progressive Controls (Operations tab) ai_doc_panel = document.getElementById("panel-ai-documenter") if ai_doc_panel: ai_doc_lvl = engine.upgrades.get("ai_documenter", 0) if ai_doc_lvl > 0: ai_doc_panel.style.display = "block" lvl_badge = document.getElementById("badge-ai-documenter-level") if lvl_badge: lvl_badge.innerText = f"Lvl {ai_doc_lvl}" cfg = resources.AI_DOCUMENTER_CONFIG.get(ai_doc_lvl, resources.AI_DOCUMENTER_CONFIG[1]) # Subroutine 1: Auto-Research for sw_id in ["switch-ai-auto-research", "switch-auto-research"]: sw_res = document.getElementById(sw_id) if sw_res: sw_res.checked = getattr(engine, "auto_research_active", False) lbl_res_sub = document.getElementById("lbl-auto-research-sub") if lbl_res_sub: lbl_res_sub.innerText = f"1 Ticket βž” +{cfg['kp_gain']:.0f} KP / {cfg['interval_sec']:.0f}s (${cfg['cash_drain_per_sec']:.2f}/s)" # Subroutine 2: Auto-KB Articles for sw_id in ["switch-ai-auto-kb", "switch-auto-kb"]: sw_kb = document.getElementById(sw_id) if sw_kb: sw_kb.checked = getattr(engine, "auto_kb_active", False) # Subroutine 3: Auto-Manuals for sw_id in ["switch-ai-auto-manuals", "switch-auto-manuals"]: sw_man = document.getElementById(sw_id) if sw_man: if ai_doc_lvl >= 2: sw_man.removeAttribute("disabled") sw_man.checked = getattr(engine, "auto_manuals_active", False) else: sw_man.setAttribute("disabled", "true") sw_man.checked = False lbl_man_sub = document.getElementById("lbl-auto-manuals-sub") if lbl_man_sub: if ai_doc_lvl >= 2: lbl_man_sub.className = "text-info font-monospace" lbl_man_sub.innerText = f"{cfg.get('manual_kb_cost', 3)} KB βž” 1 Manual / {cfg.get('manual_interval_sec', 45):.0f}s" else: lbl_man_sub.className = "text-warning font-monospace" lbl_man_sub.innerText = "πŸ”’ Req: Lvl 2 Documenter" # Subroutine 4: Auto-Courses for sw_id in ["switch-ai-auto-courses", "switch-auto-courses"]: sw_crs = document.getElementById(sw_id) if sw_crs: if ai_doc_lvl >= 3: sw_crs.removeAttribute("disabled") sw_crs.checked = getattr(engine, "auto_courses_active", False) else: sw_crs.setAttribute("disabled", "true") sw_crs.checked = False lbl_crs_sub = document.getElementById("lbl-auto-courses-sub") if lbl_crs_sub: if ai_doc_lvl >= 3: lbl_crs_sub.className = "text-info font-monospace" lbl_crs_sub.innerText = f"{engine.get_course_manual_cost()} Manuals βž” 1 Course / 60s" else: lbl_crs_sub.className = "text-warning font-monospace" lbl_crs_sub.innerText = "πŸ”’ Req: Lvl 3 Documenter" else: ai_doc_panel.style.display = "none" # Auto-Dispatch Controls (Incident Dispatcher Board) lock_ad = document.getElementById("lock-auto-dispatch") container_ad = document.getElementById("container-switch-auto-dispatch") sw_ad = document.getElementById("switch-auto-dispatch") has_ad_software = engine.upgrades.get("auto_dispatcher_software", 0) > 0 if has_ad_software: if lock_ad: lock_ad.style.display = "none" if container_ad: container_ad.style.display = "block" if sw_ad: sw_ad.checked = getattr(engine, "auto_dispatch", False) else: if lock_ad: lock_ad.style.display = "inline-block" if container_ad: container_ad.style.display = "none" if sw_ad: sw_ad.checked = False container_cert = document.getElementById("container-cert-courses") if engine.phase >= 2: container_cert.style.display = "block" document.getElementById("display-courses-count").innerText = f"{engine.certification_courses} Courses" course_rate = 5.00 * engine.certification_courses course_cp = 1.0 * engine.certification_courses document.getElementById("display-courses-income").innerText = f"+${course_rate:.2f}/s, +{course_cp:.1f} CP/m" course_cost = engine.get_course_manual_cost() btn_course = document.getElementById("btn-publish-course") btn_course.innerText = f"πŸŽ“ Publish Course ({course_cost} Manuals)" if engine.tech_manuals >= course_cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: btn_course.removeAttribute("disabled") else: btn_course.setAttribute("disabled", "true") else: container_cert.style.display = "none" # Dynamic stable incident board rendering (in-place updates to eliminate DOM rebuilding & flickering) tbody = document.getElementById("incidents-board-body") if not engine.active_incidents: tbody.innerHTML = '🟒 Incidents Queue Empty! Techs standby...' else: # Clear empty queue placeholder if present empty_ph = tbody.querySelector("td[colspan='5']") if empty_ph: tbody.innerHTML = "" active_ids = {inc["id"] for inc in engine.active_incidents} # Remove completed/expired incident rows existing_rows = list(tbody.querySelectorAll("tr[id^='inc-row-']")) for r in existing_rows: r_id = r.id.replace("inc-row-", "") if r_id not in active_ids: r.remove() for inc in engine.active_incidents: inc_id = inc["id"] cat = inc["category"] sev = inc["severity"] sla_rem = inc["sla_remaining"] sla_max = inc["sla_max"] progress = inc["progress"] assigned = inc.get("assigned_to") assigned_tech = next((t for t in engine.tech_roster if t["id"] == assigned or t.get("role") == assigned), None) assigned_role = assigned_tech.get("role") if assigned_tech else (assigned if assigned else None) assigned_name = assigned_tech.get("name") if assigned_tech else ("Unassigned" if not assigned else str(assigned)) sev_class = "bg-success" if sev == "low" else ("bg-warning text-dark" if sev == "medium" else "bg-danger") sla_pct = min(100.0, max(0.0, (sla_rem / sla_max) * 100.0)) sla_bar_class = "bg-success" if sla_pct > 50.0 else ("bg-warning" if sla_pct > 25.0 else "bg-danger progress-bar-striped progress-bar-animated") total_l1 = len([t for t in engine.tech_roster if t.get("role") == "l1_tech"]) or engine.upgrades.get("l1_techs", 0) total_l2 = len([t for t in engine.tech_roster if t.get("role") == "l2_tech"]) or engine.upgrades.get("l2_techs", 0) total_sys = len([t for t in engine.tech_roster if t.get("role") == "sysadmin"]) or engine.upgrades.get("sysadmins", 0) total_onsite = len([t for t in engine.tech_roster if t.get("role") == "onsite_tech"]) or len(getattr(engine, "hired_onsite_techs", [])) l1_disabled = not (total_l1 > 0 and sev == "low") l2_disabled = not (total_l2 > 0 and sev in ["low", "medium"]) sys_disabled = not (total_sys > 0) onsite_disabled = not (total_onsite > 0) row_el = document.getElementById(f"inc-row-{inc_id}") if not row_el: row_html = f""" {cat} {f'πŸ‘€ {assigned_name}' if assigned else '⏳ Unassigned'} {sev.upper()}
    {sla_rem:.1f}s
    {progress:.1f}%
    """ tbody.insertAdjacentHTML("beforeend", row_html) # Perform in-place updates without replacing HTML elements tech_txt = document.getElementById(f"inc-tech-text-{inc_id}") if tech_txt: tech_txt.innerHTML = f'πŸ‘€ {assigned_name}' if assigned else '⏳ Unassigned' sla_bar = document.getElementById(f"inc-sla-bar-{inc_id}") if sla_bar: sla_bar.style.width = f"{sla_pct}%" sla_bar.className = f"progress-bar {sla_bar_class}" sla_txt = document.getElementById(f"inc-sla-text-{inc_id}") if sla_txt: sla_txt.innerText = f"{sla_rem:.1f}s" prog_bar = document.getElementById(f"inc-prog-bar-{inc_id}") if prog_bar: prog_bar.style.width = f"{progress:.1f}%" prog_txt = document.getElementById(f"inc-prog-text-{inc_id}") if prog_txt: prog_txt.innerText = f"{progress:.1f}%" # Update button classes and disabled states in-place l1_btn = document.getElementById(f"btn-inc-l1-{inc_id}") if l1_btn: l1_btn.className = f"btn {'btn-info text-dark fw-bold' if (assigned_role == 'l1_tech' or str(assigned).startswith('l1_')) else 'btn-outline-secondary'} px-2 py-0 fw-bold" if l1_disabled: l1_btn.setAttribute("disabled", "true") else: l1_btn.removeAttribute("disabled") l2_btn = document.getElementById(f"btn-inc-l2-{inc_id}") if l2_btn: l2_btn.className = f"btn {'btn-primary fw-bold' if (assigned_role == 'l2_tech' or str(assigned).startswith('l2_')) else 'btn-outline-secondary'} px-2 py-0 fw-bold" if l2_disabled: l2_btn.setAttribute("disabled", "true") else: l2_btn.removeAttribute("disabled") sys_btn = document.getElementById(f"btn-inc-sys-{inc_id}") if sys_btn: sys_btn.className = f"btn {'btn-warning text-dark fw-bold' if (assigned_role == 'sysadmin' or str(assigned).startswith('sys_')) else 'btn-outline-secondary'} px-2 py-0 fw-bold" if sys_disabled: sys_btn.setAttribute("disabled", "true") else: sys_btn.removeAttribute("disabled") onsite_btn = document.getElementById(f"btn-inc-onsite-{inc_id}") if onsite_btn: is_onsite_assigned = (assigned_role == 'onsite_tech' or str(assigned).startswith('onsite_')) onsite_btn.className = f"btn {'btn-success text-white fw-bold' if is_onsite_assigned else 'btn-outline-secondary'} px-2 py-0 fw-bold" if onsite_disabled: onsite_btn.setAttribute("disabled", "true") else: onsite_btn.removeAttribute("disabled") clear_btn = document.getElementById(f"btn-inc-clear-{inc_id}") if clear_btn: clear_btn.className = f"btn {'btn-outline-danger' if assigned is not None else 'btn-outline-secondary'} px-2 py-0 fw-bold" if assigned is None: clear_btn.setAttribute("disabled", "true") else: clear_btn.removeAttribute("disabled") # Render Mobile Incident Cards (< 768px) mobile_pane = document.getElementById("incidents-board-mobile") if mobile_pane: if not engine.active_incidents: mobile_pane.innerHTML = '
    🟒 Incidents Queue Empty! Techs standby...
    ' else: active_ids = {inc["id"] for inc in engine.active_incidents} for c in list(mobile_pane.querySelectorAll("div[id^='inc-card-']")): c_id = c.id.replace("inc-card-", "") if c_id not in active_ids: c.remove() for inc in engine.active_incidents: inc_id = inc["id"] cat = inc["category"] sev = inc["severity"] sla_rem = inc["sla_remaining"] sla_max = inc["sla_max"] progress = inc["progress"] assigned = inc.get("assigned_to") assigned_tech = next((t for t in engine.tech_roster if t["id"] == assigned or t.get("role") == assigned), None) assigned_role = assigned_tech.get("role") if assigned_tech else (assigned if assigned else None) assigned_name = assigned_tech.get("name") if assigned_tech else ("Unassigned" if not assigned else str(assigned)) sev_class = "bg-success" if sev == "low" else ("bg-warning text-dark" if sev == "medium" else "bg-danger") sla_pct = min(100.0, max(0.0, (sla_rem / sla_max) * 100.0)) sla_bar_class = "bg-success" if sla_pct > 50.0 else ("bg-warning" if sla_pct > 25.0 else "bg-danger progress-bar-striped progress-bar-animated") total_l1 = len([t for t in engine.tech_roster if t.get("role") == "l1_tech"]) or engine.upgrades.get("l1_techs", 0) total_l2 = len([t for t in engine.tech_roster if t.get("role") == "l2_tech"]) or engine.upgrades.get("l2_techs", 0) total_sys = len([t for t in engine.tech_roster if t.get("role") == "sysadmin"]) or engine.upgrades.get("sysadmins", 0) total_onsite = len([t for t in engine.tech_roster if t.get("role") == "onsite_tech"]) or len(getattr(engine, "hired_onsite_techs", [])) l1_disabled = not (total_l1 > 0 and sev == "low") l2_disabled = not (total_l2 > 0 and sev in ["low", "medium"]) sys_disabled = not (total_sys > 0) onsite_disabled = not (total_onsite > 0) card_el = document.getElementById(f"inc-card-{inc_id}") if not card_el: card_html = f"""
    {cat} {sev.upper()}
    SLA:
    {sla_rem:.1f}s
    Prog:
    {progress:.0f}%
    Assign:
    """ mobile_pane.insertAdjacentHTML("beforeend", card_html) c_sla_bar = document.getElementById(f"inc-sla-card-bar-{inc_id}") if c_sla_bar: c_sla_bar.style.width = f"{sla_pct}%" c_sla_bar.className = f"progress-bar {sla_bar_class}" c_sla_txt = document.getElementById(f"inc-sla-card-text-{inc_id}") if c_sla_txt: c_sla_txt.innerText = f"{sla_rem:.1f}s" c_prog_bar = document.getElementById(f"inc-prog-card-bar-{inc_id}") if c_prog_bar: c_prog_bar.style.width = f"{progress:.1f}%" c_prog_txt = document.getElementById(f"inc-prog-card-text-{inc_id}") if c_prog_txt: c_prog_txt.innerText = f"{progress:.0f}%" sel = document.getElementById(f"inc-assign-select-{inc_id}") if sel: cur_val = assigned_role if assigned_role in ["l1_tech", "l2_tech", "sysadmin", "onsite_tech"] else ("none" if not assigned else "l1_tech") if not assigned: cur_val = "none" if sel.value != cur_val: sel.value = cur_val # Research button state btn_res = document.getElementById("btn-research") if btn_res: if engine.burnout or engine.pc_crashed or engine.is_rebooting or is_busy or engine.knowledge_points >= engine.get_max_kp() or engine.solved_tickets_inventory < 1: btn_res.setAttribute("disabled", "true") else: btn_res.removeAttribute("disabled") # Achievements badges list for ach_id in engine.achievements: ach_el = document.getElementById(f"ach-{ach_id}") if ach_el: if engine.achievements[ach_id]: ach_el.className = "list-group-item d-flex justify-content-between align-items-center bg-dark text-white border-success mb-2 p-3 rounded" status_icon = ach_el.querySelector(".achievement-status") if status_icon: status_icon.innerHTML = "🟒 Unlocked" status_icon.className = "badge bg-success achievement-status" else: ach_el.className = "list-group-item d-flex justify-content-between align-items-center bg-dark text-muted mb-2 p-3 rounded" status_icon = ach_el.querySelector(".achievement-status") if status_icon: status_icon.innerHTML = "πŸ”’ Locked" status_icon.className = "badge bg-secondary achievement-status" # Re-evaluate upgrades subtabs and automation console switches upgrade_subtabs = document.getElementById("upgradeSubTabs") if engine.phase >= 2: upgrade_subtabs.style.display = "flex" else: upgrade_subtabs.style.display = "none" panel_ai = document.getElementById("panel-ai-documenter") if panel_ai: if engine.upgrades.get("ai_documenter", 0) > 0: panel_ai.style.display = "block" sw_res = document.getElementById("switch-auto-research") if sw_res: sw_res.checked = engine.auto_research_active sw_kb = document.getElementById("switch-auto-kb") if sw_kb: sw_kb.checked = engine.auto_kb_active else: panel_ai.style.display = "none" # Phase 3 CTO View vs Phase 4 Enterprise View vs Standard View & Sidebar Hardware HUD ops_std = document.getElementById("operations-standard-view") ops_cto = document.getElementById("operations-cto-view") ops_ent = document.getElementById("operations-enterprise-view") if ops_std and ops_ent: if engine.phase >= 4: ops_std.style.display = "none" if ops_cto: ops_cto.style.display = "none" ops_ent.style.display = "block" elif engine.phase == 3: ops_std.style.display = "none" if ops_cto: ops_cto.style.display = "block" ops_ent.style.display = "none" else: ops_std.style.display = "block" if ops_cto: ops_cto.style.display = "none" ops_ent.style.display = "none" # Phase 3 CTO Command Suite In-Place UI Updates if engine.phase == 3: # 1. Executive Aura badge_aura = document.getElementById("badge-cto-aura") txt_aura_cd = document.getElementById("txt-cto-aura-cd") if badge_aura and txt_aura_cd: if engine.executive_aura_duration > 0.0: badge_aura.style.display = "inline-block" txt_aura_cd.innerText = f"{engine.executive_aura_duration:.1f}s" else: badge_aura.style.display = "none" # 2. Comprehensive Company-Wide Telemetry Matrix el_cash_val = document.getElementById("display-cto-cash-val") if el_cash_val: el_cash_val.innerText = f"${engine.cash:,.2f}" el_cash_flow = document.getElementById("display-cto-cash-flow") if el_cash_flow: mrr_sec = engine.get_mrr() / 2592000.0 if hasattr(engine, "get_mrr") else 0.0 fac_opex = engine.get_phase3_facilities_opex() tech_wages = engine.get_total_wages() if hasattr(engine, "get_total_wages") else 0.0 net_sec = mrr_sec - fac_opex - tech_wages sign_f = "+" if net_sec >= 0 else "-" el_cash_flow.innerText = f"{sign_f}${abs(net_sec):,.2f}/s net" el_cash_flow.className = "text-success font-monospace d-block" if net_sec >= 0 else "text-danger font-monospace d-block" el_staff_cnt = document.getElementById("display-cto-staff-count") if el_staff_cnt: el_staff_cnt.innerText = f"{engine.get_enterprise_headcount():,} Employees" el_mgr_cnt = document.getElementById("display-cto-mgr-count") if el_mgr_cnt: hired_mgrs = len([b for b in engine.branch_managers.values() if b.get("hired")]) el_mgr_cnt.innerText = f"{hired_mgrs}/4 Branch Leads" el_stress_val = document.getElementById("display-cto-stress-val") if el_stress_val: el_stress_val.innerText = f"{engine.stress:.1f}%" bar_stress = document.getElementById("bar-cto-stress") if bar_stress: bar_stress.style.width = f"{min(100.0, engine.stress):.1f}%" bar_stress.className = "progress-bar bg-danger" if engine.burnout or engine.stress > 75 else ("progress-bar bg-warning" if engine.stress > 40 else "progress-bar bg-success") badge_stress_st = document.getElementById("badge-cto-stress-status") if badge_stress_st: if engine.burnout: badge_stress_st.className = "badge bg-danger" badge_stress_st.innerText = "BURNOUT" elif engine.stress > 60: badge_stress_st.className = "badge bg-warning text-dark" badge_stress_st.innerText = "HIGH" else: badge_stress_st.className = "badge bg-secondary" badge_stress_st.innerText = "NORMAL" el_csi_val = document.getElementById("display-cto-csi-val") if el_csi_val: el_csi_val.innerText = f"{getattr(engine, 'customer_satisfaction', 100.0):.1f}% CSI" el_sla_val = document.getElementById("display-cto-sla-val") if el_sla_val: el_sla_val.innerText = f"{getattr(engine, 'sla_breaches_total', 0)} SLA Breaches" el_tot_fac = document.getElementById("display-cto-total-facilities") if el_tot_fac: el_tot_fac.innerText = f"{sum(engine.phase3_facilities.values())} Active" el_fac_opex = document.getElementById("display-cto-facility-opex") if el_fac_opex: el_fac_opex.innerText = f"-${engine.get_phase3_facilities_opex():.2f}/s OpEx" el_cto_vel = document.getElementById("display-cto-solve-velocity") if el_cto_vel: el_cto_vel.innerText = f"{1.0 + engine.phase3_facilities.get('helpdesk', 0)*0.25:.1f}/s Vel" el_q_cnt = document.getElementById("display-cto-queue-count") if el_q_cnt: el_q_cnt.innerText = f"{engine.tickets_queue} in Queue" el_cto_kp_ov = document.getElementById("display-cto-kp-val") if el_cto_kp_ov: el_cto_kp_ov.innerText = f"{engine.knowledge_points:,.1f} KP" el_cto_rd_nodes = document.getElementById("display-cto-rd-nodes") if el_cto_rd_nodes: el_cto_rd_nodes.innerText = f"{len(engine.rd_techs_unlocked)}/9 Techs Researched" el_mrr_val = document.getElementById("display-cto-mrr-val") if el_mrr_val: mrr_val = engine.get_mrr() if hasattr(engine, "get_mrr") else 0.0 el_mrr_val.innerText = f"${mrr_val:,.2f}/mo" el_debt_val = document.getElementById("display-cto-debt-val") if el_debt_val: debt = getattr(engine, "total_debt", getattr(engine, "loan_principal", 0.0)) el_debt_val.innerText = f"${debt:,.2f} Loan Debt" # Infrastructure Governor & SLA Compliance Console infra = engine.get_infra_status() if hasattr(engine, "get_infra_status") else {} b_sla = document.getElementById("badge-infra-sla") if b_sla and "sla_uptime_pct" in infra: sla_val = infra["sla_uptime_pct"] if sla_val >= 99.99: b_sla.className = "badge bg-success font-monospace fs-6 px-3 py-2 fw-bold" b_sla.innerText = f"{sla_val:.3f}% SLA (FIVE NINES)" elif sla_val >= 99.0: b_sla.className = "badge bg-warning text-dark font-monospace fs-6 px-3 py-2 fw-bold" b_sla.innerText = f"{sla_val:.3f}% SLA (WARNING)" else: b_sla.className = "badge bg-danger font-monospace fs-6 px-3 py-2 fw-bold" b_sla.innerText = f"{sla_val:.3f}% SLA (BREACH)" # Live Financial Cashflow & Revenue Breakdown calculation & DOM update tf = current_cashflow_timeframe mode = current_cashflow_mode # Timeframe units and multipliers (1 in-game day = 216s, 1 month = 6480s, 1 quarter = 19440s, 1 year = 77760s) tf_labels = { "sec": "/s", "day": "/day", "month": "/mo", "quarter": "/Q", "year": "/yr" } unit_str = tf_labels.get(tf, "/s") if mode == "real": # Real continuous flows (actual delta per real-time second multiplied by timeframe duration) real_seconds_map = { "sec": 1.0, "day": 216.0, "month": 6480.0, "quarter": 19440.0, "year": 77760.0 } tf_mult = real_seconds_map.get(tf, 1.0) contracts_inflow = (engine.get_mrr() / 6480.0) * tf_mult ticket_tps = engine.get_company_ticket_throughput_rate() avg_tix_payout = resources.get_ticket_cash_value("low", engine.upgrades.get("dual_monitors", 0), engine.xp, engine.sla, engine.kb_articles, engine.upgrades.get("ticket_payout_boost", 0), engine.csi) tickets_inflow = ticket_tps * avg_tix_payout * tf_mult sla_bonus_inflow = 0.0 if infra.get("sla_uptime_pct", 100.0) >= 99.99: sla_bonus_inflow = (contracts_inflow + tickets_inflow) * 0.15 total_inflow = contracts_inflow + tickets_inflow + sla_bonus_inflow fac_opex = engine.get_phase3_facilities_opex() * tf_mult mgmt_salary_sec = 0.0 for b_id, b_data in engine.branch_managers.items(): if b_data.get("hired"): spec = resources.CALL_CENTER_BRANCHES.get(b_id, {}) dir_k = b_data.get("directive", "quality") dir_spec = resources.BRANCH_DIRECTIVE_SPECS.get(dir_k, {}) mgmt_salary_sec += spec.get("base_salary", 0.35) * dir_spec.get("salary_mult", 1.0) comp_spec = resources.CTO_COMPENSATION_POLICIES.get(engine.cto_staffing_policy, {}) salary_mult = comp_spec.get("salary_mult", 1.0) mgmt_salary_sec += sum(engine.cto_hired_leadership.get(r, 0) * spec["base_salary"] * salary_mult for r, spec in getattr(resources, "CTO_LEADERSHIP_ROLES", {}).items()) mgmt_salary = mgmt_salary_sec * tf_mult tech_payroll = getattr(engine, "team_wage_burn_rate", 0.0) * tf_mult policy_opex_sec = 0.0 for pol in engine.active_it_policies: pol_spec = getattr(resources, "IT_POLICIES_REGISTRY", {}).get(pol, {}) policy_opex_sec += pol_spec.get("opex_per_sec", 0.0) policy_opex = policy_opex_sec * tf_mult total_outflow = fac_opex + mgmt_salary + tech_payroll + policy_opex else: # Calendar Accounting Run-Rate (Monthly run-rate scaled to nominal accounting periods) cal_mult_map = { "sec": 1.0 / 6480.0, "day": 1.0 / 30.0, "month": 1.0, "quarter": 3.0, "year": 12.0 } tf_mult = cal_mult_map.get(tf, 1.0) mrr_nominal = engine.get_mrr() contracts_inflow = mrr_nominal * tf_mult ticket_tps = engine.get_company_ticket_throughput_rate() avg_tix_payout = resources.get_ticket_cash_value("low", engine.upgrades.get("dual_monitors", 0), engine.xp, engine.sla, engine.kb_articles, engine.upgrades.get("ticket_payout_boost", 0), engine.csi) # 1 nominal month = 6480s of ticket velocity tickets_inflow = ticket_tps * avg_tix_payout * 6480.0 * tf_mult sla_bonus_inflow = 0.0 if infra.get("sla_uptime_pct", 100.0) >= 99.99: sla_bonus_inflow = (contracts_inflow + tickets_inflow) * 0.15 total_inflow = contracts_inflow + tickets_inflow + sla_bonus_inflow fac_opex = engine.get_phase3_facilities_opex() * 6480.0 * tf_mult mgmt_salary_sec = 0.0 for b_id, b_data in engine.branch_managers.items(): if b_data.get("hired"): spec = resources.CALL_CENTER_BRANCHES.get(b_id, {}) dir_k = b_data.get("directive", "quality") dir_spec = resources.BRANCH_DIRECTIVE_SPECS.get(dir_k, {}) mgmt_salary_sec += spec.get("base_salary", 0.35) * dir_spec.get("salary_mult", 1.0) comp_spec = resources.CTO_COMPENSATION_POLICIES.get(engine.cto_staffing_policy, {}) salary_mult = comp_spec.get("salary_mult", 1.0) mgmt_salary_sec += sum(engine.cto_hired_leadership.get(r, 0) * spec["base_salary"] * salary_mult for r, spec in getattr(resources, "CTO_LEADERSHIP_ROLES", {}).items()) mgmt_salary = mgmt_salary_sec * 6480.0 * tf_mult tech_payroll = getattr(engine, "team_wage_burn_rate", 0.0) * 6480.0 * tf_mult policy_opex_sec = 0.0 for pol in engine.active_it_policies: pol_spec = getattr(resources, "IT_POLICIES_REGISTRY", {}).get(pol, {}) policy_opex_sec += pol_spec.get("opex_per_sec", 0.0) policy_opex = policy_opex_sec * 6480.0 * tf_mult total_outflow = fac_opex + mgmt_salary + tech_payroll + policy_opex net_val = total_inflow - total_outflow el_subnote = document.getElementById("txt-breakdown-subnote") if el_subnote: if mode == "real": el_subnote.innerText = "⚑ Real Continuous: Actual real-time funds flowing into/out of your bank balance (1 game day = 216s, 1 game month = 6,480s)." else: el_subnote.innerText = "πŸ“Š Accounting Run-Rate: Nominal periodic run-rate metrics (30 days/month, 90 days/quarter, 360 days/year)." el_net_badge = document.getElementById("display-breakdown-net-badge") if el_net_badge: sign = "+" if net_val >= 0 else "-" el_net_badge.className = f"badge font-monospace {'bg-success' if net_val >= 0 else 'bg-danger'}" el_net_badge.innerText = f"Net: {sign}${abs(net_val):,.2f}{unit_str}" el_inflow_tot = document.getElementById("display-breakdown-total-income") if el_inflow_tot: el_inflow_tot.innerText = f"+${total_inflow:,.2f}{unit_str}" el_inflow_c = document.getElementById("display-breakdown-contracts") if el_inflow_c: el_inflow_c.innerText = f"+${contracts_inflow:,.2f}{unit_str}" el_inflow_t = document.getElementById("display-breakdown-tickets") if el_inflow_t: el_inflow_t.innerText = f"+${tickets_inflow:,.2f}{unit_str}" el_inflow_s = document.getElementById("display-breakdown-sla-bonus") if el_inflow_s: el_inflow_s.innerText = f"+${sla_bonus_inflow:,.2f}{unit_str}" el_outflow_tot = document.getElementById("display-breakdown-total-expense") if el_outflow_tot: el_outflow_tot.innerText = f"-${total_outflow:,.2f}{unit_str}" el_outflow_f = document.getElementById("display-breakdown-fac-opex") if el_outflow_f: el_outflow_f.innerText = f"-${fac_opex:,.2f}{unit_str}" el_outflow_m = document.getElementById("display-breakdown-mgmt-salary") if el_outflow_m: el_outflow_m.innerText = f"-${mgmt_salary:,.2f}{unit_str}" el_outflow_p = document.getElementById("display-breakdown-tech-payroll") if el_outflow_p: el_outflow_p.innerText = f"-${tech_payroll:,.2f}{unit_str}" el_outflow_pol = document.getElementById("display-breakdown-policy-opex") if el_outflow_pol: el_outflow_pol.innerText = f"-${policy_opex:,.2f}{unit_str}" el_strain = document.getElementById("display-infra-strain-val") if el_strain and "thermal_strain" in infra: el_strain.innerText = f"{infra['thermal_strain']:.1f}%" bar_strain = document.getElementById("bar-infra-strain") if bar_strain and "thermal_strain" in infra: s_val = infra["thermal_strain"] bar_strain.style.width = f"{s_val:.1f}%" bar_strain.className = "progress-bar " + ("bg-danger" if s_val >= 80 else ("bg-warning" if s_val >= 50 else "bg-info")) el_cool = document.getElementById("display-infra-cooling-val") if el_cool and "cooling_capacity" in infra: el_cool.innerText = f"+{infra['cooling_capacity']:.1f}/s" txt_maint = document.getElementById("txt-infra-maint-cd") btn_maint = document.getElementById("btn-infra-maintenance") if txt_maint and btn_maint and "cooldown_remaining" in infra: cd = infra["cooldown_remaining"] if cd > 0.0: txt_maint.innerText = f"{cd:.0f}s CD" btn_maint.disabled = True else: txt_maint.innerText = "READY" btn_maint.disabled = (engine.burnout or engine.pc_crashed or engine.is_rebooting) # Mode buttons active state cur_mode = getattr(engine, "infra_governor_mode", "nominal") for m_k in ["eco", "nominal", "turbo"]: b_m = document.getElementById(f"btn-infra-mode-{m_k}") if b_m: spec = resources.INFRASTRUCTURE_GOVERNOR_MODES.get(m_k, {}) outline_cls = spec.get("badge_class", "bg-primary").replace("bg-", "btn-outline-") if cur_mode == m_k: b_m.className = f"btn {outline_cls} active w-100 p-2 text-start h-100" else: b_m.className = f"btn {outline_cls} w-100 p-2 text-start h-100" # 2.5 Executive Company-Wide Telemetry KPI Cards el_cto_cash = document.getElementById("display-cto-cash-val") if el_cto_cash: el_cto_cash.innerText = f"${engine.cash:,.2f}" el_cto_cflow = document.getElementById("display-cto-cash-flow") if el_cto_cflow: sign_cf = "+" if net_val >= 0 else "-" el_cto_cflow.innerText = f"{sign_cf}${abs(net_val):,.2f}/s net" el_cto_cflow.className = f"font-monospace d-block {'text-success' if net_val >= 0 else 'text-danger'}" wf = engine.get_company_workforce_breakdown() if hasattr(engine, "get_company_workforce_breakdown") else {} el_cto_staff = document.getElementById("display-cto-staff-count") if el_cto_staff: el_cto_staff.innerText = f"{wf.get('total_workforce', 0):,} Staff" el_cto_mgr = document.getElementById("display-cto-mgr-count") if el_cto_mgr: hired_mgrs = len([b for b in getattr(engine, 'branch_managers', {}).values() if b.get('hired')]) el_cto_mgr.innerText = f"{hired_mgrs}/4 Branch Leads" el_cto_stress = document.getElementById("display-cto-stress-val") if el_cto_stress: el_cto_stress.innerText = f"{engine.stress:.1f}%" bar_cto_stress = document.getElementById("bar-cto-stress") if bar_cto_stress: bar_cto_stress.style.width = f"{engine.stress:.1f}%" bar_cto_stress.className = "progress-bar " + ("bg-danger" if engine.stress >= 80 else ("bg-warning" if engine.stress >= 50 else "bg-success")) badge_cto_stress = document.getElementById("badge-cto-stress-status") if badge_cto_stress: if engine.burnout: badge_cto_stress.className = "badge bg-danger" badge_cto_stress.innerText = "BURNOUT" elif engine.stress >= 75.0: badge_cto_stress.className = "badge bg-warning text-dark" badge_cto_stress.innerText = "HIGH STRESS" else: badge_cto_stress.className = "badge bg-success" badge_cto_stress.innerText = "OPTIMAL" badge_cto_sla = document.getElementById("badge-cto-sla") if badge_cto_sla: badge_cto_sla.innerText = f"SLA {engine.sla:.1f}%" badge_cto_sla.className = "badge " + ("bg-success bg-opacity-25 text-success" if engine.sla >= 85 else ("bg-warning bg-opacity-25 text-warning" if engine.sla >= 60 else "bg-danger bg-opacity-25 text-danger")) el_cto_csi = document.getElementById("display-cto-csi-val") if el_cto_csi: el_cto_csi.innerText = f"{engine.csi:.1f}% CSI" el_cto_sla = document.getElementById("display-cto-sla-val") if el_cto_sla: el_cto_sla.innerText = f"SLA: {engine.sla:.1f}% Compliance" el_cto_fac = document.getElementById("display-cto-total-facilities") if el_cto_fac: el_cto_fac.innerText = f"{sum(engine.phase3_facilities.values())} Active" el_cto_fac_op = document.getElementById("display-cto-facility-opex") if el_cto_fac_op: el_cto_fac_op.innerText = f"-${engine.get_phase3_facilities_opex():,.2f}/s OpEx" el_cto_vel = document.getElementById("display-cto-solve-velocity") if el_cto_vel: el_cto_vel.innerText = f"{ticket_tps:.1f}/s Vel" el_cto_q = document.getElementById("display-cto-queue-count") if el_cto_q: el_cto_q.innerText = f"{int(engine.tickets_queue):,} in Queue" badge_cto_q = document.getElementById("badge-cto-queue-status") if badge_cto_q: intake_p3 = getattr(engine, "phase3_facilities", {}).get("call_center", 0) * 15.0 if engine.tickets_queue <= 0: badge_cto_q.className = "badge bg-success bg-opacity-25 text-success" badge_cto_q.innerText = "CLEAR" elif ticket_tps > intake_p3: badge_cto_q.className = "badge bg-info bg-opacity-25 text-info" badge_cto_q.innerText = "DRAINING" else: badge_cto_q.className = "badge bg-warning bg-opacity-25 text-warning" badge_cto_q.innerText = "FILLING" el_cto_kp = document.getElementById("display-cto-kp-val") if el_cto_kp: el_cto_kp.innerText = f"{engine.knowledge_points:,.0f} KP" el_cto_rd = document.getElementById("display-cto-rd-nodes") if el_cto_rd: el_cto_rd.innerText = f"{len(engine.rd_techs_unlocked)}/9 Techs Researched" el_cto_mrr = document.getElementById("display-cto-mrr-val") if el_cto_mrr: el_cto_mrr.innerText = f"${engine.get_mrr():,.2f}/mo" el_cto_debt = document.getElementById("display-cto-debt-val") if el_cto_debt: el_cto_debt.innerText = f"${getattr(engine, 'total_debt', 0.0):,.2f} Loan Debt" # 3. Facilities Fleet Cards & Construction Queues fac_registry = getattr(resources, "PHASE3_FACILITIES", getattr(resources, "PHASE3_FACILITIES_CONFIG", {})) for f_type, cfg in fac_registry.items(): count = engine.phase3_facilities.get(f_type, 0) cost = engine.get_facility_cost(f_type) opex = count * cfg.get("operating_cost_per_sec", cfg.get("opex_per_sec", 0.5)) staff_per_fac = cfg.get("staff_per_facility", 10) el_cnt = document.getElementById(f"display-facility-count-{f_type}") if el_cnt: el_cnt.innerText = str(count) el_op = document.getElementById(f"display-facility-opex-{f_type}") if el_op: el_op.innerText = f"-${opex:.2f}/s" el_fac_staff = document.getElementById(f"display-facility-staff-{f_type}") if el_fac_staff: el_fac_staff.innerText = f"{count * staff_per_fac:,}" el_desc = document.getElementById(f"display-facility-desc-{f_type}") if el_desc: if hasattr(resources, "get_facility_description"): el_desc.innerText = resources.get_facility_description(f_type) elif "description" in cfg: el_desc.innerText = cfg["description"] el_cost_badge = document.getElementById(f"badge-facility-cost-{f_type}") if el_cost_badge: el_cost_badge.innerText = f"${cost:,.2f}" btn_build = document.getElementById(f"btn-build-facility-{f_type}") container_const = document.getElementById(f"container-construction-{f_type}") is_building = f_type in getattr(engine, "facility_construction_queues", {}) if is_building: q = engine.facility_construction_queues[f_type] rem = max(0.0, q.get("time_remaining", 0.0)) tot = max(1.0, q.get("total_time", 1.0)) pct = max(0.0, min(100.0, ((tot - rem) / tot) * 100.0)) rush_cost = round(q.get("cost", 5000.0) * (rem / tot) * 1.5, 2) if btn_build: btn_build.style.display = "none" if container_const: container_const.style.display = "block" t_el = document.getElementById(f"display-construction-time-{f_type}") if t_el: t_el.innerText = f"{rem:.0f}s remaining" bar_el = document.getElementById(f"bar-construction-{f_type}") if bar_el: bar_el.style.width = f"{pct:.0f}%" btn_rush = document.getElementById(f"btn-rush-facility-{f_type}") if btn_rush: btn_rush.innerText = f"⚑ Rush Construction (${rush_cost:,.2f})" btn_rush.disabled = (engine.cash < rush_cost or engine.burnout or engine.pc_crashed or engine.is_rebooting) else: if container_const: container_const.style.display = "none" if btn_build: btn_build.style.display = "block" btn_build.innerText = f"πŸ—οΈ Construct {cfg['name']} (${cost:,.2f})" btn_build.disabled = (engine.cash < cost or engine.burnout or engine.pc_crashed or engine.is_rebooting) # 4. Corporate IT Policies switches & Panel Gating el_pol_panel = document.getElementById("panel-policy-governance") has_corp_hq = engine.phase3_facilities.get("corporate", 0) >= 1 if el_pol_panel: el_pol_panel.style.display = "block" if has_corp_hq else "none" for p_id in ["zero_trust_mfa", "standard_soe", "cloud_migration", "cicd_devops", "byod_program", "ai_auto_escalate", "on_call_rotation", "cicd_automation", "green_datacenter"]: sw_p = document.getElementById(f"switch-policy-{p_id}") if sw_p: sw_p.checked = (p_id in engine.active_it_policies) sw_p.disabled = not has_corp_hq # 5. R&D Tech Tree & Progressive Research Dashboard el_rd_badge = document.getElementById("display-rd-kp-badge") if el_rd_badge: el_rd_badge.innerText = f"KP: {engine.knowledge_points:,.1f} / {engine.get_max_kp():,.0f}" el_rd_kp = document.getElementById("display-rd-kp-val") if el_rd_kp: el_rd_kp.innerText = f"{engine.knowledge_points:,.1f} / {engine.get_max_kp():,.0f}" el_rd_tix = document.getElementById("display-rd-tickets-val") if el_rd_tix: el_rd_tix.innerText = f"{engine.solved_tickets_inventory:,}" el_rd_cnt = document.getElementById("display-rd-completed-count") if el_rd_cnt: el_rd_cnt.innerText = f"{len(engine.rd_techs_unlocked)} / {len(getattr(resources, 'RD_TECH_TREE', resources.PHASE3_RD_TECHS_CONFIG))}" el_rd_rate = document.getElementById("display-rd-synthesis-rate") if el_rd_rate: el_rd_rate.innerText = f"{25.0 * (1.0 + engine.phase3_facilities.get('corporate', 0)*0.2):.1f} KP/s" if engine.auto_synthesis_active else "0 KP/s" badge_syn = document.getElementById("badge-synthesis-status") if badge_syn: badge_syn.className = "badge bg-success" if engine.auto_synthesis_active else "badge bg-secondary" badge_syn.innerText = "ACTIVE" if engine.auto_synthesis_active else "INACTIVE" sw_syn = document.getElementById("switch-auto-synthesis") if sw_syn: sw_syn.checked = engine.auto_synthesis_active # Synthesis buttons btn_syn_100 = document.getElementById("btn-synthesize-100kp") if btn_syn_100: btn_syn_100.disabled = (engine.solved_tickets_inventory < 10 or engine.knowledge_points >= engine.get_max_kp()) btn_syn_1000 = document.getElementById("btn-synthesize-1000kp") if btn_syn_1000: btn_syn_1000.disabled = (engine.solved_tickets_inventory < 100 or engine.knowledge_points >= engine.get_max_kp()) # Active Research Project Live Progress Banner is_blocked = bool(engine.burnout or engine.pc_crashed or engine.is_rebooting) panel_active_res = document.getElementById("panel-active-research") if panel_active_res: if getattr(engine, "active_research_project", None) and engine.active_research_project in getattr(resources, "RD_TECH_TREE", {}): panel_active_res.style.display = "block" act_id = engine.active_research_project act_spec = resources.RD_TECH_TREE[act_id] prog = engine.research_progress.get(act_id, {"progress": 0.0, "spent_kp": 0.0, "spent_cash": 0.0, "spent_tickets": 0.0}) total_dur = float(act_spec.get("duration_seconds", 45.0)) cur_prog = min(total_dur, prog.get("progress", 0.0)) pct = max(0.0, min(100.0, (cur_prog / max(1.0, total_dur)) * 100.0)) rem_time = max(0.0, total_dur - cur_prog) title_el = document.getElementById("display-active-research-title") if title_el: title_el.innerText = f"{act_spec.get('icon', 'πŸ”¬')} {act_spec['name']} (Tier {act_spec.get('tier', 1)})" eta_el = document.getElementById("display-active-research-eta") if eta_el: eta_el.innerText = f"ETA: {rem_time:.0f}s" bar_res = document.getElementById("bar-active-research") if bar_res: bar_res.style.width = f"{pct:.0f}%" bar_res.innerText = f"{pct:.0f}% Complete" spent_el = document.getElementById("display-active-research-spent") if spent_el: spent_el.innerText = f"Funneled: {prog.get('spent_kp', 0.0):,.0f} KP β€’ ${prog.get('spent_cash', 0.0):,.0f} β€’ {prog.get('spent_tickets', 0.0):,.0f} Tickets" btn_exp = document.getElementById("btn-expedite-research") if btn_exp: btn_exp.disabled = (engine.cash < 1000.0 or is_blocked) else: panel_active_res.style.display = "none" # Multi-Tier Tech Tree nodes rd_tech_dict = getattr(resources, "RD_TECH_TREE", resources.PHASE3_RD_TECHS_CONFIG) for tech_id, t_cfg in rd_tech_dict.items(): is_unlocked = tech_id in engine.rd_techs_unlocked is_active = (getattr(engine, "active_research_project", None) == tech_id) t_badge = document.getElementById(f"badge-tech-{tech_id}") t_btn = document.getElementById(f"btn-tech-{tech_id}") t_desc = document.getElementById(f"desc-tech-{tech_id}") if t_desc: raw_perk = t_cfg.get('perk_desc', t_cfg.get('description', '')) if raw_perk: if not raw_perk.strip().lower().startswith("perk:"): t_desc.innerText = f"Perk: {raw_perk}" else: t_desc.innerText = raw_perk if is_unlocked: if t_badge: t_badge.className = "badge bg-success" t_badge.innerText = "RESEARCHED" if t_btn: t_btn.className = "btn btn-sm btn-success w-100 fw-bold disabled" t_btn.disabled = True t_btn.innerText = "βœ… Researched" elif is_active: prog = engine.research_progress.get(tech_id, {}).get("progress", 0.0) dur = float(t_cfg.get("duration_seconds", 45.0)) pct = int((prog / max(1.0, dur)) * 100) if t_badge: t_badge.className = "badge bg-warning text-dark font-monospace" t_badge.innerText = f"SYNTHESIZING {pct}%" if t_btn: t_btn.className = "btn btn-sm btn-warning text-dark w-100 fw-bold" t_btn.disabled = True t_btn.innerText = f"⏳ Synthesizing ({pct}%)..." else: kp_c = t_cfg.get("kp_cost", t_cfg.get("cost_kp", 500.0)) cash_c = t_cfg.get("cash_cost", 0.0) tix_c = t_cfg.get("tickets_cost", 0) dur = t_cfg.get("duration_seconds", 45.0) if t_badge: t_badge.className = "badge bg-warning text-dark font-monospace" if cash_c > 0: t_badge.innerText = f"{kp_c:,.0f} KP β€’ ${cash_c:,.0f} β€’ {tix_c} Tix ({dur:.0f}s)" else: t_badge.innerText = f"{kp_c:,.0f} KP" if t_btn: t_btn.className = "btn btn-sm btn-outline-warning w-100 fw-bold" t_btn.disabled = is_blocked t_btn.innerText = f"πŸ”¬ Select for Research" # 5b. R&D Laboratory Tech Upgrades UI Update for up_id, up_spec in resources.PHASE3_RD_UPGRADES_REGISTRY.items(): curr_lvl = engine.upgrades.get(up_id, 0) costs = engine.get_rd_upgrade_cost_dict(up_id) req_tech = up_spec.get("req_tech") is_req_met = (req_tech in engine.rd_techs_unlocked) if req_tech else True el_lvl = document.getElementById(f"rd-level-{up_id}") if el_lvl: el_lvl.innerText = f"Lvl {curr_lvl}" el_kp = document.getElementById(f"rd-cost-kp-{up_id}") if el_kp: el_kp.innerText = "MAX" if costs["is_max"] else f"{costs['kp']:,} KP" el_tickets = document.getElementById(f"rd-cost-tickets-{up_id}") if el_tickets: el_tickets.innerText = "MAX" if costs["is_max"] else f"{costs['solved_tickets']:,} Tickets" el_cash = document.getElementById(f"rd-cost-cash-{up_id}") if el_cash: el_cash.innerText = "MAX" if costs["is_max"] else f"${costs['cash']:,.2f}" el_req = document.getElementById(f"rd-req-{up_id}") if el_req: if is_req_met: el_req.innerHTML = 'βœ“ Tech Unlocked' else: el_req.innerHTML = f'πŸ”’ Req: {up_spec.get("req_tech_name", "")}' btn_up = document.getElementById(f"btn-rd-buy-{up_id}") if btn_up: if costs["is_max"]: btn_up.innerText = "⭐ Max Level" btn_up.className = "btn btn-sm btn-outline-warning w-100 fw-bold disabled" btn_up.disabled = True elif not is_req_met: btn_up.innerText = "πŸ”’ Locked" btn_up.className = "btn btn-sm btn-outline-secondary w-100 fw-bold disabled" btn_up.disabled = True else: can_afford = (engine.knowledge_points >= costs["kp"] and engine.solved_tickets_inventory >= costs["solved_tickets"] and engine.cash >= costs["cash"] and not is_blocked) btn_up.innerText = f"Upgrade ({costs['kp']:,} KP + {costs['solved_tickets']:,} Tix)" btn_up.disabled = not can_afford if can_afford: btn_up.className = "btn btn-sm btn-info w-100 fw-bold" else: btn_up.className = "btn btn-sm btn-outline-info w-100 fw-bold" # Auto-Pitch Proposal Controls in Sales auto_pitch_lvl = engine.upgrades.get("auto_pitch_engine", 0) badge_ap = document.getElementById("badge-auto-pitch-status") box_ap = document.getElementById("container-switch-auto-pitch") if badge_ap and box_ap: if auto_pitch_lvl == 0: badge_ap.style.display = "inline-block" box_ap.style.display = "none" else: badge_ap.style.display = "none" box_ap.style.display = "flex" sw_ap = document.getElementById("switch-auto-pitch") if sw_ap: sw_ap.checked = getattr(engine, "auto_pitch_enabled", True) # 6. Executive Diagnostic Alerts & Action Feed container_alerts = document.getElementById("container-executive-alerts") badge_alerts_cnt = document.getElementById("badge-alerts-count") if container_alerts: alerts = [] # Check Bandwidth bw_used = engine.get_used_bandwidth() if hasattr(engine, "get_used_bandwidth") else 0.0 bw_max = engine.get_max_bandwidth() if hasattr(engine, "get_max_bandwidth") else 50.0 bw_pct = (bw_used / max(1.0, bw_max)) * 100.0 if bw_pct >= 85.0: alerts.append({ "id": "alert-bw", "icon": "⚠️", "badge": "bg-warning text-dark", "title": "High Bandwidth Load (SLA Saturation Risk)", "detail": f"{bw_used:.0f}/{bw_max:.0f} Mbps ({int(bw_pct)}%)", "action_text": "Upgrade Fiber", "tab": "sales-tab", "elem": "display-sales-bw-usage" }) # Check Queue q_cur = int(engine.tickets_queue) q_max = int(engine.get_max_tickets_queue()) if q_cur >= q_max * 0.8: alerts.append({ "id": "alert-queue-high", "icon": "🚨", "badge": "bg-danger", "title": "Incident Queue Saturation (SLA Breach Imminent)", "detail": f"{q_cur}/{q_max} Tickets", "action_text": "Build Helpdesk", "tab": "helpdesk-tab", "elem": "card-facility-helpdesk" }) elif q_cur <= 1 and engine.phase3_facilities.get("call_center", 0) >= 1: alerts.append({ "id": "alert-queue-low", "icon": "πŸ“‰", "badge": "bg-info text-dark", "title": "Incident Queue Empty (Technicians Idle)", "detail": f"{q_cur}/{q_max} Tickets", "action_text": "Pitch SLA Contracts", "tab": "sales-tab", "elem": "row-sales-proposals" }) # Check Research Project if not getattr(engine, "active_research_project", None) and len(engine.rd_techs_unlocked) < 9: alerts.append({ "id": "alert-rd-idle", "icon": "πŸ”¬", "badge": "bg-primary", "title": "R&D Innovation Labs Idle (No Active Project)", "detail": "0 Active Projects", "action_text": "Select Tech Node", "tab": "rd-tab", "elem": "panel-active-research" }) # Check Facilities Building if getattr(engine, "facility_construction_queues", {}): n_bldg = len(engine.facility_construction_queues) alerts.append({ "id": "alert-bldg", "icon": "πŸ—οΈ", "badge": "bg-warning text-dark", "title": "Active Construction Fleet Operating", "detail": f"{n_bldg} Fleet in Progress", "action_text": "View Fleet", "tab": "helpdesk-tab", "elem": "card-facility-call_center" }) # Check DC Thermal Strain if getattr(engine, "infra_thermal_strain", 0.0) >= 50.0: alerts.append({ "id": "alert-thermal", "icon": "🌑️", "badge": "bg-danger", "title": "Critical Infrastructure Thermal Strain", "detail": f"{int(engine.infra_thermal_strain)}% Strain", "action_text": "Maintenance Window", "tab": "datacenter-tab", "elem": "btn-infra-maintenance" }) if badge_alerts_cnt: badge_text = f"{len(alerts)} Active Alerts" if alerts else "Nominal (0 Alerts)" if badge_alerts_cnt.innerText != badge_text: badge_alerts_cnt.innerText = badge_text alert_ids_key = ",".join(alt["id"] for alt in alerts) if getattr(engine, "_last_alert_ids_key", None) != alert_ids_key: engine._last_alert_ids_key = alert_ids_key if alerts: items_html = [] for alt in alerts: items_html.append( f'
    ' f'
    ' f'{alt["icon"]}' f'{alt["title"]}' f'{alt.get("detail", "")}' f'
    ' f'' f'
    ' ) container_alerts.innerHTML = "".join(items_html) else: container_alerts.innerHTML = '
    🟒 All enterprise systems nominal β€’ No immediate bottlenecks detected.
    ' else: # If alert set is stable, only update detail numbers in-place to avoid button recreation and flickering for alt in alerts: d_el = document.getElementById(f"exec-detail-{alt['id']}") new_detail = alt.get("detail", "") if d_el and d_el.innerText != new_detail: d_el.innerText = new_detail # 7. Downtime buttons disabled states for btn_id in ["btn-cto-commit-prototype", "btn-cto-executive-pitch", "btn-cto-stress-relief"]: b_el = document.getElementById(btn_id) if b_el: b_el.disabled = is_blocked # 4. Branch cards for b_id, b_spec in resources.CALL_CENTER_BRANCHES.items(): b_data = engine.branch_managers.get(b_id, {}) is_hired = b_data.get("hired", False) btn_hire = document.getElementById(f"btn-branch-hire-{b_id}") badge_status = document.getElementById(f"badge-branch-status-{b_id}") ctrl_box = document.getElementById(f"branch-controls-{b_id}") txt_solved = document.getElementById(f"txt-branch-solved-{b_id}") if is_hired: if btn_hire: btn_hire.style.display = "none" if badge_status: badge_status.style.display = "inline-block" badge_status.className = "badge bg-success" badge_status.innerText = "🟒 ACTIVE" if ctrl_box: ctrl_box.style.display = "block" if txt_solved: txt_solved.innerText = str(b_data.get("tickets_solved", 0)) # Active directive styling active_dir = b_data.get("directive", "quality") for d_k in ["speed", "quality", "budget"]: d_btn = document.getElementById(f"btn-branch-dir-{b_id}-{d_k}") if d_btn: if d_k == active_dir: d_btn.className = "btn btn-info active" else: d_btn.className = "btn btn-outline-info" else: if btn_hire: btn_hire.style.display = "inline-block" btn_hire.disabled = (engine.cash < b_spec["hiring_cost"]) if badge_status: badge_status.style.display = "none" if ctrl_box: ctrl_box.style.display = "none" # 5. IT Policy toggles for p_id in resources.IT_POLICIES_REGISTRY: sw_el = document.getElementById(f"switch-policy-{p_id}") if sw_el: sw_el.checked = (p_id in engine.active_it_policies) # 6. R&D Projects cards for p_id, p_spec in resources.RD_PROJECTS_REGISTRY.items(): badge_rd = document.getElementById(f"badge-rd-{p_id}") bar_rd = document.getElementById(f"prog-bar-rd-{p_id}") btn_rd = document.getElementById(f"btn-rd-start-{p_id}") if p_id in engine.completed_rd_projects: if badge_rd: badge_rd.className = "badge bg-success" badge_rd.innerText = "🟒 COMPLETED" if bar_rd: bar_rd.style.width = "100%" bar_rd.className = "progress-bar bg-success" if btn_rd: btn_rd.innerText = "Completed" btn_rd.disabled = True btn_rd.className = "btn btn-sm btn-outline-secondary" elif engine.active_rd_project == p_id: curr_prog = engine.rd_progress.get(p_id, 0.0) pct = min(100.0, round((curr_prog / p_spec["rp_cost"]) * 100.0, 1)) if badge_rd: badge_rd.className = "badge bg-warning text-dark" badge_rd.innerText = "⚑ RESEARCHING" if bar_rd: bar_rd.style.width = f"{pct}%" bar_rd.className = "progress-bar bg-info progress-bar-striped progress-bar-animated" if btn_rd: btn_rd.innerText = f"Researching ({curr_prog:.0f}/{p_spec['rp_cost']} RP)" btn_rd.disabled = True btn_rd.className = "btn btn-sm btn-info" else: if badge_rd: badge_rd.className = "badge bg-secondary" badge_rd.innerText = "UNRESEARCHED" if bar_rd: bar_rd.style.width = "0%" bar_rd.className = "progress-bar bg-info" if btn_rd: btn_rd.innerText = "Start Research" btn_rd.disabled = (engine.cash < p_spec["cash_cost"] or is_blocked) btn_rd.className = "btn btn-sm btn-outline-info fw-bold" # Sidebar Helpdesk Meters vs Corporate Health Deck meters_ws = document.getElementById("sidebar-system-meters") meters_corp = document.getElementById("sidebar-corporate-health") breakroom_el = document.getElementById("sidebar-breakroom") if meters_ws and meters_corp: if engine.phase >= 4: meters_ws.style.display = "none" if breakroom_el: breakroom_el.style.display = "none" meters_corp.style.display = "block" else: meters_ws.style.display = "block" if breakroom_el: breakroom_el.style.display = "block" meters_corp.style.display = "none" p4_sidebar = document.getElementById("phase4-sidebar-hud") if p4_sidebar: p4_sidebar.style.display = "block" if engine.phase >= 4 else "none" if engine.phase >= 4: for s_k in ["raw_silicon", "silicon_wafers", "hbm_gpus", "server_blades"]: s_el = document.getElementById(f"display-sidebar-{s_k}") if s_el: s_el.innerText = str(engine.supply_inventory.get(s_k, 0)) # Phase 4 Enterprise & Supply Chain In-Place UI Updates if engine.phase >= 4: corp_metrics = engine.get_corporate_health_metrics() # 0. Corporate Health Sidebar Telemetry el_corp_t_inflight = document.getElementById("display-corp-tickets-inflight") el_corp_t_rates = document.getElementById("display-corp-ticket-rates") el_corp_t_resolved = document.getElementById("display-corp-tickets-resolved") el_corp_morale = document.getElementById("display-corp-morale") bar_corp_morale = document.getElementById("bar-corp-morale") el_corp_headcount = document.getElementById("display-corp-headcount") el_corp_payroll = document.getElementById("display-corp-payroll-burn") el_corp_maint = document.getElementById("display-corp-maint-burn") el_corp_sla = document.getElementById("display-corp-avg-sla") if el_corp_t_inflight: el_corp_t_inflight.innerText = f"{corp_metrics['tickets_in_flight']:,}" if el_corp_t_rates: el_corp_t_rates.innerText = f"+{corp_metrics['tickets_inflow']:.1f}/s in | -{corp_metrics['tickets_velocity']:.1f}/s vel" if el_corp_t_resolved: el_corp_t_resolved.innerText = f"{corp_metrics['tickets_resolved_total']:,}" if el_corp_morale: el_corp_morale.innerText = f"{corp_metrics['morale']:.1f}%" if bar_corp_morale: m_val = corp_metrics['morale'] bar_corp_morale.style.width = f"{m_val:.1f}%" bar_corp_morale.innerText = f"{m_val:.1f}%" bar_corp_morale.className = "progress-bar text-dark font-weight-bold " + ("bg-danger" if m_val < 40.0 else ("bg-warning" if m_val < 70.0 else "bg-success")) if el_corp_headcount: el_corp_headcount.innerText = f"{corp_metrics['headcount']:,} Employees" if el_corp_payroll: el_corp_payroll.innerText = f"-${corp_metrics['total_payroll']:,.2f}/s" if el_corp_maint: el_corp_maint.innerText = f"-${corp_metrics['gross_maintenance']:,.2f}/s" if el_corp_sla: el_corp_sla.innerText = f"{corp_metrics['avg_sla']:.1f}%" # 0.5 CEO Corporate Directives & Macro Policy Controls Active States lbl_ot = document.getElementById("lbl-active-overtime-policy") if lbl_ot and engine.overtime_policy in resources.CORPORATE_POLICIES["overtime"]: lbl_ot.innerText = resources.CORPORATE_POLICIES["overtime"][engine.overtime_policy]["name"] for ot_k in ["40h", "50h", "60h", "war_mode"]: b_ot = document.getElementById(f"btn-policy-overtime-{ot_k}") if b_ot: if engine.overtime_policy == ot_k: b_ot.className = "btn btn-info text-dark fw-bold btn-xs py-1" else: b_ot.className = "btn btn-outline-info btn-xs py-1" lbl_pr = document.getElementById("lbl-active-payroll-scale") if lbl_pr and engine.payroll_scale in resources.CORPORATE_POLICIES["payroll"]: lbl_pr.innerText = resources.CORPORATE_POLICIES["payroll"][engine.payroll_scale]["name"] for pr_k in ["frugal", "market", "premium", "elite"]: b_pr = document.getElementById(f"btn-policy-payroll-{pr_k}") if b_pr: if engine.payroll_scale == pr_k: b_pr.className = "btn btn-success text-dark fw-bold btn-xs py-1" else: b_pr.className = "btn btn-outline-success btn-xs py-1" lbl_rd = document.getElementById("lbl-active-rd-budget") if lbl_rd and engine.rd_budget_tier in resources.CORPORATE_POLICIES["rd_budget"]: lbl_rd.innerText = resources.CORPORATE_POLICIES["rd_budget"][engine.rd_budget_tier]["name"] for rd_k in ["off", "modest", "aggressive", "unlimited"]: b_rd = document.getElementById(f"btn-policy-rd-{rd_k}") if b_rd: if engine.rd_budget_tier == rd_k: b_rd.className = "btn btn-primary text-dark fw-bold btn-xs py-1" else: b_rd.className = "btn btn-outline-primary btn-xs py-1" # 1. Anno-Style IT Manufacturing & Power Grid Dashboard Telemetry mfg_summary = engine.get_manufacturing_summary() el_p_dem = document.getElementById("mfg-power-demand") el_p_cap = document.getElementById("mfg-power-capacity") el_p_stat = document.getElementById("mfg-power-status") el_mfg_upk = document.getElementById("mfg-net-upkeep") el_mfg_stg_used = document.getElementById("mfg-storage-used") el_mfg_stg_cap = document.getElementById("mfg-storage-cap") el_mfg_stg_pct = document.getElementById("mfg-storage-pct") if el_p_dem: el_p_dem.innerText = f"{mfg_summary['power_demand']:,.0f}" if el_p_cap: el_p_cap.innerText = f"{mfg_summary['power_capacity']:,.0f}" if el_p_stat: if mfg_summary["power_status"] == "OPTIMAL": el_p_stat.innerText = "GRID OPTIMAL (100%)" el_p_stat.className = "badge bg-success font-monospace" else: el_p_stat.innerText = f"BROWNOUT ({mfg_summary['power_efficiency']*100:.0f}%)" el_p_stat.className = "badge bg-warning text-dark font-monospace" if el_mfg_upk: el_mfg_upk.innerText = f"-${mfg_summary['mfg_upkeep']:,.2f}/s" if el_mfg_stg_used: el_mfg_stg_used.innerText = f"{mfg_summary['total_storage_used']:,.0f}" if el_mfg_stg_cap: el_mfg_stg_cap.innerText = f"{mfg_summary['total_storage_cap']:,}" if el_mfg_stg_pct: el_mfg_stg_pct.innerText = f"{mfg_summary['storage_utilization_pct']:.1f}%" # 1.1 Manufacturing Facility Cards Telemetry for b_id, spec in resources.MANUFACTURING_FACILITIES.items(): b_cnt = engine.manufacturing_buildings.get(b_id, 0) b_cost = engine.get_manufacturing_facility_cost(b_id) b_paused = engine.manufacturing_paused.get(b_id, False) b_oc = engine.manufacturing_overclock.get(b_id, False) el_cnt = document.getElementById(f"mfg-count-{b_id}") el_cost = document.getElementById(f"mfg-cost-{b_id}") btn_bld = document.getElementById(f"btn-mfg-build-{b_id}") btn_demo = document.getElementById(f"btn-mfg-demo-{b_id}") btn_oc = document.getElementById(f"btn-mfg-oc-{b_id}") btn_ps = document.getElementById(f"btn-mfg-pause-{b_id}") el_stat = document.getElementById(f"mfg-status-{b_id}") if el_cnt: el_cnt.innerText = f"{b_cnt} Owned" if el_cost: if b_cost >= 1000000.0: el_cost.innerText = f"${b_cost / 1000000.0:.2f}M" elif b_cost >= 10000.0: el_cost.innerText = f"${b_cost / 1000.0:.1f}k" elif b_cost >= 1000.0: el_cost.innerText = f"${b_cost:,.0f}" else: el_cost.innerText = f"${b_cost:,.0f}" if btn_bld: if engine.cash >= b_cost: btn_bld.removeAttribute("disabled") else: btn_bld.setAttribute("disabled", "true") if btn_demo: if b_cnt > 0: btn_demo.removeAttribute("disabled") else: btn_demo.setAttribute("disabled", "true") if btn_oc: if b_oc: btn_oc.className = "btn btn-warning text-dark fw-bold btn-xs py-1" else: btn_oc.className = "btn btn-outline-warning btn-xs py-1" if btn_ps: if b_paused: btn_ps.innerText = "▢️" btn_ps.className = "btn btn-warning text-dark btn-xs py-1" else: btn_ps.innerText = "⏸️" btn_ps.className = "btn btn-outline-secondary btn-xs py-1" if el_stat: if b_paused: el_stat.innerText = "PAUSED" el_stat.className = "badge bg-secondary" elif b_cnt == 0: el_stat.innerText = "OFFLINE" el_stat.className = "badge bg-secondary" else: # Check storage full stg_full = False if spec["outputs"]: stg_full = True for out_k in spec["outputs"]: if engine.manufacturing_inventory.get(out_k, 0.0) < resources.MANUFACTURING_RESOURCES[out_k]["base_cap"]: stg_full = False break # Check missing inputs missing_in = False for in_k in spec["inputs"]: if engine.manufacturing_inventory.get(in_k, 0.0) <= 0.0: missing_in = True break if stg_full: el_stat.innerText = "STORAGE FULL" el_stat.className = "badge bg-warning text-dark" elif missing_in: el_stat.innerText = "MISSING INPUTS" el_stat.className = "badge bg-danger" elif mfg_summary["power_status"] == "BROWNOUT": el_stat.innerText = f"BROWNOUT ({mfg_summary['power_efficiency']*100:.0f}%)" el_stat.className = "badge bg-warning text-dark" elif b_oc: el_stat.innerText = "OVERCLOCKED" el_stat.className = "badge bg-info text-dark" else: el_stat.innerText = "RUNNING" el_stat.className = "badge bg-success" # 1.2 Manufacturing Materials Stock & Auto-Sell Telemetry for r_id, r_spec in resources.MANUFACTURING_RESOURCES.items(): r_stock = engine.manufacturing_inventory.get(r_id, 0.0) r_cap = engine.get_resource_cap(r_id) el_stk = document.getElementById(f"mfg-stock-{r_id}") el_cap = document.getElementById(f"mfg-cap-{r_id}") el_bar = document.getElementById(f"mfg-bar-{r_id}") btn_as = document.getElementById(f"btn-mfg-autosell-{r_id}") if el_stk: el_stk.innerText = f"{r_stock:,.0f}" if el_cap: el_cap.innerText = f"{r_cap:,.0f}" if el_bar: pct = min(100.0, (r_stock / r_cap * 100.0) if r_cap > 0 else 0.0) el_bar.style.width = f"{pct:.1f}%" if btn_as: thresh = engine.auto_sell_thresholds.get(r_id, -1) if thresh < 0: btn_as.innerText = "Auto-Sell" btn_as.className = "btn btn-outline-info btn-xs py-1" else: btn_as.innerText = f"Auto>{thresh}" btn_as.className = "btn btn-info text-dark fw-bold btn-xs py-1" # 1.3 Storage Upgrade Cards Telemetry for t_idx in range(4): curr_lvl = engine.storage_upgrades.get(t_idx, 0) tiers_list = resources.STORAGE_UPGRADE_TIERS.get(t_idx, []) mult = tiers_list[curr_lvl]["multiplier"] if 0 <= curr_lvl < len(tiers_list) else 8.0 el_slvl = document.getElementById(f"storage-lvl-{t_idx}") el_scap = document.getElementById(f"storage-cap-{t_idx}") btn_supg = document.getElementById(f"btn-upgrade-storage-{t_idx}") if el_slvl: name_str = tiers_list[curr_lvl]["name"] if curr_lvl < len(tiers_list) else "Maxed" el_slvl.innerText = f"Lvl {curr_lvl} ({name_str})" if el_scap: base_val = [2000, 1000, 500, 100][t_idx] el_scap.innerText = f"{int(base_val * mult):,} units ({mult:.1f}x)" if btn_supg: next_lvl = curr_lvl + 1 if next_lvl < len(tiers_list): cost = tiers_list[next_lvl]["cost"] btn_supg.innerText = f"Upgrade ({tiers_list[next_lvl]['name']} - ${cost:,.0f})" if engine.cash >= cost: btn_supg.removeAttribute("disabled") else: btn_supg.setAttribute("disabled", "true") else: btn_supg.innerText = "MAX LEVEL REACHED" btn_supg.setAttribute("disabled", "true") # 1.4 Power Grid Tab Telemetry el_gt_dem = document.getElementById("grid-tab-demand") el_gt_cap = document.getElementById("grid-tab-cap") el_gt_pbar = document.getElementById("grid-tab-power-bar") el_gt_bchg = document.getElementById("grid-tab-battery-charge") el_gt_bcap = document.getElementById("grid-tab-battery-cap") el_gt_bbar = document.getElementById("grid-tab-battery-bar") if el_gt_dem: el_gt_dem.innerText = f"{mfg_summary['power_demand']:,.0f}" if el_gt_cap: el_gt_cap.innerText = f"{mfg_summary['power_capacity']:,.0f}" if el_gt_pbar: p_pct = min(100.0, (mfg_summary['power_demand'] / mfg_summary['power_capacity'] * 100.0) if mfg_summary['power_capacity'] > 0 else 0.0) el_gt_pbar.style.width = f"{p_pct:.1f}%" if el_gt_bchg: el_gt_bchg.innerText = f"{mfg_summary['battery_charge']:,.0f}" if el_gt_bcap: el_gt_bcap.innerText = f"{mfg_summary['battery_cap']:,.0f}" if el_gt_bbar: b_pct = min(100.0, (mfg_summary['battery_charge'] / mfg_summary['battery_cap'] * 100.0) if mfg_summary['battery_cap'] > 0 else 0.0) el_gt_bbar.style.width = f"{b_pct:.1f}%" # 1.5 Project Singularity Telemetry el_sing_nodes = document.getElementById("sing-completed-nodes") if el_sing_nodes: el_sing_nodes.innerText = f"{engine.project_singularity['completed_nodes']} Nodes Assembled" for s_k, s_req in engine.project_singularity["required"].items(): s_del = engine.project_singularity["delivered"].get(s_k, 0) el_s_del = document.getElementById(f"sing-del-{s_k}") el_s_bar = document.getElementById(f"sing-bar-{s_k}") btn_s_del = document.getElementById(f"btn-sing-deliver-{s_k}") if el_s_del: el_s_del.innerText = f"{s_del} / {s_req}" if el_s_bar: pct = min(100.0, (s_del / s_req * 100.0) if s_req > 0 else 0.0) el_s_bar.style.width = f"{pct:.1f}%" if btn_s_del: min_del = 5 if s_k in ["cooling_unit", "rack_matrix"] else 10 if engine.manufacturing_inventory.get(s_k, 0.0) >= min_del and s_del < s_req: btn_s_del.removeAttribute("disabled") else: btn_s_del.setAttribute("disabled", "true") # 1.4 Executive Action Cooldowns & Buttons (Both on Enterprise Tab & Operations Deck) btn_esp = document.getElementById("btn-exec-espresso") lbl_esp = document.getElementById("lbl-exec-espresso-cd") btn_ops_esp = document.getElementById("btn-ops-exec-espresso") lbl_ops_esp = document.getElementById("lbl-ops-exec-espresso-cd") esp_cd_text = f"Cooldown ({engine.exec_espresso_cooldown:.1f}s)" if engine.exec_espresso_cooldown > 0.0 else "-40 Stress β€’ +15 KP (Ready)" for b, l in [(btn_esp, lbl_esp), (btn_ops_esp, lbl_ops_esp)]: if b and l: l.innerText = esp_cd_text if engine.exec_espresso_cooldown > 0.0: b.setAttribute("disabled", "true") else: b.removeAttribute("disabled") btn_hot = document.getElementById("btn-exec-hotfix") lbl_hot = document.getElementById("lbl-exec-hotfix-cd") btn_ops_hot = document.getElementById("btn-ops-exec-hotfix") lbl_ops_hot = document.getElementById("lbl-ops-exec-hotfix-cd") hot_cd_text = f"Cooldown ({engine.exec_hotfix_cooldown:.1f}s)" if engine.exec_hotfix_cooldown > 0.0 else "Resolve 10 Tickets (Ready)" for b, l in [(btn_hot, lbl_hot), (btn_ops_hot, lbl_ops_hot)]: if b and l: l.innerText = hot_cd_text if engine.exec_hotfix_cooldown > 0.0: b.setAttribute("disabled", "true") else: b.removeAttribute("disabled") cro_mult = 1.5 if engine.c_suite.get("cro", False) else 1.0 payout_txt = f"+${50000.0 * cro_mult:,.0f} Cash" deal_cd_text = f"Cooldown ({engine.exec_deal_cooldown:.1f}s)" if engine.exec_deal_cooldown > 0.0 else f"{payout_txt} β€’ +2 Gold (Ready)" btn_deal = document.getElementById("btn-exec-deal") lbl_deal = document.getElementById("lbl-exec-deal-cd") btn_ops_deal = document.getElementById("btn-ops-exec-deal") lbl_ops_deal = document.getElementById("lbl-ops-exec-deal-cd") for b, l in [(btn_deal, lbl_deal), (btn_ops_deal, lbl_ops_deal)]: if b and l: l.innerText = deal_cd_text if engine.exec_deal_cooldown > 0.0: b.setAttribute("disabled", "true") else: b.removeAttribute("disabled") btn_sup = document.getElementById("btn-exec-supply") lbl_sup = document.getElementById("lbl-exec-supply-cd") btn_ops_sup = document.getElementById("btn-ops-exec-supply") lbl_ops_sup = document.getElementById("lbl-ops-exec-supply-cd") sup_cd_text = f"Cooldown ({engine.exec_supply_cooldown:.1f}s)" if engine.exec_supply_cooldown > 0.0 else "+25 Silicon β€’ +5 Wafers ($5k)" for b, l in [(btn_sup, lbl_sup), (btn_ops_sup, lbl_ops_sup)]: if b and l: l.innerText = sup_cd_text if engine.exec_supply_cooldown > 0.0 or engine.cash < 5000.0: b.setAttribute("disabled", "true") else: b.removeAttribute("disabled") # 2. Materials Inventory Counts & Spot Buttons for itm_id in resources.SUPPLY_CHAIN_REGISTRY: cnt_el = document.getElementById(f"supply-count-{itm_id}") if cnt_el: cnt_el.innerText = str(engine.supply_inventory.get(itm_id, 0)) # 2.5 Manual Component Fabrication Workbench Affordability cto_active = engine.c_suite.get("cto", False) for r_id in ["slice_wafers", "etch_gpus", "assemble_blades"]: btn_craft = document.getElementById(f"btn-craft-{r_id}") if btn_craft: r_spec = resources.SUPPLY_CHAIN_RECIPES[r_id] cash_req = r_spec["inputs"].get("cash", 0.0) * (0.80 if cto_active else 1.0) can_craft = (engine.cash >= cash_req) and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting for in_item, in_count in r_spec["inputs"].items(): if in_item != "cash" and engine.supply_inventory.get(in_item, 0) < in_count: can_craft = False break if can_craft: btn_craft.removeAttribute("disabled") else: btn_craft.setAttribute("disabled", "true") # 2.6 Automated Silicon Arc Furnaces Upgrade furnace_lvl = engine.upgrades.get("silicon_furnaces", 0) furnace_cost = resources.get_upgrade_cost("silicon_furnaces", furnace_lvl, engine.game_quarter, engine.upgrades.get("script_snippets", 0), engine.upgrades.get("procurement_specialists", 0)) lbl_furn_cnt = document.getElementById("lbl-silicon-furnace-count") lbl_furn_rate = document.getElementById("lbl-silicon-furnace-rate") lbl_furn_cost = document.getElementById("lbl-silicon-furnace-cost") btn_furn = document.getElementById("btn-buy-silicon-furnace") if lbl_furn_cnt: lbl_furn_cnt.innerText = f"Lvl {furnace_lvl}" if lbl_furn_rate: vp_supply_bonus = 1.25 if engine.c_suite.get("vp_supply", False) else 1.0 lbl_furn_rate.innerText = f"+{furnace_lvl * 2.0 * vp_supply_bonus:.1f} Silicon/s" if lbl_furn_cost: lbl_furn_cost.innerText = f"${furnace_cost:,.2f}" if btn_furn: if engine.cash >= furnace_cost and furnace_lvl < 20: btn_furn.removeAttribute("disabled") else: btn_furn.setAttribute("disabled", "true") # 3. Fab Lines & Production Recipes Progress max_fab_lines = engine.facilities.get("fabs", 0) * 2 used_fab_lines = sum(engine.active_recipes.values()) lbl_used_lines = document.getElementById("display-fab-lines-used") lbl_max_lines = document.getElementById("display-fab-lines-max") if lbl_used_lines: lbl_used_lines.innerText = str(used_fab_lines) if lbl_max_lines: lbl_max_lines.innerText = str(max_fab_lines) for r_id in resources.SUPPLY_CHAIN_RECIPES: r_spec = resources.SUPPLY_CHAIN_RECIPES[r_id] lines_cnt = engine.active_recipes.get(r_id, 0) lines_lbl = document.getElementById(f"recipe-lines-{r_id}") if lines_lbl: lines_lbl.innerText = f"{lines_cnt} Lines" bar_el = document.getElementById(f"bar-recipe-{r_id}") if bar_el: pct = min(100.0, (engine.recipe_progress.get(r_id, 0.0) / r_spec["base_cycle_time"]) * 100.0) if lines_cnt > 0 else 0.0 bar_el.style.width = f"{pct}%" # 4. Enterprise Facilities (Both on Enterprise Tab & Operations Deck) ops_summary = engine.get_global_operations_summary() lbl_ops_sites = document.getElementById("display-ops-global-sites") lbl_ops_compute = document.getElementById("display-ops-global-compute") lbl_ops_net_mrr = document.getElementById("display-ops-global-net-mrr") lbl_ops_tickets = document.getElementById("display-ops-global-tickets-rate") if lbl_ops_sites: lbl_ops_sites.innerText = f"{ops_summary['total_sites']} Sites" if lbl_ops_compute: lbl_ops_compute.innerText = f"{ops_summary['total_compute']:,} FLOPs" if lbl_ops_net_mrr: sign = "+" if ops_summary['net_mrr'] >= 0 else "-" lbl_ops_net_mrr.innerText = f"{sign}${abs(ops_summary['net_mrr']):,.2f}/s" if lbl_ops_tickets: lbl_ops_tickets.innerText = f"{ops_summary['tickets_rate']:.1f}/s" for fac_id in resources.ENTERPRISE_FACILITIES_REGISTRY: fac_cnt = engine.facilities.get(fac_id, 0) fac_cost = engine.get_facility_cost(fac_id) # Enterprise tab elements cnt_el = document.getElementById(f"facility-count-{fac_id}") cost_el = document.getElementById(f"facility-cost-{fac_id}") btn_fac = document.getElementById(f"btn-buy-facility-{fac_id}") if cnt_el: cnt_el.innerText = str(fac_cnt) if cost_el: cost_el.innerText = f"${fac_cost:,.2f}" if btn_fac: if engine.cash >= fac_cost: btn_fac.removeAttribute("disabled") else: btn_fac.setAttribute("disabled", "true") # Operations Deck elements ops_cnt_el = document.getElementById(f"display-ops-count-{fac_id}") ops_cost_el = document.getElementById(f"display-ops-cost-{fac_id}") ops_btn_fac = document.getElementById(f"btn-ops-buy-facility-{fac_id}") if ops_cnt_el: ops_cnt_el.innerText = str(fac_cnt) if ops_cost_el: ops_cost_el.innerText = f"${fac_cost:,.0f}" if fac_cost >= 1000000 else f"${fac_cost:,.2f}" if ops_btn_fac: if engine.cash >= fac_cost: ops_btn_fac.removeAttribute("disabled") else: ops_btn_fac.setAttribute("disabled", "true") # 5. C-Suite & VP Leadership Boardroom for ex_id in resources.C_SUITE_EXECUTIVES_REGISTRY: ex_spec = resources.C_SUITE_EXECUTIVES_REGISTRY[ex_id] hired = engine.c_suite.get(ex_id, False) badge_el = document.getElementById(f"badge-csuite-{ex_id}") btn_ex = document.getElementById(f"btn-hire-csuite-{ex_id}") if badge_el: if hired: badge_el.innerText = "Appointed" badge_el.className = "badge bg-success" else: badge_el.innerText = "Unappointed" badge_el.className = "badge bg-secondary" if btn_ex: if hired: btn_ex.innerText = "Appointed (Active)" btn_ex.setAttribute("disabled", "true") else: btn_ex.innerText = f"Appoint ({ex_id.upper()})" if engine.cash >= ex_spec["hire_cost"]: btn_ex.removeAttribute("disabled") else: btn_ex.setAttribute("disabled", "true") # 6. Special Projects Lock & Frontier AI Startup Incubator is_sp_unlocked = engine.is_special_projects_unlocked() badge_sp_lock = document.getElementById("badge-special-projects-lock") container_sp_locked = document.getElementById("container-special-projects-locked") container_sp_unlocked = document.getElementById("container-special-projects-unlocked") if badge_sp_lock: if is_sp_unlocked: badge_sp_lock.innerText = "🟒 Active" badge_sp_lock.className = "badge bg-success small ms-1" else: badge_sp_lock.innerText = "πŸ”’ Locked" badge_sp_lock.className = "badge bg-secondary small ms-1" if container_sp_locked and container_sp_unlocked: if is_sp_unlocked: container_sp_locked.style.display = "none" container_sp_unlocked.style.display = "block" else: container_sp_locked.style.display = "block" container_sp_unlocked.style.display = "none" stg = engine.ai_incubator.get("stage", 0) stg_badge = document.getElementById("display-ai-stage-badge") stg_fund = document.getElementById("display-ai-funding") stg_comp = document.getElementById("display-ai-compute") stg_rew = document.getElementById("display-ai-reward") caio_mult = 1.25 if engine.c_suite.get("caio", False) else 1.0 alloc_comp = int(((engine.facilities.get("datacenters", 0) * 500) + (engine.facilities.get("ai_campuses", 0) * 2500)) * caio_mult) engine.ai_incubator["allocated_compute"] = alloc_comp if stg_fund: stg_fund.innerText = f"${engine.ai_incubator.get('funding', 0.0):,.2f}" if stg_comp: stg_comp.innerText = f"{alloc_comp:,} FLOPs" if 1 <= stg <= len(resources.AI_FRONTIER_STAGES): stg_info = resources.AI_FRONTIER_STAGES[stg - 1] if stg_badge: stg_badge.innerText = f"Stage {stg}: {stg_info['name']}" if stg_rew: stg_rew.innerText = stg_info["reward_desc"] elif stg > len(resources.AI_FRONTIER_STAGES): if stg_badge: stg_badge.innerText = "Singularity Complete πŸš€" stg_badge.className = "badge bg-success fs-6 px-3 py-2 fw-bold" if stg_rew: stg_rew.innerText = "Complete IT Autonomous Singularity Achieved!" # Phase 4 Sidebar Lifestyle / Breakroom Gating & Rendering sidebar_breakroom = document.getElementById("sidebar-breakroom") sidebar_ceo_lifestyle = document.getElementById("sidebar-ceo-lifestyle") if engine.phase >= 4: if sidebar_breakroom: sidebar_breakroom.style.display = "none" if sidebar_ceo_lifestyle: sidebar_ceo_lifestyle.style.display = "block" # 1. Update Yacht Marina y_info = engine.get_ceo_yacht_info() y_tier_badge = document.getElementById("lbl-ceo-yacht-tier") y_desc = document.getElementById("lbl-ceo-yacht-desc") y_btn_cruise = document.getElementById("btn-ceo-yacht-cruise") y_btn_upgrade = document.getElementById("btn-ceo-yacht-upgrade") if y_tier_badge: if y_info["tier"] > 0: y_tier_badge.innerText = f"Tier {y_info['tier']}/{y_info['max_tier']} β€’ {y_info['active']['name']}" y_tier_badge.className = "badge bg-info text-dark font-monospace" else: y_tier_badge.innerText = "Uncommissioned" y_tier_badge.className = "badge bg-secondary font-monospace" if y_desc: if y_info["tier"] > 0: y_desc.innerText = y_info["active"]["crew_bonus_desc"] else: y_desc.innerText = "Commission a private ocean cruiser for high-seas stress relief." if y_btn_cruise: if y_info["tier"] <= 0: y_btn_cruise.innerText = "🌊 Yacht Cruise (Locked)" y_btn_cruise.setAttribute("disabled", "true") y_btn_cruise.className = "btn btn-outline-secondary btn-xs w-100 py-1 mb-1 fw-bold text-truncate" elif y_info["cooldown_remaining"] > 0.0: y_btn_cruise.innerText = f"🌊 Yacht Cruise ({y_info['cooldown_remaining']:.0f}s CD)" y_btn_cruise.setAttribute("disabled", "true") y_btn_cruise.className = "btn btn-outline-warning btn-xs w-100 py-1 mb-1 fw-bold text-truncate" else: y_btn_cruise.innerText = f"🌊 Yacht Cruise (-{y_info['active']['stress_relief']:.0f} Stress)" y_btn_cruise.removeAttribute("disabled") y_btn_cruise.className = "btn btn-outline-info btn-xs w-100 py-1 mb-1 fw-bold text-truncate" if y_btn_upgrade: if y_info["next"]: cost = y_info["next"]["cost"] y_btn_upgrade.innerText = f"${cost:,.2f} β€’ {y_info['next']['name']}" if engine.cash >= cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: y_btn_upgrade.removeAttribute("disabled") y_btn_upgrade.className = "btn btn-outline-success btn-xs w-100 py-1 fw-bold text-truncate" else: y_btn_upgrade.setAttribute("disabled", "true") y_btn_upgrade.className = "btn btn-outline-secondary btn-xs w-100 py-1 fw-bold text-truncate" else: y_btn_upgrade.innerText = "⭐ Max Yacht Fleet (400ft Giga-Yacht)" y_btn_upgrade.setAttribute("disabled", "true") y_btn_upgrade.className = "btn btn-outline-warning btn-xs w-100 py-1 fw-bold text-truncate" # 2. Update Private Island Estate isl_info = engine.get_ceo_island_info() isl_tier_badge = document.getElementById("lbl-ceo-island-tier") isl_desc = document.getElementById("lbl-ceo-island-desc") isl_btn_upgrade = document.getElementById("btn-ceo-island-upgrade") if isl_tier_badge: if isl_info["tier"] > 0: isl_tier_badge.innerText = f"Tier {isl_info['tier']}/{isl_info['max_tier']} β€’ {isl_info['active']['name']}" isl_tier_badge.className = "badge bg-success text-dark font-monospace" else: isl_tier_badge.innerText = "Unowned" isl_tier_badge.className = "badge bg-secondary font-monospace" if isl_desc: if isl_info["tier"] > 0: isl_desc.innerText = f"{isl_info['active']['desc']} (-{isl_info['active']['passive_stress_relief']:.1f} Stress/s passive)" else: isl_desc.innerText = "Acquire a secluded tropical island for permanent passive stress relief." if isl_btn_upgrade: if isl_info["next"]: cost = isl_info["next"]["cost"] isl_btn_upgrade.innerText = f"${cost:,.2f} β€’ {isl_info['next']['name']}" if engine.cash >= cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: isl_btn_upgrade.removeAttribute("disabled") isl_btn_upgrade.className = "btn btn-outline-success btn-xs w-100 py-1 fw-bold text-truncate" else: isl_btn_upgrade.setAttribute("disabled", "true") isl_btn_upgrade.className = "btn btn-outline-secondary btn-xs w-100 py-1 fw-bold text-truncate" else: isl_btn_upgrade.innerText = "⭐ Max Sanctuary (Sovereign Nation)" isl_btn_upgrade.setAttribute("disabled", "true") isl_btn_upgrade.className = "btn btn-outline-warning btn-xs w-100 py-1 fw-bold text-truncate" # 3. Update Lifestyle Excursions for act_k, act_spec in resources.CEO_LIFESTYLE_ACTIONS.items(): b_act = document.getElementById(f"btn-ceo-act-{act_k}") lbl_cd = document.getElementById(f"lbl-ceo-cd-{act_k}") cd_rem = engine.ceo_lifestyle_cooldowns.get(act_k, 0.0) cost = act_spec["cost"] if lbl_cd: if cd_rem > 0.0: lbl_cd.innerText = f"-{act_spec['stress_relief']:.0f} Stress ({cd_rem:.0f}s CD)" lbl_cd.className = "text-warning d-block" else: lbl_cd.innerText = f"-{act_spec['stress_relief']:.0f} Stress (Ready)" lbl_cd.className = "text-muted d-block" if b_act: if cd_rem <= 0.0 and engine.cash >= cost and not engine.burnout and not engine.pc_crashed and not engine.is_rebooting: b_act.removeAttribute("disabled") b_act.className = "btn btn-outline-warning btn-xs w-100 p-2 text-start d-flex flex-column justify-content-between" else: b_act.setAttribute("disabled", "true") b_act.className = "btn btn-outline-secondary btn-xs w-100 p-2 text-start d-flex flex-column justify-content-between opacity-50" else: if sidebar_breakroom: sidebar_breakroom.style.display = "block" if sidebar_ceo_lifestyle: sidebar_ceo_lifestyle.style.display = "none" # Refresh achievements UI update_achievements_ui() # Refresh Prestige Perk Tree UI render_prestige_tree_ui() # Refresh Public Relations & ESG Governance UI update_pr_ui() # Phase Tab Visibility Management (Data-Driven from resources.PHASE_TAB_CONFIG) all_tab_lis = [ "helpdesk-tab-li", "upgrades-tab-li", "staff-tab-li", "datacenter-tab-li", "rd-tab-li", "sales-tab-li", "finance-tab-li", "stocks-tab-li", "pr-tab-li", "regional-ops-tab-li", "enterprise-tab-li", "career-tab-li", "achievements-tab-li" ] cur_phase = engine.phase if not hasattr(engine, "_last_rendered_tab_phase") or engine._last_rendered_tab_phase != cur_phase: engine._last_rendered_tab_phase = cur_phase phase_config = resources.PHASE_TAB_CONFIG.get(cur_phase, resources.PHASE_TAB_CONFIG.get(1, [])) active_map = {tab["id"]: tab for tab in phase_config} for li_id in all_tab_lis: el_li = document.getElementById(li_id) if el_li: if li_id in active_map: if el_li.style.display != "block": el_li.style.display = "block" tab_info = active_map[li_id] el_btn = document.getElementById(tab_info.get("tab_id", "")) if el_btn: new_title = tab_info.get("title", "") if new_title and el_btn.innerText != new_title: el_btn.innerText = new_title else: if el_li.style.display != "none": el_li.style.display = "none" # Evaluate declarative component visibility and milestone gating update_declarative_visibility() # Game Time Update Engine last_tick = time.time() auto_save_timer = 0.0 # Rolling resource rates for estimated purchase times cash_rate = 0.0 solved_tickets_rate = 0.0 kp_rate = 0.0 def game_loop(): global last_tick, auto_save_timer global cash_rate, solved_tickets_rate, kp_rate now = time.time() dt = now - last_tick last_tick = now # Capture states before tick to compute rolling change rates prev_cash = engine.cash prev_solved = engine.solved_tickets_inventory prev_kp = engine.knowledge_points engine.update(dt) if dt > 0.0: inst_cash = (engine.cash - prev_cash) / dt inst_solved = (engine.solved_tickets_inventory - prev_solved) / dt inst_kp = (engine.knowledge_points - prev_kp) / dt # Smooth with EMA (alpha = 0.15) alpha = 0.15 cash_rate = (alpha * inst_cash) + ((1.0 - alpha) * cash_rate) solved_tickets_rate = (alpha * inst_solved) + ((1.0 - alpha) * solved_tickets_rate) kp_rate = (alpha * inst_kp) + ((1.0 - alpha) * kp_rate) # Autosave every 15 seconds auto_save_timer += dt if auto_save_timer >= 15.0: auto_save_timer = 0.0 if save.save_to_localstorage(engine): flash_autosave_status() update_ui() def set_cashflow_mode_js(mode: str): global current_cashflow_mode if mode in ["real", "calendar"]: current_cashflow_mode = mode for opt in ["real", "calendar"]: b = document.getElementById(f"btn-cashflow-mode-{opt}") if b: if opt == mode: b.classList.add("active") else: b.classList.remove("active") update_ui() def set_cashflow_timeframe_js(tf: str): global current_cashflow_timeframe if tf in ["sec", "day", "month", "quarter", "year"]: current_cashflow_timeframe = tf for opt in ["sec", "day", "month", "quarter", "year"]: b = document.getElementById(f"btn-cashflow-tf-{opt}") if b: if opt == tf: b.classList.add("active") else: b.classList.remove("active") update_ui() def update_pr_ui(): """Renders the Public Relations (PR), Brand Sentiment & Corporate ESG Governance tab.""" score = getattr(engine, "pr_score", 75.0) bracket = engine.get_pr_bracket() scrutiny = engine.get_pr_scrutiny() mip = getattr(engine, "media_influence_points", 0.0) mip_cap = getattr(engine, "media_influence_cap", 500.0) # 1. Update Gauges & Indicators el_score = document.getElementById("pr-score-val") if el_score: el_score.innerText = f"{score:.1f}%" el_badge = document.getElementById("pr-tier-badge") if el_badge: el_badge.className = f"badge px-2 py-1 {bracket.get('badge_class', 'bg-info text-dark')}" el_badge.innerText = bracket.get("title", "βš–οΈ Respected Industry Standard") el_pbar = document.getElementById("pr-score-progress-bar") if el_pbar: el_pbar.style.width = f"{max(5.0, min(100.0, score))}%" if score >= 90.0: el_pbar.className = "progress-bar bg-success progress-bar-striped progress-bar-animated" elif score >= 65.0: el_pbar.className = "progress-bar bg-info progress-bar-striped progress-bar-animated" elif score >= 35.0: el_pbar.className = "progress-bar bg-warning text-dark progress-bar-striped progress-bar-animated" else: el_pbar.className = "progress-bar bg-danger progress-bar-striped progress-bar-animated" el_mip = document.getElementById("pr-mip-val") if el_mip: el_mip.innerText = f"{mip:.1f} / {mip_cap:,.0f}" el_mip_bar = document.getElementById("pr-mip-progress-bar") if el_mip_bar: pct = (mip / mip_cap * 100.0) if mip_cap > 0 else 0.0 el_mip_bar.style.width = f"{max(0.0, min(100.0, pct))}%" el_scrutiny = document.getElementById("pr-scrutiny-val") if el_scrutiny: el_scrutiny.innerText = f"{scrutiny:.1f} / 100" # 2. Multipliers el_m_lead = document.getElementById("pr-mult-leads") if el_m_lead: el_m_lead.innerText = f"{bracket.get('b2b_lead_mult', 1.0):.2f}x" el_m_stock = document.getElementById("pr-mult-stock") if el_m_stock: el_m_stock.innerText = f"{bracket.get('stock_valuation_mult', 1.0):.2f}x" el_m_burnout = document.getElementById("pr-mult-burnout") if el_m_burnout: el_m_burnout.innerText = f"{bracket.get('staff_burnout_mult', 1.0):.2f}x" el_m_mrr = document.getElementById("pr-mult-mrr") if el_m_mrr: el_m_mrr.innerText = f"{bracket.get('mrr_mult', 1.0):.2f}x" # 3. PR Campaigns Container el_camps = document.getElementById("pr-campaigns-container") if el_camps: cards_html = [] for c_id, spec in resources.PR_CAMPAIGNS.items(): if engine.phase < spec.get("phase", 2): continue cd_rem = engine.pr_campaign_cooldowns.get(c_id, 0.0) act_info = engine.active_pr_campaigns.get(c_id) is_active = act_info is not None cash_cost = spec.get("cash_cost", 0.0) mip_cost = spec.get("mip_cost", 0.0) can_afford = (engine.cash >= cash_cost and mip >= mip_cost and cd_rem <= 0.0 and not is_active) cost_parts = [] if cash_cost > 0: cost_parts.append(f"${cash_cost:,.2f}") if mip_cost > 0: cost_parts.append(f"{mip_cost:,.0f} MIP") cost_str = " + ".join(cost_parts) if cost_parts else "Free" if is_active: btn_state = f'' elif cd_rem > 0.0: btn_state = f'' elif can_afford: btn_state = f'' else: btn_state = f'' cards_html.append(f"""
    {spec.get('icon', 'πŸ“£')} {spec['name']} +{spec.get('pr_gain', 10):.0f}% PR
    {spec.get('desc', '')}
    {btn_state}
    """) el_camps.innerHTML = "".join(cards_html) # 4. Proactive ESG Policies Container el_esg = document.getElementById("pr-esg-policies-container") if el_esg: policies_html = [] for p_id, spec in resources.PR_ESG_POLICIES.items(): if engine.phase < spec.get("phase", 3): continue is_active = engine.active_esg_policies.get(p_id, False) checked_attr = "checked" if is_active else "" status_badge = 'ENACTED' if is_active else 'INACTIVE' policies_html.append(f"""
    {spec.get('icon', '🌿')} {spec['name']} {status_badge}
    {spec.get('desc', '')}
    OpEx: ${spec.get('monthly_opex', 0):,.0f}/mo Accretion: +{spec.get('pr_rate_per_sec', 0.1):.2f}%/s
    """) el_esg.innerHTML = "".join(policies_html) # 5. Media Empire Acquisitions (Phase 4) panel_media = document.getElementById("pr-media-empire-panel") if panel_media: panel_media.style.display = "block" if engine.phase >= 4 else "none" el_media = document.getElementById("pr-media-outlets-container") if el_media and engine.phase >= 4: media_html = [] for out_id, spec in resources.PR_MEDIA_OUTLETS.items(): is_owned = out_id in engine.owned_media_outlets cost = spec.get("cost", 1000000.0) can_afford = (engine.cash >= cost and not is_owned) if is_owned: btn_state = '' elif can_afford: btn_state = f'' else: btn_state = f'' media_html.append(f"""
    {spec.get('icon', 'πŸ“‘')} {spec['name']} +{spec.get('mip_per_sec', 1.0):.1f} MIP/s
    {spec.get('desc', '')}
    {btn_state}
    """) el_media.innerHTML = "".join(media_html) def launch_pr_campaign_js(campaign_id: str): if engine.launch_pr_campaign(str(campaign_id)): update_ui() def toggle_esg_policy_js(policy_id: str, state: bool = None): if engine.toggle_esg_policy(str(policy_id), state): update_ui() def buy_media_outlet_js(outlet_id: str): if engine.buy_media_outlet(str(outlet_id)): update_ui() def jump_to_tab_js(tab_id: str, target_elem_id: str = None): try: t_btn = document.getElementById(str(tab_id)) if t_btn: t_btn.click() if target_elem_id: t_el = document.getElementById(str(target_elem_id)) if t_el: t_el.scrollIntoView({"behavior": "smooth", "block": "center"}) except Exception as ex: print(f"[HIT] Tab jump error: {ex}") # Window global proxies for template onclick calls window.jump_to_tab_js = create_proxy(jump_to_tab_js) window.launch_pr_campaign_js = create_proxy(launch_pr_campaign_js) window.toggle_esg_policy_js = create_proxy(toggle_esg_policy_js) window.buy_media_outlet_js = create_proxy(buy_media_outlet_js) window.set_infra_governor_mode_js = create_proxy(lambda mode: (engine.set_infra_governor_mode(mode), update_ui())) window.trigger_maintenance_js = create_proxy(lambda: (engine.trigger_maintenance_window(), update_ui())) window.build_facility_js = create_proxy(lambda f_type: (engine.build_phase3_facility(f_type), update_ui())) window.rush_facility_js = create_proxy(lambda f_type: (engine.rush_facility_construction(f_type), update_ui())) window.hire_branch_manager_js = create_proxy(lambda b_id: (engine.hire_branch_manager(b_id), update_ui())) window.set_branch_directive_js = create_proxy(lambda b_id, d: (engine.set_branch_directive(b_id, d), update_ui())) window.toggle_it_policy_js = create_proxy(lambda p_id: (engine.toggle_it_policy(p_id), update_ui())) window.select_research_project_js = create_proxy(lambda t_id: (engine.select_research_project(t_id), update_ui())) window.set_research_funnel_rate_js = create_proxy(lambda rate: (engine.set_research_funnel_rate(float(rate)), update_ui())) window.expedite_research_js = create_proxy(lambda amt: (engine.expedite_research_cash(float(amt)), update_ui())) window.toggle_auto_pitch_js = create_proxy(lambda checked: (setattr(engine, "auto_pitch_enabled", bool(checked)), update_ui())) window.research_rd_tech_js = create_proxy(lambda t_id: (engine.select_research_project(t_id), update_ui())) window.research_tech_js = create_proxy(lambda t_id: (engine.select_research_project(t_id), update_ui())) window.synthesize_knowledge_js = create_proxy(lambda amt: (engine.synthesize_knowledge(amt), update_ui())) window.buy_rd_upgrade_js = create_proxy(lambda up_id: (engine.buy_rd_upgrade(up_id), update_ui())) window.engine_buy_upgrade_js = create_proxy(lambda up_id: (engine.buy_upgrade(up_id), update_ui())) def hot_reload_balance_js(show_alert=False): try: raw_bal = window.localStorage.getItem("HIT_BALANCE_DATA") applied = False if raw_bal: bal_dict = json.loads(str(raw_bal)) if bal_dict and isinstance(bal_dict, dict): if hasattr(resources, "_apply_balance_dict"): resources._apply_balance_dict(bal_dict) else: for k, v in bal_dict.items(): if hasattr(resources, k): cur = getattr(resources, k) if isinstance(cur, dict) and isinstance(v, dict): cur.clear() cur.update(v) elif isinstance(cur, list) and isinstance(v, list): cur.clear() cur.extend(v) else: setattr(resources, k, v) else: setattr(resources, k, v) applied = True if not applied and hasattr(resources, "reload_game_balance"): applied = resources.reload_game_balance() update_ui() print("[HIT] Live balance hot-reloaded successfully!") if show_alert: window.alert("βœ… Hot-reloaded game balance successfully!") elif hasattr(window, "showToast"): try: window.showToast("⚑ Game Balance Hot-Reloaded!", "Updated stats and costs applied instantly.") except Exception: pass return True except Exception as ex: print(f"[HIT] Error reloading balance: {ex}") if show_alert: window.alert(f"⚠️ Error reloading balance: {ex}") return False window.hot_reload_balance_js = create_proxy(lambda: hot_reload_balance_js(show_alert=True)) window.auto_hot_reload_balance_js = create_proxy(lambda: hot_reload_balance_js(show_alert=False)) window.set_cashflow_mode_js = create_proxy(set_cashflow_mode_js) window.set_cashflow_timeframe_js = create_proxy(set_cashflow_timeframe_js) # Initialize Game # Hot reload customized game balance from localStorage if present hot_reload_balance_js(show_alert=False) # Load existing save if present save.load_from_localstorage(engine) setup_listeners() update_ui() check_offline_progress() # Start 100ms clock interval for 10Hz game and UI ticking window.setInterval(create_proxy(game_loop), 100) print("Hello, IT! (HIT!) Engine Scaffolding Successfully Loaded.")