您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows rolling from forge steel character sheets into roll20.
// ==UserScript== // @name Forged20 // @namespace jackpoll4100 // @version 1.1 // @description Allows rolling from forge steel character sheets into roll20. // @author jackpoll4100 // @match https://andyaiken.github.io/forgesteel* // @match https://app.roll20.net/* // @match https://*.discordsays.com/* // @icon https://raw.githubusercontent.com/jackpoll4100/Forged20/refs/heads/main/DS%20logo.png // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // ==/UserScript== (function() { 'use strict'; if (!window.location.href.includes('forgesteel')){ window.forgesteelEnabled = false; function forgesteelToggle(){ window.forgesteelEnabled = !window.forgesteelEnabled; }; let forgesteelSettingsTemplate = `<div id="forgesteelSettings" style="display: flex; flex-direction: row; justify-content: space-between;"> <input type="checkbox" id="forgesteelEnabled" title="Enables rolling from your Forge Steel character sheet in another tab."> <input id="autoCheckLabel" style="margin: 5px 5px 5px 5px; width: 90%" disabled value="Enable rolls from Forge Steel" type="text" title="Enables rolling from your Forge Steel character sheet in another tab."> </div>`; function GM_onMessage(label, callback){ GM_addValueChangeListener(label, function(){ callback.apply(undefined, arguments[2]); }); } function execMacro(macro){ console.log('Forge Steel - Executing Macro: ', macro); if (!window.forgesteelEnabled){ console.log('cancelling macro execution, forgesteel connection not enabled.'); return; } document.querySelectorAll('[title="Text Chat Input"]')[0].value = macro; document.getElementById('chatSendBtn').click(); } GM_onMessage('forgesteel-pipe', function(message) { console.log('forgesteel message received: ', message); if (message.includes('template')){ let cleanedString = message.split('---')[1]; execMacro(cleanedString); } }); function appendForgeSteelSettings(){ let uiContainer = document.createElement('div'); uiContainer.innerHTML = forgesteelSettingsTemplate; document.getElementById('textchat-input').appendChild(uiContainer); document.getElementById('forgesteelEnabled').addEventListener('click', forgesteelToggle); } function timer (){ if (document.getElementById('chatSendBtn')){ appendForgeSteelSettings(); } else{ setTimeout(timer, 500); } } setTimeout(timer, 0); console.log('forgesteel listener registered'); } else { function GM_sendMessage(label){ GM_setValue(label, Array.from(arguments).slice(1)); } console.log('sending open message'); GM_sendMessage('forgesteel-pipe', 'forgesteel opened'); let classMap = { rollSelector: '.total > div.ant-statistic-content > span > span', rollTitles: ['.modal-content .characteristic-modal > div.ant-statistic > div.ant-statistic-title', '.modal-content .ability-panel > div.header-text-panel > div'], characterSelector: '.hero-main-column > div.header-text-panel > div', rollButton: '.die-roll-panel > .ant-btn', effectsSelector: '.ant-drawer .power-roll-row .effect', criticalSuccess: '.ant-alert-success', tierAlert: '.ant-alert-warning .ant-alert-message' }; function rollWatcher(){ const roll = document.querySelector(classMap.rollSelector)?.innerHTML; let rollTitle = ''; classMap.rollTitles.forEach((selector)=>{ rollTitle = document.querySelector(selector)?.innerHTML ? document.querySelector(selector).innerHTML : rollTitle; }); const tierAlert = document.querySelector(classMap.tierAlert); let modifierText = ''; let rollTier = roll < 12 ? 1 : roll < 17 ? 2 : 3; if (tierAlert?.innerHTML?.includes('down') && rollTier > 1){ rollTier --; modifierText = '(Tier was decreased by a Double Bane)'; } else if (tierAlert?.innerHTML?.includes('up') && rollTier < 3){ rollTier ++; modifierText = '(Tier was increased by a Double Edge)'; } const effects = document.querySelectorAll(classMap.effectsSelector); const constructedEffect = effects.length === 3 ? `{{effect=${ effects[rollTier - 1].innerHTML } ${ modifierText }}}` : ''; const charName = document.querySelector(classMap.characterSelector)?.innerHTML; const constructedMessage = `&{template:default} {{name=${ charName ? `${ charName }` : '' }}} ${ rollTitle ? `{{type=${ rollTitle }}}` : '' } {{result=${ roll } ${ document.querySelector(classMap.criticalSuccess) ? '(Critical Success)' : ''}}} ${ constructedEffect }`; console.log('Sending message to roll20: ', constructedMessage); GM_sendMessage('forgesteel-pipe', `${ Math.random() }---` + constructedMessage); } const bodyTarget = document.querySelector('body'); const config = { attributes: false, childList: true, subtree: true }; const listenerSetup = ()=>{ const foundElement = document.querySelector(classMap.rollButton); if (foundElement && !foundElement.getAttribute('listener-applied')){ foundElement.setAttribute('listener-applied', true); foundElement.addEventListener('click', ()=>{ setTimeout(rollWatcher, 100); }); } }; const observer = new MutationObserver(listenerSetup); observer.observe(bodyTarget, config); } })();