added ovo 1 & 2 w/ mods

This commit is contained in:
MonkeyGG2
2023-10-22 16:15:54 -04:00
parent 074e18ecd5
commit 5de70db9c1
310 changed files with 85974 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
// we should probably re-write this...
(async () => {
// Get mods
const mods = await fetch("/ModLoader/V1/mods.json").then(r => r.json());
// Get runtime
let runtime;
let notify;
// Util stuff
let onFinishLoad = () => {
if ((cr_getC2Runtime() || { isloading: true }).isloading) {
setTimeout(onFinishLoad, 100);
} else {
runtime = cr_getC2Runtime();
notify = (title, text, image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
modloader.init();
}
}
const validURL = (str) => {
const pattern = new RegExp('^(https?:\\/\\/)?' +
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
'((\\d{1,3}\\.){3}\\d{1,3}))' +
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
'(\\?[;&a-z\\d%_.~+=-]*)?' +
'(\\#[-a-z\\d_]*)?$', 'i');
return !!pattern.test(str);
}
const loadModUrl = (modURL) => {
const name = modloader.getURLName(modURL);
if (modloader.getIsScriptLoaded(name)) {
notify("Mod already loaded", name);
return;
}
const js = document.createElement("script");
js.type = "application/javascript";
js.src = modURL;
js.id = name;
document.head.appendChild(js);
}
const loadModJS = (modJS) => {
setTimeout(modJS, 0);
}
const promptMod = () => {
let mod = prompt("Please enter a mod name/url");
if (!mod) return;
if (!validURL(mod)) {
mod = mods[mod.toLowerCase()].url || mod;
}
if (validURL(mod) || mod.startsWith("/")) {
loadModUrl(mod);
} else {
loadModJS(mod);
}
notify("Code Ran/Added", "Please wait");
}
const modloader = {
init() {
document.addEventListener("keydown", (event) => {
if (event.code === "KeyL") {
if (event.shiftKey) {
promptMod();
}
}
});
this.initDomUI();
globalThis.ovoModLoader = this;
notify("Mod loaded", "Modloader mod loaded");
},
initDomUI() {
const style = document.createElement("style");
style.type = "text/css";
style.innerHTML = `
.ovo-modloader-button {
background-color: white;
border: solid;
border-color: black;
border-width: 6px;
font-family: "Retron2000";
position: absolute;
z-index: 3;
cursor: pointer;
}
.ovo-modloader-button:hover {
background-color: rgba(200, 200, 200, 1);
}
`
document.head.appendChild(style);
const toggleButton = document.createElement("button");
toggleButton.id = "ovo-modloader-toggle-button";
toggleButton.innerText = "";
const loadIcon = document.createElement("img");
loadIcon.src = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gU3ZnIFZlY3RvciBJY29ucyA6IGh0dHA6Ly93d3cub25saW5ld2ViZm9udHMuY29tL2ljb24gLS0+DQo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPG1ldGFkYXRhPiBTdmcgVmVjdG9yIEljb25zIDogaHR0cDovL3d3dy5vbmxpbmV3ZWJmb250cy5jb20vaWNvbiA8L21ldGFkYXRhPg0KPGc+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsNTExLjAwMDAwMCkgc2NhbGUoMC4xMDAwMDAsLTAuMTAwMDAwKSI+PHBhdGggZD0iTTQ4ODIuOSw0NzQ5LjRjLTEwNy4zLTMyLjYtMjE2LjQtMTQzLjYtMjQ1LjItMjUwLjljLTE1LjMtNDkuOC0yMS4xLTExMDctMjEuMS0zMzc2LjZWLTIxODJMMzUwMi0xMDY3LjNDMjg4Ny4yLTQ1Ni4zLDIzNTYuNyw2MC44LDIzMjAuMyw3OGMtMTAxLjUsNTEuNy0yODMuNSwzMi42LTM3NS40LTM2LjRjLTEzNC4xLTEwMy40LTE4NS44LTI0OS0xNDMuNi00MDkuOWMyMy04NC4zLDEzMi4yLTE5Ny4zLDE0OTItMTU2MC45Yzk4Ni40LTk4OC4zLDE0OTItMTQ4Mi40LDE1NDUuNi0xNTA3LjNjOTEuOS00NiwyMTIuNi00OS44LDMxMC4zLTkuNmM5MiwzOC4zLDI5NTUuMywyODk0LDMwMTYuNiwzMDA3YzEyMC43LDIyNy45LTI0LjksNTE3LjEtMjcyLDU0MmMtMjE2LjQsMjEuMS0xMzIuMSw5MS45LTEzNzUuMi0xMTUxLjFMNTM4Mi44LTIxODJ2MzMwMy44YzAsMjI2OS42LTUuNywzMzI2LjgtMjEuMSwzMzc2LjZjLTMwLjcsMTExLjEtMTM3LjksMjE4LjMtMjUyLjgsMjUyLjhTNDk5Niw0Nzg1LjgsNDg4Mi45LDQ3NDkuNHoiLz48cGF0aCBkPSJNNDMxLjgtMTgzNy4yYy05LjYtMy44LTQyLjEtMTEuNS03Mi44LTE3LjJjLTc4LjUtMTcuMi0xOTkuMi0xMzYtMjM1LjYtMjMzLjdjLTU5LjQtMTU3LTEuOS01OTcuNiwxMjQuNS05NDQuMmMyNzkuNi03NjQuMiw5NjUuMy0xMzM0LjksMTc5Mi43LTE0OTJjMTU3LjEtMzAuNiw0MDkuOS0zMi42LDI5NTkuMS0zMi42YzI1NTYuOSwwLDI4MDAuMSwxLjksMjk2MSwzMi42YzEwNzQuNSwyMDQuOSwxODU3LjgsMTA4MC4yLDE5MzQuNCwyMTY2LjJjMTUuMywyMDYuOC0zLjgsMjg5LjItOTMuOCwzODguOGMtMTM3LjksMTU5LTM1Ni4zLDE3OC4xLTUxNS4yLDQ0Yy05Ny43LTc4LjUtMTMwLjItMTYyLjgtMTQ5LjQtMzk4LjRjLTExLjUtMTEzLTM2LjQtMjY2LjItNTkuNC0zMzljLTEzNi00NDQuMy00NDguMi04MDIuNS04NjUuNy05OTRjLTMxNC4xLTE0NS41LTU3LjUtMTM0LjEtMzIxMS45LTEzNC4xYy0yNjg1LjIsMC0yODMwLjgsMS45LTI5NDkuNSwzNC41Yy00OTYuMSwxNDEuNy04OTQuNCw0OTQuMS0xMDc4LjMsOTU1LjdjLTY3LDE2NC43LTEwOS4yLDM0Ni43LTEwOS4yLDQ2OS4zYzAsMTgzLjgtMzQuNSwyOTEuMS0xMjIuNiwzNzkuMkM2NTIuMS0xODY0LjEsNTA4LjUtMTgxMC40LDQzMS44LTE4MzcuMnoiLz48L2c+PC9nPg0KPC9zdmc+"
loadIcon.style.width = "38px";
loadIcon.style.height = "38px";
toggleButton.appendChild(loadIcon);
toggleButton.classList.add("ovo-modloader-button");
toggleButton.style.top = "50%";
toggleButton.style.right = "0%";
toggleButton.style.transform = "translateY(-50%)";
toggleButton.style.width = "50px";
toggleButton.style.height = "50px";
toggleButton.style.zIndex = "3";
toggleButton.onclick = promptMod;
document.body.appendChild(toggleButton);
},
getIsScriptLoaded(script) {
if (validURL(script)) {
const scripts = document.getElementsByTagName('script');
for (let i = scripts.length; i--;) {
if (scripts[i].src == script) return true;
}
return false;
}
const element = document.getElementById(script);
return (!!element && (element.tagName == "SCRIPT"));
},
getURLName(url) {
return url.substring(url.lastIndexOf("/") + 1).replace(/\.[^/.]+$/, "");
},
getModURL(name) {
return mods[name.toLowerCase()];
},
loadScriptURL(url) {
return loadModUrl(url);
},
loadScript(js) {
return loadModJS(js);
}
}
// Uneeded?
alert("This is a MODDED client. Press Shift+L to load mods. (url, script, or default mods on homepage)");
setTimeout(onFinishLoad, 100);
})();
+180
View File
@@ -0,0 +1,180 @@
(async () => {
// Get mods
const mods = await fetch("../modloader/mods/v2.json").then(r => r.json());
// Get runtime
var c3interface;
var runtime;
var notify;
// Util stuff
const onFinishLoad = () => {
c3interface = c3_runtimeInterface;
runtime = c3interface._GetLocalRuntime();
if ((runtime && runtime.IsLoading) && runtime.IsLoading()) {
setTimeout(onFinishLoad, 100);
} else {
notify = () => {};
modloader.init();
setTimeout(() => {
notify("Mod loaded", "Modloader mod loaded");
}, 1000);
}
}
const validURL = (str) => {
const pattern = new RegExp('^(https?:\\/\\/)?' +
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
'((\\d{1,3}\\.){3}\\d{1,3}))' +
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
'(\\?[;&a-z\\d%_.~+=-]*)?' +
'(\\#[-a-z\\d_]*)?$', 'i');
return !!pattern.test(str);
}
const loadModUrl = (modURL) => {
const name = modloader.getURLName(modURL);
if (modloader.getIsScriptLoaded(name)) {
notify("Mod already loaded", name);
return;
}
const js = document.createElement("script");
js.type = "application/javascript";
js.src = modURL;
js.id = name;
document.head.appendChild(js);
}
const loadModJS = (modJS) => {
setTimeout(modJS, 0);
}
const promptMod = () => {
let mod = prompt("Please enter a mod name/url");
if (!mod) return;
if (!validURL(mod)) {
mod = mods[mod.toLowerCase()].url ? "../modloader/mods/v2/" + mods[mod.toLowerCase()].url : mod;
}
if (validURL(mod) || mod.startsWith("/")) {
loadModUrl(mod);
} else {
loadModJS(mod);
}
notify("Code Ran/Added", "Please wait");
}
const modloader = {
init() {
document.addEventListener("keydown", (event) => {
if (event.code === "KeyL" && !this.removed) {
if (event.shiftKey) {
promptMod();
}
}
});
this.initDomUI();
this.updateDomContainers();
this.initialised = true;
this.removed = false;
this.mainElements = [];
globalThis.ovoModLoader = this;
},
initDomUI() {
const style = document.createElement("style");
style.type = "text/css";
style.innerHTML = `
.ovo-modloader-button {
background-color: white;
border: solid;
border-color: black;
border-width: 6px;
font-family: "Retron2000";
position: absolute;
z-index: 3;
cursor: pointer;
}
.ovo-modloader-button img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.ovo-modloader-button:hover {
background-color: rgba(200, 200, 200, 1);
}
`
document.head.appendChild(style);
const toggleButton = document.createElement("button");
toggleButton.id = "ovo-modloader-toggle-button";
toggleButton.innerText = "";
const loadIcon = document.createElement("img");
loadIcon.src = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gU3ZnIFZlY3RvciBJY29ucyA6IGh0dHA6Ly93d3cub25saW5ld2ViZm9udHMuY29tL2ljb24gLS0+DQo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPG1ldGFkYXRhPiBTdmcgVmVjdG9yIEljb25zIDogaHR0cDovL3d3dy5vbmxpbmV3ZWJmb250cy5jb20vaWNvbiA8L21ldGFkYXRhPg0KPGc+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsNTExLjAwMDAwMCkgc2NhbGUoMC4xMDAwMDAsLTAuMTAwMDAwKSI+PHBhdGggZD0iTTQ4ODIuOSw0NzQ5LjRjLTEwNy4zLTMyLjYtMjE2LjQtMTQzLjYtMjQ1LjItMjUwLjljLTE1LjMtNDkuOC0yMS4xLTExMDctMjEuMS0zMzc2LjZWLTIxODJMMzUwMi0xMDY3LjNDMjg4Ny4yLTQ1Ni4zLDIzNTYuNyw2MC44LDIzMjAuMyw3OGMtMTAxLjUsNTEuNy0yODMuNSwzMi42LTM3NS40LTM2LjRjLTEzNC4xLTEwMy40LTE4NS44LTI0OS0xNDMuNi00MDkuOWMyMy04NC4zLDEzMi4yLTE5Ny4zLDE0OTItMTU2MC45Yzk4Ni40LTk4OC4zLDE0OTItMTQ4Mi40LDE1NDUuNi0xNTA3LjNjOTEuOS00NiwyMTIuNi00OS44LDMxMC4zLTkuNmM5MiwzOC4zLDI5NTUuMywyODk0LDMwMTYuNiwzMDA3YzEyMC43LDIyNy45LTI0LjksNTE3LjEtMjcyLDU0MmMtMjE2LjQsMjEuMS0xMzIuMSw5MS45LTEzNzUuMi0xMTUxLjFMNTM4Mi44LTIxODJ2MzMwMy44YzAsMjI2OS42LTUuNywzMzI2LjgtMjEuMSwzMzc2LjZjLTMwLjcsMTExLjEtMTM3LjksMjE4LjMtMjUyLjgsMjUyLjhTNDk5Niw0Nzg1LjgsNDg4Mi45LDQ3NDkuNHoiLz48cGF0aCBkPSJNNDMxLjgtMTgzNy4yYy05LjYtMy44LTQyLjEtMTEuNS03Mi44LTE3LjJjLTc4LjUtMTcuMi0xOTkuMi0xMzYtMjM1LjYtMjMzLjdjLTU5LjQtMTU3LTEuOS01OTcuNiwxMjQuNS05NDQuMmMyNzkuNi03NjQuMiw5NjUuMy0xMzM0LjksMTc5Mi43LTE0OTJjMTU3LjEtMzAuNiw0MDkuOS0zMi42LDI5NTkuMS0zMi42YzI1NTYuOSwwLDI4MDAuMSwxLjksMjk2MSwzMi42YzEwNzQuNSwyMDQuOSwxODU3LjgsMTA4MC4yLDE5MzQuNCwyMTY2LjJjMTUuMywyMDYuOC0zLjgsMjg5LjItOTMuOCwzODguOGMtMTM3LjksMTU5LTM1Ni4zLDE3OC4xLTUxNS4yLDQ0Yy05Ny43LTc4LjUtMTMwLjItMTYyLjgtMTQ5LjQtMzk4LjRjLTExLjUtMTEzLTM2LjQtMjY2LjItNTkuNC0zMzljLTEzNi00NDQuMy00NDguMi04MDIuNS04NjUuNy05OTRjLTMxNC4xLTE0NS41LTU3LjUtMTM0LjEtMzIxMS45LTEzNC4xYy0yNjg1LjIsMC0yODMwLjgsMS45LTI5NDkuNSwzNC41Yy00OTYuMSwxNDEuNy04OTQuNCw0OTQuMS0xMDc4LjMsOTU1LjdjLTY3LDE2NC43LTEwOS4yLDM0Ni43LTEwOS4yLDQ2OS4zYzAsMTgzLjgtMzQuNSwyOTEuMS0xMjIuNiwzNzkuMkM2NTIuMS0xODY0LjEsNTA4LjUtMTgxMC40LDQzMS44LTE4MzcuMnoiLz48L2c+PC9nPg0KPC9zdmc+"
loadIcon.style.width = "38px";
loadIcon.style.height = "38px";
toggleButton.appendChild(loadIcon);
toggleButton.classList.add("ovo-modloader-button");
toggleButton.style.top = "50%";
toggleButton.style.right = "0%";
toggleButton.style.transform = "translateY(-50%)";
toggleButton.style.width = "50px";
toggleButton.style.height = "50px";
toggleButton.style.zIndex = "3";
toggleButton.onclick = promptMod;
document.body.appendChild(toggleButton);
this.mainElements += [style, toggleButton];
},
updateDomContainers() {
},
getIsScriptLoaded(script) {
if (validURL(script)) {
const scripts = document.getElementsByTagName("script");
for (let i = scripts.length; i--;) {
if (scripts[i].src == script) return true;
}
return false;
}
const element = document.getElementById(script);
return (!!element && (element.tagName == "SCRIPT"));
},
getURLName(url) {
return url.substring(url.lastIndexOf("/")+1).replace(/\.[^/.]+$/, "");
},
getModURL(name) {
return mods[name.toLowerCase()];
},
loadScriptURL(url) {
return loadModUrl(url);
},
loadScript(js) {
return loadModJS(js);
}
}
alert("This is a MODDED client. Press Shift+L to load mods. (url, script, or default mods on homepage)");
setTimeout(onFinishLoad, 100);
})();
+107
View File
@@ -0,0 +1,107 @@
{
"ai": {
"name": "AI",
"author": "drakeerv",
"description": "Basic AI that plays ovo.",
"advanced": true,
"url": "ai.js"
},
"chaos": {
"name": "Chaos",
"author": "drakeerv",
"description": "CHAOS!",
"advanced": false,
"url": "chaos.js"
},
"explorer": {
"name": "Explorer",
"author": "Toadi",
"description": "Explore OvO levels.",
"advanced": true,
"url": "explorer.js"
},
"hurricane": {
"name": "Hurricane",
"author": "Toadi",
"description": "Creates a hurricane!",
"advanced": false,
"url": "hurricane.js"
},
"flymod": {
"name": "Fly",
"author": "Toadi",
"description": "Fly around the level.",
"advanced": false,
"url": "flymod.js"
},
"levelselector": {
"name": "Level Selector",
"author": "drakeerv",
"description": "Load any level in the game.",
"advanced": false,
"url": "levelselector.js"
},
"modapi": {
"name": "Mod API",
"author": "drakeerv",
"description": "A basic mod API for other mods to use.",
"advanced": true,
"url": "modapi.js"
},
"multiplayer": {
"name": "Multiplayer",
"author": "Skymen",
"description": "Play multiplayer with your friends!",
"advanced": false,
"url": "multiplayer.js"
},
"randomlevel": {
"name": "Random Level",
"author": "drakeerv",
"description": "Hop into a random level.",
"advanced": false,
"url": "randomlevel.js"
},
"savestate": {
"name": "Save State",
"author": "Skymen",
"description": "Pause the game in place.",
"advanced": true,
"url": "savestate.js"
},
"tas": {
"name": "TAS",
"author": "Skymen",
"description": "TAS tools for OvO.",
"advanced": true,
"url": "tas.js"
},
"dark": {
"name": "Dark",
"author": "drakeerv",
"description": "Dark Mode for OvO!",
"advanced": false,
"url": "dark.js"
},
"keystrokes": {
"name": "Keystrokes",
"author": "R3XFadeaway",
"description": "See you keystrokes.",
"advanced": false,
"url": "keystrokes.js"
},
"collision": {
"name": "Collision",
"author": "OvOPlant",
"description": "Enable or disable collisions",
"advanced": false,
"url": "collision.js"
},
"picture": {
"name": "Picture",
"author": "drakeerv",
"description": "Loads a image from your computer and loads it into the game",
"advanced": false,
"url": "picture.js"
}
}
+141
View File
@@ -0,0 +1,141 @@
(function () {
// Get runtime
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
// Util stuff
let clamp = (num, min, max) => Math.min(Math.max(num, min), max);
let notify = (title, text, image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
let addScript = (src, id, onload) => {
if (document.getElementById(id)) return;
let fjs = document.getElementsByTagName("script")[0];
let js = document.createElement("script");
js.id = id;
fjs.parentNode.insertBefore(js, fjs);
js.onload = onload;
js.src = src;
};
let getPlayer = () => {
return runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
)[0];
};
let getFlag = () => {
return runtime.types_by_index.find(
(x) =>
x.name === "EndFlag" ||
(x.plugin instanceof cr.plugins_.Sprite &&
x.all_frames &&
x.all_frames[0].texture_file.includes("endflag"))
).instances[0];
};
let ai = {
init() {
this.network_config = { hiddenLayers: [3, 4, 5] };
this.train_config = { log: false };
this.enabled = false;
this.threshold = 0.5;
this.prevInputs = { up: Math.random(), down: Math.random(), left: Math.random(), right: Math.random() };
this.prevDistance = Infinity;
this.failRandomness = 0.2;
this.correctRandomness = 0.1;
this.frameLimit = 500;
this.frame = 0;
runtime.tickMe(this);
globalThis.ovoAi = this;
},
resetLevel() {
c2_callFunction("Menu > Replay");
},
playInputs(inputs) {
if (inputs.up > this.threshold) c2_callFunction("Controls > Buffer", ["Jump"]);
if (inputs.down > this.threshold) c2_callFunction("Controls > Down");
if (inputs.left > this.threshold) c2_callFunction("Controls > Left In");
else if (inputs.left <= this.threshold) c2_callFunction("Controls > Left Out");
if (inputs.right > this.threshold) c2_callFunction("Controls > Right In");
else if (inputs.right <= this.threshold) c2_callFunction("Controls > Right Out");
},
start() {
this.network = new brain.NeuralNetwork(this.network_config);
this.network.train([{ input: {}, output: { up: Math.random(), down: Math.random(), left: Math.random(), right: Math.random() } }], this.train_config); // figure out what data to send, possible position, solid, flag, spike
this.enabled = true;
},
stop() {
this.prevInputs = { up: Math.random(), down: Math.random(), left: Math.random(), right: Math.random() };
this.prevDistance = Infinity;
this.frame = 0;
delete this.beginDistance;
delete this.endDistance;
delete this.network;
this.enabled = false;
},
addRandom(json) {
newJson = {}
Object.entries(json).forEach((item) => {
newJson[item[0]] = clamp(item[1] + ((Math.random() - 0.5) * this.failRandomness * 2), 0, 1);
});
return newJson;
},
tick() {
let player = getPlayer();
let flag = getFlag();
let layout = runtime.running_layout;
if (this.enabled && player && flag && this.frame < 1) {
this.beginDistance = Math.sqrt((flag.x - player.x) ** 2, (flag.y - player.y) ** 2);
}
if (this.enabled && player && flag && this.frame <= this.frameLimit) {
let pos = { playerx: player.x / layout.width, playery: player.y / layout.height, flagx: flag.x / layout.width, flagy: flag.y / layout.height };
let inputs = this.network.run({ ...this.prevInputs, ...pos });
this.playInputs(inputs);
this.prevInputs = inputs;
this.frame++;
} else if (this.enabled && player && flag && this.frame > this.frameLimit) {
let pos = { playerx: player.x / layout.width, playery: player.y / layout.height, flagx: flag.x / layout.width, flagy: flag.y / layout.height };
let endDistance = Math.sqrt((flag.x - player.x) ** 2, (flag.y - player.y) ** 2);
if (endDistance < this.beginDistance && endDistance <= this.prevDistance) {
let inputs = this.network.run({ ...this.prevInputs, ...pos });
this.network.train([{ input: { ...this.prevInputs, ...pos }, output: { ...this.addRandom(inputs) } }]);
} else if (endDistance <= this.prevDistance) {
this.prevDistance = endDistance;
}
this.resetLevel();
this.frame = 0;
}
}
};
addScript("https://unpkg.com/brain.js@latest/dist/brain-browser.min.js", "brainJs", ai.init.bind(ai));
})();
+117
View File
@@ -0,0 +1,117 @@
let chaosPresets = {
"original": {
"sin": "random",
"cos": "random",
"tan": "random"
},
"funky": {
"sin": "sinh",
"cos": "cosh",
"tan": "tanh"
},
"size": {
"sin": "fib"
}
};
(function () {
let chaos = {
init() {
this.random = Math.random;
this.abs = Math.abs;
this.acos = Math.acos;
this.acosh = Math.acosh;
this.asin = Math.asin;
this.asinh = Math.asinh;
this.atan = Math.atan;
this.atanh = Math.atanh;
this.cbrt = Math.cbrt;
this.ceil = Math.ceil;
this.clz32 = Math.clz32;
this.cos = Math.cos;
this.cosh = Math.cosh;
this.exp = Math.exp;
this.expm1 = Math.expm1;
this.floor = Math.floor;
this.fround = Math.fround;
this.log = Math.log;
this.log1p = Math.log1p;
this.log10 = Math.log10;
this.log2 = Math.log2;
this.sign = Math.sign;
this.sin = Math.sin;
this.sinh = Math.sinh;
this.sqrt = Math.sqrt;
this.tan = Math.tan;
this.tanh = Math.tanh;
this.trunc = Math.trunc;
this.atan2 = Math.atan2;
this.hypot = Math.hypot;
this.imul = Math.imul;
this.max = Math.max;
this.min = Math.min;
this.pow = Math.pow;
this.fib = (n) => { for (var r, c = 1, f = 0; n >= 0;)r = c, c += f, f = r, n--; return f };
this.carea = (a) => { return a * a * Math.PI };
this.fix = (n) => { return n < 0 ? Math.ceil(n) : n > 0 ? Math.floor(n) : n };
this.cat = (n) => { if (n <= 1) return 1; let t = 0; for (let a = 0; a < n; n++)t += this.cat(a) * this.cat(n - a - 1); return t }
this.start("original");
globalThis.ovoChaos = this;
},
start(presetName = "original") {
let preset = chaosPresets[presetName];
if (preset) {
for (const [key, value] of Object.entries(preset)) {
eval(`Math.${key} = this.${value}`)
}
}
},
stop() {
Math.random = this.random;
Math.abs = this.abs;
Math.acos = this.acos;
Math.acosh = this.acosh;
Math.asin = this.asin;
Math.asinh = this.asinh;
Math.atan = this.atan;
Math.atanh = this.atanh;
Math.cbrt = this.cbrt;
Math.ceil = this.ceil;
Math.clz32 = this.clz32;
Math.cos = this.cos;
Math.cosh = this.cosh;
Math.exp = this.exp;
Math.expm1 = this.expm1;
Math.floor = this.floor;
Math.fround = this.fround;
Math.log = this.log;
Math.log1p = this.log1p;
Math.log10 = this.log10;
Math.log2 = this.log2;
Math.sign = this.sign;
Math.sin = this.sin;
Math.sinh = this.sinh;
Math.sqrt = this.sqrt;
Math.tan = this.tan;
Math.tanh = this.tanh;
Math.trunc = this.trunc;
Math.atan2 = this.atan2;
Math.hypot = this.hypot;
Math.imul = this.imul;
Math.max = this.max;
Math.min = this.min;
Math.pow = this.pow;
}
}
chaos.init();
})();
+249
View File
@@ -0,0 +1,249 @@
(function () {
let modapi = {
init() {
this.game.init();
this.ui.init();
this.keybind.init();
globalThis.ovoModAPI = this;
this.game.notify("Mod Loaded", "ModAPI mod loaded");
},
math: {
degreeToRadian(radians) {
return (radians / 180) * Math.PI;
},
radianToDegree(degrees) {
return (180 / Math.PI) * degrees;
},
},
game: {
init() {
this.runtime = cr_getC2Runtime();
},
notify(title, text, image = "./speedrunner.png") {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
this.runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
},
getPlayer() {
return this.runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
)[0];
},
getFlag() {
return this.runtime.types_by_index.find(
(x) =>
x.name === "EndFlag" ||
(x.plugin instanceof cr.plugins_.Sprite &&
x.all_frames &&
x.all_frames[0].texture_file.includes("endflag"))
).instances[0];
},
getCoin() {
return this.runtime.types_by_index.find(
(x) =>
x.name === "Coin" ||
(x.plugin instanceof cr.plugins_.Sprite &&
x.all_frames &&
x.all_frames[0].texture_file.includes("coin"))
).instances[0];
},
getLayout(layoutName) {
return this.runtime.layouts[layoutName] || this.runtime.running_layout;
},
setLayout(layout) {
this.runtime.changelayout = layout;
},
getLayer(layerName = "Layer 0") {
return this.runtime.running_layout.layers.find(x => x.name === layerName)
},
layerScale(layer, scale) {
layer.scale = scale;
return layer;
},
//platformScale(platform, scale) {
// platform.behavior.my_types[0].all_frames.forEach((frame) => {
// frame.height /= scale;
// frame.width /= scale;
// });
//
// platform.inst.height *= scale;
// platform.inst.width *= scale;
//
// return platform;
//},
moveInstance(instance, x, y) {
instance.x = x;
instance.y = y;
instance.set_bbox_changed();
return instance;
},
rotateInstance(instance, angle) {
instance.angle = (angle / 180) * Math.PI;
instance.set_bbox_changed();
return instance;
},
resizeInstance(instance, width, height) {
instance.width = width;
instance.height = height;
instance.set_bbox_changed();
return instance;
},
instanceOpacity(instance, opacity) {
instance.opacity = opacity;
return instance;
},
destroyInstance(instance) {
this.runtime.DestroyInstance(instance);
},
createSolid(x, y, width, height, angle, layerName = "Layer 0") {
let solidType = this.runtime.types_by_index.find(x => x.plugin instanceof cr.plugins_.TiledBg && x.texture_file && x.texture_file.includes("/solid.png") && x.behs_count === 2)
let layer = this.runtime.running_layout.layers.find(x => x.name === layerName)
let solid = this.runtime.createInstance(solidType, layer, x, y)
solid.width = width || solid.width;
solid.height = height || solid.height;;
solid.angle = angle || solid.angle;
solid.set_bbox_changed();
return solid;
},
createSprite(x, y, width, height, angle, spriteName = "", layerName = "Layer 0") {
let spriteType = this.runtime.types_by_index.find(x => x.plugin instanceof cr.plugins_.Sprite && x.all_frames && x.all_frames[0].texture_file.includes(spriteName))
let layer = this.runtime.running_layout.layers.find(x => x.name === layerName)
let sprite = this.runtime.createInstance(spriteType, layer, x, y)
sprite.width = width || sprite.width;
sprite.height = height || sprite.height;;
sprite.angle = angle || sprite.angle;
sprite.set_bbox_changed();
return sprite;
},
isInLevel() {
return this.runtime.running_layout.name.startsWith("Level")
},
isPaused() {
if (this.isInLevel()) return this.runtime.running_layout.layers.find(function (a) {
return "Pause" === a.name
}).visible
},
},
ui: {
init() {
}
},
keybind: {
init() {
//this.events = [[name, callback, code, {"alt": event.altKey, "ctrl": event.ctrlKey, "meta": event.metaKey, "shift": event.shiftKey}]]
this.events = [];
//this.events.push({name: "Test", callback: () => {console.log("YEY");}, key: "G", modifiers: {shift: true}})
document.addEventListener("keydown", (event) => {
this.events.forEach((keybind) => {
if (event.key.toLowerCase() == keybind.key.toLowerCase() && !event.isComposing && (!!keybind.modifiers.alt == event.altKey && !!keybind.modifiers.ctrl == event.ctrlKey && !!keybind.modifiers.meta == event.metaKey && !!keybind.modifiers.shift == event.shiftKey)) {
console.log("here1")
keybind.callback(event);
}
});
});
}
}
};
modapi.init();
})();
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
let notify = (title, text, image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
let collision = {
init() {
document.addEventListener("keydown", (event) => {
if (event.code === "KeyQ") {
if (event.shiftKey) {
this.loadCollisionOn();
}
}
if (event.code === "KeyW") {
if (event.shiftKey) {
this.loadCollisionOff();
}
}
});
this.interval = null;
globalThis.ovoCollision = this;
notify("Mod loaded", "Collision mod loaded");
},
loadCollisionOn() {
ovoModAPI.game.getPlayer().angle = 0 * (Math.PI / 180);
},
loadCollisionOff() {
ovoModAPI.game.getPlayer().angle = undefined;
},
startCycle(time) {
if (!this.interval) {
this.interval = setInterval(this.loadCollisionOn, time); (this.loadCollisionOff, time + time);
}
},
stopCycle() {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
}
}
collision.init();
})()
+18
View File
@@ -0,0 +1,18 @@
(function () {
// create a div element
const div = document.createElement("div");
// set styles for the div element
div.style.width = "100%";
div.style.height = "100%";
div.style.position = "fixed";
div.style.top = "0";
div.style.left = "0";
div.style.mixBlendMode = "difference";
div.style.backgroundColor = "white";
div.style.zIndex = "99999"; // set an extremely high z-index value
div.style.pointerEvents = "none"; // make the div pass events to the items below it
// add the div element to the body of the webpage
document.body.appendChild(div);
})();
+229
View File
@@ -0,0 +1,229 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
targetY = null;
let prevY = 0;
let prevSm = 0;
let prevS = 0;
let xprevY = 0;
let xprevSm = 0;
let xprevS = 0;
let sex = new Date();
let sexs = sex.getMilliseconds();
let timescale = 1;
let rounder = 5;
let showPosition = {
tick() {
if (
!cr_getC2Runtime().running_layout.layers.find(function (a) {
return "Pause" === a.name;
}).visible
) {
cr_getC2Runtime().timescale = timescale;
try {
if (timescale > 0) {
deeznuts();
}
} catch (err) { }
}
},
};
function deeznuts() {
let baka = timescale == 0 ? 1 : timescale
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
let sex = new Date();
let sexsincelast = sex.getMilliseconds() - sexs;
document.getElementById("69").innerText =
"Timescale: " +
timescale +
"\n" +
"\n" +
"x: " +
balls(player.x, rounder).toString() +
"\n" +
"y: " +
balls(player.y, rounder).toString() +
"\n" +
"\n" +
"Vertical Speed: " +
"\n" +
"p/ms: " +
balls((player.y - prevY) / sexsincelast / baka, rounder).toString() +
"\n" +
"A/ms: " +
balls(
prevS - (player.y - prevY) / sexsincelast / baka,
rounder
).toString() +
"\n" +
"p/f: " +
balls((player.y - prevY) / baka, rounder).toString() +
"\n" +
"A/f: " +
balls(prevSm - (player.y - prevY) / baka, rounder).toString() +
"\n" +
"\n" +
//Owa oaw------------------------------------------
"Horizontal Speed: " +
"\n" +
"p/ms: " +
balls(
(player.x - xprevY) / sexsincelast / baka,
rounder
).toString() +
"\n" +
"A/ms: " +
balls(
xprevS - (player.x - xprevY) / sexsincelast / baka,
rounder
).toString() +
"\n" +
"p/f: " +
balls((player.x - xprevY) / baka, rounder).toString() +
"\n" +
"A/f: " +
balls(xprevSm - (player.x - xprevY) / baka, rounder).toString() +
"\n" +
"\n" +
"PPms: " +
balls(player.x + xprevS, rounder).toString() +
", " +
balls(player.y + prevS, rounder).toString() +
"\n" +
"PPf: " +
balls(player.x + xprevSm, rounder).toString() +
", " +
balls(player.y + prevSm, rounder).toString() +
"\n" +
"\n" +
"ms/f:" +
sexsincelast.toString() +
"\n" +
"A ms/f:" +
sexsincelast.toString() * baka;
prevSm = (player.y - prevY) / baka;
prevS = (player.y - prevY) / sexsincelast / baka;
prevY = player.y;
xprevS = (player.x - xprevY) / sexsincelast / baka;
xprevSm = (player.x - xprevY) / baka;
xprevY = player.x;
sexs = sex.getMilliseconds();
}
function balls(b, af) {
return Math.round(b * 10 ** af) / 10 ** af;
}
let fly = {
tick() {
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
try {
player.y = targetY;
} catch (err) { }
},
};
var b = document.createElement("div"),
c = {
backgroundColor: "rgba(150,10,1,0.7)",
width: "500px",
height: "600px",
position: "absolute",
top: "100px",
left: "100px",
fontSize: "x-large",
};
Object.keys(c).forEach(function (a) {
b.style[a] = c[a];
});
b.id = 69;
const newContent = document.createTextNode("poggers");
// add the text node to the newly created div
b.appendChild(newContent);
document.body.appendChild(b);
g = globalThis.ovoExplorer = {
init: function () {
runtime.tickMe(showPosition);
},
trackOvO: function (a) {
a ? runtime.tickMe(showPosition) : runtime.untickMe(showPosition);
},
step: function () {
var a = cr_getC2Runtime().timescale;
cr_getC2Runtime().timescale = 1;
cr_getC2Runtime().tick(!0, null, null);
cr_getC2Runtime().timescale = a;
deeznuts();
},
suspend: function () {
cr_getC2Runtime().timescale = 0;
timescale = 0;
},
updateTimescale: function (a) {
timescale = a;
cr_getC2Runtime().timescale = a;
},
setRoundDigits: function (a) {
rounder = a;
},
warp: function (x, y) {
targetY = y;
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
player.x = x;
player.y = y;
},
levitate: function (a) {
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
targetY = player.y;
a ? runtime.tickMe(fly) : runtime.untickMe(fly);
},
};
g.init();
})();
+138
View File
@@ -0,0 +1,138 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
let getPlayer = () => {
return runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
)[0];
}
let notify = (title, text, image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
let flyMod = {
init() {
this.movementKeys = [false, false, false, false];
this.activatorKeyHeld = false;
this.activated = false;
this.speed = { x: 10, y: 10 };
this.stored = [1500, true];
this.override = false;
document.addEventListener("keydown", (event) => { this.keyDown(event) });
document.addEventListener("keyup", (event) => { this.keyUp(event) });
runtime.tickMe(this);
globalThis.ovoFlyMod = this;
},
keyDown(event) {
let key = event.key.toLowerCase();
if (key == "control" && !this.override) {
this.activatorKeyHeld = true;
} else if (event.keyCode >= 37 && event.keyCode <= 40 && this.activatorKeyHeld) {
if (!this.activated) {
this.startActivation();
this.activated = true;
}
this.movementKeys[event.keyCode - 37] = true;
}
},
keyUp(event) {
let key = event.key.toLowerCase();
if (key == "control" && this.activatorKeyHeld) {
this.activatorKeyHeld = false;
if (this.activated) {
this.movementKeys = [false, false, false, false];
this.activated = false;
this.endActivation();
}
} else if (event.keyCode >= 37 && event.keyCode <= 40 && this.activatorKeyHeld) {
this.movementKeys[event.keyCode - 37] = false;
}
},
startActivation() {
let player = getPlayer();
if (player) {
this.stored = [player.behavior_insts[0].g, player.collisionsEnabled];
} else {
this.stored = [1500, true];
}
notify("Fly Mod", "Fly Enabled");
},
endActivation() {
let player = getPlayer();
if (player) {
player.behavior_insts[0].g = this.stored[0];
player.collisionsEnabled = this.stored[1];
}
notify("Fly Mod", "Fly Disabled");
},
speedX(speed) {
this.speed.x = speed;
},
speedY(speed) {
this.speed.y = speed;
},
setSpeed(speed) {
this.speed.x = speed;
this.speed.y = speed;
},
setOverride(value) {
this.override = !!value;
},
tick() {
if (this.activated) {
let player = getPlayer();
if (player) {
if (!!player.behavior_insts[0].g || player.collisionsEnabled) {
player.behavior_insts[0].g = 0;
player.collisionsEnabled = false
}
let moveX = this.movementKeys[2] - this.movementKeys[0];
let moveY = this.movementKeys[3] - this.movementKeys[1];
player.x += moveX * this.speed.x;
player.y += moveY * this.speed.y;
}
}
}
};
flyMod.init();
})();
+38
View File
@@ -0,0 +1,38 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
let notify = (title, text, image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
let gyro = {
init() {
globalThis.ovoGyro = this;
if (window.DeviceOrientationEvent) {
window.addEventListener("deviceorientation", (event) => { this.updateGyro });
} else {
notify("Gyro", "This device does not support gyroscope");
}
},
updateGyro(event) {
let absolute = event.absolute;
let alpha = event.alpha;
let beta = event.beta;
let gamma = event.gamma;
}
};
gyro.init();
})();
+46
View File
@@ -0,0 +1,46 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
let counter = 0;
let wind = {
tick() {
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
try {
counter++;
switch (Math.floor((counter % 2000) / 500)) {
case 0:
player.x = player.x - 1;
break;
case 1:
player.x = player.x + 1;
break;
case 2:
player.y = player.y - 3;
break;
case 3:
player.y = player.y + 3;
break;
}
} catch (err) { }
},
};
g = globalThis.ovoExplorer = {
init: function () {
runtime.tickMe(wind);
},
};
g.init();
})();
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+198
View File
@@ -0,0 +1,198 @@
// new features: onLevelLoad
// onbboxchanged
// speed
// Math.round(Math.sqrt(Math.pow(player.behavior_insts[0].dx, 2) + Math.pow(player.behavior_insts[0].dy, 2)));
(function () {
const modapi = {
init() {
this.game.init();
this.ui.init();
this.keybind.init();
globalThis.ovoModAPI = this;
},
game: {
init() {
this.runtime = cr_getC2Runtime();
},
tick(functionToTick) {
const tick = {
tick() {
functionToTick();
}
}
this.runtime.tickMe(tick);
},
getGameValues() {
return {
tickCount: this.runtime.tickcount,
fps: this.runtime.fps,
frameCount: this.runtime.framecount,
deltaTime: this.runtime.dt,
width: this.runtime.width,
height: this.runtime.height,
};
},
notify(title, text, image = "./speedrunner.png") {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
this.runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
},
getPlayer() {
return this.runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
)[0];
},
getFlag() {
return this.runtime.types_by_index.find(
(x) =>
x.name === "EndFlag" ||
(x.plugin instanceof cr.plugins_.Sprite &&
x.all_frames &&
x.all_frames[0].texture_file.includes("endflag"))
).instances[0];
},
getCoin() {
return this.runtime.types_by_index.find(
(x) =>
x.name === "Coin" ||
(x.plugin instanceof cr.plugins_.Sprite &&
x.all_frames &&
x.all_frames[0].texture_file.includes("coin"))
).instances[0];
},
getLayout(layoutName) {
return this.runtime.layouts[layoutName] || this.runtime.running_layout;
},
setLayout(layout) {
this.runtime.changelayout = layout;
},
getLayer(layerName = "Layer 0") {
return this.runtime.running_layout.layers.find(x => x.name === layerName)
},
layerScale(layer, scale) {
layer.scale = scale;
return layer;
},
//platformScale(platform, scale) {
// platform.behavior.my_types[0].all_frames.forEach((frame) => {
// frame.height /= scale;
// frame.width /= scale;
// });
//
// platform.inst.height *= scale;
// platform.inst.width *= scale;
//
// return platform;
//},
moveInstance(instance, x, y) {
instance.x = x;
instance.y = y;
instance.set_bbox_changed();
return instance;
},
rotateInstance(instance, angle) {
instance.angle = modapi.math.degreeToRadian(angle);
instance.set_bbox_changed();
return instance;
},
resizeInstance(instance, width, height) {
instance.width = width;
instance.height = height;
instance.set_bbox_changed();
return instance;
},
instanceOpacity(instance, opacity) {
instance.opacity = opacity;
return instance;
},
destroyInstance(instance) {
this.runtime.DestroyInstance(instance);
},
createSolid(x, y, width, height, angle, layer) {
const solidType = this.runtime.types_by_index.find(x => x.plugin instanceof cr.plugins_.TiledBg && x.texture_file && x.texture_file.includes("/solid.png") && x.behs_count === 2);
const solid = this.runtime.createInstance(solidType, layer, x, y);
solid.width = width || solid.width;
solid.height = height || solid.height;;
solid.angle = angle || solid.angle;
solid.set_bbox_changed();
return solid;
},
createSprite(x, y, width, height, angle, spriteName, layer) {
const spriteType = this.runtime.types_by_index.find(x => x.plugin instanceof cr.plugins_.Sprite && x.all_frames && x.all_frames[0].texture_file.includes(spriteName));
const sprite = this.runtime.createInstance(spriteType, layer, x, y);
sprite.width = width || sprite.width;
sprite.height = height || sprite.height;;
sprite.angle = angle || sprite.angle;
sprite.set_bbox_changed();
return sprite;
},
isInLevel() {
return this.runtime.running_layout.name.startsWith("Level")
},
isPaused() {
if (this.isInLevel()) return this.runtime.running_layout.layers.find(function (a) {
return "Pause" === a.name
}).visible
},
},
ui: {
init() {
}
},
keybind: {
init() {
this.events = [];
document.addEventListener("keydown", (event) => {
this.events.forEach((keybind) => {
if (event.key.toLowerCase() == keybind.key.toLowerCase() && !event.isComposing && (!!keybind.modifiers.alt == event.altKey && !!keybind.modifiers.ctrl == event.ctrlKey && !!keybind.modifiers.meta == event.metaKey && !!keybind.modifiers.shift == event.shiftKey)) {
keybind.callback(event);
}
});
});
}
}
};
modapi.init();
})();
File diff suppressed because it is too large Load Diff
+80
View File
@@ -0,0 +1,80 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
let stored = [1500, true];
let go = false;
let held = [false, false, false, false];
let xSpeed = 10;
let ySpeed = 10;
let getPlayer = () => {
return runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
)[0];
}
let ba = {
tick() {
try {
let player = getPlayer();
if (go) {
moveX = held[2] - held[0];
moveY = held[3] - held[1];
player.behavior_insts[0].dx = 0;
player.behavior_insts[0].dy = 0;
player.behavior_insts[0].g = 0;
player.collisionsEnabled = false
player.x += moveX * xSpeed;
player.y += moveY * ySpeed;
} else { }
} catch (err) { }
},
};
g = globalThis.FlyMod = {
xSpeed: function (a) {
xSpeed = a;
},
ySpeed: function (a) {
ySpeed = a;
},
}
runtime.tickMe(ba);
document.addEventListener("keydown", function (event) {
if (event.keyCode >= 37 && event.keyCode <= 40) {
held[event.keyCode - 37] = true;
}
if (event.keyCode == 16) {
let player = getPlayer();
if (player) {
stored = [player.behavior_insts[0].g, player.collisionsEnabled];
}
go = true;
}
});
document.addEventListener("keyup", function (event) {
if (event.keyCode >= 37 && event.keyCode <= 40) {
held[event.keyCode - 37] = false;
}
if (event.keyCode == 16) {
go = false;
let player = getPlayer();
if (player) {
player.behavior_insts[0].g = stored[0];
player.collisionsEnabled = stored[1];
}
}
});
})();
+77
View File
@@ -0,0 +1,77 @@
(function () {
const imagePosition = {
x: 0,
y: 0
};
const pixelSize = 5;
const collisionsEnabled = false;
const advancedCollisions = true;
const enableAlpha = true;
const layerName = "Layer 0";
const inputElement = document.createElement("input");
inputElement.type = "file";
inputElement.accept = "image/webp, image/png, image/jpeg";
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
const fileList = this.files;
if (fileList.length > 0) {
const file = fileList[0];
const reader = new FileReader();
reader.onload = function (e) {
const img = new Image();
img.onload = function () {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
const newWidth = 120;
const newHeight = Math.round(img.height * 120 / img.width);
ctx.drawImage(img, 0, 0, newWidth, newHeight);
const data = ctx.getImageData(0, 0, newWidth, newHeight).data;
const layer = ovoModAPI.game.getLayer(layerName);
if (!enableAlpha) {
const solidType = cr_getC2Runtime().types_by_index.find(x => x.texture_file && x.texture_file.includes("solid2"));
const whiteSolid = cr_getC2Runtime().createInstance(solidType, layer, 0, 0);
ovoModAPI.game.resizeInstance(whiteSolid, newWidth * pixelSize + imagePosition.x, newHeight * pixelSize + imagePosition.y);
whiteSolid.collisionsEnabled = collisionsEnabled;
}
for (let x = 0; x < newWidth; x++) {
for (let y = 0; y < newHeight; y++) {
const position = (y * newWidth + x) * 4;
const average = (data[position] + data[position + 1] + data[position + 2]) / 3;
if (average === 255) continue;
const inst = ovoModAPI.game.createSolid(x * pixelSize + imagePosition.x, y * pixelSize + imagePosition.y, pixelSize, pixelSize, 0, layer);
if (advancedCollisions && average < 127) {
inst.collisionsEnabled = true;
} else {
inst.collisionsEnabled = false;
}
inst.opacity = 1 - average / 255;
}
}
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
}
}
const button = document.createElement("button");
button.innerText = "Open file picker";
button.style.position = "absolute";
button.style.top = "0";
button.style.left = "0";
button.style.zIndex = "99999";
button.addEventListener("click", () => inputElement.click());
document.body.appendChild(button);
})();
+25
View File
@@ -0,0 +1,25 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
let notify = (title, text, image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
let randomKeys = {
init() {
this.availibleKeys = []
globalThis.ovoRandomKeys = this;
}
}
})();
@@ -0,0 +1,51 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
let notify = (title, text, image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
let randomlevel = {
init() {
document.addEventListener("keydown", (event) => {
if (event.code === "KeyY") {
if (event.shiftKey) {
this.loadRandomLevel();
}
}
});
this.interval = null;
globalThis.ovoRandomLevel = this;
},
loadRandomLevel() {
runtime.changelayout = runtime.layouts[Object.keys(runtime.layouts)[Math.floor(Math.random() * Object.keys(runtime.layouts).length)]]
},
startCycle(time) {
if (!this.interval) {
this.interval = setInterval(this.loadRandomLevel, time);
}
},
stopCycle() {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
}
}
}
randomlevel.init();
})()
+252
View File
@@ -0,0 +1,252 @@
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
targetY = null;
let showPosition = {
tick() {
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
try {
document.getElementById("pos").innerHTML =
Math.round(player.x.toString()) +
", " +
Math.round(player.y.toString());
} catch (err) { }
},
};
let fly = {
tick() {
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
try {
player.y = targetY;
} catch (err) { }
},
};
var b = document.createElement("div"),
c = {
backgroundColor: "white",
border: "solid",
borderColor: "black",
borderWidth: "6px",
fontFamily: "Retron2000",
position: "absolute",
top: "115px",
left: "86px",
padding: "10px",
color: "black",
fontSize: "20pt",
};
Object.keys(c).forEach(function (a) {
b.style[a] = c[a];
});
b.id = "pos";
const newContent = document.createTextNode("N/A");
// add the text node to the newly created div
b.appendChild(newContent);
document.body.appendChild(b);
g = globalThis.ovoExplorer = {
init: function () {
runtime.tickMe(showPosition);
},
trackOvO: function (a) {
a ? runtime.tickMe(showPosition) : runtime.untickMe(showPosition);
},
warp: function (x, y) {
targetY = y;
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
player.x = x;
player.y = y;
},
levitate: function (a) {
let playerInstances = runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
);
let player = playerInstances[0];
targetY = player.y;
a ? runtime.tickMe(fly) : runtime.untickMe(fly);
},
};
g.init();
})();
(function () {
let old = globalThis.sdk_runtime;
c2_callFunction("execCode", ["globalThis.sdk_runtime = this.runtime"]);
let runtime = globalThis.sdk_runtime;
globalThis.sdk_runtime = old;
//Get all valid players on the layout
// Ghosts don't count as valid players, and replays don't count either
let notify = (text, title = "Save state", image = "./speedrunner.png") => {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
};
notify(
"Save state with Shift+S, load with S, reset with R, reset sate with Shift+R, skip level with Shift+N, go back in level with Shift+B",
"Mod loaded"
);
let getPlayer = () =>
runtime.types_by_index
.filter(
(x) =>
!!x.animations &&
x.animations[0].frames[0].texture_file.includes("collider")
)[0]
.instances.filter(
(x) => x.instance_vars[17] === "" && x.behavior_insts[0].enabled
)[0];
let getFlag = () =>
runtime.types_by_index.find(
(x) =>
x.name === "EndFlag" ||
(x.plugin instanceof cr.plugins_.Sprite &&
x.all_frames &&
x.all_frames[0].texture_file.includes("endflag"))
).instances[0];
let getCoin = () =>
runtime.types_by_index.find(
(x) =>
x.name === "Coin" ||
(x.plugin instanceof cr.plugins_.Sprite &&
x.all_frames &&
x.all_frames[0].texture_file.includes("coin"))
).instances[0];
let curState = null;
let curLayout = null;
let saveState = () => {
notify("Saved player state", "State Saved");
let state = runtime.saveInstanceToJSON(getPlayer(), true);
return state;
};
let loadState = (state) => {
let player = getPlayer();
player.y -= 10;
player.set_bbox_changed();
player.behavior_insts[0].lastFloorObject = null;
notify("Loaded player state", "State Loaded");
runtime.loadInstanceFromJSON(player, state, true);
};
document.addEventListener("keydown", (event) => {
if (!getFlag()) {
return;
}
if (event.code === "KeyS") {
if (event.shiftKey) {
curState = saveState();
} else if (curState != null) {
loadState(curState);
}
}
if (event.code === "KeyR" && event.shiftKey) {
curState = null;
runtime.changeLayout = runtime.runningLayout;
notify("State reset by soft level reset (Shift + R)", "State Reset");
}
if (event.code === "KeyN") {
if (event.shiftKey) {
runtime.changelayout = runtime.layouts["Level " + String(parseInt(runtime.running_layout.name.split(' ')[1]) + 1)]
setTimeout(() => {
notify("Going to next level bypass (Shift + N)", "Next Level");
}, 300);
}
}
if (event.code === "KeyM") {
if (event.shiftKey) {
let player = getPlayer();
let flag = getFlag();
player.x = flag.x;
player.y = flag.y;
player.set_bbox_changed();
setTimeout(() => {
notify("Going to next level (Shift + M)", "Next Level");
}, 300);
}
}
if (event.code === "KeyB") {
if (event.shiftKey) {
runtime.changelayout = runtime.layouts["Level " + String(parseInt(runtime.running_layout.name.split(' ')[1]) - 1)]
setTimeout(() => {
notify("Going to next level (Shift + N)", "Next Level");
}, 300);
}
}
if (event.code === "KeyC") {
if (event.shiftKey) {
let player = getPlayer();
let flag = getCoin();
player.x = flag.x;
player.y = flag.y;
player.set_bbox_changed();
setTimeout(() => {
notify("Going to coin (Shift + C)", "Coin");
}, 300);
}
}
});
Object.values(runtime.layouts).forEach((layout) => {
let oldFn = layout.startRunning.bind(layout);
layout.startRunning = () => {
oldFn();
if (!getFlag()) {
curLayout = layout.name;
curState = null;
} else {
if (curState && curLayout === layout.name) loadState(curState);
else curState = null;
curLayout = layout.name;
}
};
});
})();
+106
View File
@@ -0,0 +1,106 @@
var $jscomp = $jscomp || {};
$jscomp.scope = {};
$jscomp.arrayIteratorImpl = function (a) {
var b = 0;
return function () {
return b < a.length ? {
done: !1,
value: a[b++]
} : {
done: !0
}
}
};
$jscomp.arrayIterator = function (a) {
return {
next: $jscomp.arrayIteratorImpl(a)
}
};
$jscomp.makeIterator = function (a) {
var b = "undefined" != typeof Symbol && Symbol.iterator && a[Symbol.iterator];
return b ? b.call(a) : $jscomp.arrayIterator(a)
};
$jscomp.arrayFromIterator = function (a) {
for (var b, c = []; !(b = a.next()).done;) c.push(b.value);
return c
};
$jscomp.arrayFromIterable = function (a) {
return a instanceof Array ? a : $jscomp.arrayFromIterator($jscomp.makeIterator(a))
};
$jscomp.ASSUME_ES5 = !1;
$jscomp.ASSUME_NO_NATIVE_MAP = !1;
$jscomp.ASSUME_NO_NATIVE_SET = !1;
$jscomp.defineProperty = $jscomp.ASSUME_ES5 || "function" == typeof Object.defineProperties ? Object.defineProperty : function (a, b, c) {
a != Array.prototype && a != Object.prototype && (a[b] = c.value)
};
$jscomp.getGlobal = function (a) {
return "undefined" != typeof window && window === a ? a : "undefined" != typeof global && null != global ? global : a
};
$jscomp.global = $jscomp.getGlobal(this);
$jscomp.SYMBOL_PREFIX = "jscomp_symbol_";
$jscomp.initSymbol = function () {
$jscomp.initSymbol = function () { };
$jscomp.global.Symbol || ($jscomp.global.Symbol = $jscomp.Symbol)
};
$jscomp.Symbol = function () {
var a = 0;
return function (b) {
return $jscomp.SYMBOL_PREFIX + (b || "") + a++
}
}();
$jscomp.initSymbolIterator = function () {
$jscomp.initSymbol();
var a = $jscomp.global.Symbol.iterator;
a || (a = $jscomp.global.Symbol.iterator = $jscomp.global.Symbol("iterator"));
"function" != typeof Array.prototype[a] && $jscomp.defineProperty(Array.prototype, a, {
configurable: !0,
writable: !0,
value: function () {
return $jscomp.iteratorPrototype($jscomp.arrayIteratorImpl(this))
}
});
$jscomp.initSymbolIterator = function () { }
};
$jscomp.initSymbolAsyncIterator = function () {
$jscomp.initSymbol();
var a = $jscomp.global.Symbol.asyncIterator;
a || (a = $jscomp.global.Symbol.asyncIterator = $jscomp.global.Symbol("asyncIterator"));
$jscomp.initSymbolAsyncIterator = function () { }
};
$jscomp.iteratorPrototype = function (a) {
$jscomp.initSymbolIterator();
a = {
next: a
};
a[$jscomp.global.Symbol.iterator] = function () {
return this
};
return a
};
$jscomp.iteratorFromArray = function (a, b) {
$jscomp.initSymbolIterator();
a instanceof String && (a += "");
var c = 0,
e = {
next: function () {
if (c < a.length) {
var d = c++;
return {
value: b(d, a[d]),
done: !1
}
}
e.next = function () {
return {
done: !0,
value: void 0
}
};
return e.next()
}
};
e[Symbol.iterator] = function () {
return e
};
return e
};
+30
View File
@@ -0,0 +1,30 @@
{
"chaos": {
"name": "Chaos",
"author": "drakeerv",
"description": "CHAOS!",
"advanced": false,
"url": "chaos.js"
},
"levelselector": {
"name": "Level Selector",
"author": "drakeerv",
"description": "Load any level in the game.",
"advanced": false,
"url": "levelselector.js"
},
"dark": {
"name": "Dark",
"author": "drakeerv",
"description": "Dark Mode for OvO!",
"advanced": false,
"url": "dark.js"
},
"resetbutton": {
"name": "Reset Button",
"author": "drakeerv",
"description": "Allows you to reset using R.",
"advanced": false,
"url": "resetbutton.js"
}
}
+118
View File
@@ -0,0 +1,118 @@
let chaosPresets = {
"original": {
"sin": "random",
"cos": "random",
"tan": "random"
},
"funky": {
"sin": "sinh",
"cos": "cosh",
"tan": "tanh"
},
"size": {
"sin": "fib"
}
};
(function() {
let chaos = {
init() {
this.random = Math.random;
this.abs = Math.abs;
this.acos = Math.acos;
this.acosh = Math.acosh;
this.asin = Math.asin;
this.asinh = Math.asinh;
this.atan = Math.atan;
this.atanh = Math.atanh;
this.cbrt = Math.cbrt;
this.ceil = Math.ceil;
this.clz32 = Math.clz32;
this.cos = Math.cos;
this.cosh = Math.cosh;
this.exp = Math.exp;
this.expm1 = Math.expm1;
this.floor = Math.floor;
this.fround = Math.fround;
this.log = Math.log;
this.log1p = Math.log1p;
this.log10 = Math.log10;
this.log2 = Math.log2;
this.sign = Math.sign;
this.sin = Math.sin;
this.sinh = Math.sinh;
this.sqrt = Math.sqrt;
this.tan = Math.tan;
this.tanh = Math.tanh;
this.trunc = Math.trunc;
this.atan2 = Math.atan2;
this.hypot = Math.hypot;
this.imul = Math.imul;
this.max = Math.max;
this.min = Math.min;
this.pow = Math.pow;
this.fib = (n) => {for(var r,c=1,f=0;n>=0;)r=c,c+=f,f=r,n--;return f};
this.carea = (a) => {return a*a*Math.PI};
this.fix = (n) => {return n<0?Math.ceil(n):n>0?Math.floor(n):n};
this.cat = (n) => {if(n<=1)return 1;let t=0;for(let a=0;a<n;n++)t+=this.cat(a)*this.cat(n-a-1);return t}
this.erandom = (n) => {(Math.random()-0.5)*2}
this.start("original");
globalThis.ovoChaos = this;
},
start(presetName="original") {
let preset = chaosPresets[presetName];
if (preset) {
for (const [key, value] of Object.entries(preset)) {
eval(`Math.${key} = this.${value}`)
}
}
},
stop() {
Math.random = this.random;
Math.abs = this.abs;
Math.acos = this.acos;
Math.acosh = this.acosh;
Math.asin = this.asin;
Math.asinh = this.asinh;
Math.atan = this.atan;
Math.atanh = this.atanh;
Math.cbrt = this.cbrt;
Math.ceil = this.ceil;
Math.clz32 = this.clz32;
Math.cos = this.cos;
Math.cosh = this.cosh;
Math.exp = this.exp;
Math.expm1 = this.expm1;
Math.floor = this.floor;
Math.fround = this.fround;
Math.log = this.log;
Math.log1p = this.log1p;
Math.log10 = this.log10;
Math.log2 = this.log2;
Math.sign = this.sign;
Math.sin = this.sin;
Math.sinh = this.sinh;
Math.sqrt = this.sqrt;
Math.tan = this.tan;
Math.tanh = this.tanh;
Math.trunc = this.trunc;
Math.atan2 = this.atan2;
Math.hypot = this.hypot;
Math.imul = this.imul;
Math.max = this.max;
Math.min = this.min;
Math.pow = this.pow;
}
}
chaos.init();
})();
+18
View File
@@ -0,0 +1,18 @@
(function() {
// create a div element
const div = document.createElement("div");
// set styles for the div element
div.style.width = "100%";
div.style.height = "100%";
div.style.position = "fixed";
div.style.top = "0";
div.style.left = "0";
div.style.mixBlendMode = "difference";
div.style.backgroundColor = "white";
div.style.zIndex = "99999"; // set an extremely high z-index value
div.style.pointerEvents = "none"; // make the div pass events to the items below it
// add the div element to the body of the webpage
document.body.appendChild(div);
})();
File diff suppressed because one or more lines are too long
@@ -0,0 +1,20 @@
(function() {
let runtime = c3_runtimeInterface._GetLocalRuntime();
let notify = () => {};
let resetButton = {
init() {
document.addEventListener("keydown", (event) => {this.keydown(event)})
globalThis.ovoResetButton = this;
notify("Mod Loaded", "Reset Button Mod Loaded");
},
keydown(event) {
if (event.key.toLowerCase() == "r") {
runtime._layoutManager._pendingChangeLayout = runtime._layoutManager._mainRunningLayout;
}
}
};
resetButton.init();
})();
+203
View File
@@ -0,0 +1,203 @@
(function () {
const modDirectory = "/mods/";
const versionFolder = "v1";
class ModLoader {
constructor(runtime) {
window.ovoModLoader = this;
this.runtime = runtime;
this.initialised = false;
this.mods = {};
this.loadedMods = [];
this.#init();
}
async #init() {
this.mods = await fetch(this.getModDirectory() + "v1.json").then(res => res.json());
this.loadModURL("modapi.js", true, false);
window.addEventListener("keydown", (event) => {
if (event.code === "KeyL") {
if (event.shiftKey) {
this.promptMod();
}
}
});
this.#initDomUI();
this.initialised = true;
}
async #initDomUI() {
const style = document.createElement("style");
style.innerHTML = `
.ovo-modloader-button {
background-color: white;
border: solid;
border-color: black;
border-width: 6px;
font-family: "Retron2000";
position: absolute;
z-index: 999999;
cursor: pointer;
}
.ovo-modloader-button:hover {
background-color: rgba(200, 200, 200, 1);
}
`
document.head.appendChild(style);
const toggleButton = document.createElement("button");
toggleButton.id = "ovo-modloader-toggle-button";
toggleButton.innerText = "";
const loadIcon = document.createElement("img");
loadIcon.src = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gU3ZnIFZlY3RvciBJY29ucyA6IGh0dHA6Ly93d3cub25saW5ld2ViZm9udHMuY29tL2ljb24gLS0+DQo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPG1ldGFkYXRhPiBTdmcgVmVjdG9yIEljb25zIDogaHR0cDovL3d3dy5vbmxpbmV3ZWJmb250cy5jb20vaWNvbiA8L21ldGFkYXRhPg0KPGc+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsNTExLjAwMDAwMCkgc2NhbGUoMC4xMDAwMDAsLTAuMTAwMDAwKSI+PHBhdGggZD0iTTQ4ODIuOSw0NzQ5LjRjLTEwNy4zLTMyLjYtMjE2LjQtMTQzLjYtMjQ1LjItMjUwLjljLTE1LjMtNDkuOC0yMS4xLTExMDctMjEuMS0zMzc2LjZWLTIxODJMMzUwMi0xMDY3LjNDMjg4Ny4yLTQ1Ni4zLDIzNTYuNyw2MC44LDIzMjAuMyw3OGMtMTAxLjUsNTEuNy0yODMuNSwzMi42LTM3NS40LTM2LjRjLTEzNC4xLTEwMy40LTE4NS44LTI0OS0xNDMuNi00MDkuOWMyMy04NC4zLDEzMi4yLTE5Ny4zLDE0OTItMTU2MC45Yzk4Ni40LTk4OC4zLDE0OTItMTQ4Mi40LDE1NDUuNi0xNTA3LjNjOTEuOS00NiwyMTIuNi00OS44LDMxMC4zLTkuNmM5MiwzOC4zLDI5NTUuMywyODk0LDMwMTYuNiwzMDA3YzEyMC43LDIyNy45LTI0LjksNTE3LjEtMjcyLDU0MmMtMjE2LjQsMjEuMS0xMzIuMSw5MS45LTEzNzUuMi0xMTUxLjFMNTM4Mi44LTIxODJ2MzMwMy44YzAsMjI2OS42LTUuNywzMzI2LjgtMjEuMSwzMzc2LjZjLTMwLjcsMTExLjEtMTM3LjksMjE4LjMtMjUyLjgsMjUyLjhTNDk5Niw0Nzg1LjgsNDg4Mi45LDQ3NDkuNHoiLz48cGF0aCBkPSJNNDMxLjgtMTgzNy4yYy05LjYtMy44LTQyLjEtMTEuNS03Mi44LTE3LjJjLTc4LjUtMTcuMi0xOTkuMi0xMzYtMjM1LjYtMjMzLjdjLTU5LjQtMTU3LTEuOS01OTcuNiwxMjQuNS05NDQuMmMyNzkuNi03NjQuMiw5NjUuMy0xMzM0LjksMTc5Mi43LTE0OTJjMTU3LjEtMzAuNiw0MDkuOS0zMi42LDI5NTkuMS0zMi42YzI1NTYuOSwwLDI4MDAuMSwxLjksMjk2MSwzMi42YzEwNzQuNSwyMDQuOSwxODU3LjgsMTA4MC4yLDE5MzQuNCwyMTY2LjJjMTUuMywyMDYuOC0zLjgsMjg5LjItOTMuOCwzODguOGMtMTM3LjksMTU5LTM1Ni4zLDE3OC4xLTUxNS4yLDQ0Yy05Ny43LTc4LjUtMTMwLjItMTYyLjgtMTQ5LjQtMzk4LjRjLTExLjUtMTEzLTM2LjQtMjY2LjItNTkuNC0zMzljLTEzNi00NDQuMy00NDguMi04MDIuNS04NjUuNy05OTRjLTMxNC4xLTE0NS41LTU3LjUtMTM0LjEtMzIxMS45LTEzNC4xYy0yNjg1LjIsMC0yODMwLjgsMS45LTI5NDkuNSwzNC41Yy00OTYuMSwxNDEuNy04OTQuNCw0OTQuMS0xMDc4LjMsOTU1LjdjLTY3LDE2NC43LTEwOS4yLDM0Ni43LTEwOS4yLDQ2OS4zYzAsMTgzLjgtMzQuNSwyOTEuMS0xMjIuNiwzNzkuMkM2NTIuMS0xODY0LjEsNTA4LjUtMTgxMC40LDQzMS44LTE4MzcuMnoiLz48L2c+PC9nPg0KPC9zdmc+"
loadIcon.style.width = "38px";
loadIcon.style.height = "38px";
toggleButton.appendChild(loadIcon);
toggleButton.classList.add("ovo-modloader-button");
toggleButton.style.top = "50%";
toggleButton.style.right = "0%";
toggleButton.style.transform = "translateY(-50%)";
toggleButton.style.width = "50px";
toggleButton.style.height = "50px";
toggleButton.onclick = this.promptMod.bind(this);
document.body.appendChild(toggleButton);
}
notify(title, text, image = "./speedrunner.png") {
cr.plugins_.sirg_notifications.prototype.acts.AddSimpleNotification.call(
this.runtime.types_by_index.find(
(type) => type.plugin instanceof cr.plugins_.sirg_notifications
).instances[0],
title,
text,
image
);
}
getModDirectory() {
return this.getScriptDir() + modDirectory;
}
getScriptPath() {
return decodeURI(new Error().stack.match(/([^ \n\(@])*([a-z]*:\/\/\/?)*?[a-z0-9\/\\]*\.js/ig)[0]);
}
getScriptDir() {
return this.getScriptPath().split("/").slice(0, -1).join("/") + "/";
}
getURLName(url) {
return url.split("/").pop().split(".").slice(0, -1).join(".");
}
getIsScriptLoaded(name) {
return this.loadedMods.includes(name);
}
getIsValidURL(str) {
const pattern = new RegExp('^(https?:\\/\\/)?' +
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
'((\\d{1,3}\\.){3}\\d{1,3}))' +
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
'(\\?[;&a-z\\d%_.~+=-]*)?' +
'(\\#[-a-z\\d_]*)?$', 'i');
return pattern.test(str);
}
#createScript(src, key, name, notify = true) {
const js = document.createElement("script");
js.type = "application/javascript";
js.src = src;
js.onload = () => {
this.loadedMods.push(key);
if (notify) {
this.notify("Mod loaded", name);
}
}
js.onerror = () => {
if (notify) {
this.notify("Error loading mod", name);
}
}
document.head.appendChild(js);
}
loadModURL(url, local = false, notify = true) {
if (local) {
url = this.getModDirectory() + "v1/" + url;
}
const name = this.getURLName(url);
if (this.getIsScriptLoaded(name)) {
if (notify) {
this.notify("Mod already loaded", name);
}
return;
}
this.#createScript(url, name, name, notify);
}
loadModJSON(key, json, notify = true) {
const url = this.getModDirectory() + "v1/" + json.url;
const name = json.name;
if (this.getIsScriptLoaded(key)) {
if (notify) {
this.notify("Mod already loaded", name);
}
return;
}
this.#createScript(url, key, name, notify);
}
loadModJS(modJS, notify = true) {
setTimeout(modJS, 0);
if (notify) {
this.notify("Mod loaded", "Loaded JS code");
}
}
promptMod() {
const input = prompt("Enter mod URL, ID, or JS code");
const key = input.toLocaleLowerCase();
if (!input) return;
if (this.mods[key]) {
this.loadModJSON(key, this.mods[key], true);
} else {
if (this.getIsValidURL(input)) {
this.loadModURL(input, false, true);
} else {
this.loadModJS(input, true);
}
}
}
}
if (typeof window.ovoModLoader === "undefined") {
if (typeof window.cr_getC2Runtime !== "undefined" && typeof window.cr_getC2Runtime() !== "undefined" && window.cr_getC2Runtime().isloading === false) {
new ModLoader(cr_getC2Runtime());
} else {
const createCommand = window.cr_createRuntime;
const hookCommand = (canvasId) => {
new ModLoader(createCommand(canvasId));
}
window.cr_createRuntime = hookCommand;
}
}
})();