IdlePixel Breeding Overhaul

Redesigned breeding UI and QoL features.

  1. // ==UserScript==
  2. // @name IdlePixel Breeding Overhaul
  3. // @namespace com.anwinity.idlepixel
  4. // @version 1.0.18
  5. // @description Redesigned breeding UI and QoL features.
  6. // @author Anwinity
  7. // @license MIT
  8. // @match *://idle-pixel.com/login/play*
  9. // @grant none
  10. // @require https://gf.qytechs.cn/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const IMAGE_URL = "https://cdn.idle-pixel.com/images/";
  17.  
  18. const ANIMAL_LIST = ["chicken", "sheep", "spider", "horse", "ant", "camel", "beaver", "dove", "pig", "haron", "toysoldier", "squidshroom", "snail", "rocky", "geody", "skeleton"];
  19. const ANIMAL_SORT = ANIMAL_LIST.reduce((acc, value, index) => {
  20. acc[value] = index;
  21. return acc;
  22. }, {});
  23.  
  24. const FOOD_MAP = {
  25. Seeds: "breeding_food_seeds",
  26. Leaves: "breeding_food_leaves",
  27. // breeding_food_strange_leaves
  28. Seafood: "breeding_food_fish",
  29. // breeding_food_fruits
  30. Insects: "breeding_food_insects",
  31. Bark: "breeding_food_wood",
  32. Metal: "breeding_food_metal",
  33. Flesh: "breeding_food_flesh",
  34. };
  35.  
  36. class BreedingOverhaulPlugin extends IdlePixelPlusPlugin {
  37. constructor() {
  38. super("breedingoverhaul", {
  39. about: {
  40. name: GM_info.script.name,
  41. version: GM_info.script.version,
  42. author: GM_info.script.author,
  43. description: GM_info.script.description
  44. },
  45. config: [
  46. {
  47. id: "newAnimalPopup",
  48. label: "New Animal Popup",
  49. type: "boolean",
  50. default: true,
  51. },
  52. /* // TODO
  53. {
  54. id: "focusFirePeck",
  55. label: "Focus Fire Peck (Requires Peck to be set to MANUAL)",
  56. type: "boolean",
  57. default: false,
  58. }
  59. */
  60. ]
  61. });
  62. this.focusFireTarget = null;
  63. this.animalData = null;
  64. this.lastAnimalActivation = null;
  65. }
  66.  
  67. parseAnimals(raw) {
  68. if(!raw) {
  69. return [];
  70. }
  71. return raw.split("=").map((rawAnimal) => {
  72. const result = {
  73. raw: rawAnimal
  74. };
  75. const [
  76. slug,
  77. animal,
  78. gender,
  79. tier,
  80. millis,
  81. shiny,
  82. sick,
  83. foodPerDay,
  84. foodConsumed,
  85. lootItemReady,
  86. lootItemAmount,
  87. xpGained,
  88. traitLabel,
  89. extraData1,
  90. extraData2,
  91. extraData3,
  92. extraData4,
  93. extraData5
  94. ] = rawAnimal.split(",");
  95.  
  96. let food = Breeding.getBreedFoodLabel(animal);
  97. if(food == "null") {
  98. food = null;
  99. }
  100.  
  101. result.slug = slug;
  102. result.animal = animal;
  103. result.sexNumber = parseInt(gender); // useful for some pre-existing Breeding functions
  104. result.sex = (gender==1 ? "M" : (gender==2 ? "F" : "?"));
  105. result.tier = parseInt(tier);
  106. result.birth = parseInt(millis);
  107. result.shiny = parseInt(shiny);
  108. result.sick = sick!=0;
  109. result.food = food;
  110. result.foodPerDay = parseInt(foodPerDay);
  111. //result.foodConsumed = parseInt(foodConsumed);
  112. result.lootItem = lootItemReady == "none" ? null : lootItemReady;
  113. result.lootCount = parseInt(lootItemAmount);
  114. result.xp = parseInt(xpGained);
  115. result.trait = traitLabel;
  116. //result.hornyRate = Breeding.getReproductionRate(animal, null);
  117.  
  118. return result;
  119. }).sort((a, b) => {
  120. const aSort = ANIMAL_SORT[a.animal] ?? 9999;
  121. const bSort = ANIMAL_SORT[b.animal] ?? 9999;
  122. if(aSort == bSort) {
  123. return a.birth - b.birth;
  124. }
  125. return aSort - bSort;
  126. });
  127. }
  128.  
  129. calculateReproductionRate(animalData, animal) {
  130. if(["toysoldier"].includes(animal)) {
  131. return Number.POSITIVE_INFINITY;
  132. }
  133. // calculates expected # of ticks for this species to reproduce
  134. let rate = Breeding.getReproductionRate(animal, null);
  135. let males = 0;
  136. let females = 0;
  137. let infertile = 0;
  138. let fertile = 0;
  139. for(const data of animalData) {
  140. if(data.animal != animal) {
  141. continue;
  142. }
  143. if(data.sick) {
  144. continue;
  145. }
  146. if(data.sex == "M") {
  147. males++;
  148. }
  149. else if(data.sex == "F") {
  150. females++;
  151. }
  152. if(data.trait == "Fertile") {
  153. fertile++;
  154. }
  155. else if(data.trait == "Less Fertile") {
  156. infertile++;
  157. }
  158. }
  159. let couples = Math.min(males, females);
  160. if(infertile > 0) {
  161. rate = Math.floor(rate * Math.pow(1.1, infertile));
  162. }
  163. if(fertile > 0) {
  164. rate = Math.floor(rate * Math.pow(0.9, fertile));
  165. }
  166. rate = 600 * Math.floor(1 + (rate / 600 / couples));
  167. return rate;
  168. }
  169.  
  170. summarizeAnimals(animalData) {
  171. let xp = 0;
  172. const animals = [];
  173. const loot = {};
  174. const dailyFood = {};
  175. const reproduction = {};
  176. for(const animal of animalData) {
  177. xp += animal.xp || 0;
  178.  
  179. if(!animals.includes(animal.animal)) {
  180. animals.push(animal.animal);
  181. }
  182.  
  183. if(animal.lootItem) {
  184. if(animal.lootCount) {
  185. loot[animal.lootItem] ??= 0;
  186. loot[animal.lootItem] += animal.lootCount;
  187. }
  188. }
  189.  
  190. if(animal.food && animal.foodPerDay) {
  191. dailyFood[animal.food] ??= 0;
  192. dailyFood[animal.food] += animal.foodPerDay;
  193. }
  194. }
  195. let result = { xp, animals, loot, dailyFood, reproduction };
  196. for(const animal of animals) {
  197. const rate = this.calculateReproductionRate(animalData, animal);
  198. result.reproduction[animal] = rate;
  199. }
  200.  
  201. // temporary hack until var_ant_capacity_used is fixed
  202. let antCount = animalData.filter(animal => animal.animal=="ant").length;
  203. window.var_ant_capacity_used = `${antCount}`;
  204.  
  205. return result;
  206. }
  207.  
  208. updateAnimalSummary(summary) {
  209. if(!summary) {
  210. return;
  211. }
  212.  
  213. const totalXPElement = document.getElementById("breeding-overhaul-total-xp");
  214. if(totalXPElement) {
  215. totalXPElement.innerHTML = `${summary.xp?.toLocaleString(navigator.language)}`;
  216. }
  217.  
  218. const dailyFoodElement = document.getElementById("breeding-overhaul-daily-food");
  219. const foodDaysElement = document.getElementById("breeding-overhaul-food-days");
  220. if(dailyFoodElement && summary.dailyFood) {
  221. let content = "";
  222. let contentDays = "";
  223. const foodSpans = [];
  224. const foodDaysSpans = [];
  225. for(const food in summary.dailyFood) {
  226. const amount = summary.dailyFood[food]?.toLocaleString(navigator.language);
  227. const foodVar = FOOD_MAP[food];
  228. const img = `<img src="${IMAGE_URL}${foodVar}.png" class="inline" />`;
  229.  
  230. let danger = false;
  231. let owned = "";
  232. let days = 0;
  233. if(foodVar) {
  234. owned = IdlePixelPlus.getVarOrDefault(foodVar, 0, "int");
  235. if(owned < amount) {
  236. danger = true;
  237. }
  238. days = amount==0 ? 0 : owned/amount;
  239. owned = `/${owned.toLocaleString(navigator.language)}`;
  240. }
  241.  
  242. foodSpans.push(`<span class="${danger?'danger':''}" title="${food}: ${amount}/day">&nbsp;${img}&nbsp;${amount}${owned}&nbsp;</span>`);
  243. foodDaysSpans.push(`<span class="${danger?'danger':''}" title="${food}: ${days.toFixed(2)} day${days==1?'':'s'}">&nbsp;${img}&nbsp;${days.toFixed(1)} day${days==1?'':'s'}&nbsp;</span>`);
  244. }
  245.  
  246. content += foodSpans.join(" ");
  247. if(!content) {
  248. content = "None";
  249. }
  250. if(dailyFoodElement) {
  251. dailyFoodElement.innerHTML = content;
  252. }
  253.  
  254. contentDays += foodDaysSpans.join(" ");
  255. if(!contentDays) {
  256. contentDays = "None";
  257. }
  258. if(foodDaysElement) {
  259. foodDaysElement.innerHTML = contentDays;
  260. }
  261. }
  262.  
  263. const reproductionElement = document.getElementById("breeding-overhaul-reproduction");
  264. if(reproductionElement && summary.reproduction && summary.animals) {
  265. let content = "";
  266. const hornySpans = [];
  267. for(const animal of summary.animals) {
  268. const img = `<img src="${IMAGE_URL}${animal}_male_1.png" class="inline" />`;
  269. let rate = summary.reproduction[animal];
  270. if(Number.isFinite(rate)) {
  271. rate = format_time(rate);
  272. if(rate < 600) {
  273. // animals can reproduce at most once every 10 minutes
  274. rate = 600;
  275. }
  276. rate = rate.replace(/, 0:00$/, "");
  277. }
  278. else {
  279. rate = "Never";
  280. }
  281. hornySpans.push(`<span title="${capitalizeFirstLetter(animal)} Expected Reproduction: ${rate}">&nbsp;${img}&nbsp;${rate}&nbsp;</span>`);
  282. }
  283. content += hornySpans.join(" ");
  284. if(!content) {
  285. content = "None";
  286. }
  287. reproductionElement.innerHTML = content;
  288. }
  289.  
  290. const lootAvailableElement = document.getElementById("breeding-overhaul-loot-available");
  291. if(lootAvailableElement && summary.loot) {
  292. let content = "";
  293. const lootSpans = [];
  294. for(const loot in summary.loot) {
  295. const amount = summary.loot[loot]?.toLocaleString(navigator.language);
  296. const img = `<img src="${IMAGE_URL}${loot.toLowerCase()}.png" class="inline" />`;
  297. lootSpans.push(`<span title="${loot}: ${amount}">&nbsp;${img}&nbsp;${amount}&nbsp;</span>`);
  298. }
  299. content += lootSpans.join(" ");
  300. let anyLoot = true;
  301. if(!content) {
  302. content = "None";
  303. anyLoot = false;
  304. }
  305. lootAvailableElement.innerHTML = content;
  306. const lootAllButton = document.getElementById("breeding-overhaul-loot-all");
  307. if(lootAllButton) {
  308. lootAllButton.disabled = !anyLoot;
  309. }
  310. }
  311. }
  312.  
  313. clickLootAll() {
  314. const animalData = IdlePixelPlus.getVar("animal_data");
  315. if(!animalData) {
  316. return;
  317. }
  318. this.parseAnimals(animalData)
  319. .filter(animal => animal.lootCount)
  320. .forEach(animal => IdlePixelPlus.sendMessage(`COLLECT_ALL_LOOT_ANIMAL`));
  321. }
  322.  
  323. updateAnimalData(animals) {
  324. if(!animals) {
  325. return;
  326. }
  327.  
  328. const summary = this.summarizeAnimals(animals);
  329. this.updateAnimalSummary(summary);
  330. }
  331.  
  332. focusFire(event, enemyID) {
  333. event.stopPropagation();
  334. this.focusFireTarget = enemyID;
  335. const breedingFightingHeroes = document.querySelectorAll(".breeding-fighting-entry");
  336. breedingFightingHeroes.forEach((el) => {
  337. const allyID = parseInt(el.id.match(/(\d+)$/));
  338. if(typeof allyID !== "number" || isNaN(allyID)) {
  339. return;
  340. }
  341. websocket.send(`BREEDING_FIGHT_SET_TARGET=${allyID}~${enemyID}`);
  342. Breeding.flashBorder3Seconds(allyID, enemyID);
  343. });
  344. Breeding.global_last_click_fighting_hero_animal_index = "none";
  345. }
  346.  
  347. initUI() {
  348. const style = document.createElement("style");
  349. style.id = "styles-breeding-overhaul";
  350. style.textContent = `
  351. #panel-breeding #breeding-overhaul-breeding-summary {
  352. display: flex;
  353. flex-direction: column;
  354. }
  355. #panel-breeding #breeding-overhaul-breeding-summary .danger {
  356. color: red;
  357. }
  358. #panel-breeding #breeding-overhaul-breeding-summary img.inline {
  359. height: 1em;
  360. width: auto;
  361. }
  362. #breeding-overhaul-new-animal-dialog-backdrop {
  363. position: fixed;
  364. z-index: 9999;
  365. top: 0;
  366. left: 0;
  367. right: 0;
  368. bottom: 0;
  369. width: 100%;
  370. height: 100%;
  371. background-color: rgba(0, 0, 0, 0.5);
  372. pointer-events: all;
  373. }
  374. #breeding-overhaul-new-animal-dialog {
  375. position: sticky;
  376. top: 3rem;
  377. border: none;
  378. padding: 1em;
  379. background-color: white;
  380. border: 4px solid black;
  381. border-radius: 8px;
  382. box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
  383. min-width: 300px;
  384. }
  385. #breeding-overhaul-new-animal-dialog > img#breeding-overhaul-new-animal-image {
  386. position: absolute;
  387. top: 1em;
  388. right: 1em;
  389. height: 50px;
  390. }
  391. `;
  392. document.head.appendChild(style);
  393.  
  394. const panel = document.getElementById("panel-breeding");
  395. if(!panel) {
  396. console.error("Breeding Overhaul: Failed to initialize UI - #panel-breeding not found");
  397. return;
  398. }
  399. const craftableButtons = panel.querySelector(".craftable-btns");
  400. if(craftableButtons) {
  401. craftableButtons.insertAdjacentHTML("afterend", `
  402. <div id="breeding-overhaul-breeding-summary">
  403. <div>
  404. <strong>Total XP: </strong>
  405. <span id="breeding-overhaul-total-xp"></span>
  406. </div>
  407. <div>
  408. <strong>Daily Food: </strong>
  409. <span id="breeding-overhaul-daily-food"></span>
  410. </div>
  411. <div>
  412. <strong>Food Days: </strong>
  413. <span id="breeding-overhaul-food-days"></span>
  414. </div>
  415. <div>
  416. <strong>Reproduction: </strong>
  417. <span id="breeding-overhaul-reproduction"></span>
  418. </div>
  419. <div>
  420. <strong>Loot Available: </strong>
  421. <span id="breeding-overhaul-loot-available"></span>
  422. </div>
  423. <div>
  424. <button id="breeding-overhaul-loot-all" type="button" onclick="IdlePixelPlus.plugins.breedingoverhaul.clickLootAll()">Loot All</button>
  425. </div>
  426. </div>
  427. `);
  428. }
  429. else {
  430. console.error("Breeding Overhaul: Failed to fully initialize UI - .craftable-btns not found");
  431. }
  432.  
  433. // Breeding "Focus Fire" buttons
  434. const breedingFightingEnemies = document.querySelectorAll(".breeding-fighting-entry-monster");
  435. breedingFightingEnemies.forEach((el) => {
  436. const enemyID = parseInt(el.id.match(/(\d+)$/));
  437. if(typeof enemyID !== "number" || isNaN(enemyID)) {
  438. return;
  439. }
  440. const newDiv = document.createElement("div");
  441. newDiv.innerHTML = `
  442. <div class="center">
  443. <button onclick="IdlePixelPlus.plugins.breedingoverhaul.focusFire(event, ${enemyID})">Focus Fire</button>
  444. </div>
  445. `;
  446. el.appendChild(newDiv);
  447. });
  448.  
  449. }
  450.  
  451. markAnimalAsNew(slug) {
  452. /* FOR DEBUGGING */
  453. delete this.animalData[slug];
  454. }
  455.  
  456. detectNewAnimals(animals) {
  457. const first = this.animalData === null;
  458. const animalIdsBefore = first ? new Set() : new Set(Object.keys(this.animalData));
  459. const animalIdsAfter = new Set(animals.map(animal => animal.slug));
  460. this.animalData = {};
  461. animals.forEach(animal => this.animalData[animal.slug] = animal);
  462.  
  463. if(!first) {
  464. const newAnimalIds = [...animalIdsAfter].filter(id => !animalIdsBefore.has(id));
  465. if(newAnimalIds.length && this.getConfig("newAnimalPopup")) {
  466. const animal = this.animalData[newAnimalIds[0]];
  467.  
  468. const name = capitalizeFirstLetter(get_item_name(animal.animal));
  469. const tier = animal.tier;
  470. const tierDots = `<span class="dot-green"></span> `.repeat(tier);
  471. const slug = animal.slug;
  472. const animalImage = `${IMAGE_URL}${animal.animal}_${Breeding.get_gender_label(animal.sexNumber)}_${animal.tier}.png`;
  473. const sexColor = animal.sex == "F" ? "pink" : "blue";
  474. const sexImage = `${IMAGE_URL}${animal.sex=='F' ? 'female' : 'male'}.png`;
  475. const shiny = animal.shiny;
  476. const shinyDivStyle = shiny==0 ? "" : "background-color: rgba(255, 223, 0, 0.5);";
  477. const foodAmount = animal.foodPerDay;
  478. const foodLabel = animal.food;
  479. const foodType = FOOD_MAP[animal.food];
  480. const foodImage = `${IMAGE_URL}${foodType}.png`;
  481. const trait = animal.trait;
  482. const traitIcon = `${IMAGE_URL}${Breeding.getTraitDesc(trait, slug)[0]}.png`;
  483. const shinyLabel = (function() {
  484. switch(shiny) {
  485. case 0: return "No";
  486. case 1: return "Shiny!";
  487. case 2: return "Mega Shiny!";
  488. default: return "Unknown";
  489. }
  490. })();
  491. const inactiveVar = `inactive_${animal.animal}_${animal.sex=='F' ? 'female' : 'male'}`;
  492. const inactiveCount = IdlePixelPlus.getVarOrDefault(inactiveVar, 0, "int").toLocaleString();
  493.  
  494. let extraButtonsHTML = "";
  495. if(trait == "Fighter") {
  496. extraButtonsHTML += `<button onclick="IdlePixelPlus.sendMessage('ADD_FIGHT_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Make Fighter</button> `;
  497. extraButtonsHTML += `<button onclick="IdlePixelPlus.sendMessage('ADD_FIGHT_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.retryLastAnimalActivation(); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Make Fighter &amp; Try Again</button> `;
  498. }
  499.  
  500. const dialogHTML = `
  501. <div id="breeding-overhaul-new-animal-dialog-backdrop">
  502. <dialog id="breeding-overhaul-new-animal-dialog" open>
  503. <img id="breeding-overhaul-new-animal-image" src="${animalImage}" />
  504. <div class="v-flex half-gap">
  505. <h5><img src="${sexImage}" class="inline" style="height: 1em" /> T-${tier} ${name}</h5>
  506. <div><strong>Tier: </strong> ${tierDots}</div>
  507. <div><strong>Trait: </strong> <img src="${traitIcon}" class="inline" style="height: 1em" /> ${trait}</div>
  508. <div style="${shinyDivStyle}"><strong>Shiny: </strong> ${shinyLabel}</div>
  509. <div><strong>Food: </strong> <img src="${foodImage}" class="inline" style="height: 1em" /> ${foodAmount} ${foodLabel} / day</div>
  510. <div><strong>Inactive Remaining: </strong> <span id="breeding-overhaul-new-animal-dialog-inactive-count">${inactiveCount}</span></div>
  511. <div class="v-flex half-gap">
  512. <button onclick="IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Keep</button>
  513. <button onclick="IdlePixelPlus.plugins.breedingoverhaul.retryLastAnimalActivation(); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Keep &amp; Try Again</button>
  514. ${extraButtonsHTML}
  515. <button onclick="IdlePixelPlus.sendMessage('KILL_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Kill</button>
  516. <button onclick="IdlePixelPlus.sendMessage('KILL_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.retryLastAnimalActivation(); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Kill &amp; Try Again</button>
  517. </div>
  518. </div>
  519. </dialog>
  520. </div>
  521. `;
  522. this.closeNewAnimalDialog();
  523. document.body.insertAdjacentHTML("afterbegin", dialogHTML);
  524. setTimeout(function() {
  525. const updatedInactiveCount = IdlePixelPlus.getVarOrDefault(inactiveVar, 0, "int").toLocaleString();
  526. if(inactiveCount != updatedInactiveCount) {
  527. const inactiveCountSpan = document.getElementById("breeding-overhaul-new-animal-dialog-inactive-count");
  528. if(inactiveCountSpan) {
  529. inactiveCountSpan.textContent = updatedInactiveCount;
  530. }
  531. }
  532. }, 50);
  533. }
  534. }
  535. }
  536.  
  537. retryLastAnimalActivation() {
  538. if(typeof this.lastAnimalActivation === "string") {
  539. if(this.lastAnimalActivation.startsWith("SLAUGHTER_FOR_TIER_")) {
  540. // if number is more than we have, find new max to send
  541. const parts = this.lastAnimalActivation.split(/[~=]/g);
  542. const owned = IdlePixelPlus.getVarOrDefault(parts[1], 0, "int");
  543. const attempted = parseInt(parts[3]);
  544. if(attempted > owned) {
  545. parts[3] = owned;
  546. }
  547. this.lastAnimalActivation = `${parts[0]}=${parts.slice(1).join("~")}`;
  548. }
  549. IdlePixelPlus.sendMessage(this.lastAnimalActivation);
  550. }
  551. }
  552.  
  553. closeNewAnimalDialog() {
  554. const dialog = document.getElementById("breeding-overhaul-new-animal-dialog-backdrop");
  555. if(dialog) {
  556. dialog.remove();
  557. }
  558. }
  559.  
  560. onMessageSent(message) {
  561. if(typeof message === "string") {
  562. if(message.startsWith("ACTIVATE_ANIMAL=") || message.startsWith("SLAUGHTER_FOR_TIER_")) {
  563. this.lastAnimalActivation = message;
  564. }
  565. }
  566. }
  567.  
  568. onLogin() {
  569. // Intercept sent messages - used to track last animal action
  570. if(window.websocket) {
  571. const plugin = this;
  572. const originalSend = window.websocket.send;
  573. window.websocket.send = function(data) {
  574. plugin.onMessageSent(data);
  575. return originalSend.call(this, data);
  576. }
  577. }
  578.  
  579. // Initialize new ui components
  580. this.initUI();
  581.  
  582. // override refresh function
  583. const original_refresh_animals_data = Breeding.refresh_animals_data;
  584. Breeding.refresh_animals_data = (...args) => {
  585. const animalArea = document.getElementById("breeding-chicken-area");
  586.  
  587. let raw = args[0] || null;
  588. let animals = this.parseAnimals(raw);
  589.  
  590. // rejoin (sorted) for original function
  591. if(animals?.length) {
  592. raw = animals.map(animal => animal.raw).join("=");
  593. }
  594. // run original smitty function
  595. try {
  596. original_refresh_animals_data.apply(this, [raw]);
  597. }
  598. catch(err) {
  599. console.error("Error running original Breeding.refresh_animals_data: ", err);
  600. throw err;
  601. }
  602.  
  603. // update ui
  604. try {
  605. this.updateAnimalData(animals);
  606. }
  607. catch(err) {
  608. console.error("Error running BreedingOverhaulPlugin.updateAnimalData: ", err);
  609. }
  610.  
  611. // detect new animals
  612. try {
  613. this.detectNewAnimals(animals);
  614. }
  615. catch(err) {
  616. console.error("Error running BreedingOverhaulPlugin.detectNewAnimals: ", err);
  617. }
  618.  
  619. }
  620. }
  621.  
  622. onVariableSet(key, valueBefore, valueAfter) {
  623. // TODO
  624. return;
  625.  
  626. switch(key) {
  627. case "is_in_breeding_fight": {
  628. console.log(`${key}: "${valueBefore}" -> "${valueAfter}"`);
  629. if(valueAfter == "1") {
  630. this.fight = {};
  631. }
  632. else {
  633. this.fight = null;
  634. }
  635. break;
  636. }
  637. }
  638. }
  639.  
  640. onMessageReceived(data) {
  641. // TODO
  642. return;
  643.  
  644. try {
  645. const manual = window.var_large_chicken_beak_automated == "0";
  646. const focusFirePeck = this.getConfig("focusFirePeck");
  647.  
  648. // requires manual since auto-mode functions server side
  649. if(manual && focusFirePeck) {
  650. if(data?.startsWith("REFRESH_BREEDING_FIGHTING=")) {
  651. data = data.substring("REFRESH_BREEDING_FIGHTING=".length).split(",");
  652. const pecksAvailable = [];
  653. for(let i = 0; i < data.length; i++) {
  654. const fighterData = data[i].split("~");
  655. const hp = parseInt(fighterData[1]);
  656. const stamina = parseInt(fighterData[5]);
  657. const requiredStamina = parseInt(fighterData[6]);
  658. const special = fighterData[7];
  659. console.log(`i=${i}, hp=${hp}, special=${special}, stamina=${stamina}, requiredStamina=${requiredStamina}`);
  660. if(special == "peck" && hp > 0 && stamina >= requiredStamina) {
  661. pecksAvailable.push(i);
  662. console.log(`${i} peck ready`);
  663.  
  664. break;
  665. }
  666. }
  667. }
  668. }
  669. }
  670. catch(err) {
  671. console.error("Breeding Overhaul - onMessageReceived - Something went wrong while processing focus fire for special abilities. A recent game update may have broken this functionality.");
  672. }
  673. }
  674.  
  675. }
  676.  
  677. const plugin = new BreedingOverhaulPlugin();
  678. IdlePixelPlus.registerPlugin(plugin);
  679.  
  680. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址