AQ Ruffle Enhance

Adds multi-windowed capability for serious AQ play.

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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);
})();