// ==UserScript==
// @name PokeRogue - Modmenu
// @namespace http://tampermonkey.net/
// @version 2024-11-09
// @description Modmenu for PokeRogue - Open with [F1 = default]
// @author ApyroNox
// @match https://pokerogue.net/
// @icon https://www.google.com/s2/favicons?sz=64&domain=pokerogue.net
// @grant none
// @license MIT
// @run-at document-start
// ==/UserScript==
function debug(message, color, level = 'log') {
console[level](`%c ${message}`, `color:${color};`);
}
/**
* @param {string} tagName
* @param {object} properties
* @returns {HTMLElement}
*/
const createElement = (tagName, properties = {}) => Object.assign(document.createElement(tagName), properties);
/**
* @param {HTMLElement} element
* @param {object} styles
*/
const setStyles = (element, styles) => Object.assign(element.style, styles);
function loadStyles() {
const stylesheet = createElement('style');
stylesheet.innerHTML = [
':root{--logo-border-color:#da3838;--logo-inner_dark-color:#2e4069;--logo-inner_light-color:#346485;--card-bg-color:#362d3e;--card-border-color:#c73625;--card-text-color:#f8f8f8}',
'*,:after,:before{box-sizing:border-box;margin:0;padding:0}',
'.noselect{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}',
'a{color:inherit;cursor:default;text-decoration:none}',
'a[href]{cursor:pointer;text-decoration:underline}',
'ul{list-style:none}',
'.card{--clip-top-left:0 0%;--clip-top-right:100% 0%;--clip-bottom-right:100% 100%;--clip-bottom-left:0 100%;--clip-br:10px;--clip-brm:1.5;overflow-x:hidden;overflow-y:scroll;background-color:var(--card-border-color)!important;color:var(--card-text-color);padding:calc(var(--clip-br) * 1);max-height:50dvh;max-width:50dvw;top:-2rem;visibility:hidden;transition:all .5s ease-in-out}',
'.card:has(.card-body.open){top:1rem;visibility:visible}',
'.card-body input{color:var(--card-text-color);background-color:transparent}',
'.card::before{--clip-brm:1;content:"";position:absolute;inset:0;z-index:-1;background-color:var(--card-bg-color);margin:var(--clip-br)}',
'.card-r,.card-r::before{--clip-top-left:0 calc(0% + var(--clip-br) * var(--clip-brm)),calc(0% + var(--clip-br) * var(--clip-brm)) 0%;--clip-top-right:calc(100% - var(--clip-br) * var(--clip-brm)) 0%,100% calc(0% + var(--clip-br) * var(--clip-brm));--clip-bottom-right:100% calc(100% - var(--clip-br) * var(--clip-brm)),calc(100% - var(--clip-br) * var(--clip-brm)) 100%;--clip-bottom-left:calc(0% + var(--clip-br) * var(--clip-brm)) 100%,0% calc(100% - var(--clip-br) * var(--clip-brm))}',
'.card-tl-s,.card-tl-s::before{--clip-top-left:0 0%}',
'.card-tr-s,.card-tr-s::before{--clip-top-right:100% 0%}',
'.card-br-s,.card-br-s::before{--clip-bottom-right:100% 100%}',
'.card-bl-s,.card-bl-s::before{--clip-bottom-left:0 100%}',
'.card-top-s,.card-top-s::before{--clip-top-left:0 0%;--clip-top-right:100% 0%;margin-top:0;padding-top:var(--clip-br)}',
'.card-right-s,.card-right-s::before{--clip-top-right:100% 0%;--clip-bottom-right:100% 100%;margin-right:0;padding-right:var(--clip-br)}',
'.card-bottom-s,.card-bottom-s::before{--clip-bottom-right:100% 100%;--clip-bottom-left:0 100%;margin-bottom:0;padding-bottom:var(--clip-br)}',
'.card-left-s,.card-left-s::before{--clip-top-left:0 0%;--clip-bottom-left:0 100%;margin-left:0;padding-left:var(--clip-br)}',
'.card,.card::before{--card-clip-path:var(--clip-top-left),var(--clip-top-right),var(--clip-bottom-right),var(--clip-bottom-left);-webkit-clip-path:polygon(var(--card-clip-path));clip-path:polygon(var(--card-clip-path))}',
].join('\n');
document.head.append(stylesheet);
document.head.append(
(function () {
const style = document.createElement('link');
style.href = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css';
style.rel = 'stylesheet';
return style;
})()
);
document.body.append(
(function () {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js';
return script;
})()
);
document.body.style.backgroundColor = 'var(--color-base)';
}
const __config_namespace = 'pokerogue.modmenu';
function __get(key, fallback = undefined) {
if (!localStorage.getItem(__config_namespace)) localStorage.setItem(__config_namespace, JSON.stringify({}));
const storage = localStorage.getItem(__config_namespace) ?? {};
try {
const config = JSON.parse(storage);
return config[key] ?? fallback;
} catch (error) {}
}
function __set(key, value) {
const storage = localStorage.getItem(__config_namespace) ?? {};
try {
const config = JSON.parse(storage);
config[key] = value;
localStorage.setItem(__config_namespace, JSON.stringify(config));
return;
} catch (error) {}
}
(async function (callback = (Game, SpeciesStarterCosts, keybind) => {}) {
window.localStorage.getItem('pokerogue.modmenu');
new Promise(function (resolve, reject) {
var Game, SpeciesStarterCosts;
const originalObjectDefineProperty = Object.defineProperty;
const originalObjectEntries = Object.entries;
Object.defineProperty = function (...definePropertyArguments) {
if (definePropertyArguments[2].value === 'Game') {
const originalFunction = definePropertyArguments[0];
definePropertyArguments[0] = function (...argumentsOnFunctionCall) {
Game = this;
debug(`${definePropertyArguments[2].value} object intercepted`, '#198754');
return originalFunction.call(this, ...argumentsOnFunctionCall);
};
}
return originalObjectDefineProperty.call(this, ...definePropertyArguments);
};
Object.entries = function (possiblePokemonCostObject) {
debug(`Pokemon Starter Costs object intercepted`, '#198754');
SpeciesStarterCosts = possiblePokemonCostObject;
return originalObjectEntries.call(this, possiblePokemonCostObject);
};
const resolveThis = () => {
if (typeof Game !== 'undefined' && typeof SpeciesStarterCosts !== 'undefined') {
Object.defineProperty = originalObjectDefineProperty;
Object.entries = originalObjectEntries;
resolve();
callback(Game, SpeciesStarterCosts, __get('menu_toggle_keybind', 'F1'));
} else {
setTimeout(function () {
resolveThis();
}, 1000);
}
};
resolveThis();
});
})(function (Game, SpeciesStarterCosts, keybind) {
class PokeRogue {
Game;
SpeciesStarterCosts;
constructor(Game, SpeciesStarterCosts) {
this.Game = Game;
this.SpeciesStarterCosts = SpeciesStarterCosts;
}
getScene() {
return this.Game.scene.scenes[0];
}
getGameData() {
return this.getScene().gameData;
}
getParty(index = -1) {
const party = this.getScene().party;
if (index >= 0 && index < party.length) return party[index];
return party;
}
setCostOfStarterPokemens(price = 2) {
if (typeof price != 'number') return;
Object.keys(this.SpeciesStarterCosts).forEach((pokemonId) => {
this.SpeciesStarterCosts[pokemonId] = price;
});
}
enableFastForward() {
const scene = this.getScene();
scene.typeHints = true;
scene.battleStyle = 1;
scene.expParty = 2;
}
setBestStatsForParty() {
this.getParty().forEach((pokemon) => {
// pokemon.level = 200;
setPokemonStats(pokemon);
});
}
multiplyBoosterModifiers() {
this.getScene()
.modifiers.filter((item) => item.type.id === 'BASE_STAT_BOOSTER' || item.type.id === 'ATTACK_TYPE_BOOSTER')
.forEach((item) => (item.stackCount *= 2));
}
getEggGachaUiHandler() {
return this.getScene().ui.handlers.find((handler) => handler.constructor.name === 'EggGachaUiHandler');
}
getShopUiHandler() {
return this.getScene().ui.handlers.find((handler) => handler.constructor.name === 'ModifierSelectUiHandler');
}
}
function setPokemonStats(pokemon) {
pokemon.luck = 2;
pokemon.nature = 10;
pokemon.ivs = [999, 999, 999, 999, 999, 999];
pokemon.stats = [999, 999, 999, 999, 999, 999];
pokemon.moveset.forEach((move) => {
move.ppUp = 10;
move.ppUsed = 0;
});
}
const pokerogue = new PokeRogue(Game, SpeciesStarterCosts);
globalThis['pokerogue'] = pokerogue;
const container = (function createContainerElement() {
const container = createElement('div', { className: 'card card-r' });
setStyles(container, {
position: 'absolute',
fontFamily: 'emerald',
});
return container;
})();
const wrapper = (function createWrapperElement() {
const wrapper = createElement('div', { className: 'card-body' });
container.append(wrapper);
return wrapper;
})();
const row = (function createFluidRowElement() {
const row = createElement('div', { className: 'row g-3' });
wrapper.append(row);
return row;
})();
(function SectionModifyStarterCosts() {
const container = createElement('div', { className: 'col col-6 d-grid' });
const button = createElement('button', { className: 'btn btn-outline-secondary', type: 'button', textContent: 'Lower Starter Costs' });
button.addEventListener('click', function (event) {
pokerogue.setCostOfStarterPokemens(1);
});
container.append(button);
row.append(container);
})();
(function SectionModifyMoney() {
const container = createElement('div', { className: 'col col-6' });
const group = createElement('div', { className: 'input-group' });
const input = createElement('input', { className: 'form-control border-secondary text-secondary', type: 'number', value: '999000', max: '999999' });
const button = createElement('button', { className: 'btn btn-outline-secondary', type: 'button', textContent: 'Add Money' });
button.addEventListener('click', function (event) {
pokerogue.getScene().addMoney(input.value);
});
group.append(input, button);
container.append(group);
row.append(container);
})();
(function SectionGiveMasterballs() {
const container = createElement('div', { className: 'col col-6 d-grid' });
const button = createElement('button', { className: 'btn btn-outline-secondary', type: 'button', textContent: 'add 10x Masterball' });
button.addEventListener('click', function (event) {
pokerogue.getScene().pokeballCounts[4] += 10;
});
container.append(button);
row.append(container);
})();
(function SectionModifyLuck() {
const container = createElement('div', { className: 'col col-6 d-grid' });
const button = createElement('button', { className: 'btn btn-outline-secondary', type: 'button', textContent: "Set Luck to 'SSS'" });
button.addEventListener('click', function (event) {
pokerogue.getParty().forEach((pokemon) => {
pokemon.luck = 16;
});
});
container.append(button);
row.append(container);
})();
(function SectionShopUpdateItemRarity() {
const hintContainer = createElement('div', { className: 'col col-12 clearfix' });
const hintText = createElement('p', { textContent: 'After setting the Item Rarity you need to lock the rarity and Reroll once!', className: 'fs-4 text-danger' });
const hintUnderstoodButton = createElement('button', { textContent: 'Understood', className: 'float-end btn btn-primary' });
hintContainer.append(hintText, hintUnderstoodButton);
localStorage.getItem('modmenu.lockitemrarity.understood') ? null : row.append(hintContainer);
hintUnderstoodButton.addEventListener('click', () => {
localStorage.setItem('modmenu.lockitemrarity.understood', true);
hintContainer.remove();
});
const container = createElement('div', { className: 'col col-6' });
const group = createElement('div', { className: 'input-group' });
const input = createElement('input', { className: 'form-control border-secondary text-secondary', type: 'number', value: '0', max: '3' });
const button = createElement('button', { className: 'btn btn-outline-secondary', type: 'button', textContent: 'Set Item Rarity' });
button.addEventListener('click', function (event) {
const shop = pokerogue.getShopUiHandler();
const originalShopShopFunction = shop.show;
shop.show = function customOverrideFunction(args) {
args[1].forEach((item) => {
item.type.tier = Number(input.value ?? 3);
item.upgradeCount = Number(input.value ?? 3);
});
shop.show = originalShopShopFunction;
return originalShopShopFunction.call(shop, args);
};
shop.onActionInput(0, 0);
});
group.append(input, button);
container.append(group);
row.append(container);
})();
(function SectionLockShopRarity() {
const container = createElement('div', { className: 'col col-6 d-grid' });
const button = createElement('button', { className: 'btn btn-outline-secondary', type: 'button', textContent: 'Lock Item Rarity' });
button.addEventListener('click', function (event) {
const scene = pokerogue.getScene();
scene.lockModifierTiers = !scene.lockModifierTiers;
scene.lockModifierTiers
? (button.classList.add('btn-outline-success'), button.classList.remove('btn-outline-secondary'))
: (button.classList.remove('btn-outline-success'), button.classList.add('btn-outline-secondary'));
});
container.append(button);
row.append(container);
})();
(function SectionPullGatchas() {
const container1 = createElement('div', { className: 'col col-4' });
const container2 = createElement('div', { className: 'col col-4' });
const container3 = createElement('div', { className: 'col col-4' });
const setAmmoutInput = createElement('input', { className: 'form-control border-secondary text-secondary', type: 'number', value: '100', max: '100' });
const addLegendaryButton = createElement('button', { className: 'btn btn-outline-secondary w-100', type: 'button', textContent: 'Gacha Legendaries' });
const addShinyButton = createElement('button', { className: 'btn btn-outline-secondary w-100', type: 'button', textContent: 'Gacha Shinies' });
addLegendaryButton.addEventListener('click', function (event) {
const gachaContainer = pokerogue.getEggGachaUiHandler();
gachaContainer.setCursor(0);
gachaContainer.setGachaCursor(1);
gachaContainer.pull(setAmmoutInput.value, 0);
});
addShinyButton.addEventListener('click', function (event) {
const gachaContainer = pokerogue.getEggGachaUiHandler();
gachaContainer.setCursor(0);
gachaContainer.setGachaCursor(2);
gachaContainer.pull(setAmmoutInput.value, 0);
});
container1.append(setAmmoutInput);
container2.append(addLegendaryButton);
container3.append(addShinyButton);
row.append(container1, container2, container3);
})();
//
//
//
const configRow = (function SectionConfiguration() {
const configRow = createElement('div', { className: 'row g-3' });
const container = createElement('div', { className: 'col col-12' });
container.append(createElement('hr', { className: 'mb-0' }));
configRow.append(container);
row.after(configRow);
return configRow;
})();
(function SectionConfiguration() {
const container = createElement('div', { className: 'col col-6' });
const group = createElement('div', { className: 'input-group' });
const input = createElement('input', { className: 'form-control border-secondary text-secondary', type: 'text', placeholder: 'default = F1' });
const button = createElement('button', { className: 'btn btn-outline-secondary', type: 'button', textContent: 'Set Modmenu Keybind' });
button.addEventListener('click', function (event) {
if (input.value) {
__set('menu_toggle_keybind', input.value);
keybind = input.value;
container.remove();
}
});
group.append(input, button);
container.append(group);
configRow.append(container);
})();
//
//
//
const canvas = document.querySelector('canvas');
canvas.after(container);
window.addEventListener('keydown', function (event) {
switch (String(event.key).toLowerCase()) {
case String(keybind).toLowerCase():
if (!wrapper.classList.contains('open')) {
wrapper.classList.add('open');
} else {
wrapper.classList.remove('open');
container.querySelectorAll('*').forEach(function (element) {
try {
element.blur();
} catch (error) {}
});
}
break;
case String('*').toLowerCase():
configRow.classList.toggle('hidden');
break;
default:
break;
}
});
loadStyles();
if (!__get('menu_toggle_keybind', false)) {
wrapper.classList.add('open');
}
});