diff --git a/README.md b/README.md index 71b7f73a139..8f58d2425da 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ scratch-gui modified for use in [TurboWarp](https://turbowarp.org/) then modified for use in [PenguinMod](https://penguinmod.github.io/penguinmod.github.io/) 😀 + + [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/PenguinMod/penguinmod.github.io/) ## Setup diff --git a/src/addons/addons.js b/src/addons/addons.js index d2e8f55c047..9ec7764a636 100644 --- a/src/addons/addons.js +++ b/src/addons/addons.js @@ -75,6 +75,7 @@ const addons = [ const newAddons = [ 'vol-slider', + '60fps', 'number-pad', 'rename-broadcasts', 'sprite-properties', diff --git a/src/addons/addons/60fps/addon.json b/src/addons/addons/60fps/addon.json new file mode 100644 index 00000000000..15c190fd803 --- /dev/null +++ b/src/addons/addons/60fps/addon.json @@ -0,0 +1,46 @@ +{ + "name": "60FPS project player mode", + "description": "Alt+Click the green flag to toggle 60FPS.", + "info": [ + { + "type": "notice", + "text": "Most projects will not behave properly when running at 60FPS, because increasing the frame rate also increases the speed at which scripts run. This should only be used on projects that support 60FPS.", + "id": "projectRunsFasterNotice" + } + ], + "credits": [ + { + "name": "Jeffalo", + "link": "https://scratch.mit.edu/users/Jeffalo/" + }, + { + "name": "TheColaber", + "link": "https://scratch.mit.edu/users/TheColaber/" + } + { + "name": "kokofixcomputers", + "link": "https://scratch.mit.edu/users/kokofixcomputers/" + } + ], + "dynamicEnable": true, + "dynamicDisable": true, + "userscripts": [ + { + "url": "userscript.js", + "matches": ["projects", "projectEmbeds"] + } + ], + "settings": [ + { + "name": "Alt+GreenFlag FPS", + "id": "framerate", + "type": "integer", + "min": 31, + "max": 240, + "default": 60 + } + ], + "tags": ["editor", "projectPlayer", "featured"], + "versionAdded": "1.1.0", + "enabledByDefault": false +} diff --git a/src/addons/addons/60fps/svg/fast-flag.svg b/src/addons/addons/60fps/svg/fast-flag.svg new file mode 100644 index 00000000000..de1d8cd8be8 --- /dev/null +++ b/src/addons/addons/60fps/svg/fast-flag.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/addons/addons/60fps/userscript.js b/src/addons/addons/60fps/userscript.js new file mode 100644 index 00000000000..92fb791c864 --- /dev/null +++ b/src/addons/addons/60fps/userscript.js @@ -0,0 +1,82 @@ +export default async function ({ addon, console }) { + // TODO: test whether e.altKey is true in chromebooks when alt+clicking. + // If so, no timeout needed, similar to mute-project addon. + + let global_fps = 30; + const vm = addon.tab.traps.vm; + let mode = false; + let monitorUpdateFixed = false; + + const fastFlag = addon.self.dir + "/svg/fast-flag.svg"; + let vanillaFlag = null; + + while (true) { + let button = await addon.tab.waitForElement("[class^='green-flag_green-flag']", { + markAsSeen: true, + reduxEvents: ["scratch-gui/mode/SET_PLAYER", "fontsLoaded/SET_FONTS_LOADED", "scratch-gui/locales/SELECT_LOCALE"], + }); + + const updateFlag = () => { + if (!vanillaFlag) vanillaFlag = button.src; + button.src = mode ? fastFlag : vanillaFlag; + }; + + const changeMode = (_mode = !mode) => { + mode = _mode; + if (mode) { + setFPS(addon.settings.get("framerate")); + + // monitor updates are throttled by default + // https://github.com/scratchfoundation/scratch-gui/blob/ba76db7/src/reducers/monitors.js + if (!monitorUpdateFixed) { + const originalListener = vm.listeners("MONITORS_UPDATE").find((f) => f.name === "onMonitorsUpdate"); + if (originalListener) vm.removeListener("MONITORS_UPDATE", originalListener); + vm.on("MONITORS_UPDATE", (monitors) => + addon.tab.redux.dispatch({ + type: "scratch-gui/monitors/UPDATE_MONITORS", + monitors, + }) + ); + monitorUpdateFixed = true; + } + } else setFPS(30); + updateFlag(); + }; + const flagListener = (e) => { + if (addon.self.disabled) return; + const isAltClick = e.type === "click" && e.altKey; + const isChromebookAltClick = navigator.userAgent.includes("CrOS") && e.type === "contextmenu"; + if (isAltClick || isChromebookAltClick) { + e.cancelBubble = true; + e.preventDefault(); + changeMode(); + } + }; + button.addEventListener("click", flagListener); + button.addEventListener("contextmenu", flagListener); + + const setFPS = (fps) => { + global_fps = addon.self.disabled ? 30 : fps; + + clearInterval(vm.runtime._steppingInterval); + vm.runtime._steppingInterval = null; + vm.runtime.start(); + }; + addon.settings.addEventListener("change", () => { + if (vm.runtime._steppingInterval) { + setFPS(addon.settings.get("framerate")); + } + }); + addon.self.addEventListener("disabled", () => changeMode(false)); + vm.runtime.start = function () { + if (this._steppingInterval) return; + let interval = 1000 / global_fps; + this.currentStepTime = interval; + this._steppingInterval = setInterval(() => { + this._step(); + }, interval); + this.emit("RUNTIME_STARTED"); + }; + updateFlag(); + } +}