IdlePixel+

Idle-Pixel plugin framework

当前为 2022-07-09 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/441206/1068464/IdlePixel%2B.js

  1. // ==UserScript==
  2. // @name IdlePixel+
  3. // @namespace com.anwinity.idlepixel
  4. // @version 0.2.1
  5. // @description Idle-Pixel plugin framework
  6. // @author Anwinity
  7. // @match *://idle-pixel.com/login/play*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. const CONFIG_TYPES_LABEL = ["label"];
  15. const CONFIG_TYPES_BOOLEAN = ["boolean", "bool", "checkbox"];
  16. const CONFIG_TYPES_INTEGER = ["integer", "int"];
  17. const CONFIG_TYPES_FLOAT = ["number", "num", "float"];
  18. const CONFIG_TYPES_STRING = ["string", "text"];
  19. const CONFIG_TYPES_SELECT = ["select"];
  20. const CONFIG_TYPES_COLOR = ["color"];
  21.  
  22. const INFO = {
  23. ores: {
  24. stone: {
  25. id: "stone",
  26. smeltable: false,
  27. oil: 0,
  28. bar: null,
  29. sellsFor: 1,
  30. miningXP: 0.1
  31. },
  32. copper: {
  33. id: "copper",
  34. smeltable: true,
  35. oil: 1,
  36. bar: "bronze_bar",
  37. sellsFor: 2,
  38. miningXP: 1
  39. },
  40. iron: {
  41. id: "iron",
  42. smeltable: true,
  43. oil: 5,
  44. bar: "iron_bar",
  45. sellsFor: 3,
  46. miningXP: 5
  47. },
  48. silver: {
  49. id: "silver",
  50. smeltable: true,
  51. oil: 20,
  52. bar: "silver_bar",
  53. sellsFor: 5,
  54. miningXP: 10
  55. },
  56. gold: {
  57. id: "gold",
  58. smeltable: true,
  59. oil: 100,
  60. bar: "gold_bar",
  61. sellsFor: 10,
  62. miningXP: 20
  63. },
  64. promethium: {
  65. id: "promethium",
  66. smeltable: false,
  67. oil: 0,
  68. bar: "promethium_bar",
  69. sellsFor: 90,
  70. miningXP: 100
  71. }
  72. },
  73. bars: {
  74. bronze_bar: {
  75. id: "bronze_bar",
  76. sellsFor: 3,
  77. craftingXP: 5
  78. },
  79. iron_bar: {
  80. id: "iron_bar",
  81. sellsFor: 5,
  82. craftingXP: 25
  83. },
  84. silver_bar: {
  85. id: "silver_bar",
  86. sellsFor: 10,
  87. craftingXP: 50
  88. },
  89. gold_bar: {
  90. id: "gold_bar",
  91. sellsFor: 50,
  92. craftingXP: 100
  93. },
  94. promethium_bar: {
  95. id: "promethium_bar",
  96. sellsFor: 1000,
  97. craftingXP: 500
  98. }
  99. },
  100. seeds: {
  101. dotted_green_leaf_seeds: {
  102. id: "dotted_green_leaf_seeds",
  103. level: 1,
  104. stopsDying: 15,
  105. time: 15,
  106. bonemealCost: 0
  107. },
  108. green_leaf_seeds: {
  109. id: "green_leaf_seeds",
  110. level: 15,
  111. stopsDying: 30,
  112. time: 30,
  113. bonemealCost: 0
  114. },
  115. lime_leaf_seeds: {
  116. id: "lime_leaf_seeds",
  117. level: 30,
  118. stopsDying: 40,
  119. time: 1*60,
  120. bonemealCost: 1
  121. },
  122. gold_leaf_seeds: {
  123. id: "gold_leaf_seeds",
  124. level: 50,
  125. stopsDying: 60,
  126. time: 2*60,
  127. bonemealCost: 10
  128. },
  129. crystal_leaf_seeds: {
  130. id: "crystal_leaf_seeds",
  131. level: 70,
  132. stopsDying: 80,
  133. time: 5*60,
  134. bonemealCost: 25
  135. },
  136. red_mushroom_seeds: {
  137. id: "red_mushroom_seeds",
  138. level: 1,
  139. stopsDying: 5,
  140. time: 5,
  141. bonemealCost: 0
  142. },
  143. tree_seeds: {
  144. id: "tree_seeds",
  145. level: 10,
  146. stopsDying: 25,
  147. time: 5*60,
  148. bonemealCost: 10
  149. },
  150. oak_tree_seeds: {
  151. id: "oak_tree_seeds",
  152. level: 25,
  153. stopsDying: 40,
  154. time: 8*60,
  155. bonemealCost: 25
  156. },
  157. willow_tree_seeds: {
  158. id: "willow_tree_seeds",
  159. level: 37,
  160. stopsDying: 55,
  161. time: 12*60,
  162. bonemealCost: 50
  163. },
  164. maple_tree_seeds: {
  165. id: "maple_tree_seeds",
  166. level: 50,
  167. stopsDying: 65,
  168. time: 15*60,
  169. bonemealCost: 120
  170. }
  171. },
  172. combatZones: {
  173. field: {
  174. id: "field",
  175. energyCost: 50,
  176. fightPointCost: 300
  177. },
  178. forest: {
  179. id: "forest",
  180. energyCost: 200,
  181. fightPointCost: 600
  182. },
  183. cave: {
  184. id: "cave",
  185. energyCost: 500,
  186. fightPointCost: 900
  187. },
  188. volcano: {
  189. id: "volcano",
  190. energyCost: 2000,
  191. fightPointCost: 1500
  192. }
  193. },
  194. spellManaCost: {
  195. heal: 2
  196. }
  197. };
  198.  
  199. if(window.IdlePixelPlus) {
  200. // already loaded
  201. return;
  202. }
  203.  
  204. class IdlePixelPlusPlugin {
  205.  
  206. constructor(id, opts) {
  207. if(typeof id !== "string") {
  208. throw new TypeError("IdlePixelPlusPlugin constructor takes the following arguments: (id:string, opts?:object)");
  209. }
  210. this.id = id;
  211. this.opts = opts || {};
  212. this.config = null;
  213. }
  214.  
  215. getConfig(name) {
  216. if(!this.config) {
  217. IdlePixelPlus.loadPluginConfigs(this.id);
  218. }
  219. if(this.config) {
  220. return this.config[name];
  221. }
  222. }
  223.  
  224. /*
  225. onConfigsChanged() { }
  226. onLogin() { }
  227. onMessageReceived(data) { }
  228. onVariableSet(key, valueBefore, valueAfter) { }
  229. onChat(data) { }
  230. onPanelChanged(panelBefore, panelAfter) { }
  231. onCombatStart() { }
  232. onCombatEnd() { }
  233. */
  234.  
  235. }
  236.  
  237. const internal = {
  238. init() {
  239. const self = this;
  240.  
  241. // hook into websocket messages
  242. const original_onmessage = window.websocket.websocket.onmessage;
  243. $(function() {
  244. window.websocket.websocket.onmessage = function(event) {
  245. original_onmessage.apply(window.websocket.websocket, arguments);
  246. self.onMessageReceived(event.data);
  247. }
  248. });
  249.  
  250. /*
  251. const original_open_websocket = window.open_websocket;
  252. window.open_websocket = function() {
  253. original_open_websocket.apply(this, arguments);
  254. const original_onmessage = window.websocket.websocket.onmessage;
  255. window.websocket.websocket.onmessage = function(event) {
  256. original_onmessage.apply(window.websocket.websocket, arguments);
  257. self.onMessageReceived(event.data);
  258. }
  259. }
  260. */
  261.  
  262. // hook into Items.set, which is where var_ values are set
  263. const original_items_set = Items.set;
  264. Items.set = function(key, value) {
  265. let valueBefore = window["var_"+key];
  266. original_items_set.apply(this, arguments);
  267. let valueAfter = window["var_"+key];
  268. self.onVariableSet(key, valueBefore, valueAfter);
  269. }
  270.  
  271. // hook into switch_panels, which is called when the main panel is changed. This is also used for custom panels.
  272. const original_switch_panels = window.switch_panels;
  273. window.switch_panels = function(id) {
  274. let panelBefore = Globals.currentPanel;
  275. if(panelBefore && panelBefore.startsWith("panel-")) {
  276. panelBefore = panelBefore.substring("panel-".length);
  277. }
  278. self.hideCustomPanels();
  279. original_switch_panels.apply(this, arguments);
  280. let panelAfter = Globals.currentPanel;
  281. if(panelAfter && panelAfter.startsWith("panel-")) {
  282. panelAfter = panelAfter.substring("panel-".length);
  283. }
  284. self.onPanelChanged(panelBefore, panelAfter);
  285. }
  286.  
  287. // create plugin menu item and panel
  288. const lastMenuItem = $("#menu-bar-buttons > .hover-menu-bar-item").last();
  289. lastMenuItem.after(`
  290. <div onclick="IdlePixelPlus.setPanel('idlepixelplus')" class="hover hover-menu-bar-item">
  291. <img id="menu-bar-idlepixelplus-icon" src="https://anwinity.com/idlepixelplus/plugins.png"> PLUGINS
  292. </div>
  293. `);
  294. self.addPanel("idlepixelplus", "IdlePixel+ Plugins", function() {
  295. let content = `
  296. <style>
  297. .idlepixelplus-plugin-box {
  298. display: block;
  299. position: relative;
  300. padding: 0.25em;
  301. color: white;
  302. background-color: rgb(107, 107, 107);
  303. border: 1px solid black;
  304. border-radius: 6px;
  305. margin-bottom: 0.5em;
  306. }
  307. .idlepixelplus-plugin-box .idlepixelplus-plugin-settings-button {
  308. position: absolute;
  309. right: 2px;
  310. top: 2px;
  311. cursor: pointer;
  312. }
  313. .idlepixelplus-plugin-box .idlepixelplus-plugin-config-section {
  314. display: grid;
  315. grid-template-columns: minmax(100px, min-content) 1fr;
  316. row-gap: 0.5em;
  317. column-gap: 0.5em;
  318. white-space: nowrap;
  319. }
  320. </style>
  321. `;
  322. self.forEachPlugin(plugin => {
  323. let id = plugin.id;
  324. let name = "An IdlePixel+ Plugin!";
  325. let description = "";
  326. let author = "unknown";
  327. if(plugin.opts.about) {
  328. let about = plugin.opts.about;
  329. name = about.name || name;
  330. description = about.description || description;
  331. author = about.author || author;
  332. }
  333. content += `
  334. <div id="idlepixelplus-plugin-box-${id}" class="idlepixelplus-plugin-box">
  335. <strong><u>${name||id}</u></strong> (by ${author})<br />
  336. <span>${description}</span><br />
  337. <div class="idlepixelplus-plugin-config-section" style="display: none">
  338. <hr style="grid-column: span 2">
  339. `;
  340. if(plugin.opts.config && Array.isArray(plugin.opts.config)) {
  341. plugin.opts.config.forEach(cfg => {
  342. if(CONFIG_TYPES_LABEL.includes(cfg.type)) {
  343. content += `<h5 style="grid-column: span 2; margin-bottom: 0; font-weight: 600">${cfg.label}</h5>`;
  344. }
  345. else if(CONFIG_TYPES_BOOLEAN.includes(cfg.type)) {
  346. content += `
  347. <div>
  348. <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label>
  349. </div>
  350. <div>
  351. <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="checkbox" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" />
  352. </div>
  353. `;
  354. }
  355. else if(CONFIG_TYPES_INTEGER.includes(cfg.type)) {
  356. content += `
  357. <div>
  358. <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label>
  359. </div>
  360. <div>
  361. <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="number" step="1" min="${cfg.min || ''}" max="${cfg.max || ''}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" />
  362. </div>
  363. `;
  364. }
  365. else if(CONFIG_TYPES_FLOAT.includes(cfg.type)) {
  366. content += `
  367. <div>
  368. <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label>
  369. </div>
  370. <div>
  371. <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="number" step="${cfg.step || ''}" min="${cfg.min || ''}" max="${cfg.max || ''}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" />
  372. </div>
  373. `;
  374. }
  375. else if(CONFIG_TYPES_STRING.includes(cfg.type)) {
  376. content += `
  377. <div>
  378. <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label>
  379. </div>
  380. <div>
  381. <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="text" maxlength="${cfg.max || ''}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" />
  382. </div>
  383. `;
  384. }
  385. else if(CONFIG_TYPES_COLOR.includes(cfg.type)) {
  386. content += `
  387. <div>
  388. <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label>
  389. </div>
  390. <div>
  391. <input id="idlepixelplus-config-${plugin.id}-${cfg.id}" type="color" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)" />
  392. </div>
  393. `;
  394. }
  395. else if(CONFIG_TYPES_SELECT.includes(cfg.type)) {
  396. content += `
  397. <div>
  398. <label for="idlepixelplus-config-${plugin.id}-${cfg.id}">${cfg.label || cfg.id}</label>
  399. </div>
  400. <div>
  401. <select id="idlepixelplus-config-${plugin.id}-${cfg.id}" onchange="IdlePixelPlus.setPluginConfigUIDirty('${id}', true)">
  402. `;
  403. if(cfg.options && Array.isArray(cfg.options)) {
  404. cfg.options.forEach(option => {
  405. if(typeof option === "string") {
  406. content += `<option value="${option}">${option}</option>`;
  407. }
  408. else {
  409. content += `<option value="${option.value}">${option.label || option.value}</option>`;
  410. }
  411. });
  412. }
  413. content += `
  414. </select>
  415. </div>
  416. `;
  417. }
  418. });
  419. content += `
  420. <div style="grid-column: span 2">
  421. <button id="idlepixelplus-configbutton-${plugin.id}-reload" onclick="IdlePixelPlus.loadPluginConfigs('${id}')">Reload</button>
  422. <button id="idlepixelplus-configbutton-${plugin.id}-apply" onclick="IdlePixelPlus.savePluginConfigs('${id}')">Apply</button>
  423. </div>
  424. `;
  425. }
  426. content += "</div>";
  427. if(plugin.opts.config) {
  428. content += `
  429. <div class="idlepixelplus-plugin-settings-button">
  430. <button onclick="$('#idlepixelplus-plugin-box-${id} .idlepixelplus-plugin-config-section').toggle()">Settings</button>
  431. </div>`;
  432. }
  433. content += "</div>";
  434. });
  435.  
  436. return content;
  437. });
  438.  
  439. console.log(`IdlePixelPlus (v${self.version}) initialized.`);
  440. }
  441. };
  442.  
  443. class IdlePixelPlus {
  444.  
  445. constructor() {
  446. this.version = GM_info.script.version;
  447. this.plugins = {};
  448. this.panels = {};
  449. this.debug = false;
  450. this.info = INFO;
  451. }
  452.  
  453. getVar(name, type) {
  454. let s = window[`var_${name}`];
  455. if(type) {
  456. switch(type) {
  457. case "int":
  458. case "integer":
  459. return parseInt(s);
  460. case "number":
  461. case "float":
  462. return parseFloat(s);
  463. case "boolean":
  464. case "bool":
  465. if(s=="true") return true;
  466. if(s=="false") return false;
  467. return undefined;
  468. }
  469. }
  470. return s;
  471. }
  472.  
  473. getVarOrDefault(name, defaultValue, type) {
  474. let s = window[`var_${name}`];
  475. if(s==null || typeof s === "undefined") {
  476. return defaultValue;
  477. }
  478. if(type) {
  479. let value;
  480. switch(type) {
  481. case "int":
  482. case "integer":
  483. value = parseInt(s);
  484. return isNaN(value) ? defaultValue : value;
  485. case "number":
  486. case "float":
  487. value = parseFloat(s);
  488. return isNaN(value) ? defaultValue : value;
  489. case "boolean":
  490. case "bool":
  491. if(s=="true") return true;
  492. if(s=="false") return false;
  493. return defaultValue;
  494. }
  495. }
  496. return s;
  497. }
  498.  
  499. setPluginConfigUIDirty(id, dirty) {
  500. if(typeof id !== "string" || typeof dirty !== "boolean") {
  501. throw new TypeError("IdlePixelPlus.setPluginConfigUIDirty takes the following arguments: (id:string, dirty:boolean)");
  502. }
  503. const plugin = this.plugins[id];
  504. const button = $(`#idlepixelplus-configbutton-${plugin.id}-apply`);
  505. if(button) {
  506. button.prop("disabled", !(dirty));
  507. }
  508. }
  509.  
  510. loadPluginConfigs(id) {
  511. if(typeof id !== "string") {
  512. throw new TypeError("IdlePixelPlus.reloadPluginConfigs takes the following arguments: (id:string)");
  513. }
  514. const plugin = this.plugins[id];
  515. const config = {};
  516. let stored;
  517. try {
  518. stored = JSON.parse(localStorage.getItem(`idlepixelplus.${id}.config`) || "{}");
  519. }
  520. catch(err) {
  521. console.error(`Failed to load configs for plugin with id "${id} - will use defaults instead."`);
  522. stored = {};
  523. }
  524. if(plugin.opts.config && Array.isArray(plugin.opts.config)) {
  525. plugin.opts.config.forEach(cfg => {
  526. const el = $(`#idlepixelplus-config-${plugin.id}-${cfg.id}`);
  527. let value = stored[cfg.id];
  528. if(value==null || typeof value === "undefined") {
  529. value = cfg.default;
  530. }
  531. config[cfg.id] = value;
  532.  
  533. if(el) {
  534. if(CONFIG_TYPES_BOOLEAN.includes(cfg.type) && typeof value === "boolean") {
  535. el.prop("checked", value);
  536. }
  537. else if(CONFIG_TYPES_INTEGER.includes(cfg.type) && typeof value === "number") {
  538. el.val(value);
  539. }
  540. else if(CONFIG_TYPES_FLOAT.includes(cfg.type) && typeof value === "number") {
  541. el.val(value);
  542. }
  543. else if(CONFIG_TYPES_STRING.includes(cfg.type) && typeof value === "string") {
  544. el.val(value);
  545. }
  546. else if(CONFIG_TYPES_SELECT.includes(cfg.type) && typeof value === "string") {
  547. el.val(value);
  548. }
  549. else if(CONFIG_TYPES_COLOR.includes(cfg.type) && typeof value === "string") {
  550. el.val(value);
  551. }
  552. }
  553. });
  554. }
  555. plugin.config = config;
  556. this.setPluginConfigUIDirty(id, false);
  557. if(typeof plugin.onConfigsChanged === "function") {
  558. plugin.onConfigsChanged();
  559. }
  560. }
  561.  
  562. savePluginConfigs(id) {
  563. if(typeof id !== "string") {
  564. throw new TypeError("IdlePixelPlus.savePluginConfigs takes the following arguments: (id:string)");
  565. }
  566. const plugin = this.plugins[id];
  567. const config = {};
  568. if(plugin.opts.config && Array.isArray(plugin.opts.config)) {
  569. plugin.opts.config.forEach(cfg => {
  570. const el = $(`#idlepixelplus-config-${plugin.id}-${cfg.id}`);
  571. let value;
  572. if(CONFIG_TYPES_BOOLEAN.includes(cfg.type)) {
  573. config[cfg.id] = el.is(":checked");
  574. }
  575. else if(CONFIG_TYPES_INTEGER.includes(cfg.type)) {
  576. config[cfg.id] = parseInt(el.val());
  577. }
  578. else if(CONFIG_TYPES_FLOAT.includes(cfg.type)) {
  579. config[cfg.id] = parseFloat(el.val());
  580. }
  581. else if(CONFIG_TYPES_STRING.includes(cfg.type)) {
  582. config[cfg.id] = el.val();
  583. }
  584. else if(CONFIG_TYPES_SELECT.includes(cfg.type)) {
  585. config[cfg.id] = el.val();
  586. }
  587. else if(CONFIG_TYPES_COLOR.includes(cfg.type)) {
  588. config[cfg.id] = el.val();
  589. }
  590. });
  591. }
  592. plugin.config = config;
  593. localStorage.setItem(`idlepixelplus.${id}.config`, JSON.stringify(config));
  594. this.setPluginConfigUIDirty(id, false);
  595. if(typeof plugin.onConfigsChanged === "function") {
  596. plugin.onConfigsChanged();
  597. }
  598. }
  599.  
  600. addPanel(id, title, content) {
  601. if(typeof id !== "string" || typeof title !== "string" || (typeof content !== "string" && typeof content !== "function") ) {
  602. throw new TypeError("IdlePixelPlus.addPanel takes the following arguments: (id:string, title:string, content:string|function)");
  603. }
  604. const panels = $("#panels");
  605. panels.append(`
  606. <div id="panel-${id}" style="display: none">
  607. <h1>${title}</h1>
  608. <hr>
  609. <div class="idlepixelplus-panel-content"></div>
  610. </div>
  611. `);
  612. this.panels[id] = {
  613. id: id,
  614. title: title,
  615. content: content
  616. };
  617. this.refreshPanel(id);
  618. }
  619.  
  620. refreshPanel(id) {
  621. if(typeof id !== "string") {
  622. throw new TypeError("IdlePixelPlus.refreshPanel takes the following arguments: (id:string)");
  623. }
  624. const panel = this.panels[id];
  625. if(!panel) {
  626. throw new TypeError(`Error rendering panel with id="${id}" - panel has not be added.`);
  627. }
  628. let content = panel.content;
  629. if(!["string", "function"].includes(typeof content)) {
  630. throw new TypeError(`Error rendering panel with id="${id}" - panel.content must be a string or a function returning a string.`);
  631. }
  632. if(typeof content === "function") {
  633. content = content();
  634. if(typeof content !== "string") {
  635. throw new TypeError(`Error rendering panel with id="${id}" - panel.content must be a string or a function returning a string.`);
  636. }
  637. }
  638. const panelContent = $(`#panel-${id} .idlepixelplus-panel-content`);
  639. panelContent.html(content);
  640. if(id === "idlepixelplus") {
  641. this.forEachPlugin(plugin => {
  642. this.loadPluginConfigs(plugin.id);
  643. });
  644. }
  645. }
  646.  
  647. registerPlugin(plugin) {
  648. if(!(plugin instanceof IdlePixelPlusPlugin)) {
  649. throw new TypeError("IdlePixelPlus.registerPlugin takes the following arguments: (plugin:IdlePixelPlusPlugin)");
  650. }
  651. if(plugin.id in this.plugins) {
  652. throw new Error(`IdlePixelPlusPlugin with id "${plugin.id}" is already registered. Make sure your plugin id is unique!`);
  653. }
  654.  
  655. // TODO: easy config system
  656. // TODO: custom panels
  657.  
  658. this.plugins[plugin.id] = plugin;
  659. this.loadPluginConfigs(plugin.id);
  660. console.log(`IdlePixelPlus registered plugin "${plugin.id}"`);
  661. }
  662.  
  663. forEachPlugin(f) {
  664. if(typeof f !== "function") {
  665. throw new TypeError("IdlePixelPlus.forEachPlugin takes the following arguments: (f:function)");
  666. }
  667. Object.values(this.plugins).forEach(plugin => {
  668. try {
  669. f(plugin);
  670. }
  671. catch(err) {
  672. console.error(`Error occurred while executing function for plugin "${plugin.id}."`);
  673. console.error(err);
  674. }
  675. });
  676. }
  677.  
  678. setPanel(panel) {
  679. if(typeof panel !== "string") {
  680. throw new TypeError("IdlePixelPlus.setPanel takes the following arguments: (panel:string)");
  681. }
  682. window.switch_panels(`panel-${panel}`);
  683. }
  684.  
  685. sendMessage(message) {
  686. if(typeof message !== "string") {
  687. throw new TypeError("IdlePixelPlus.sendMessage takes the following arguments: (message:string)");
  688. }
  689. if(window.websocket && window.websocket.websocket && window.websocket.websocket.readyState==1) {
  690. window.websocket.websocket.send(message);
  691. }
  692. }
  693.  
  694. showToast(title, content) {
  695. show_toast(title, content);
  696. }
  697.  
  698. hideCustomPanels() {
  699. Object.values(this.panels).forEach((panel) => {
  700. const el = $(`#panel-${panel.id}`);
  701. if(el) {
  702. el.css("display", "none");
  703. }
  704. });
  705. }
  706.  
  707. onMessageReceived(data) {
  708. if(this.debug) {
  709. console.log(`IP+ onMessageReceived: ${data}`);
  710. }
  711. if(data) {
  712. this.forEachPlugin((plugin) => {
  713. if(typeof plugin.onMessageReceived === "function") {
  714. plugin.onMessageReceived(data);
  715. }
  716. });
  717. if(data.startsWith("VALID_LOGIN")) {
  718. this.onLogin();
  719. }
  720. else if(data.startsWith("CHAT=")) {
  721. const split = data.substring("CHAT=".length).split("~");
  722. const chatData = {
  723. username: split[0],
  724. tag: split[1],
  725. sigil: split[2],
  726. level: parseInt(split[3]),
  727. message: split[4]
  728. };
  729. this.onChat(chatData);
  730. // CHAT=anwinity~none~none~1565~test
  731. }
  732. }
  733. }
  734.  
  735. onCombatStart() {
  736. if(this.debug) {
  737. console.log(`IP+ onCombatStart`);
  738. }
  739. this.forEachPlugin((plugin) => {
  740. if(typeof plugin.onCombatStart === "function") {
  741. plugin.onCombatStart();
  742. }
  743. });
  744. }
  745.  
  746. onCombatEnd() {
  747. if(this.debug) {
  748. console.log(`IP+ onCombatEnd`);
  749. }
  750. this.forEachPlugin((plugin) => {
  751. if(typeof plugin.onCombatEnd === "function") {
  752. plugin.onCombatEnd();
  753. }
  754. });
  755. }
  756.  
  757. onLogin() {
  758. if(this.debug) {
  759. console.log(`IP+ onLogin`);
  760. }
  761. console.log("idlePixelPlus login detected");
  762. this.forEachPlugin((plugin) => {
  763. if(typeof plugin.onLogin === "function") {
  764. plugin.onLogin();
  765. }
  766. });
  767. }
  768.  
  769. onVariableSet(key, valueBefore, valueAfter) {
  770. if(this.debug) {
  771. console.log(`IP+ onVariableSet "${key}": "${valueBefore}" -> "${valueAfter}"`);
  772. }
  773. this.forEachPlugin((plugin) => {
  774. if(typeof plugin.onVariableSet === "function") {
  775. plugin.onVariableSet(key, valueBefore, valueAfter);
  776. }
  777. });
  778. if(key == "monster_name") {
  779. const combatBefore = !!(valueBefore && valueBefore!="none");
  780. const combatAfter = !!(valueAfter && valueAfter!="none");
  781. if(!combatBefore && combatAfter) {
  782. this.onCombatStart();
  783. }
  784. else if(combatBefore && !combatAfter) {
  785. this.onCombatEnd();
  786. }
  787. }
  788. }
  789.  
  790. onChat(data) {
  791. if(this.debug) {
  792. console.log(`IP+ onChat`, data);
  793. }
  794. this.forEachPlugin((plugin) => {
  795. if(typeof plugin.onChat === "function") {
  796. plugin.onChat(data);
  797. }
  798. });
  799. }
  800.  
  801. onPanelChanged(panelBefore, panelAfter) {
  802. if(this.debug) {
  803. console.log(`IP+ onPanelChanged "${panelBefore}" -> "${panelAfter}"`);
  804. }
  805. if(panelAfter === "idlepixelplus") {
  806. this.refreshPanel("idlepixelplus");
  807. }
  808. this.forEachPlugin((plugin) => {
  809. if(typeof plugin.onPanelChanged === "function") {
  810. plugin.onPanelChanged(panelBefore, panelAfter);
  811. }
  812. });
  813. }
  814.  
  815. }
  816.  
  817. // Add to window and init
  818. window.IdlePixelPlusPlugin = IdlePixelPlusPlugin;
  819. window.IdlePixelPlus = new IdlePixelPlus();
  820. internal.init.call(window.IdlePixelPlus);
  821.  
  822. })();