// ==UserScript==
// @name AQ Ruffle Enhance
// @namespace http://tampermonkey.net/
// @version 1.1.0
// @description A set of enhancements for serious AdventureQuest play!
// @author You
// @match https://*.battleon.com/game/web*
// @icon https://www.google.com/s2/favicons?sz=64&domain=battleon.com
// @grant none
// @license MIT
// ==/UserScript==
const gameSwf = document.getElementsByClassName("base")[0].src;
const baseSwf = window.location.origin + '/game/flash/';
const MAX_PLAYERS = 5;
const MENU_BAR_HEIGHT = 20;
const serverOptions = [
{ value: 'aq', name: 'aq' },
{ value: 'guardian', name: 'guardian' },
{ value: 'new', name: 'new' }
];
const renderOptions = [
{ value: 'webgpu', name: "WebGPU" },
{ value: 'wgpu-webgl', name: "WGPU" },
{ value: 'webgl', name: "WebGL" },
{ value: 'canvas', name: 'Canvas2D' }
];
const links = [
{ url: 'https://account.battleon.com/', name: 'Manage Account' },
{ url: 'https://aq-char-info.firebaseapp.com/', name: 'Char Info' },
{ url: 'https://battleon.com/', name: 'Home' },
{ url: 'https://adventurequestwiki.fandom.com/wiki/AdventureQuestWiki_Wiki', name: 'Wiki' },
{ url: 'https://forums2.battleon.com/f/tt.asp?forumid=1', name: 'Forums' },
{ url: 'https://discord.com/invite/FQsFCGV', name: 'Discord' },
];
const defaultPlayerConfig = {
url: gameSwf,
base: baseSwf,
preferredRenderer: "webgl",
warnOnUnsupportedContent: false,
};
const createElement = (tag, options = {}) => Object.assign(document.createElement(tag), options);
const renderDropdown = (options, selectOptions = {}) => {
const select = createElement('select', selectOptions);
options.forEach((option) => {
const selectOption = createElement('option', {
value: option.value,
text: option.name,
selected: defaultPlayerConfig.preferredRenderer === option.value
});
select.appendChild(selectOption);
});
return select;
};
const renderOptionGroup = (dropdown, button) => {
const optionGroup = createElement('span');
optionGroup.appendChild(dropdown);
optionGroup.appendChild(button);
return optionGroup;
};
const resizeWindows = () => {
const container = document.getElementById('container');
container.style.width = window.innerWidth;
const playerContainers = Array.from(document.getElementsByClassName('player-container'));
if (playerContainers.length === 0) {
addWindow();
resizeWindows();
return;
}
// Thanks to nivp for the window resize algorithm!
const availableHeight = window.innerHeight - MENU_BAR_HEIGHT;
let max_width = 0;
let max_height = 0;
// maximize cell size through column search
for (let i = 1; i <= playerContainers.length; i++) {
let temp_height = availableHeight / Math.ceil(playerContainers.length / i);
let temp_width = temp_height * 4 / 3;
if (temp_width * i > (window.innerWidth * i) / (i + 1) && temp_width * i <= window.innerWidth) {
max_width = temp_width;
max_height = temp_height;
}
}
// maximize cell size through row search
for (let i = 1; i <= playerContainers.length; i++) {
let temp_width = window.innerWidth / Math.ceil(playerContainers.length / i);
let temp_height = temp_width * 3 / 4;
if (
temp_height * i > (availableHeight * i) / (i + 1) &&
temp_height * i <= availableHeight
) {
max_width = temp_width;
max_height = temp_height;
}
}
playerContainers.forEach((player) => {
player.style.width = max_width;
player.style.height = max_height;
const rufflePlayer = player.getElementsByTagName('ruffle-player')[0];
const playerMenuBar = player.getElementsByClassName('player-menu-bar')[0];
rufflePlayer.style.height = max_height;
rufflePlayer.style.width = max_height * 4 / 3;
playerMenuBar.style.width = max_height * 4 / 3;
});
};
const addWindow = () => {
const container = document.getElementById('container');
const player = window.RufflePlayer.newest().createPlayer();
player.style = 'margin: 0 auto;';
const playerContainer = createElement('div', {
className: 'player-container',
style: 'display: flex; flex-direction: column; align-items: center;'
});
const playerMenuBar = createElement('div', {
className: 'player-menu-bar',
style: `height: ${MENU_BAR_HEIGHT}px; background-color: grey; display: flex; justify-content: center;`
});
const killWindowButton = createElement('button', {
innerText: 'x',
onclick: () => {
playerContainer.remove();
document.getElementById('add-button').disabled = false;
document.getElementById('add-all-button').disabled = false;
resizeWindows();
}
});
const refreshPlayerButton = createElement('button', {
innerText: 'Refresh',
onclick: () => player.reload()
});
const dropdown = renderDropdown(renderOptions, { className: 'player-dropdown' });
const changeRendererButton = createElement('button', {
innerText: 'Change Renderer',
onclick: () => {
const selectedValue = dropdown.value;
const playerConfig = Object.assign({}, defaultPlayerConfig);
playerConfig.preferredRenderer = selectedValue;
player.load(playerConfig);
}
});
const rendererGroup = renderOptionGroup(dropdown, changeRendererButton);
playerMenuBar.appendChild(rendererGroup);
playerMenuBar.appendChild(refreshPlayerButton);
playerMenuBar.appendChild(killWindowButton);
playerContainer.appendChild(playerMenuBar);
playerContainer.appendChild(player);
container.appendChild(playerContainer);
player.load(defaultPlayerConfig);
};
const addAllWindows = () => {
const playerContainers = document.getElementsByTagName('ruffle-player');
for (let i = playerContainers.length; i < MAX_PLAYERS; i++) {
addWindow();
}
};
const renderMenuBar = () => {
const menuBar = createElement('div', {
style: `height: ${MENU_BAR_HEIGHT}px; width: 100%; display: flex; justify-content: space-between;`
});
const addButton = createElement("button", {
id: 'add-button',
innerText: '+Add',
onclick: () => {
addWindow();
const playerContainers = document.getElementsByTagName('ruffle-player');
document.getElementById('add-button').disabled = playerContainers.length === MAX_PLAYERS;
document.getElementById('add-all-button').disabled = playerContainers.length === MAX_PLAYERS;
resizeWindows();
}
});
const addAllButton = createElement("button", {
id: 'add-all-button',
innerText: `+Max (${MAX_PLAYERS})`,
onclick: () => {
addAllWindows();
document.getElementById('add-button').disabled = true;
addAllButton.disabled = true;
resizeWindows();
}
});
const killAllButton = createElement('button', {
id: 'kill-all-button',
innerText: 'Kill Windows',
onclick: () => {
const playerContainers = Array.from(document.getElementsByClassName('player-container'));
playerContainers.forEach((playerContainer) => {
playerContainer.remove();
});
document.getElementById('add-button').disabled = false;
document.getElementById('add-all-button').disabled = false;
addWindow();
resizeWindows();
}
});
const rendererDropdown = renderDropdown(renderOptions);
const changeRendererButton = createElement('button', {
innerText: 'Change Renderers',
onclick: () => {
const selectedValue = rendererDropdown.value;
const players = Array.from(document.getElementsByTagName('ruffle-player'));
players.forEach((player) => {
const playerConfig = Object.assign({}, defaultPlayerConfig);
playerConfig.preferredRenderer = selectedValue;
defaultPlayerConfig.preferredRenderer = selectedValue;
player.load(playerConfig);
});
const playerDropdowns = Array.from(document.getElementsByClassName('player-dropdown'));
playerDropdowns.forEach((dropdown) => {
dropdown.value = selectedValue
});
}
});
const rendererOptionsGroup = renderOptionGroup(rendererDropdown, changeRendererButton);
const goToServer = createElement('button', {
textContent: 'New Tab',
onclick: () => {
const select = document.getElementById('server-select');
window.open(`https://${select.value}.battleon.com/game/web`);
}
});
const dropdown = renderDropdown(serverOptions, {
id: 'server-select'
});
const buttonGroup = createElement('div');
buttonGroup.appendChild(addButton);
buttonGroup.appendChild(addAllButton);
buttonGroup.appendChild(killAllButton);
const linksGroup = createElement('div');
links.forEach((link, index) => {
const newLink = createElement('a', {
textContent: link.name,
href: link.url,
target: '_blank',
rel: 'noopener',
style: 'font-size: 16px;'
});
linksGroup.appendChild(newLink);
if (index !== links.length - 1) {
const sep = createElement('span', {
textContent: '|',
style: 'font-size: 16px; margin: 5px;'
});
linksGroup.appendChild(sep);
}
});
const selectGroups = createElement('div');
const serverOptionsGroup = renderOptionGroup(dropdown, goToServer);
selectGroups.appendChild(rendererOptionsGroup);
selectGroups.appendChild(serverOptionsGroup);
menuBar.appendChild(buttonGroup);
menuBar.appendChild(linksGroup);
menuBar.appendChild(selectGroups);
const playersGroup = createElement('div', {
id: "container",
style: 'display: flex; flex-wrap: wrap; justify-content: center;'
});
document.body.appendChild(playersGroup);
playersGroup.appendChild(menuBar);
}
const checkForRuffle = () => {
const checker = setInterval(() => {
if (window.RufflePlayer && window.RufflePlayer.invoked) {
clearInterval(checker);
window.addEventListener('resize', () => {
let timer;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
resizeWindows();
}, 250);
});
document.body.style.margin = 0;
document.getElementById('main').remove();
renderMenuBar();
addWindow();
resizeWindows();
window.onbeforeunload = (e) => {
return 'Are you sure you want to quit?'
}
}
}, 100);
setTimeout(() => {
clearInterval(checker);
if (!window.RufflePlayer && window.confirm('I could not detect the Ruffle Plugin. Would you like to attempt importing Ruffle?')) {
const script = createElement('script', {
type: 'text/javascript',
src: 'https://unpkg.com/@ruffle-rs/ruffle'
});
document.getElementsByTagName('head')[0].appendChild(script);
checkForRuffle();
}
}, 2000);
}
(function() {
'use strict';
checkForRuffle();
})();