let gearData = null; let gearThreadActive = false; let keepStatsUpdated = false; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } document.addEventListener('DOMContentLoaded', () => { loadGearData(); sendToCSharp("OSType"); }); const delay = (ms) => new Promise(res => setTimeout(res, ms)); async function updateStatsPage() { while (keepStatsUpdated) { await fetchCemuBase(); await delay(2000); } } async function loadGearData() { try { const response = await fetch('ids.json'); gearData = await response.json(); populateAbilities(); syncGearDropdown(); } catch (e) { console.error("Gear IDs failed to load. Check if ids.json exists.", e); } } function onCategoryChange() { syncGearDropdown(); const grid = document.getElementById('gear-selection-grid'); grid.innerHTML = '

Fetching Gear...

'; execute('FetchGear'); } function syncGearDropdown() { if (!gearData) return; const cat = document.getElementById('gear-category').value; const key = (cat === "clothing") ? "clothes" : cat; const select = document.getElementById('gear-item'); if (!select) return; select.innerHTML = ''; if (gearData[key]) { for (const [id, name] of Object.entries(gearData[key])) { const opt = document.createElement('option'); opt.value = id; opt.textContent = name; select.appendChild(opt); } } } function populateAbilities() { const selectors = ['ability-sub1', 'ability-sub2', 'ability-sub3']; selectors.forEach(id => { const select = document.getElementById(id); if (!select || !gearData.abilities) return; select.innerHTML = ''; for (const [val, name] of Object.entries(gearData.abilities)) { const opt = document.createElement('option'); opt.value = val; opt.textContent = name; select.appendChild(opt); } }); } function populateEquipment() { const weapons = document.getElementById("weapon-sets"); const headgear = document.getElementById("headgear-sets"); const clothes = document.getElementById("clothes-sets"); const shoes = document.getElementById("shoes-sets"); weapons.innerHTML = ''; for (const [val, name] of Object.entries(gearData.WeaponSetIds)) { const opt = document.createElement('option'); opt.value = val; opt.textContent = name; weapons.appendChild(opt); } headgear.innerHTML = ''; for (const [val, name] of Object.entries(gearData.headgear)) { const opt = document.createElement('option'); opt.value = val; opt.textContent = name; headgear.appendChild(opt); } clothes.innerHTML = ''; for (const [val, name] of Object.entries(gearData.clothes)) { const opt = document.createElement('option'); opt.value = val; opt.textContent = name; clothes.appendChild(opt); } shoes.innerHTML = ''; for (const [val, name] of Object.entries(gearData.shoes)) { const opt = document.createElement('option'); opt.value = val; opt.textContent = name; shoes.appendChild(opt); } sendToCSharp("AllEquipment"); } function warnUser() { if (!sessionStorage.getItem("warnShown")) { alert("DO NOT USE ANY OF THESE BEFORE THE TITLE SCREEN LOADED OR YOU WILL WIPE YOUR SAVE FILE YOU HAVE BEEN WARNED!!!"); sessionStorage.setItem('warnShown', 'true'); } } window.external.receiveMessage(message => { // console.log(message) if (message.startsWith("Cemu Base:")) { document.getElementById("cbase").innerHTML = message; } if (message === "2") { alert("MacOS Build is Untested. Expect Issues!"); } try { const data = JSON.parse(message); console.log("Parsed Data:", data); if (data.EquippedWepId !== undefined) { const weapons = document.getElementById("weapon-sets"); const headgear = document.getElementById("headgear-sets"); const clothes = document.getElementById("clothes-sets"); const shoes = document.getElementById("shoes-sets"); weapons.value = data.EquippedWepId; headgear.value = data.EquippedHeadgearId; clothes.value = data.EquippedClothesId; shoes.value = data.EquippedShoesId; return; } const container = document.getElementById("pid-display"); if (data.Players) { container.innerHTML = `

Fetching data....

`; let html = `
SESSION: ${data.SessionID}
`; data.Players.forEach((p, i) => { html += `
P${i}: ${p.Name}
PID: ${p.PID}
NNID: ${p.NNID}
`; }); container.innerHTML = html || '

No players found.

'; } else if (data.headgear || data.clothing || data.shoes || data.clothes) { renderGearGrid(data); } } catch (e) { console.log(e.message); } }); function renderGearGrid(fullData) { const category = document.getElementById('gear-category').value; const grid = document.getElementById('gear-selection-grid'); grid.innerHTML = ""; let items = fullData[category]; if (!items && category === "clothing") items = fullData["clothes"]; if (!items || items.length === 0) { grid.innerHTML = '

No gear found in this category.

'; return; } const idLookupKey = (category === "clothing") ? "clothes" : category; items.forEach((item, index) => { const isEmpty = (item.GearID === 4294967295 || item.GearID === -1); let gearName = "Unknown Item"; if (isEmpty) { gearName = "Empty Slot (+)"; } else if (gearData && gearData[idLookupKey] && gearData[idLookupKey][item.GearID]) { gearName = gearData[idLookupKey][item.GearID]; } else { gearName = `ID: ${item.GearID}`; } const card = document.createElement('div'); card.className = 'pid-card'; card.style.cursor = 'pointer'; if (isEmpty) { card.style.borderLeft = '4px dashed #444'; card.style.opacity = '0.7'; } card.innerHTML = `${gearName}
Slot: ${index}`; card.onclick = () => { document.querySelectorAll('.pid-card').forEach(c => { c.style.borderColor = 'var(--border)'; c.style.background = 'transparent'; }); card.style.borderColor = 'var(--highlight)'; card.style.background = 'rgba(3, 218, 198, 0.05)'; syncGearDropdown(); document.getElementById('gear-slot-idx').value = index; if (!isEmpty) { document.getElementById('gear-item').value = item.GearID; document.getElementById('ability-sub1').value = (item.Slot1 === 4294967295) ? 0 : item.Slot1; document.getElementById('ability-sub2').value = (item.Slot2 === 4294967295) ? 0 : item.Slot2; document.getElementById('ability-sub3').value = (item.Slot3 === 4294967295) ? 0 : item.Slot3; } else { document.getElementById('gear-item').selectedIndex = 0; document.getElementById('ability-sub1').value = 0; document.getElementById('ability-sub2').value = 0; document.getElementById('ability-sub3').value = 0; } }; grid.appendChild(card); }); } function applyGearChange() { const category = document.getElementById('gear-category').value; const slot = document.getElementById('gear-slot-idx').value; const id = document.getElementById('gear-item').value; const s1 = document.getElementById('ability-sub1').value; const s2 = document.getElementById('ability-sub2').value; const s3 = document.getElementById('ability-sub3').value; sendToCSharp("WriteToGearQueue", [slot, category, id, s1, s2, s3]); } function toggleGearThread() { gearThreadActive = !gearThreadActive; const btn = document.getElementById('gear-thread-btn'); btn.textContent = gearThreadActive ? "Stop Queue" : "Write Queue"; btn.style.background = gearThreadActive ? "var(--highlight)" : "#333"; sendToCSharp("WriteGearQueue"); } function sendToCSharp(action, settings = []) { if (window.external && window.external.sendMessage) { window.external.sendMessage(JSON.stringify({ action: action, settings: settings })); } } function openTab(evt, tabName) { const content = document.getElementsByClassName("tab-content"); for (let i = 0; i < content.length; i++) { content[i].classList.remove("active"); content[i].style.display = "none"; } const links = document.getElementsByClassName("tab-link"); for (let i = 0; i < links.length; i++) links[i].classList.remove("active"); document.getElementById(tabName).style.display = "block"; document.getElementById(tabName).classList.add("active"); evt.currentTarget.classList.add("active"); if (tabName === 'Tab6') { keepStatsUpdated = true; updateStatsPage(); } else if (tabName == 'Tab8') { warnUser() } else if (tabName == 'Tab9') { warnUser() populateEquipment() } else { keepStatsUpdated = false; } } function namechange() { sendToCSharp("namechange", [document.getElementById("name-input").value]); } function sessionid() { sendToCSharp("sessionid", [document.getElementById("session-input").value]); } function faceimgtga() { sendToCSharp("faceimgtga", [document.getElementById("format-select").value]); } function GenderChange() { sendToCSharp("ChangeGender", [document.getElementById("gender-select").value]); } function Level() { sendToCSharp("ChangeLevel", [document.getElementById("level-input").value]); } function Cash() { sendToCSharp("ChangeCash", [document.getElementById("cash-input").value]); } function SeaSnails() { sendToCSharp("ChangeSeaSnails", [document.getElementById("snails-input").value]); } function execute(cheat) { sendToCSharp(cheat); } function fetchCemuBase() { sendToCSharp("cbase"); } function fetchPids() { sendToCSharp("sessiondata"); } function changeHeadgear() { sendToCSharp("HeadgearChange", [document.getElementById("headgear-sets").value]); } function changeHeadgearApply() { sendToCSharp("HeadgearChangeApply", [document.getElementById("headgear-sets").value]); } function changeClothes() { sendToCSharp("ClothesChange", [document.getElementById("clothes-sets").value]); } function changeClothesApply() { sendToCSharp("ClothesChangeApply", [document.getElementById("clothes-sets").value]); } function changeShoes() { sendToCSharp("ShoesChange", [document.getElementById("shoes-sets").value]); } function changeShoesApply() { sendToCSharp("ShoesChangeApply", [document.getElementById("shoes-sets").value]); } function changeWeapon() { sendToCSharp("WepChange", [document.getElementById("weapon-sets").value]); } function changeWeaponApply() { sendToCSharp("WepChangeApply", [document.getElementById("weapon-sets").value]); }