
Good morning, everyone. I am running a 2024 game and couldn't get any XP trackers to work. I developed a custom script that works to track and divide it by the play using chat push buttons; however, I couldn't get it to auto-push to the sheet like Lazy Experience does. It is a rudimentary script at best. I am hoping the community will find some use for it until API scripts are fully functional for 2024. on('ready', () => { log("=== XP Splitter API with UI Buttons Ready ==="); const XP_ATTRIBUTE = "npc_xp"; const DEAD_MARKER = "dead"; function getTokenXP(token) { const character = getObj('character', token.get('represents')); if (!character) return 0; const attr = findObjs({ type: 'attribute', characterid: character.id, name: XP_ATTRIBUTE })[0]; return attr ? parseInt(attr.get('current'), 10) || 0 : 0; } // Function to send the main menu button for counting XP function sendMainMenu() { const menu = [ `/w gm <b>XP Splitter Menu</b>`, `<a href="!xp-count"><b>Count XP from Dead NPCs</b></a>` ].join('<br>'); sendChat('XP Splitter', menu); } // Function to send divide XP options menu with buttons function sendDivideMenu(totalXP) { let buttons = []; // Add buttons for 2 to 6 players distribution for (let i=2; i<=10; i++) { buttons.push(`<a href="!xp-divide ${totalXP} ${i}">${i} Players</a>`); } // Button for custom input buttons.push(`<a href="!xp-divide-prompt ${totalXP}">Custom Player Count</a>`); const menu = [ `/w gm <b>Total XP: ${totalXP}</b>`, 'Divide XP among players:', buttons.join(' | ') ].join('<br>'); sendChat('XP Splitter', menu); } // Handler for chat commands on('chat:message', (msg) => { if (msg.type !== 'api') return; let args = msg.content.split(' '); let command = args[0]; if (command === '!xp-menu') { sendMainMenu(); return; } if (command === '!xp-count') { const pageId = Campaign().get('playerpageid'); let tokens = findObjs({ type: 'graphic', _pageid: pageId, subtype: 'token' }); let deadTokens = tokens.filter(token => token.get('status_' + DEAD_MARKER)); if (deadTokens.length === 0) { sendChat('XP Splitter', '/w gm No dead tokens found on the current page.'); return; } let totalXP = 0; deadTokens.forEach(token => { const xp = getTokenXP(token); if (xp > 0) { totalXP += xp; // Zero out so it won't be counted next time const character = getObj('character', token.get('represents')); const attr = findObjs({ type: 'attribute', characterid: character.id, name: XP_ATTRIBUTE })[0]; if (attr) attr.set('current', 0); } }); if (totalXP === 0) { sendChat('XP Splitter', '/w gm No XP values found on dead tokens.'); return; } // Store totalXP in state for later use state.XPSplitter = state.XPSplitter || {}; state.XPSplitter.totalXP = totalXP; sendDivideMenu(totalXP); return; } if (command === '!xp-divide') { if (args.length < 3) { sendChat('XP Splitter', '/w gm Usage: !xp-divide [totalXP] [number_of_players]'); return; } const totalXP = parseInt(args[1]); const numPlayers = parseInt(args[2]); if (isNaN(totalXP) || isNaN(numPlayers) || numPlayers <= 0) { sendChat('XP Splitter', '/w gm Invalid arguments for !xp-divide.'); return; } // Confirm and whisper the division result const each = Math.floor(totalXP / numPlayers); const leftover = totalXP % numPlayers; let message = `<b>Dividing ${totalXP} XP among ${numPlayers} players</b>:<br>`; message += `Each player receives: ${each} XP.<br>`; if (leftover > 0) { message += `Leftover XP: ${leftover}.`; } sendChat('XP Splitter', `/w gm ${message}`); // Optionally, here you could automatically distribute XP // by pushing via ChatSetAttr or your preferred method. // Clear stored XP delete state.XPSplitter.totalXP; return; } if (command === "!xp-divide-prompt") { // Custom player count prompt (if optional extra features desired) if (args.length < 2) { sendChat('XP Splitter', '/w gm Usage: !xp-divide-prompt [totalXP]'); return; } const totalXP = parseInt(args[1]); if (isNaN(totalXP)) { sendChat('XP Splitter', '/w gm Invalid totalXP'); return; } const prompt = `/w gm &{template:default} {{name=XP Divide Custom}} {{Total XP=${totalXP}}} {{How many players? ?{Number of players|1}}}`; sendChat('XP Splitter', prompt); return; } }); });