您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Idle-Pixel plugin framework
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/441206/1071010/IdlePixel%2B.js
// ==UserScript== // @name IdlePixel+ // @namespace com.anwinity.idlepixel // @version 0.2.3 // @description Idle-Pixel plugin framework // @author Anwinity // @match *://idle-pixel.com/login/play* // @grant none // ==/UserScript== (function() { 'use strict'; const CONFIG_TYPES_LABEL = ["label"]; const CONFIG_TYPES_BOOLEAN = ["boolean", "bool", "checkbox"]; const CONFIG_TYPES_INTEGER = ["integer", "int"]; const CONFIG_TYPES_FLOAT = ["number", "num", "float"]; const CONFIG_TYPES_STRING = ["string", "text"]; const CONFIG_TYPES_SELECT = ["select"]; const CONFIG_TYPES_COLOR = ["color"]; const INFO = { ores: { stone: { id: "stone", smeltable: false, oil: 0, bar: null, sellsFor: 1, miningXP: 0.1 }, copper: { id: "copper", smeltable: true, oil: 1, bar: "bronze_bar", sellsFor: 2, miningXP: 1 }, iron: { id: "iron", smeltable: true, oil: 5, bar: "iron_bar", sellsFor: 3, miningXP: 5 }, silver: { id: "silver", smeltable: true, oil: 20, bar: "silver_bar", sellsFor: 5, miningXP: 10 }, gold: { id: "gold", smeltable: true, oil: 100, bar: "gold_bar", sellsFor: 10, miningXP: 20 }, promethium: { id: "promethium", smeltable: false, oil: 0, bar: "promethium_bar", sellsFor: 90, miningXP: 100 } }, bars: { bronze_bar: { id: "bronze_bar", sellsFor: 3, craftingXP: 5 }, iron_bar: { id: "iron_bar", sellsFor: 5, craftingXP: 25 }, silver_bar: { id: "silver_bar", sellsFor: 10, craftingXP: 50 }, gold_bar: { id: "gold_bar", sellsFor: 50, craftingXP: 100 }, promethium_bar: { id: "promethium_bar", sellsFor: 1000, craftingXP: 500 } }, seeds: { dotted_green_leaf_seeds: { id: "dotted_green_leaf_seeds", level: 1, stopsDying: 15, time: 15, bonemealCost: 0 }, green_leaf_seeds: { id: "green_leaf_seeds", level: 15, stopsDying: 30, time: 30, bonemealCost: 0 }, lime_leaf_seeds: { id: "lime_leaf_seeds", level: 30, stopsDying: 40, time: 1*60, bonemealCost: 1 }, gold_leaf_seeds: { id: "gold_leaf_seeds", level: 50, stopsDying: 60, time: 2*60, bonemealCost: 10 }, crystal_leaf_seeds: { id: "crystal_leaf_seeds", level: 70, stopsDying: 80, time: 5*60, bonemealCost: 25 }, red_mushroom_seeds: { id: "red_mushroom_seeds", level: 1, stopsDying: 5, time: 5, bonemealCost: 0 }, tree_seeds: { id: "tree_seeds", level: 10, stopsDying: 25, time: 5*60, bonemealCost: 10 }, oak_tree_seeds: { id: "oak_tree_seeds", level: 25, stopsDying: 40, time: 8*60, bonemealCost: 25 }, willow_tree_seeds: { id: "willow_tree_seeds", level: 37, stopsDying: 55, time: 12*60, bonemealCost: 50 }, maple_tree_seeds: { id: "maple_tree_seeds", level: 50, stopsDying: 65, time: 15*60, bonemealCost: 120 } }, combatZones: { field: { id: "field", energyCost: 50, fightPointCost: 300 }, forest: { id: "forest", energyCost: 200, fightPointCost: 600 }, cave: { id: "cave", energyCost: 500, fightPointCost: 900 }, volcano: { id: "volcano", energyCost: 2000, fightPointCost: 1500 } }, spellManaCost: { heal: 2 } }; if(window.IdlePixelPlus) { // already loaded return; } class IdlePixelPlusPlugin { constructor(id, opts) { if(typeof id !== "string") { throw new TypeError("IdlePixelPlusPlugin constructor takes the following arguments: (id:string, opts?:object)"); } this.id = id; this.opts = opts || {}; this.config = null; } getConfig(name) { if(!this.config) { IdlePixelPlus.loadPluginConfigs(this.id); } if(this.config) { return this.config[name]; } } /* onConfigsChanged() { } onLogin() { } onMessageReceived(data) { } onVariableSet(key, valueBefore, valueAfter) { } onChat(data) { } onPanelChanged(panelBefore, panelAfter) { } onCombatStart() { } onCombatEnd() { } */ } const internal = { init() { const self = this; // hook into websocket messages const original_onmessage = window.websocket.websocket.onmessage; $(function() { window.websocket.websocket.onmessage = function(event) { original_onmessage.apply(window.websocket.websocket, arguments); self.onMessageReceived(event.data); } }); /* const original_open_websocket = window.open_websocket; window.open_websocket = function() { original_open_websocket.apply(this, arguments); const original_onmessage = window.websocket.websocket.onmessage; window.websocket.websocket.onmessage = function(event) { original_onmessage.apply(window.websocket.websocket, arguments); self.onMessageReceived(event.data); } } */ // hook into Items.set, which is where var_ values are set const original_items_set = Items.set; Items.set = function(key, value) { let valueBefore = window["var_"+key]; original_items_set.apply(this, arguments); let valueAfter = window["var_"+key]; self.onVariableSet(key, valueBefore, valueAfter); } // hook into switch_panels, which is called when the main panel is changed. This is also used for custom panels. const original_switch_panels = window.switch_panels; window.switch_panels = function(id) { let panelBefore = Globals.currentPanel; if(panelBefore && panelBefore.startsWith("panel-")) { panelBefore = panelBefore.substring("panel-".length); } self.hideCustomPanels(); original_switch_panels.apply(this, arguments); let panelAfter = Globals.currentPanel; if(panelAfter && panelAfter.startsWith("panel-")) { panelAfter = panelAfter.substring("panel-".length); } self.onPanelChanged(panelBefore, panelAfter); } // create plugin menu item and panel const lastMenuItem = $("#menu-bar-buttons > .hover-menu-bar-item").last(); lastMenuItem.after(` <div onclick="IdlePixelPlus.setPanel('idlepixelplus')" class="hover hover-menu-bar-item"> <img id="menu-bar-idlepixelplus-icon" src="https://anwinity.com/idlepixelplus/plugins.png"> PLUGINS </div> `); self.addPanel("idlepixelplus", "IdlePixel+ Plugins", function() { let content = ` <style> .idlepixelplus-plugin-box { display: block; position: relative; padding: 0.25em; color: white; background-color: rgb(107, 107, 107); border: 1px solid black; border-radius: 6px; margin-bottom: 0.5em; } .idlepixelplus-plugin-box .idlepixelplus-plugin-settings-button { position: absolute; right: 2px; top: 2px; cursor: pointer; } .idlepixelplus-plugin-box .idlepixelplus-plugin-config-section { display: grid; grid-template-columns: minmax(100px, min-content) 1fr; row-gap: 0.5em; column-gap: 0.5em; white-space: nowrap; } </style> `; self.forEachPlugin(plugin => { let id = plugin.id; let name = "An IdlePixel+ Plugin!"; let description = ""; let author = "unknown"; if(plugin.opts.about) { let about = plugin.opts.about; name = about.name || name; description = about.description || description; author = about.author || author; } content += ` <div id="idlepixelplus-plugin-box-${id}" class="idlepixelplus-plugin-box"> <strong><u>${name||id}</u></strong> (by ${author})<br /> <span>${description}</span><br /> <div class="idlepixelplus-plugin-config-section" style="display: none"> <hr style="grid-column: span 2"> `; if(plugin.opts.config && Array.isArray(plugin.opts.config)) { plugin.opts.config.forEach(cfg => { if(CONFIG_TYPES_LABEL.includes(cfg.type)) { content += `<h5 style="grid-column: span 2; margin-bottom: 0; font-weight: 600">${cfg.label}</h5>`; } else if(CONFIG_TYPES_BOOLEAN.includes(cfg.type)) { content += ` <div> <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label> </div> <div> <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="checkbox" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" /> </div> `; } else if(CONFIG_TYPES_INTEGER.includes(cfg.type)) { content += ` <div> <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label> </div> <div> <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="number" step="1" min="${cfg.min || ''}" max="${cfg.max || ''}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" /> </div> `; } else if(CONFIG_TYPES_FLOAT.includes(cfg.type)) { content += ` <div> <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label> </div> <div> <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="number" step="${cfg.step || ''}" min="${cfg.min || ''}" max="${cfg.max || ''}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" /> </div> `; } else if(CONFIG_TYPES_STRING.includes(cfg.type)) { content += ` <div> <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label> </div> <div> <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="text" maxlength="${cfg.max || ''}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" /> </div> `; } else if(CONFIG_TYPES_COLOR.includes(cfg.type)) { content += ` <div> <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label> </div> <div> <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="color" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" /> </div> `; } else if(CONFIG_TYPES_SELECT.includes(cfg.type)) { content += ` <div> <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label> </div> <div> <select id="idlepixelplus-config-${plugin.id}-${cfg.id}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)"> `; if(cfg.options && Array.isArray(cfg.options)) { cfg.options.forEach(option => { if(typeof option === "string") { content += `<option value="${option}">${option}</option>`; } else { content += `<option value="${option.value}">${option.label || option.value}</option>`; } }); } content += ` </select> </div> `; } }); content += ` <div style="grid-column: span 2"> <button id="idlepixelplus-configbutton-${plugin.id}-reload" onclick="IdlePixelPlus.loadPluginConfigs('${id}')">Reload</button> <button id="idlepixelplus-configbutton-${plugin.id}-apply" onclick="IdlePixelPlus.savePluginConfigs('${id}')">Apply</button> </div> `; } content += "</div>"; if(plugin.opts.config) { content += ` <div class="idlepixelplus-plugin-settings-button"> <button onclick="$('#idlepixelplus-plugin-box-${id} .idlepixelplus-plugin-config-section').toggle()">Settings</button> </div>`; } content += "</div>"; }); return content; }); console.log(`IdlePixelPlus (v${self.version}) initialized.`); } }; class IdlePixelPlus { constructor() { this.version = VERSION; this.plugins = {}; this.panels = {}; this.debug = false; this.info = INFO; } getVar(name, type) { let s = window[`var_${name}`]; if(type) { switch(type) { case "int": case "integer": return parseInt(s); case "number": case "float": return parseFloat(s); case "boolean": case "bool": if(s=="true") return true; if(s=="false") return false; return undefined; } } return s; } getVarOrDefault(name, defaultValue, type) { let s = window[`var_${name}`]; if(s==null || typeof s === "undefined") { return defaultValue; } if(type) { let value; switch(type) { case "int": case "integer": value = parseInt(s); return isNaN(value) ? defaultValue : value; case "number": case "float": value = parseFloat(s); return isNaN(value) ? defaultValue : value; case "boolean": case "bool": if(s=="true") return true; if(s=="false") return false; return defaultValue; } } return s; } setPluginConfigUIDirty(id, dirty) { if(typeof id !== "string" || typeof dirty !== "boolean") { throw new TypeError("IdlePixelPlus.setPluginConfigUIDirty takes the following arguments: (id:string, dirty:boolean)"); } const plugin = this.plugins[id]; const button = $(`#idlepixelplus-configbutton-${plugin.id}-apply`); if(button) { button.prop("disabled", !(dirty)); } } loadPluginConfigs(id) { if(typeof id !== "string") { throw new TypeError("IdlePixelPlus.reloadPluginConfigs takes the following arguments: (id:string)"); } const plugin = this.plugins[id]; const config = {}; let stored; try { stored = JSON.parse(localStorage.getItem(`idlepixelplus.${id}.config`) || "{}"); } catch(err) { console.error(`Failed to load configs for plugin with id "${id} - will use defaults instead."`); stored = {}; } if(plugin.opts.config && Array.isArray(plugin.opts.config)) { plugin.opts.config.forEach(cfg => { const el = $(`#idlepixelplus-config-${plugin.id}-${cfg.id}`); let value = stored[cfg.id]; if(value==null || typeof value === "undefined") { value = cfg.default; } config[cfg.id] = value; if(el) { if(CONFIG_TYPES_BOOLEAN.includes(cfg.type) && typeof value === "boolean") { el.prop("checked", value); } else if(CONFIG_TYPES_INTEGER.includes(cfg.type) && typeof value === "number") { el.val(value); } else if(CONFIG_TYPES_FLOAT.includes(cfg.type) && typeof value === "number") { el.val(value); } else if(CONFIG_TYPES_STRING.includes(cfg.type) && typeof value === "string") { el.val(value); } else if(CONFIG_TYPES_SELECT.includes(cfg.type) && typeof value === "string") { el.val(value); } else if(CONFIG_TYPES_COLOR.includes(cfg.type) && typeof value === "string") { el.val(value); } } }); } plugin.config = config; this.setPluginConfigUIDirty(id, false); if(typeof plugin.onConfigsChanged === "function") { plugin.onConfigsChanged(); } } savePluginConfigs(id) { if(typeof id !== "string") { throw new TypeError("IdlePixelPlus.savePluginConfigs takes the following arguments: (id:string)"); } const plugin = this.plugins[id]; const config = {}; if(plugin.opts.config && Array.isArray(plugin.opts.config)) { plugin.opts.config.forEach(cfg => { const el = $(`#idlepixelplus-config-${plugin.id}-${cfg.id}`); let value; if(CONFIG_TYPES_BOOLEAN.includes(cfg.type)) { config[cfg.id] = el.is(":checked"); } else if(CONFIG_TYPES_INTEGER.includes(cfg.type)) { config[cfg.id] = parseInt(el.val()); } else if(CONFIG_TYPES_FLOAT.includes(cfg.type)) { config[cfg.id] = parseFloat(el.val()); } else if(CONFIG_TYPES_STRING.includes(cfg.type)) { config[cfg.id] = el.val(); } else if(CONFIG_TYPES_SELECT.includes(cfg.type)) { config[cfg.id] = el.val(); } else if(CONFIG_TYPES_COLOR.includes(cfg.type)) { config[cfg.id] = el.val(); } }); } plugin.config = config; localStorage.setItem(`idlepixelplus.${id}.config`, JSON.stringify(config)); this.setPluginConfigUIDirty(id, false); if(typeof plugin.onConfigsChanged === "function") { plugin.onConfigsChanged(); } } addPanel(id, title, content) { if(typeof id !== "string" || typeof title !== "string" || (typeof content !== "string" && typeof content !== "function") ) { throw new TypeError("IdlePixelPlus.addPanel takes the following arguments: (id:string, title:string, content:string|function)"); } const panels = $("#panels"); panels.append(` <div id="panel-${id}" style="display: none"> <h1>${title}</h1> <hr> <div class="idlepixelplus-panel-content"></div> </div> `); this.panels[id] = { id: id, title: title, content: content }; this.refreshPanel(id); } refreshPanel(id) { if(typeof id !== "string") { throw new TypeError("IdlePixelPlus.refreshPanel takes the following arguments: (id:string)"); } const panel = this.panels[id]; if(!panel) { throw new TypeError(`Error rendering panel with id="${id}" - panel has not be added.`); } let content = panel.content; if(!["string", "function"].includes(typeof content)) { throw new TypeError(`Error rendering panel with id="${id}" - panel.content must be a string or a function returning a string.`); } if(typeof content === "function") { content = content(); if(typeof content !== "string") { throw new TypeError(`Error rendering panel with id="${id}" - panel.content must be a string or a function returning a string.`); } } const panelContent = $(`#panel-${id} .idlepixelplus-panel-content`); panelContent.html(content); if(id === "idlepixelplus") { this.forEachPlugin(plugin => { this.loadPluginConfigs(plugin.id); }); } } registerPlugin(plugin) { if(!(plugin instanceof IdlePixelPlusPlugin)) { throw new TypeError("IdlePixelPlus.registerPlugin takes the following arguments: (plugin:IdlePixelPlusPlugin)"); } if(plugin.id in this.plugins) { throw new Error(`IdlePixelPlusPlugin with id "${plugin.id}" is already registered. Make sure your plugin id is unique!`); } this.plugins[plugin.id] = plugin; this.loadPluginConfigs(plugin.id); console.log(`IdlePixelPlus registered plugin "${plugin.id}"`); } forEachPlugin(f) { if(typeof f !== "function") { throw new TypeError("IdlePixelPlus.forEachPlugin takes the following arguments: (f:function)"); } Object.values(this.plugins).forEach(plugin => { try { f(plugin); } catch(err) { console.error(`Error occurred while executing function for plugin "${plugin.id}."`); console.error(err); } }); } setPanel(panel) { if(typeof panel !== "string") { throw new TypeError("IdlePixelPlus.setPanel takes the following arguments: (panel:string)"); } window.switch_panels(`panel-${panel}`); } sendMessage(message) { if(typeof message !== "string") { throw new TypeError("IdlePixelPlus.sendMessage takes the following arguments: (message:string)"); } if(window.websocket && window.websocket.websocket && window.websocket.websocket.readyState==1) { window.websocket.websocket.send(message); } } showToast(title, content) { show_toast(title, content); } hideCustomPanels() { Object.values(this.panels).forEach((panel) => { const el = $(`#panel-${panel.id}`); if(el) { el.css("display", "none"); } }); } onMessageReceived(data) { if(this.debug) { console.log(`IP+ onMessageReceived: ${data}`); } if(data) { this.forEachPlugin((plugin) => { if(typeof plugin.onMessageReceived === "function") { plugin.onMessageReceived(data); } }); if(data.startsWith("VALID_LOGIN")) { this.onLogin(); } else if(data.startsWith("CHAT=")) { const split = data.substring("CHAT=".length).split("~"); const chatData = { username: split[0], tag: split[1], sigil: split[2], level: parseInt(split[3]), message: split[4] }; this.onChat(chatData); // CHAT=anwinity~none~none~1565~test } } } onCombatStart() { if(this.debug) { console.log(`IP+ onCombatStart`); } this.forEachPlugin((plugin) => { if(typeof plugin.onCombatStart === "function") { plugin.onCombatStart(); } }); } onCombatEnd() { if(this.debug) { console.log(`IP+ onCombatEnd`); } this.forEachPlugin((plugin) => { if(typeof plugin.onCombatEnd === "function") { plugin.onCombatEnd(); } }); } onLogin() { if(this.debug) { console.log(`IP+ onLogin`); } console.log("idlePixelPlus login detected"); this.forEachPlugin((plugin) => { if(typeof plugin.onLogin === "function") { plugin.onLogin(); } }); } onVariableSet(key, valueBefore, valueAfter) { if(this.debug) { console.log(`IP+ onVariableSet "${key}": "${valueBefore}" -> "${valueAfter}"`); } this.forEachPlugin((plugin) => { if(typeof plugin.onVariableSet === "function") { plugin.onVariableSet(key, valueBefore, valueAfter); } }); if(key == "monster_name") { const combatBefore = !!(valueBefore && valueBefore!="none"); const combatAfter = !!(valueAfter && valueAfter!="none"); if(!combatBefore && combatAfter) { this.onCombatStart(); } else if(combatBefore && !combatAfter) { this.onCombatEnd(); } } } onChat(data) { if(this.debug) { console.log(`IP+ onChat`, data); } this.forEachPlugin((plugin) => { if(typeof plugin.onChat === "function") { plugin.onChat(data); } }); } onPanelChanged(panelBefore, panelAfter) { if(this.debug) { console.log(`IP+ onPanelChanged "${panelBefore}" -> "${panelAfter}"`); } if(panelAfter === "idlepixelplus") { this.refreshPanel("idlepixelplus"); } this.forEachPlugin((plugin) => { if(typeof plugin.onPanelChanged === "function") { plugin.onPanelChanged(panelBefore, panelAfter); } }); } } // Add to window and init window.IdlePixelPlusPlugin = IdlePixelPlusPlugin; window.IdlePixelPlus = new IdlePixelPlus(); internal.init.call(window.IdlePixelPlus); })();