Programming some client scripts (3D models, RTB, InstaCalc and Online Builder)

Maxouille

Gastrodon east sea <3
is a Tiering Contributor Alumnus
as of 2017 I made a bunch of tampermonkey script either for my personal usage (mostly in order to record data) or just adding cool features to PS! some still work, some dont, some were uploaded as extension, so from today, i'll share some useful ones here starting with my random battle one which was taken down for featuring "Kyogre EX" in the picture apparently (?) i mostly have chrome links but i'll add tampermonkey and firefox soon.

- https://chromewebstore.google.com/d...miz/dgbjcmccheghgeihjfcmkpfdkdkppolh?hl=en-US

3D SV models in PS! its basically just a bunch of mutation observers. originally did that for SwSh sprites but then I forgot ^^` thx to Zuki for hosting them on github and adamsb0303 who captured each of them, sadly there are not any back models. Also i didnt compressed them for bad wifi users, maybe i'll do it when we got back models too.

- https://chromewebstore.google.com/detail/ps-3d-sprites/aniekkoifibdmglhbmijfbmadgimohgn?hl=en-US

and InstaCalc which i wanted to make a long time ago but again i forgot, basically its currently supposed to be a showdex alternative which would not require to be maintained as much as showdex because it relies on the official calc UI because showdex can be laggy sometimes especially on smartphone and arm device. I wanted to make a version for randbatt using the official smogon@calc thing but maybe showdex already do that now i'll check one day. I put it there in the Pre's tooltips to replace a buggy calc feature made by a Pre fan https://chromewebstore.google.com/detail/showdown-tooltip/ipfdjoljmkcfabfppnclebjgbehjemch?hl=en-US
its not finished yet and its very frail rn (i made the parser myself bc showdown pokemon objet is completly different than the calc's one) but it'll better once im done with my set detection project (basically some team and replays indexing tool that I wanted to make years ago)
JavaScript:
//ME

var utm = "";
var battles_safety_check = {};

var config = {
    childList: true,
    attributes: true,
    characterData: true,
    subtree: true,

};

function stringify(obj) {
    let cache = [];
    let str = JSON.stringify(obj, function(key, value) {
        if (typeof value === "object" && value !== null) {
            if (cache.indexOf(value) !== -1) {
                // Circular reference found, discard key
                return;
            }
            // Store value in our collection
            cache.push(value);
        }
        return value;
    });
    cache = null; // reset the cache
    return str;
}

function InstaCalc(tempLink) {
    let arg = {
        ...tempLink
    };
    console.debug(arg.tier);
    var param = (toID(arg.tier)
        .includes('random')) ? `randoms.html?=1&gen=${encodeURIComponent(arg.gen)}&` : `?=1&gen=${encodeURIComponent(arg.gen)}&`;
    var weather = arg.weather;
    var pseudoweather = typeof(arg.pseudoWeather[0] === 'undefined') ? "" : arg.pseudoweather[0];
    for (let i = 1; i < 3; i++) {
        txt = "";
        console.debug(eval("arg.p" + i));
        console.debug(i);
        //myPokemon = JSON.parse(JSON.stringify(arg.myPokemon));
        if (toID(arg.tier)
            .includes('random')) {
            txt = txt + `p${i}=` + ((eval(`arg.p${i}.active[0]`) === null) ? "" : eval(`arg.p${i}.active[0].speciesForme`)) + `&`;
            txt = txt + `boost${i}=` + JSON.stringify(eval(`arg.p${i}.active[0].boosts`)) + `&`;
        } else if (arg.myPokemon != null && eval("arg.p" + i + ".name") == app.user.attributes.name) {
            //alert("if myPoke");
            txt = txt + `p${i}=` + eval(`arg.p${i}.active[0].speciesForme`) + `&`;
            txt = txt + `boost${i}=` + JSON.stringify(eval(`arg.p${i}.active[0].boosts`)) + `&`;
            var team = Teams.unpack(battles_safety_check[arg.id]);
            for (var poke of team) {
                poke.name = `p${i}`;
            }
            txt = txt + `team${i}=` + encodeURIComponent(Teams.export(team)) + `&`;
            console.debug(battles_safety_check[arg.id]);
            console.debug("TEST1");
        } else {
            var yourPokemon = [];
            for (const poke of eval(`arg.p${i}.pokemon`)) {
                yourPokemon.push(JSON.parse(stringify(poke)));
                //var teste = (({ side, battle, scene, sprite, ...o}) => o)(app.curRoom.battle.p1.pokemon) // remove b and c

            };
            //console.debug(yourPokemon);
            txt = txt + `p${i}=` + ((eval(`arg.p${i}.active[0]`) === null) ? "" : eval(`arg.p${i}.active[0].speciesForme`)) + `&`;
            txt = txt + `boost${i}=` + JSON.stringify(eval(`arg.p${i}.active[0].boosts`)) + `&`;
            for (e of yourPokemon) {
                e.species = e.speciesForme;
                e.name = `p${i}`;
                e.moves = e.moveTrack;
                e.moveTrack.forEach(function(part, index, theArray) {
                    e.moves[index] = e.moveTrack[index][0];
                });
            };
            //console.debug(e);
            txt = txt + `team${i}=` + encodeURIComponent(eval(`Teams.export(yourPokemon)`)) + `&`
            //console.debug(txt);
        };
        param = param + txt;
    };
    param = param.slice(0, -1);
    console.debug("PARAM:");
    console.debug(param);
    param.replaceAll("\n", "%0A")
        .replaceAll("\r", "%0D");
    console.debug(param);
    open(`https://calc.pokemonshowdown.com/${param}`);
};

var roomObserver = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        if (mutation.addedNodes && mutation.addedNodes.length > 0) {
            // element added to DOM
            [].forEach.call(mutation.addedNodes, function(el) {
                if (el.classList != null && el.classList.contains('ps-room-opaque')) {
                    //room.send("/hidereplay");
                    let truc = document.createElement('DIV');
                    truc.id = "instaCalc";
                    truc.classList.add("battle-controls");
                    truc.style.top = "600px";
                    let button = document.createElement('BUTTON');
                    button.id = "instaCalc";
                    button.classList.add("button");
                    button.setAttribute("onclick", "InstaCalc(app.curRoom.battle);");
                    let text = document.createTextNode("InstaCalc");
                    button.appendChild(text);
                    truc.appendChild(button);
                    el.append(truc);
                }
            });
        }
    });
});

// Start Observer for new Battles
roomObserver.observe(document.body, config);

app.send = (function(previousFn) {
    return function() {
        var result = previousFn.apply(this, arguments);
        if (arguments[0].includes("/utm")) {
            if (utm != null) {
                utm = arguments[0].replace("/utm ", "");
                //alert(utm);
            };
        };
        return result;
    }
})(app.send);

app.receive = (function(previousFn) {
    return function() {
        var result = previousFn.apply(this, arguments);
        //
        var data = arguments[0].split("|");
        var id = data[0].replace(">", "")
            .trim();
        if (!(id.includes("random"))) {
            if (arguments[0].includes("|init|battle") && data[4].includes(app.user.attributes.name)) {
                if (!(typeof cancel_rtb == "undefined")) {
                    cancel_rtb();
                };
                battles_safety_check[id] = utm;
            }
        };
        return result;
    }
})(app.receive)
JavaScript:
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has("")) {
    console.debug("test");
    window.alert = function() {};
    const dic = ["pv", "atk", "def", "spa", "spd", "spe"]
    const team1 = urlParams.get("team1");
    const team2 = urlParams.get("team2");
    const p1 = urlParams.get("p1");
    const p2 = urlParams.get("p2");
    const boost1 = JSON.parse(urlParams.get("boost1"));
    const boost2 = JSON.parse(urlParams.get("boost2"));
    function test(fullSetName, boost, a) {
        console.debug(fullSetName);
        console.debug(a);
        var pokemonName = fullSetName.substring(0, fullSetName.indexOf(" ("));
        var setName = fullSetName.substring(fullSetName.indexOf("(") + 1, fullSetName.lastIndexOf(")"));
        var pokemon = pokedex[pokemonName];
        if (pokemon) {
            var pokeObj = a.closest(".poke-info");
            if (stickyMoves.getSelectedSide() === pokeObj.prop("id")) {
                stickyMoves.clearStickyMove();
            }
            pokeObj.find(".teraToggle")
                .prop("checked", false);
            pokeObj.find(".boostedStat")
                .val("Auto-Select");
            pokeObj.find(".analysis")
                .attr("href", smogonAnalysis(pokemonName));
            pokeObj.find(".type1")
                .val(pokemon.types[0]);
            pokeObj.find(".type2")
                .val(pokemon.types[1]);
            pokeObj.find(".hp .base")
                .val(pokemon.bs.hp);
            var i;
            console.debug("TESTESTSTEST")
            for (i = 0; i < LEGACY_STATS[gen].length; i++) {
                pokeObj.find("." + LEGACY_STATS[gen][i] + " .base")
                    .val(pokemon.bs[LEGACY_STATS[gen][i]]);
                pokeObj.find("." + LEGACY_STATS[gen][i] + " .boost")
                    .val(typeof(boost[dic[i]]) === 'undefined' ? 0 : boost[dic[i]]);
            }
            pokeObj.find(".percent-hp")
                .val(100);
            pokeObj.find(".status")
                .val("Healthy");
            $(".status")
                .change();
            var moveObj;
            var abilityObj = pokeObj.find(".ability");
            var itemObj = pokeObj.find(".item");
            var randset = $("#randoms")
                .prop("checked") ? randdex[pokemonName] : undefined;
            var regSets = pokemonName in setdex && setName in setdex[pokemonName];
            if (randset) {
                var listItems = randdex[pokemonName].items ? randdex[pokemonName].items : [];
                var listAbilities = randdex[pokemonName].abilities ? randdex[pokemonName].abilities : [];
                if (gen >= 3) a.closest('.poke-info')
                    .find(".ability-pool")
                    .show();
                a.closest('.poke-info')
                    .find(".extraSetAbilities")
                    .text(listAbilities.join(', '));
                if (gen >= 2) a.closest('.poke-info')
                    .find(".item-pool")
                    .show();
                a.closest('.poke-info')
                    .find(".extraSetItems")
                    .text(listItems.join(', '));
                if (gen >= 9) {
                    a.closest('.poke-info')
                        .find(".role-pool")
                        .show();
                    a.closest('.poke-info')
                        .find(".tera-type-pool")
                        .show();
                }
                var listRoles = randdex[pokemonName].roles ? Object.keys(randdex[pokemonName].roles) : [];
                a.closest('.poke-info')
                    .find(".extraSetRoles")
                    .text(listRoles.join(', '));
                var listTeraTypes = [];
                if (randdex[pokemonName].roles) {
                    for (var roleName in randdex[pokemonName].roles) {
                        var role = randdex[pokemonName].roles[roleName];
                        for (var q = 0; q < role.teraTypes.length; q++) {
                            if (listTeraTypes.indexOf(role.teraTypes[q]) === -1) {
                                listTeraTypes.push(role.teraTypes[q]);
                            }
                        }
                    }
                }
                pokeObj.find(".teraType")
                    .val(listTeraTypes[0] || pokemon.types[0]);
                a.closest('.poke-info')
                    .find(".extraSetTeraTypes")
                    .text(listTeraTypes.join(', '));
            } else {
                a.closest('.poke-info')
                    .find(".ability-pool")
                    .hide();
                a.closest('.poke-info')
                    .find(".item-pool")
                    .hide();
                a.closest('.poke-info')
                    .find(".role-pool")
                    .hide();
                a.closest('.poke-info')
                    .find(".tera-type-pool")
                    .hide();
            }
            if (regSets || randset) {
                var set = regSets ? correctHiddenPower(setdex[pokemonName][setName]) : randset;
                if (regSets) {
                    pokeObj.find(".teraType")
                        .val(set.teraType || pokemon.types[0]);
                }
                pokeObj.find(".level")
                    .val(set.level);
                pokeObj.find(".hp .evs")
                    .val((set.evs && set.evs.hp !== undefined) ? set.evs.hp : 0);
                pokeObj.find(".hp .ivs")
                    .val((set.ivs && set.ivs.hp !== undefined) ? set.ivs.hp : 31);
                pokeObj.find(".hp .dvs")
                    .val((set.dvs && set.dvs.hp !== undefined) ? set.dvs.hp : 15);
                for (i = 0; i < LEGACY_STATS[gen].length; i++) {
                    pokeObj.find("." + LEGACY_STATS[gen][i] + " .evs")
                        .val(
                            (set.evs && set.evs[LEGACY_STATS[gen][i]] !== undefined) ?
                            set.evs[LEGACY_STATS[gen][i]] : ($("#randoms")
                                .prop("checked") ? 84 : 0));
                    pokeObj.find("." + LEGACY_STATS[gen][i] + " .ivs")
                        .val(
                            (set.ivs && set.ivs[LEGACY_STATS[gen][i]] !== undefined) ? set.ivs[LEGACY_STATS[gen][i]] : 31);
                    pokeObj.find("." + LEGACY_STATS[gen][i] + " .dvs")
                        .val(
                            (set.dvs && set.dvs[LEGACY_STATS[gen][i]] !== undefined) ? set.dvs[LEGACY_STATS[gen][i]] : 15);
                }
                setSelectValueIfValid(pokeObj.find(".nature"), set.nature, "Hardy");
                var abilityFallback = (typeof pokemon.abilities !== "undefined") ? pokemon.abilities[0] : "";
                if ($("#randoms")
                    .prop("checked")) {
                    setSelectValueIfValid(abilityObj, randset.abilities && randset.abilities[0], abilityFallback);
                    setSelectValueIfValid(itemObj, randset.items && randset.items[0], "");
                } else {
                    setSelectValueIfValid(abilityObj, set.ability, abilityFallback);
                    setSelectValueIfValid(itemObj, set.item, "");
                }
                var setMoves = set.moves;
                if (randset) {
                    if (gen < 9) {
                        setMoves = randset.moves;
                    } else {
                        setMoves = [];
                        for (var role in randset.roles) {
                            for (var q = 0; q < randset.roles[role].moves.length; q++) {
                                var moveName = randset.roles[role].moves[q];
                                if (setMoves.indexOf(moveName) === -1) setMoves.push(moveName);
                            }
                        }
                    }
                }
                var moves = selectMovesFromRandomOptions(setMoves);
                for (i = 0; i < 4; i++) {
                    moveObj = pokeObj.find(".move" + (i + 1) + " select.move-selector");
                    moveObj.attr('data-prev', moveObj.val());
                    setSelectValueIfValid(moveObj, moves[i], "(No Move)");
                    moveObj.change();
                }
                if (randset) {
                    a.closest('.poke-info')
                        .find(".move-pool")
                        .show();
                    a.closest('.poke-info')
                        .find(".extraSetMoves")
                        .html(formatMovePool(setMoves));
                }
            } else {
                pokeObj.find(".teraType")
                    .val(pokemon.types[0]);
                pokeObj.find(".level")
                    .val(100);
                pokeObj.find(".hp .evs")
                    .val(0);
                pokeObj.find(".hp .ivs")
                    .val(31);
                pokeObj.find(".hp .dvs")
                    .val(15);
                for (i = 0; i < LEGACY_STATS[gen].length; i++) {
                    pokeObj.find("." + LEGACY_STATS[gen][i] + " .evs")
                        .val(0);
                    pokeObj.find("." + LEGACY_STATS[gen][i] + " .ivs")
                        .val(31);
                    pokeObj.find("." + LEGACY_STATS[gen][i] + " .dvs")
                        .val(15);
                }
                pokeObj.find(".nature")
                    .val("Hardy");
                setSelectValueIfValid(abilityObj, pokemon.ab, "");
                itemObj.val("");
                for (i = 0; i < 4; i++) {
                    moveObj = pokeObj.find(".move" + (i + 1) + " select.move-selector");
                    moveObj.attr('data-prev', moveObj.val());
                    moveObj.val("(No Move)");
                    moveObj.change();
                }
                if ($("#randoms")
                    .prop("checked")) {
                    a.closest('.poke-info')
                        .find(".move-pool")
                        .hide();
                }
            }
            if (typeof getSelectedTiers === "function") { // doesn't exist when in 1vs1 mode
                var format = getSelectedTiers()[0];
                var is50lvl = startsWith(format, "VGC") || startsWith(format, "Battle Spot");
                //var isDoubles = format === 'Doubles' || has50lvl; *TODO*
                if (format === "LC") pokeObj.find(".level")
                    .val(5);
                if (is50lvl) pokeObj.find(".level")
                    .val(50);
                //if (isDoubles) field.gameType = 'Doubles'; *TODO*
            }
            var formeObj = a.siblings()
                .find(".forme")
                .parent();
            itemObj.prop("disabled", false);
            var baseForme;
            if (pokemon.baseSpecies && pokemon.baseSpecies !== pokemon.name) {
                baseForme = pokedex[pokemon.baseSpecies];
            }
            if (pokemon.otherFormes) {
                showFormes(formeObj, pokemonName, pokemon, pokemonName);
            } else if (baseForme && baseForme.otherFormes) {
                showFormes(formeObj, pokemonName, baseForme, pokemon.baseSpecies);
            } else {
                formeObj.hide();
            }
            calcHP(pokeObj);
            calcStats(pokeObj);
            abilityObj.change();
            itemObj.change();
            if (pokemon.gender === "N") {
                pokeObj.find(".gender")
                    .parent()
                    .hide();
                pokeObj.find(".gender")
                    .val("");
            } else pokeObj.find(".gender")
                .parent()
                .show();
        }
    };
    function test2(gen) {
        /*eslint-disable */
        GENERATION = calc.Generations.get(gen);
        var params = new URLSearchParams(window.location.search);
        if (gen === 9) {
            params.delete('gen');
            params = '' + params;
            if (window.history && window.history.replaceState) {
                window.history.replaceState({}, document.title, window.location.pathname + (params.length ? '?' + params : ''));
            }
        } else {
            params.set('gen', gen);
            if (window.history && window.history.pushState) {
                params.sort();
                var path = window.location.pathname + '?' + params;
                window.history.pushState({}, document.title, path);
                gtag('config', 'UA-26211653-3', {
                    'page_path': path
                });
            }
        }
        genWasChanged = true;
        /* eslint-enable */
        // declaring these variables with var here makes z moves not work; TODO
        pokedex = calc.SPECIES[gen];
        setdex = SETDEX[gen];
        randdex = RANDDEX[gen];
        typeChart = calc.TYPE_CHART[gen];
        moves = calc.MOVES[gen];
        items = calc.ITEMS[gen];
        abilities = calc.ABILITIES[gen];
        clearField();
        $("#importedSets")
            .prop("checked", false);
        loadDefaultLists();
        $(".gen-specific.g" + gen)
            .show();
        $(".gen-specific")
            .not(".g" + gen)
            .hide();
        var typeOptions = getSelectOptions(Object.keys(typeChart));
        $("select.type1, select.move-type")
            .find("option")
            .remove()
            .end()
            .append(typeOptions);
        $("select.teraType")
            .find("option")
            .remove()
            .end()
            .append(getSelectOptions(Object.keys(typeChart)
                .slice(1)));
        $("select.type2")
            .find("option")
            .remove()
            .end()
            .append("<option value=\"\">(none)</option>" + typeOptions);
        var moveOptions = getSelectOptions(Object.keys(moves), true);
        $("select.move-selector")
            .find("option")
            .remove()
            .end()
            .append(moveOptions);
        var abilityOptions = getSelectOptions(abilities, true);
        $("select.ability")
            .find("option")
            .remove()
            .end()
            .append("<option value=\"\">(other)</option>" + abilityOptions);
        var itemOptions = getSelectOptions(items, true);
        $("select.item")
            .find("option")
            .remove()
            .end()
            .append("<option value=\"\">(none)</option>" + itemOptions);
        $(".set-selector")
            .val(getFirstValidSetOption()
                .id);
        $(".set-selector")
            .change();
    };
    if (window.location.pathname == '/randoms.html') {
        alert("test");
        test(`${p1} (Randoms)`, boost1, $("#s2id_autogen6"))
        test(`${p2} (Randoms)`, boost2, $("#s2id_autogen9"))
        //$("#s2id_autogen6").closest(".poke-info").find("." + LEGACY_STATS[gen][3] + " .boost").val("3");
        //$("#s2id_autogen9").closest(".poke-info").find("." + LEGACY_STATS[gen][3] + " .boost").val("3");
        $("#p1")
            .find("input.set-selector")
            .val(`${p1} Randoms`);
        $("#p2")
            .find("input.set-selector")
            .val(`${p2} Randoms`);
        $("#p1")
            .find(".select2-chosen")
            .first()[0].innerHTML = `${p1} (Randoms)`;
        $("#p2")
            .find(".select2-chosen")
            .first()[0].innerHTML = `${p2} (Randoms)`;
    } else {
        console.debug(team1);
        console.debug(team2);
        console.debug(urlParams.get("gen"));
        //test2(urlParams.get("gen"));
        addSets(team1, "p1");
        addSets(team2, "p2");
        test(`${p1} (p1)`, boost1, $("#s2id_autogen6"))
        test(`${p2} (p2)`, boost2, $("#s2id_autogen9"))
        //$("#s2id_autogen6").closest(".poke-info").find("." + LEGACY_STATS[gen][3] + " .boost").val("3");
        //$("#s2id_autogen9").closest(".poke-info").find("." + LEGACY_STATS[gen][3] + " .boost").val("3");
        $("#p1")
            .find("input.set-selector")
            .val(`${p1} p1`);
        $("#p2")
            .find("input.set-selector")
            .val(`${p2} p2`);
        $("#p1")
            .find(".select2-chosen")
            .first()[0].innerHTML = `${p1} (p1)`;
        $("#p2")
            .find(".select2-chosen")
            .first()[0].innerHTML = `${p2} (p2)`;
    }
    performCalculations();
    //$(".set-selector").trigger("change")
};

I'll also add a better backup system which can automatically sync, smth I and other people tried to make before showdown make their own baclup system (which is bad for multiple teams backups). Mine would aim backing up large amount of teams and manage them on an external website (pokepost in better) for people who use multiple device like me. I'll add other minor stuff later maybe like more music and visuals..and other things maybe.

Have a good day
 
Last edited:

Users Who Are Viewing This Thread (Users: 1, Guests: 0)

Top