Kongregate Flash games fixer

You can now chose between Flash, SuperNova, Ruffle and AwayFL to play Flash games on Kongregate!

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Kongregate Flash games fixer
// @version      12.0
// @author       Matrix4348
// @namespace    https://greasyfork.org/users/4818
// @description  You can now chose between Flash, SuperNova, Ruffle and AwayFL to play Flash games on Kongregate!
// @license      MIT
// @match        *://*.konggames.com/games/*/*/frame/*
// @match        *://*.konggames.com/*/games/*/*/frame/*
// @match        *://www.kongregate.com/games/*/*
// @match        *://www.kongregate.com/*/games/*/*
// @match        *://www.kongregate.com/accounts/*/card_album*
// @match        *://www.kongregate.com/*/accounts/*/card_album*
// @run-at       document-end
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        unsafeWindow
// ==/UserScript==

/* ************************************************ NOTICE ABOUT @grant ************************************************
Starting with version 10.0, GM_setValue and GM_getValue will be used to store the default SWF player that the user will set.
Previously, this was made using a separate user script because I liked the fact that this one did not require any specific permission.
In version 12.0, I have also added a preference for Ruffle's renderer since the default one, under certain circumstances, is apparently not always the best one.

With @grant none, the window object (and the related context in general) is freely available to user scripts (in Tampermonkey, at least).
However, granting access to certain GM_ functions such as setValue and getValue prohibits that. Therefore, I have to use @grant unsafeWindow, too.
********************************************************************************************************************* */
/* ************************************************** Global functions ********************************************** */
function FlashSupport(){
    var np=navigator.plugins, npl=np.length;
    for(var k=0;k<npl;k++){ if( (np[k].name=="Shockwave Flash") & (np[k].filename!="ruffle.js") ){return true;} }
    return false;
}

function return_default_behaviour(){
    if(FlashSupport()==true){ return "Flash"; }
    else if(navigator.userAgent.search("Windows")>-1){ return "SuperNova"; } // At least while Ruffle is not finished.
    else if(navigator.userAgent.search("Windows")==-1){ return "Ruffle"; } // Because SuperNova only works on Windows.
    else { return "Flash"; }
}
/* ****************************************************************************************************************** */

// Iframes part. The script must be limited to Flash iframes (the URL for the html games iframes look a bit different, but there may be some exceptions), so we will check if the iframe includes the variable kong_flash_variables.
if ((document.URL.search("/frame/")>-1)&&(document.body.innerHTML.search("kong_flash_variables")>-1)){
    // SuperNova and Ruffle scripts:
    var SuperNova_script_was_executed;
    if(( document.head.innerHTML.search("getsupernova.com/tags/supernovalauncher.js")==-1 )&&( document.body.innerHTML.search("getsupernova.com/tags/supernovalauncher.js")==-1 )){
        try{
            var sn=document.createElement("script");
            sn.onload=function(){ SuperNova_script_was_executed=true; };
            sn.onerror=function(){ SuperNova_script_was_executed=false; };
            sn.src="https://web.archive.org/web/20210426110734id_/https://cdn.getsupernova.com/tags/supernovalauncher.js";
            document.body.appendChild(sn);
        }
        catch(e){
            SuperNova_script_was_executed=false;
        }
    }
    var Ruffle_script_was_executed;
    if(( document.head.innerHTML.search("unpkg.com/@ruffle-rs/ruffle")==-1 )&&( document.body.innerHTML.search("unpkg.com/@ruffle-rs/ruffle")==-1 )){
        try{
            var rp=document.createElement("script");
            rp.type="text/javascript";
            rp.id="No Ruffle polyfills";
            rp.innerHTML=`window.RufflePlayer = window.RufflePlayer || {}; window.RufflePlayer.config = { "polyfills": false };`;
            document.body.appendChild(rp);
            var r=document.createElement("script");
            r.onload=function(){ Ruffle_script_was_executed=true; };
            r.onerror=function(){ Ruffle_script_was_executed=false; };
            r.src="https://unpkg.com/@ruffle-rs/ruffle";
            document.body.appendChild(r);
        }
        catch(e){
            Ruffle_script_was_executed=false;
        }
    }
    // First, we need to extract the link to the swf (the game file) and a few other things.
    var file_url0, file_url, file_url_base; // Basic URL.
    var my_first_link, my_second_link, blablabla, GameShellAPI; // For loading the games in a gameshell when they should, because in these cases the gameshell is required for submitting scores. [Update:] The API file can replace the game shell for this, but is their any little feature that makes this replacement fail?
    var kongregate_split_treatments = "none"; // Various Flashvars to find on the page
    var my_width, my_height, my_bgcolor, my_wmode=""; // Parameters.
    function rgbtohex(rgb){ // The bgcolor Flash parameter must be an hexadecimal value.
        if(rgb.length==7){ return rgb }
        else if(rgb.length<7){ return "#ffffff" }
        else{
            var a = rgb.split("(")[1].split(")")[0];
            a = a.split(",");
            var b = a.map(function(x){ x = parseInt(x).toString(16); return (x.length==1) ? "0"+x : x; });
            b = "#"+b.join("");
            return b
        }
    }
    function set_up_variables(){
        var scripts=document.getElementsByTagName("script");
        for(var i=0; i<scripts.length;i++) {
            var script_text=scripts[i].text, url_text;
            /*
            Note for the future: with August 24th, 2022's update, the difference between AVM1 and AMV2 games is the "old" superNova code for AMV2 and the "new" Ruffle code for AMV1,
            however the game shell is no longer mentionned there. This makes me fear that, if they implement one day Ruffle for every games, the difference between them may be VERY HARD
            to make.
            Fear confirmed on February 3rd, 2023.
            Partial solution: using document.getElementById("gamediv").firstElementChild._metadata.isActionScript3.
            Problem: this only works if Ruffle works...
            Temporary solution: using the value of kongregate_api_path pasted in the HTML, as long as the value stays different between AVM1 and AVM2.
            */
            if(script_text.indexOf("swf_location")!== -1) {
                var isAS3;
                var sometimes_wrong_API_link=script_text.match(`"kongregate_api_path":"(.*?)"`)[1];
                if( sometimes_wrong_API_link.search("API_AS3")==-1 ){isAS3=false;}
                else {isAS3=true;}
                if(isAS3){
                    url_text=script_text.match(`swf_location = "(.*?)"`)[1];
                    file_url0=decodeURIComponent(url_text);

                    // Various Flashvars.
                    if(script_text.search(`kongregate_split_treatments":"(.*?)"`)>-1){kongregate_split_treatments=script_text.match(`kongregate_split_treatments":"(.*?)"`)[1];}

                    // Parameters for swf injection.
                    my_bgcolor=rgbtohex(document.getElementById("game_wrapper").style.backgroundColor);
                    if(script_text.search(`wmode":"(.*?)"`)>-1){my_wmode=script_text.match(`wmode":"(.*?)"`)[1];}
                    else{ my_wmode="direct"; } // The normal value can no longer be found anywhere (when it is needed) so I will just try that everywhere (default should actually be "window").

                    //  Flash, game shell and API (even though, in this part of the loop, games do not need game shells).
                    my_first_link=file_url0.substring(file_url0.search("//"),file_url0.length);
                    my_second_link="";
                    GameShellAPI="";

                    // Values for the SuperNova part.
                    my_width=script_text.match(`player.style.width = "(.*?)px"`)?.[1]||document.getElementById("game_wrapper").clientWidth;
                    my_height=script_text.match(`player.style.height = "(.*?)px"`)?.[1]||document.getElementById("game_wrapper").clientHeight;

                    break;
                }
                else{
                    url_text=script_text.match(`swf_location = "(.*?)"`)[1];
                    file_url0=decodeURIComponent(url_text);
                    if(file_url0.search("/flash/GameShell")>-1){ file_url0=decodeURIComponent(script_text.match(`game_swf":"(.*?)"`)[1]); }

                    // Various Flashvars.
                    if(script_text.search(`kongregate_split_treatments":"(.*?)"`)>-1){kongregate_split_treatments=script_text.match(`kongregate_split_treatments":"(.*?)"`)[1];}

                    // Parameters for swf injection.
                    my_bgcolor=rgbtohex(document.getElementById("game_wrapper").style.backgroundColor);
                    if(script_text.search(`wmode":"(.*?)"`)>-1){my_wmode=script_text.match(`wmode":"(.*?)"`)[1];}
                    else{ my_wmode="direct"; } // The normal value can no longer be found anywhere (when it is needed) so I will just try that everywhere (default should actually be "window").

                    //  Flash, game shell and API.
                    let sub=file_url0.substring(file_url0.search("//")+2,file_url0.search(".kongregate.com"));
                    my_first_link="//"+sub+".kongregate.com/flash/GameShell.swf";
                    my_second_link=encodeURIComponent("http:"+file_url0.substring(file_url0.search("//"),file_url0.length)); // HTTPS WILL NOT MAKE THE API LOAD/WORK/WHATEVER!
                    GameShellAPI="/flash/API.swf";

                    // Values for the SuperNova part.
                    my_width=script_text.match(`player.style.width = "(.*?)px"`)?.[1]||document.getElementById("game_wrapper").clientWidth;
                    my_height=script_text.match(`player.style.height = "(.*?)px"`)?.[1]||document.getElementById("game_wrapper").clientHeight;

                    break;
                }
            }
        }
        file_url=file_url0.substring(file_url0.search("//"),file_url0.length);
        if (file_url.search("/live/")>-1){
            file_url_base="http:"+file_url.substring(0,file_url.search("/live/")+6);
        }
        else {
            file_url_base="http:"+file_url.substring(0,file_url.search("/game_files/")+22);
        }
        if(GameShellAPI.length==0){file_url_base=file_url_base.substring(file_url_base.search("//"),file_url_base.length);} // This fixes a logging issue for Bloons TD 5, but does it create problems elsewhere?
    }
    // Then, we build the functions that will load the games.
    function more_players(){
        var a=document.createElement("script");
        a.id="Kongregate Flash games fixer: swf players";
        a.type="text/javascript";
        // IMPORTANT: The checkParams function (originally present in the web page) inputs most of the flashvars into kong_flash_variables.
        a.innerHTML=`
//<![CDATA[

var swf_location = "`+my_first_link+`";
var true_swf_location = "`+file_url+`";
var swfobject_flash_vars = {};
var Ruffle_flash_vars = {};
var swf_parameters = {"bgcolor":"`+my_bgcolor+`","allownetworking":"all","allowscriptaccess":"always","base":"`+file_url_base+`","wmode":"`+my_wmode+`"};

function setFlashvars4348(){
    // Missing Flash variables (for high scores, for multiplayers game, for premium content...):
    if(typeof(kong_flash_variables) == "object"){
        for(var k in kong_flash_variables){ swfobject_flash_vars[k] = encodeURIComponent(kong_flash_variables[k]); };
        // Several flashvars needing attention had already been set by code_for_iframes (located far down below, in the parent iframe part) and were already present in kong_flash_variables.
        swfobject_flash_vars.kongregate_split_treatments="`+kongregate_split_treatments+`"; // May have disappeared from kong_flash_variables in November 2025 or may have never been there...
        swfobject_flash_vars.kongregate_api_path=encodeURIComponent("/flash/API_AS3.swf"); // Disappeared from kong_flash_variables around November 2025.
        swfobject_flash_vars.api_path=swfobject_flash_vars.kongregate_api_path; // This line is required for a lot of games (AVM1 games specifically?)
        swfobject_flash_vars.api_host=swfobject_flash_vars.kongregate_api_host; // Needed? Useless?
        swfobject_flash_vars.game_url=swfobject_flash_vars.kongregate_game_url; // May be useless, or may reactivate API on games where we supposed it broken for good, who knows...
        swfobject_flash_vars.game_auth_token=swfobject_flash_vars.kongregate_game_auth_token;
        swfobject_flash_vars.kongregate_stamp=swfobject_flash_vars.kongregate_game_auth_token; // Probably useless.
    }
    swfobject_flash_vars.game_swf="`+my_second_link+`";
    if(swfobject_flash_vars.game_swf.length!=0){
        swfobject_flash_vars.kongregate_api_path=encodeURIComponent("`+GameShellAPI+`"); // Yep, game shells and AVM1 games need a different API file!
        swfobject_flash_vars.api_path=swfobject_flash_vars.kongregate_api_path;
    };

    // Ruffle needs non-encoded flashvars.
    for(var k in swfobject_flash_vars){ Ruffle_flash_vars[k] = decodeURIComponent(swfobject_flash_vars[k]); };
}

function activateGame4348(swf_player){
    var prot="http";
    // Some games may always need to load via https, and other games will only need it when getsupernova.com is down or have problems (why, seriously?...).
    var games_needing_https=["144037","195142"];
    if(games_needing_https.includes(document.URL.substring(document.URL.search("//")+6,document.URL.search(".konggames")))){ prot="https"; }
    // Some games might still lack CORS headers but I forgot the list since last time I checked. Things have improved, at least, and remaining one may have all been added but better be safe than sorry.
    var no_CORS;
    if( typeof(window.no_cors) == "undefined" ){ no_CORS=false; }
    else{ no_CORS=window.no_cors; }

    reset_gamediv();

    if(swf_player=="Flash"){
        // Note: Extension excepted, as of June 2023 Ruffle no longer polyfills Flash content in browser with Flash supported (and installed?). This may even be true with replacements to Flash like Lightspark.
        swfobject.embedSWF( swf_location, "gamediv2", "100%", "100%", "6", "/flash/expressInstall.swf", swfobject_flash_vars, swf_parameters, {} );

        // Sometimes, if a browser does not support Adobe Flash Player, then swfobject.embedSWF may not work, leaving a mere black screen instead of a "this plugin is not supported" message.
        // Therefore, the game will be injected using another method, just so the users know that the script works.
        if (document.getElementById("gamediv2").type==undefined){
            document.getElementById("gamediv2").remove();
            var truc=document.createElement("object");
            truc.id="gamediv2";
            truc.type="application/x-shockwave-flash";
            truc.data=swf_location;
            truc.width="100%";
            truc.height="100%";
            for(var p in swf_parameters){ var p1=document.createElement("param"); p1.name=p; p1.value=swf_parameters[p]; truc.appendChild(p1); }
            var fv0=""; for(var k in swfobject_flash_vars){fv0=fv0+k+"="+swfobject_flash_vars[k]+"&"};
            var fv=document.createElement("param"); fv.name="flashvars"; fv.value=fv0.substring(0,fv0.length-1); truc.appendChild(fv);
            document.getElementById("game_wrapper").appendChild(truc);
        };
    }

    else if(swf_player=="SuperNova"){
        // Note; as opposed to Flash and Ruffle, game shells do not resize to the game's values with the SuperNova player. This is why they are not used by default for AVM1 games.
        var SNoptions = { "swfurl" : prot+":"+true_swf_location, "flashvars":"", "title" : ttgArgs.game_title, "width" : `+my_width+`, "height" : `+my_height+` };
        var SNflashvars={}; for(var k in swfobject_flash_vars){ SNflashvars[k] = decodeURIComponent(swfobject_flash_vars[k]); }; SNoptions.flashvars = $j.param(SNflashvars);
        SNoptions.el = document.querySelector('#gamediv2');
        SNoptions.pageUrl=window.location.href;
        //SNoptions.autoplay=true;
        custom_SuperNova_launcher(SNoptions);
    }

    else if(swf_player=="Ruffle"){
        var Ruffle_swf_location=swf_location;
        if(swfobject_flash_vars.game_swf.length!=0){ // Skips gameshell and loads AS2 api directly, until Ruffle supports it
            Ruffle_swf_location = window.location.protocol.slice(0,-1)+"://www.kongregate.com/flash/API.swf";
        }
        // For games that could be somehow still lacking CORS headers
        if( no_CORS ){
            if(swfobject_flash_vars.game_swf.length!=0){
                Ruffle_flash_vars.game_swf=Ruffle_flash_vars.game_swf.replace(/chat.kongregate.com|assets.kongregate.com|internal.kongregate.com/,"game"+swfobject_flash_vars.kongregate_game_id+".konggames.com");
            }
            else{
                Ruffle_swf_location = window.location.protocol.slice(0,-1)+"://game"+swfobject_flash_vars.kongregate_game_id+".konggames"+swf_location.substring(swf_location.indexOf(".com"),swf_location.length);
            };
        }

        window.RufflePlayer.config = {
            "publicPath": undefined, "polyfills": false, // Options affecting the whole page, but that should no longer matter at this point in the user script code.
            "autoplay": "on",
            "allowNetworking": swf_parameters.allownetworking,
            "allowScriptAccess": swf_parameters.allowscriptaccess == "always" ? true : false,
            "backgroundColor": swf_parameters.bgcolor,
            "wmode": swf_parameters.wmode,
            "contextMenu": "on",
            "favorFlash": true,
            "openUrlMode": "allow",
            "showSwfDownload": `+(my_second_link.length==0)+`,
            "maxExecutionDuration": 100,
            "base": no_CORS ? swf_parameters.base.replace(/chat.kongregate.com|assets.kongregate.com|internal.kongregate.com/,"game"+swfobject_flash_vars.kongregate_game_id+".konggames.com") : swf_parameters.base,
            "menu": true,
            "scale": "showAll",
            "quality": "high",
        };
        var renderer = "`+GM_getValue("preferred_ruffle_renderer", "default")+`";
        if( renderer!="default" ){ window.RufflePlayer.config.preferredRenderer = renderer; }

        const ruffle = window.RufflePlayer.newest();
        const player = ruffle.createPlayer();
        player.style.width = "100%";
        player.style.height = "100%";
        const container = document.getElementById("game_wrapper");
        document.getElementById("gamediv2").remove();
        container.appendChild(player);
        player.load({
            url: Ruffle_swf_location,
            parameters: Ruffle_flash_vars,
            allowScriptAccess: true
        });
    }

    else if(swf_player=="AwayFL"){
        var AwayFL_swf_location=swf_location;
        window.AWAY_EMBED_CFG = {
            enableFilters: true,
            enableBlends: true
        }
        if(swfobject_flash_vars.game_swf.length!=0){ // Until AwayFL can load AVM1 games into their game shell.
            AwayFL_swf_location = window.location.protocol+true_swf_location;
        }
        // For games that could be somehow still lacking CORS headers
        if( no_CORS ){
            if(swfobject_flash_vars.game_swf.length!=0){ // Until AwayFL can load AVM1 games into their game shell.
                AwayFL_swf_location = window.location.protocol.slice(0,-1)+"://game"+swfobject_flash_vars.kongregate_game_id+".konggames"+true_swf_location.substring(true_swf_location.indexOf(".com"),true_swf_location.length);
            }
            else{
                AwayFL_swf_location = window.location.protocol.slice(0,-1)+"://game"+swfobject_flash_vars.kongregate_game_id+".konggames"+swf_location.substring(swf_location.indexOf(".com"),swf_location.length);
            };
        };
        var a=document.createElement("script");
        a.src="https://cdn.jsdelivr.net/gh/awayfl/awayfl-embed@master/dist/embed.js";
        a.addEventListener("load",function(){ swfobject.embedSWF(AwayFL_swf_location,"gamediv2","100%","100%","6","/flash/expressInstall.swf",swfobject_flash_vars,swf_parameters,{}); });
        document.body.appendChild(a);
    }

    else{ activateGame4348("Flash"); }
}

function reset_gamediv(){
    if( document.getElementById("game_wrapper").firstDescendant()!=null ){ document.getElementById("game_wrapper").firstDescendant().remove(); };
    var d=document.createElement("div"); d.id="gamediv2"; document.getElementById("game_wrapper").appendChild(d);
}

function custom_SuperNova_launcher(options){
    var manual_url=options.swfurl.substring(options.swfurl.search("//"),options.swfurl.search(".swf")+4),
        manual_launch_http="supernova://play/?swfurl="+encodeURIComponent("http:"+manual_url)+"&flashvars="+encodeURIComponent(options.flashvars)+"&pageurl="+encodeURIComponent(options.pageUrl),
        manual_launch_https="supernova://play/?swfurl="+encodeURIComponent("https:"+manual_url)+"&flashvars="+encodeURIComponent(options.flashvars)+"&pageurl="+encodeURIComponent(options.pageUrl),
        manual_launch_gameshell="supernova://play/?swfurl="+encodeURIComponent("http:"+swf_location)+"&flashvars="+encodeURIComponent(options.flashvars)+"&pageurl="+encodeURIComponent(options.pageUrl);
    if(typeof(supernova)==='object'){
        document.getElementById("game_wrapper").style.overflow="auto";
        supernova.launch(options);
        setTimeout(function(){
            supernova.openGame(options);
            // Adding three buttons to launch the game three different ways (http, https and inside a game shell for Actionscript 1 or 2 games) in case the default way would not work while another would.
            var working_div=document.getElementById("supernovaplayeriframe").contentDocument.getElementsByClassName("splashLaunchTitle")[0].parentElement;
            var play_div=document.createElement("div"), play_buttons_div=document.createElement("div"), play_button_http=document.createElement("button"), play_button_https=document.createElement("button");
            play_div.id="Extra buttons"; play_div.style="display: block; position:relative; width: 90%; height:auto; top:auto; left: 5%; right: 15%; bottom:4%; margin-top: 25px;";
            play_div.innerHTML="<i>If, for some reasons, the game does not load properly, then try the buttons below:";
            play_buttons_div.id="Play buttons"; play_buttons_div.style="margin-top: 2%; margin-bottom:2%";
            play_button_http.id="playbuttonhttp";
            play_button_http.setAttribute("class","refreshbutton");
            play_button_http.innerHTML="Play (http)";
            play_button_http.setAttribute("onclick", "window.location='"+manual_launch_http+"';");
            play_buttons_div.appendChild(play_button_http);
            play_button_https.id="playbuttonhttps";
            play_button_https.setAttribute("class","refreshbutton");
            play_button_https.innerHTML="Play (https)";
            play_button_https.setAttribute("onclick", "window.location='"+manual_launch_https+"';");
            play_buttons_div.appendChild(play_button_https);
            if(swfobject_flash_vars?.game_swf.length!=0){
                var play_button_gameshell=document.createElement("button");
                play_button_gameshell.id="playbuttongameshell";
                play_button_gameshell.setAttribute("class","refreshbutton");
                play_button_gameshell.innerHTML="Play (in game shell)";
                play_button_gameshell.setAttribute("onclick", "window.location='"+manual_launch_gameshell+"';");
                play_buttons_div.appendChild(play_button_gameshell);
            }
            play_div.appendChild(play_buttons_div);
            working_div.appendChild(play_div);
        },3000);
    }
    else{ // The SuperNova code does not work in some browsers (like Pale Moon and Basilisk in the past) but the game can still be launched using the "manual" command.
        var unofficial_interface=document.createElement("div");
        var Roboto_stylesheet=document.createElement("link"), interface_subdiv=document.createElement("div");
        var intro_div=document.createElement("div"), first_line=document.createElement("hr"), download_div=document.createElement("div"), second_line=document.createElement("hr"), play_div=document.createElement("div");
        var download_button_div=document.createElement("div"), download_button=document.createElement("button"), download_span=document.createElement("span"), warning_span=document.createElement("span");
        var play_div=document.createElement("div"), play_buttons_div=document.createElement("div"), play_button_http=document.createElement("button"), play_button_https=document.createElement("button");
        unofficial_interface.id="Unofficial SuperNova interface"; unofficial_interface.style="text-align: center; background:#FFFFFF; left: 0px; top:0px; width:100%; height:100%; position:absolute";
        Roboto_stylesheet.rel="stylesheet"; Roboto_stylesheet.type="text/css"; Roboto_stylesheet.href="https://fonts.googleapis.com/css?family=Roboto";
        unofficial_interface.appendChild(Roboto_stylesheet);
        interface_subdiv.style="text-align:center; align-items:center; background:#FFFFFF; width:100%; height:100%; position:fixed; font-family: Roboto; font-style: normal; font-weight: 500; line-height: normal; overflow:auto";
        intro_div.id="Introduction"; intro_div.style="display:block; position: relative; width: 90%; height:auto; top:4%; left: 5%; right: 15%";
        intro_div.innerHTML="It seems that, for some technical reasons, your browser could not load the official SuperNova interface, which explains why you are reading this replacement frame. Well, fear not! The process stays the same."
        interface_subdiv.appendChild(intro_div);
        first_line.style="margin-top:6%; margin-bottom:3%";
        interface_subdiv.appendChild(first_line);
        download_div.id="Download SuperNova"; download_div.style="display:block; position: relative; width: 90%; height:auto; top:auto; left: 5%; right: 15%";
        download_div.innerHTML="To play <i>"+options.title+"</i> with the SuperNova player, you first need to download the SuperNova player from <i>https://www.getsupernova.com/download/</i> and install it.";
        download_button_div.id="Download button"; download_button_div.style="margin-top:2%";
        download_button.id="downloadbutton"; download_button.style="background: #0D5694; color: white; height:40px";
        //download_button.setAttribute("onclick", "window.location='https://cdn.getsupernova.com/SuperNovaSetup.exe'");
        download_button.setAttribute("onclick", "window.open('http://web.archive.org/web/20210426173012id_/https://cdn.getsupernova.com/update/SuperNovaSetup0.1.23.exe')");
        download_span.style="left:50px; font-weight: 700; text-decoration:underline; font-size:20px"; download_span.innerHTML="Download";
        download_button.appendChild(download_span);
        download_button_div.appendChild(download_button);
        download_div.appendChild(download_button_div);
        warning_span.style="font-size:12px"; warning_span.innerHTML="(Warning: At the moment, it is only available on Windows.)";
        download_div.appendChild(warning_span);
        interface_subdiv.appendChild(download_div);
        second_line.style="margin-top:3%; margin-bottom:4%";
        interface_subdiv.appendChild(second_line);
        play_div.id="Play with SuperNova"; play_div.style="display: block; position:relative; width: 90%; height:auto; top:auto; left: 5%; right: 15%; bottom:4%";
        play_div.innerHTML="Once you have the SuperNova player installed on your computer, you can play the game by clicking one of the buttons below. If it does not load properly, try another one.";
        play_buttons_div.id="Play buttons"; play_buttons_div.style="margin-top: 2%; margin-bottom:2%";
        play_button_http.id="playbuttonhttp"; play_button_http.style="border-radius: 30px; background: #0D5694; color: white; margin-right: 10px; width: 130px; height: 26px; font-size: 15px; border: none";
        play_button_http.innerHTML="Play (http)";
        play_button_http.setAttribute("onclick", "window.location=manual_launch_http");
        play_buttons_div.appendChild(play_button_http);
        play_button_https.id="playbuttonhttps"; play_button_https.style="border-radius: 30px; background: #0D5694; color: white; margin-left: 10px; width: 130px; height: 26px; font-size: 15px; border: none";
        play_button_https.innerHTML="Play (https)";
        play_button_https.setAttribute("onclick", "window.location=manual_launch_https");
        play_buttons_div.appendChild(play_button_https);
        if("game_swf" in swfobject_flash_vars){
            var play_button_gameshell=document.createElement("button");
            play_button_gameshell.id="playbuttongameshell";
            play_button_gameshell.style="border-radius: 30px; background: #0D5694; color: white; margin-left: 20px; width: 150px; height: 26px; font-size: 15px; border: none";
            play_button_gameshell.innerHTML="Play (in game shell)";
            play_button_gameshell.setAttribute("onclick", "window.location=manual_launch_gameshell");
            play_buttons_div.appendChild(play_button_gameshell);
        }
        play_div.appendChild(play_buttons_div);
        interface_subdiv.appendChild(play_div);
        unofficial_interface.appendChild(interface_subdiv);
        document.getElementById("gamediv2").appendChild(unofficial_interface);
    }
}

function FlashSupport(){
    var np=navigator.plugins, npl=np.length;
    for(var k=0;k<npl;k++){ if((np[k].name=="Shockwave Flash")&(np[k].filename!="ruffle.js")){return true}; }
    return false;
};

function time_to_play(){
    setFlashvars4348();
    if(document.URL.search("swf_player_to_use=Flash")>-1){ activateGame4348("Flash"); }
    else if(document.URL.search("swf_player_to_use=SuperNova")>-1){ activateGame4348("SuperNova"); }
    else if(document.URL.search("swf_player_to_use=Ruffle")>-1){ activateGame4348("Ruffle"); }
    else if(document.URL.search("swf_player_to_use=AwayFL")>-1){ activateGame4348("AwayFL"); }
    else{
        if(FlashSupport()==true){ activateGame4348("Flash"); }
        else if(navigator.userAgent.search("Windows")>-1){ activateGame4348("SuperNova"); } // At least while Ruffle is not finished.
        else if(navigator.userAgent.search("Windows")==-1){ activateGame4348("Ruffle"); }   // Because SuperNova only works on Windows.
        else { activateGame4348("Flash"); }
    }
};

/* To make API.swf and API_AS3.swf compatible with Ruffle (by stopping executing arbitrary Javascript code from within the SWF), as described
on https://github.com/colin969/Kongregate-Patched-APIs, this code must be defined in functions on the web page and the SWF must call these functions directly.
Kongregate updated their AS3 API accordingly at the end of November 2025 (but the AVM1 one has not been touched yet).
As of February 14th, 2026 (and since somewhere in December, if not the beginning...), the functions are missing from the Javascript side so the AS3 API is currently broken.
I will (re)define them here until Kongregate fixes that. */
    KongregateAPI.prototype._externalMessage = (opcode, parameters) => {
        setTimeout(() => {
            kongregateAPI.messageConnection.sendMessage(opcode, decodeURIComponent(parameters));
        });
    };

    KongregateAPI.prototype._externalInit = (swfId, apiUrl) => {
        console.log('Flash setting up from ' + swfId);
        setTimeout(() => {
            function setGameSwf(){
                if (typeof kongregateAPI !== 'undefined') {
                    // Ruffle doesn't have Play function, removed from check
                    kongregateAPI._isSwf = function(e) {
                        return e && void 0 !== e.setConnectionObject;
                    };

                    // Modified to check for ruffle-player, not just objects and embeds
                    kongregateAPI._findSwf = function(e) {
                        var t = document.getElementById(e),
                            n = this,
                            s = function(e) {
                                for (var s = 0; s < e.length; s++)
                                    if (t = e[s], n._isSwf(t)) return t
                            };
                        return this._isSwf(t) ? t : t = s(document.getElementsByName(e)) || s(document.querySelectorAll("[id='" + e + "']")) || s(document.getElementsByTagName("object")) || s(document.getElementsByTagName("embed")) || s(document.getElementsByTagName("ruffle-player"))
                    };
                    setTimeout(function(){ kongregateAPI._setGameSwf(swfId); }, 1);
                    return true;
                }
            }

            function loadKongregateApi() {
                var s = document.createElement('script');
                s.type ='text/javascript';
                s.src = apiUrl;
                if(s.addEventListener) {
                    s.addEventListener('load', setGameSwf)
                } else if(s.readyState) {
                    s.onreadystatechange = setGameSwf;
                }
                document.getElementsByTagName('head')[0].appendChild(s);
            }

            if (!setGameSwf()) {
                loadKongregateApi();
            }
            return 'pending';
        });
        return "pending";
    };
/* **************************************************************** */

async function check_cors_then_launch_the_game(){
    var url=window.location.protocol.slice(0,-1)+":"+true_swf_location.substring(true_swf_location.search("//"),true_swf_location.length);
    try{
        var r = await fetch(url);
        window.no_cors=false;
    }
    catch(e){
        window.no_cors=true;
    }
    finally{
        time_to_play();
    }
}
check_cors_then_launch_the_game();

//]]>
        `;
        document.body.appendChild(a);
    }

    function iframe_main_function(){
        var SuperNova_state=typeof(SuperNova_script_was_executed)=="boolean"||"supernova" in unsafeWindow;
        var Ruffle_state=typeof(Ruffle_script_was_executed)=="boolean"||typeof(unsafeWindow.RufflePlayer)=="object";
        if(unsafeWindow.activateGame._alreadyActivated&&SuperNova_state&&Ruffle_state){
            set_up_variables();
            more_players();
        }
        else{ setTimeout(iframe_main_function,1000); }
    }
    iframe_main_function();

}

// Game pages part (first iframe layer and top-level document, respectively). Only for Flash games, so I will make sure that active_user.gameType()="flash".
else if(document.URL.search("www.kongregate.com(.*?)/games/(.*?)/embed")>-1){
    // Useful variables.
    var urlstart=document.URL.substring(0,document.URL.search("://"));

    var my_game_version, my_iframe_class, my_game_host, my_preview;
    function getting_the_variables(){
        var game_scripts=document.getElementsByClassName("game-embed-container")[0].getElementsByTagName("script");
        for(var j=0; j<game_scripts.length;j++){
            var game_script_text=game_scripts[j].text;
            if((game_script_text.indexOf("game_version")!== -1)&(game_script_text.indexOf("svid")!== -1)) {
                my_game_version=game_script_text.match(`game_version":(.*?),`)[1];
                my_iframe_class=game_script_text.match(`iframe_class":"(.*?)"`)[1];
                my_game_host=game_script_text.match(`game_host":"(.*?)"`)[1];
                my_preview=game_script_text.match(`preview":(.*?),`)[1];
                break;
            }
        }
    }

    // What will be inserted in the web page, to load the different iframes that we will use.
    function code_for_iframes(){
        var ic=document.createElement("script");
        ic.id="Kongregate Flash games fixer: iframes";
        ic.innerHTML=`
//<![CDATA[
var iframeConfig4348 = {}, iframeURL4348;

function LoadFrame4348(swf_player,urlOptions){
    if (!urlOptions) { urlOptions = ''; }
    if (!swf_player) { swf_player = 'none'; }

    // Process missing or corrected flashvars to the Kongregate API

    window.kongregateAPI._processParam("kongregate",true);
    window.kongregateAPI._processParam("kongregate_svid",active_user.siteVisitorUID());
    window.kongregateAPI._processParam("kongregate_flash_postmessage",true); // This value is FUNDAMENTAL for activating statistics submission.
    window.kongregateAPI._processParam("kongregate_game_version",`+my_game_version+`);
    window.kongregateAPI._processParam("kongregate_api_host","https://api.kongregate.com");
    window.kongregateAPI._processParam("api_host","https://api.kongregate.com");
    window.kongregateAPI._processParam("game_url","http://www.kongregate.com"+active_user.gamePath().substring(active_user.gamePath().search("/games/")));
    window.kongregateAPI._processParam("kongregate_preview",`+my_preview+`); // Note: that one may be useless as I doubt that it is supposed to be a Flashvar.

    // iframeConfig (copy and adjustements)

    // NOTE: I suspect that most of the parameters no longer have any use but some definitely do.
    iframeURL4348 = ( window?.iframeConfig?.iframe_url ||
                      window?.iframeUrl ||
                      "`+urlstart+`"+"://game"+active_user.gameId()+".konggames.com"+active_user.gameResourcePath().substring(active_user.gameResourcePath().search("/games/"))+"/frame/"+channel_id+"/?kongregate_host=www.kongregate.com"
                     )+"&swf_player_to_use="+swf_player;

    var c = window?.iframeConfig;
    for(let k in c){ iframeConfig4348[k] = c[k]; }

    var c2 = {
        "iframe_url":iframeURL4348,
        "game_width":holodeck._html_dimensions.game_width,
        "game_height":holodeck._html_dimensions.game_height,
        "max_game_width":holodeck._html_dimensions.max_game_width,
        "max_game_height":holodeck._html_dimensions.max_game_height,
        "game_left":holodeck._html_dimensions.game_left,
        "game_top":holodeck._html_dimensions.game_top,
        "game_permalink":active_user.gamePermalink(),
        "game_id":active_user.gameId(),
        "flash_var_prefix":"kv_",
        "post_message":true,
        "channel_id":encodeURIComponent(channel_id),
        "svid":active_user.siteVisitorUID(),
        "game_type":"flash"
    };
    for(let k in c2){ iframeConfig4348[k] = c2[k]; } // Values that should always be set this way.

    var c3 = {
        "auto_resize":null,
        "alternate_game_file_url":null,
        "iframe_class":"`+my_iframe_class+`",
        "host":"`+urlstart+`"+"://www.kongregate.com",
        "api_host":"https://api.kongregate.com",
        "api_path":"https://chat.kongregate.com/flash/API_AS3.swf", // A way to differenciate between AMV1 and AMV2 games would be great, but the URL will be corrected in the iframe.
        "preview":`+my_preview+`,
        "game_url":"http://www.kongregate.com"+active_user.gamePath().substring(active_user.gamePath().search("/games/")),
        "game_version":`+my_game_version+`,
        "iframe_host":"`+urlstart+`"+"://game"+active_user.gameId()+".konggames.com",
        "game_host":"`+my_game_host+`"
    }
    for(let k in c3){
        if(!(k in iframeConfig4348)){ iframeConfig4348[k] = c3[k]; } // Values that should be set this way... if unset.
    }

    // Game iframe creation

    new GameIframe(iframeConfig4348, urlOptions, channel_id).createGameIframeElement();

    document.fire('game:activated');
}

// In october 2025, their new redesign came with an extra layer of iframe. Basically, most of the old code is in that layer. window.postMessage circumvents that.
window.addEventListener('message', function(event) {
var keyword = "SWF player to use: ";
    if(typeof(event.data)=="string"){
        if(event.data.search(keyword)==0){
            var swf_player=event.data.substring(keyword.length);
            LoadFrame4348(swf_player);
        }
     }
});
//]]>
        `;
        document.getElementsByClassName("game-embed-container")[0].appendChild(ic);
    }

    // ...
    function do_things(){
        var is_Flash_game, everything_ready;
        if(document.readyState=="complete"&&typeof(unsafeWindow.active_user)!="undefined"&&typeof(unsafeWindow.activateGame)!="undefined"&&typeof(unsafeWindow.holodeck)!="undefined"){
            if(typeof(unsafeWindow.active_user.gameType)!="undefined"){
                if(unsafeWindow.active_user.gameType()=="flash"){ is_Flash_game=true; } else{ is_Flash_game=false; }
            }
            else{ is_Flash_game=-1 }
            if(unsafeWindow.activateGame._alreadyActivated&&unsafeWindow.holodeck.ready){ everything_ready=true; }
            else{ everything_ready=false; }
        }
        if(is_Flash_game==true&&everything_ready){
            getting_the_variables();
            code_for_iframes();
        }
        else if(is_Flash_game!=false){ setTimeout(function(){ do_things(); },1000); }
    }
    do_things();

}

else if(document.URL.search("www.kongregate.com(.*?)/games/")>-1){
    function press_play_now_then_change_swf_player(swf_player){
        if(document.getElementById("game")?.style?.display=="none"){
            document.getElementById("play-now-overlay").getElementsByTagName("BUTTON")?.[0].click();
            setTimeout(function(){ press_play_now_then_change_swf_player(swf_player); },1000);
        }
        else if(document.getElementById("fullscreen_button")?.style?.display=="none"){
            setTimeout(function(){ press_play_now_then_change_swf_player(swf_player); },1000);
        }
        else{
            document.getElementsByClassName("game-embed-iframe")[0].contentWindow.postMessage("SWF player to use: "+swf_player);
            document.getElementById("Button to (re)load with "+current_SWF_player).getElementsByTagName("span")[0].innerHTML = " "+current_SWF_player;
            document.getElementById("Button to (re)load with "+swf_player).getElementsByTagName("span")[0].innerHTML = "<b> "+swf_player+"</b>";
            current_SWF_player = swf_player;
        }
    }

    function open_settings(){
        var default_player=prompt("Which SWF player do you want to use by default when a page loads: Flash, SuperNova, Ruffle or AwayFL?\n\n Note: Typing anything else will be treated like "+return_default_behaviour()+".");
        switch((default_player||"null").toLowerCase()) {
            case "null":
                break;
            case "flash":
                GM_setValue("default_swf_player","Flash");
                break;
            case "supernova":
                GM_setValue("default_swf_player","SuperNova");
                break;
            case "ruffle":
                GM_setValue("default_swf_player","Ruffle");
                break;
            case "awayfl":
                GM_setValue("default_swf_player","AwayFL");
                break;
            default:
                GM_setValue("default_swf_player",return_default_behaviour());
        }
        document.getElementById("default swf player setting button").innerHTML="Set default swf player <br> <i>Currently: "+GM_getValue( "default_swf_player", return_default_behaviour() )+"</i>";
    }

    function set_ruffle_preferred_renderer(){
        var current_renderer = GM_getValue("preferred_ruffle_renderer", "default");
        var text = `
Normally, Ruffle automatically chooses the most appropriate render backend that it should use. However, it appears that in some unknown circumstances, the default one is not necessarily the best (the game might not even load). If you want to change it, you can input your choice below.
See the Ruffle documentation at https://github.com/ruffle-rs/ruffle/wiki/Using-Ruffle#list-of-options (search for the preferredRenderer key in that list) to know the possible values that you can choose.
If you want to go back to Ruffle's default choice, input "default".

WARNING: if Ruffle is the SWF player currently in use, then clicking "OK" will reload the game - you have been warned!
        `;
        var renderer = prompt(text, current_renderer);
        if(renderer){
            GM_setValue("preferred_ruffle_renderer", renderer);
            if(current_SWF_player == "Ruffle"){ press_play_now_then_change_swf_player(current_SWF_player); }
        }
    }

    // Let's make some shiny buttons!
    function ButtonMaker(){
        var fullscreen_button=document.getElementById("fullscreen_button");
        var ql2=fullscreen_button?.parentElement;
        var is_Flash_game=-1, everything_ready;
        if(document.readyState=="complete" && unsafeWindow[0]?.active_user?.gameType){
            is_Flash_game = unsafeWindow[0].active_user.gameType()=="flash";
            everything_ready = ql2!==null;
        }
        if(is_Flash_game==true&&everything_ready){
            // Flash button
            var FlashButton=document.createElement('button');
            FlashButton.id="Button to (re)load with Flash";
            FlashButton.style.display="flex";
            FlashButton.innerHTML=`<img width="15" height="15" src="//bluemaxima.org/flashpoint/images/icons/flash.png" style=""><span> Flash</span>`;
            FlashButton.addEventListener("click",function(){ press_play_now_then_change_swf_player("Flash"); });
            ql2.insertBefore(FlashButton,fullscreen_button);
            // SuperNova button
            var SuperNovaButton=document.createElement('button');
            SuperNovaButton.id="Button to (re)load with SuperNova";
            SuperNovaButton.style.display="flex";
            SuperNovaButton.innerHTML=`<img width="16" height="16" src="//web.archive.org/web/20210420194152id_/https://www.getsupernova.com/images/icon-32x32.png" style=""><span> SuperNova</span>`;
            SuperNovaButton.addEventListener("click",function(){ press_play_now_then_change_swf_player("SuperNova"); });
            ql2.insertBefore(SuperNovaButton,fullscreen_button);
            // Ruffle button
            var RuffleButton=document.createElement('button');
            RuffleButton.id="Button to (re)load with Ruffle";
            RuffleButton.style.display="flex";
            RuffleButton.innerHTML=`<img width="15" height="15" src="//web.archive.org/web/20220709055848/https://addons.mozilla.org/user-media/userpics/17/17184/17184207.png?modified=1639619051" style=""><span> Ruffle</span>`;
            RuffleButton.addEventListener("click",function(){ press_play_now_then_change_swf_player("Ruffle"); });
            ql2.insertBefore(RuffleButton,fullscreen_button);
            // AwayFL button
            var AwayFLButton=document.createElement('button');
            AwayFLButton.id="Button to (re)load with AwayFL";
            AwayFLButton.style.display="flex";
            AwayFLButton.innerHTML=`<img width="15" height="15" src="//awayfl.org/img/awayfl-logo_large_trans_white.png" style=""><span> AwayFL</span>`;
            AwayFLButton.addEventListener("click",function(){ press_play_now_then_change_swf_player("AwayFL"); });
            ql2.insertBefore(AwayFLButton,fullscreen_button);
            // Settings button
            var SettingsButton=document.createElement("button");
            SettingsButton.id="default swf player setting button";
            SettingsButton.style.borderWidth="1px";
            SettingsButton.style.backgroundColor="grey";
            SettingsButton.style.width="150px";
            SettingsButton.style.fontSize="14px";
            SettingsButton.innerHTML="Set default swf player <br> <i>Currently: "+GM_getValue( "default_swf_player", return_default_behaviour() )+"</i>";
            SettingsButton.addEventListener("click",open_settings);
            fullscreen_button.parentElement.insertBefore(SettingsButton,fullscreen_button);
            // Ruffle renderer button
            var renderer_button=document.createElement("button");
            renderer_button.id="preferred Ruffle renderer";
            renderer_button.style.borderWidth="1px";
            renderer_button.style.backgroundColor="grey";
            renderer_button.style.width="110px";
            renderer_button.style.fontSize="12px";
            renderer_button.innerHTML="Set your preferred Ruffle renderer";
            renderer_button.addEventListener("click",set_ruffle_preferred_renderer);
            fullscreen_button.parentElement.insertBefore(renderer_button,fullscreen_button);
            // Launch the game using the default SWF player that the user has set
            press_play_now_then_change_swf_player(current_SWF_player);
        }
        else if(is_Flash_game!=false){ setTimeout(function(){ ButtonMaker(); },1000); }
    }

    var current_SWF_player = GM_getValue("default_swf_player", return_default_behaviour());
    ButtonMaker();
}

// Stuff related to card albums:
else if (document.URL.search(`www.kongregate.com/accounts/(.*?)/card_album`)>-1){
    // Since Kongai's in-game "card album" button has been pointing to the wrong page for ages, we'd better fix it!
    if(document.URL.search(`www.kongregate.com/accounts/(.*?)/card_albums`)==-1){
        unsafeWindow.location=document.URL.substring(0,document.URL.search("/card_album")+11)+"s"+document.URL.substring(document.URL.search("/card_album")+11,document.URL.length);
    }
    else{
        // Switching card set when viewing another player's album should not redirect you to your own album!
        var true_core_href=document.location.pathname;
        if (document.getElementById("martial_artists_nav").href.search(true_core_href)==-1){
            document.getElementById("martial_artists_nav").setAttribute("href",true_core_href+"?card_set=1");
            document.getElementById("amazons_nav").setAttribute("href",true_core_href+"?card_set=2");
            document.getElementById("tiki_villagers_nav").setAttribute("href",true_core_href+"?card_set=3");
            document.getElementById("vampires_nav").setAttribute("href",true_core_href+"?card_set=4");
            document.getElementById("pirates_nav").setAttribute("href",true_core_href+"?card_set=5");
            document.getElementById("knights_nav").setAttribute("href",true_core_href+"?card_set=6");
            document.getElementById("robots_nav").setAttribute("href",true_core_href+"?card_set=7");
            document.getElementById("witches_nav").setAttribute("href",true_core_href+"?card_set=8");
            document.getElementById("general_items_nav").setAttribute("href",true_core_href+"?card_set=0");
        }
    }
}