AQ Ruffle Enhance

Adds multi-windowed capability for serious AQ play.

目前為 2023-09-05 提交的版本,檢視 最新版本

// ==UserScript==
// @name         AQ Ruffle Enhance
// @namespace    http://tampermonkey.net/
// @version      0.4.4
// @description  Adds multi-windowed capability for serious AQ 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 = 6;
const MENU_BAR_HEIGHT = 20;
const serverOptions = [
    { value: 'aq', name: 'aq' },
    { value: 'guardian', name: 'guardian' },
    { value: 'new', name: 'new' }
];

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 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();
    const playerContainer = document.createElement('div');
    const playerMenuBar = document.createElement('div');
    playerMenuBar.className = 'player-menu-bar';

    playerContainer.className = 'player-container';
    playerContainer.style = 'display: flex; flex-direction: column; align-items: center;';
    playerMenuBar.style = `height: ${MENU_BAR_HEIGHT}px; background-color: grey; display: flex; justify-content: center;`;
    player.style = 'margin: 0 auto;';

    const killWindowButton = document.createElement('button');
    killWindowButton.innerText = 'x';
    killWindowButton.onclick = () => {
        playerContainer.remove();
        document.getElementById('add-button').disabled = false;
        document.getElementById('add-all-button').disabled = false;
        resizeWindows();
    };

    const refreshPlayerButton = document.createElement('button');
    refreshPlayerButton.innerText = 'Refresh Window';
    refreshPlayerButton.onclick = () => player.reload();

    playerContainer.appendChild(playerMenuBar);
    playerMenuBar.appendChild(refreshPlayerButton);
    playerMenuBar.appendChild(killWindowButton);
    playerContainer.appendChild(player);
    container.appendChild(playerContainer);

    player.load({
        url: gameSwf,
        base: baseSwf,
        preferredRenderer: "webgpu"
    });
};

const addAllWindows = () => {
    const playerContainers = document.getElementsByTagName('ruffle-player');
    for (let i = playerContainers.length; i < MAX_PLAYERS; i++) {
        addWindow();
    }
}

const renderMenuBar = () => {
    const menuBar = document.createElement('div');
    menuBar.style = `height: ${MENU_BAR_HEIGHT}px; width: 100%; display: flex; justify-content: space-between;`;

    const addButton = document.createElement("button");
    addButton.id = 'add-button';
    addButton.innerText = 'Add Window';

    addButton.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 = document.createElement("button");
    addAllButton.id = 'add-all-button';
    addAllButton.innerText = 'Max Windows (6)';

    addAllButton.onclick = () => {
        addAllWindows();
        document.getElementById('add-button').disabled = true;
        addAllButton.disabled = true;
        resizeWindows();
    };

    const killAllButton = document.createElement('button');
    killAllButton.id = 'kill-all-button';
    killAllButton.innerText = 'Kill All Windows';
    killAllButton.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 goToServer = document.createElement('button');
    goToServer.textContent = 'Open New Tab';
    goToServer.onclick = () => {
        const select = document.getElementById('server-select');
        window.open(`https://${select.value}.battleon.com/game/web`);
    };

    const dropdown = document.createElement('select');
    dropdown.id = 'server-select';
    serverOptions.forEach((serverOption) => {
        const option = document.createElement('option');
        option.value = serverOption.value;
        option.text = serverOption.name;
        dropdown.appendChild(option);
    });

    const buttonGroup = document.createElement('div');
    buttonGroup.appendChild(addButton);
    buttonGroup.appendChild(addAllButton);
    buttonGroup.appendChild(killAllButton);

    const linksGroup = document.createElement('div');
    links.forEach((link, index) => {
        const newLink = document.createElement('a');
        newLink.textContent = link.name;
        newLink.href = link.url;
        newLink.target = '_blank';
        newLink.rel = 'noopener';
        newLink.style = 'font-size: 16px;';
        linksGroup.appendChild(newLink);
        if (index !== links.length - 1) {
            const sep = document.createElement('span');
            sep.textContent = '|';
            sep.style = 'font-size: 16px; margin: 5px;';
            linksGroup.appendChild(sep);
        }
    })

    const selectGroup = document.createElement('div');
    selectGroup.appendChild(dropdown);
    selectGroup.appendChild(goToServer);


    menuBar.appendChild(buttonGroup);
    menuBar.appendChild(linksGroup);
    menuBar.appendChild(selectGroup);

    const playersGroup = document.createElement('div');
    playersGroup.id = "container";
    playersGroup.style = 'display: flex; flex-wrap: wrap; justify-content: center;';

    document.body.appendChild(playersGroup);
    playersGroup.appendChild(menuBar);
}

(function() {
    'use strict';
    let resizer = window.addEventListener('resize', () => {
        let timer;
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            resizeWindows();
        }, 250);
    });

    const checker = setInterval(() => {
        if (window.RufflePlayer && window.RufflePlayer.invoked) {
            clearInterval(checker);
            document.body.style.margin = 0;
            document.getElementById('main').remove();

            renderMenuBar();
            addWindow();
            resizeWindows();
        }
    }, 500);

    setTimeout(() => {
        clearInterval(checker);
    }, 2500);
})();