您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Redesigned breeding UI and QoL features.
// ==UserScript== // @name IdlePixel Breeding Overhaul // @namespace com.anwinity.idlepixel // @version 1.0.18 // @description Redesigned breeding UI and QoL features. // @author Anwinity // @license MIT // @match *://idle-pixel.com/login/play* // @grant none // @require https://gf.qytechs.cn/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905 // ==/UserScript== (function() { 'use strict'; const IMAGE_URL = "https://cdn.idle-pixel.com/images/"; const ANIMAL_LIST = ["chicken", "sheep", "spider", "horse", "ant", "camel", "beaver", "dove", "pig", "haron", "toysoldier", "squidshroom", "snail", "rocky", "geody", "skeleton"]; const ANIMAL_SORT = ANIMAL_LIST.reduce((acc, value, index) => { acc[value] = index; return acc; }, {}); const FOOD_MAP = { Seeds: "breeding_food_seeds", Leaves: "breeding_food_leaves", // breeding_food_strange_leaves Seafood: "breeding_food_fish", // breeding_food_fruits Insects: "breeding_food_insects", Bark: "breeding_food_wood", Metal: "breeding_food_metal", Flesh: "breeding_food_flesh", }; class BreedingOverhaulPlugin extends IdlePixelPlusPlugin { constructor() { super("breedingoverhaul", { about: { name: GM_info.script.name, version: GM_info.script.version, author: GM_info.script.author, description: GM_info.script.description }, config: [ { id: "newAnimalPopup", label: "New Animal Popup", type: "boolean", default: true, }, /* // TODO { id: "focusFirePeck", label: "Focus Fire Peck (Requires Peck to be set to MANUAL)", type: "boolean", default: false, } */ ] }); this.focusFireTarget = null; this.animalData = null; this.lastAnimalActivation = null; } parseAnimals(raw) { if(!raw) { return []; } return raw.split("=").map((rawAnimal) => { const result = { raw: rawAnimal }; const [ slug, animal, gender, tier, millis, shiny, sick, foodPerDay, foodConsumed, lootItemReady, lootItemAmount, xpGained, traitLabel, extraData1, extraData2, extraData3, extraData4, extraData5 ] = rawAnimal.split(","); let food = Breeding.getBreedFoodLabel(animal); if(food == "null") { food = null; } result.slug = slug; result.animal = animal; result.sexNumber = parseInt(gender); // useful for some pre-existing Breeding functions result.sex = (gender==1 ? "M" : (gender==2 ? "F" : "?")); result.tier = parseInt(tier); result.birth = parseInt(millis); result.shiny = parseInt(shiny); result.sick = sick!=0; result.food = food; result.foodPerDay = parseInt(foodPerDay); //result.foodConsumed = parseInt(foodConsumed); result.lootItem = lootItemReady == "none" ? null : lootItemReady; result.lootCount = parseInt(lootItemAmount); result.xp = parseInt(xpGained); result.trait = traitLabel; //result.hornyRate = Breeding.getReproductionRate(animal, null); return result; }).sort((a, b) => { const aSort = ANIMAL_SORT[a.animal] ?? 9999; const bSort = ANIMAL_SORT[b.animal] ?? 9999; if(aSort == bSort) { return a.birth - b.birth; } return aSort - bSort; }); } calculateReproductionRate(animalData, animal) { if(["toysoldier"].includes(animal)) { return Number.POSITIVE_INFINITY; } // calculates expected # of ticks for this species to reproduce let rate = Breeding.getReproductionRate(animal, null); let males = 0; let females = 0; let infertile = 0; let fertile = 0; for(const data of animalData) { if(data.animal != animal) { continue; } if(data.sick) { continue; } if(data.sex == "M") { males++; } else if(data.sex == "F") { females++; } if(data.trait == "Fertile") { fertile++; } else if(data.trait == "Less Fertile") { infertile++; } } let couples = Math.min(males, females); if(infertile > 0) { rate = Math.floor(rate * Math.pow(1.1, infertile)); } if(fertile > 0) { rate = Math.floor(rate * Math.pow(0.9, fertile)); } rate = 600 * Math.floor(1 + (rate / 600 / couples)); return rate; } summarizeAnimals(animalData) { let xp = 0; const animals = []; const loot = {}; const dailyFood = {}; const reproduction = {}; for(const animal of animalData) { xp += animal.xp || 0; if(!animals.includes(animal.animal)) { animals.push(animal.animal); } if(animal.lootItem) { if(animal.lootCount) { loot[animal.lootItem] ??= 0; loot[animal.lootItem] += animal.lootCount; } } if(animal.food && animal.foodPerDay) { dailyFood[animal.food] ??= 0; dailyFood[animal.food] += animal.foodPerDay; } } let result = { xp, animals, loot, dailyFood, reproduction }; for(const animal of animals) { const rate = this.calculateReproductionRate(animalData, animal); result.reproduction[animal] = rate; } // temporary hack until var_ant_capacity_used is fixed let antCount = animalData.filter(animal => animal.animal=="ant").length; window.var_ant_capacity_used = `${antCount}`; return result; } updateAnimalSummary(summary) { if(!summary) { return; } const totalXPElement = document.getElementById("breeding-overhaul-total-xp"); if(totalXPElement) { totalXPElement.innerHTML = `${summary.xp?.toLocaleString(navigator.language)}`; } const dailyFoodElement = document.getElementById("breeding-overhaul-daily-food"); const foodDaysElement = document.getElementById("breeding-overhaul-food-days"); if(dailyFoodElement && summary.dailyFood) { let content = ""; let contentDays = ""; const foodSpans = []; const foodDaysSpans = []; for(const food in summary.dailyFood) { const amount = summary.dailyFood[food]?.toLocaleString(navigator.language); const foodVar = FOOD_MAP[food]; const img = `<img src="${IMAGE_URL}${foodVar}.png" class="inline" />`; let danger = false; let owned = ""; let days = 0; if(foodVar) { owned = IdlePixelPlus.getVarOrDefault(foodVar, 0, "int"); if(owned < amount) { danger = true; } days = amount==0 ? 0 : owned/amount; owned = `/${owned.toLocaleString(navigator.language)}`; } foodSpans.push(`<span class="${danger?'danger':''}" title="${food}: ${amount}/day"> ${img} ${amount}${owned} </span>`); foodDaysSpans.push(`<span class="${danger?'danger':''}" title="${food}: ${days.toFixed(2)} day${days==1?'':'s'}"> ${img} ${days.toFixed(1)} day${days==1?'':'s'} </span>`); } content += foodSpans.join(" "); if(!content) { content = "None"; } if(dailyFoodElement) { dailyFoodElement.innerHTML = content; } contentDays += foodDaysSpans.join(" "); if(!contentDays) { contentDays = "None"; } if(foodDaysElement) { foodDaysElement.innerHTML = contentDays; } } const reproductionElement = document.getElementById("breeding-overhaul-reproduction"); if(reproductionElement && summary.reproduction && summary.animals) { let content = ""; const hornySpans = []; for(const animal of summary.animals) { const img = `<img src="${IMAGE_URL}${animal}_male_1.png" class="inline" />`; let rate = summary.reproduction[animal]; if(Number.isFinite(rate)) { rate = format_time(rate); if(rate < 600) { // animals can reproduce at most once every 10 minutes rate = 600; } rate = rate.replace(/, 0:00$/, ""); } else { rate = "Never"; } hornySpans.push(`<span title="${capitalizeFirstLetter(animal)} Expected Reproduction: ${rate}"> ${img} ${rate} </span>`); } content += hornySpans.join(" "); if(!content) { content = "None"; } reproductionElement.innerHTML = content; } const lootAvailableElement = document.getElementById("breeding-overhaul-loot-available"); if(lootAvailableElement && summary.loot) { let content = ""; const lootSpans = []; for(const loot in summary.loot) { const amount = summary.loot[loot]?.toLocaleString(navigator.language); const img = `<img src="${IMAGE_URL}${loot.toLowerCase()}.png" class="inline" />`; lootSpans.push(`<span title="${loot}: ${amount}"> ${img} ${amount} </span>`); } content += lootSpans.join(" "); let anyLoot = true; if(!content) { content = "None"; anyLoot = false; } lootAvailableElement.innerHTML = content; const lootAllButton = document.getElementById("breeding-overhaul-loot-all"); if(lootAllButton) { lootAllButton.disabled = !anyLoot; } } } clickLootAll() { const animalData = IdlePixelPlus.getVar("animal_data"); if(!animalData) { return; } this.parseAnimals(animalData) .filter(animal => animal.lootCount) .forEach(animal => IdlePixelPlus.sendMessage(`COLLECT_ALL_LOOT_ANIMAL`)); } updateAnimalData(animals) { if(!animals) { return; } const summary = this.summarizeAnimals(animals); this.updateAnimalSummary(summary); } focusFire(event, enemyID) { event.stopPropagation(); this.focusFireTarget = enemyID; const breedingFightingHeroes = document.querySelectorAll(".breeding-fighting-entry"); breedingFightingHeroes.forEach((el) => { const allyID = parseInt(el.id.match(/(\d+)$/)); if(typeof allyID !== "number" || isNaN(allyID)) { return; } websocket.send(`BREEDING_FIGHT_SET_TARGET=${allyID}~${enemyID}`); Breeding.flashBorder3Seconds(allyID, enemyID); }); Breeding.global_last_click_fighting_hero_animal_index = "none"; } initUI() { const style = document.createElement("style"); style.id = "styles-breeding-overhaul"; style.textContent = ` #panel-breeding #breeding-overhaul-breeding-summary { display: flex; flex-direction: column; } #panel-breeding #breeding-overhaul-breeding-summary .danger { color: red; } #panel-breeding #breeding-overhaul-breeding-summary img.inline { height: 1em; width: auto; } #breeding-overhaul-new-animal-dialog-backdrop { position: fixed; z-index: 9999; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); pointer-events: all; } #breeding-overhaul-new-animal-dialog { position: sticky; top: 3rem; border: none; padding: 1em; background-color: white; border: 4px solid black; border-radius: 8px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); min-width: 300px; } #breeding-overhaul-new-animal-dialog > img#breeding-overhaul-new-animal-image { position: absolute; top: 1em; right: 1em; height: 50px; } `; document.head.appendChild(style); const panel = document.getElementById("panel-breeding"); if(!panel) { console.error("Breeding Overhaul: Failed to initialize UI - #panel-breeding not found"); return; } const craftableButtons = panel.querySelector(".craftable-btns"); if(craftableButtons) { craftableButtons.insertAdjacentHTML("afterend", ` <div id="breeding-overhaul-breeding-summary"> <div> <strong>Total XP: </strong> <span id="breeding-overhaul-total-xp"></span> </div> <div> <strong>Daily Food: </strong> <span id="breeding-overhaul-daily-food"></span> </div> <div> <strong>Food Days: </strong> <span id="breeding-overhaul-food-days"></span> </div> <div> <strong>Reproduction: </strong> <span id="breeding-overhaul-reproduction"></span> </div> <div> <strong>Loot Available: </strong> <span id="breeding-overhaul-loot-available"></span> </div> <div> <button id="breeding-overhaul-loot-all" type="button" onclick="IdlePixelPlus.plugins.breedingoverhaul.clickLootAll()">Loot All</button> </div> </div> `); } else { console.error("Breeding Overhaul: Failed to fully initialize UI - .craftable-btns not found"); } // Breeding "Focus Fire" buttons const breedingFightingEnemies = document.querySelectorAll(".breeding-fighting-entry-monster"); breedingFightingEnemies.forEach((el) => { const enemyID = parseInt(el.id.match(/(\d+)$/)); if(typeof enemyID !== "number" || isNaN(enemyID)) { return; } const newDiv = document.createElement("div"); newDiv.innerHTML = ` <div class="center"> <button onclick="IdlePixelPlus.plugins.breedingoverhaul.focusFire(event, ${enemyID})">Focus Fire</button> </div> `; el.appendChild(newDiv); }); } markAnimalAsNew(slug) { /* FOR DEBUGGING */ delete this.animalData[slug]; } detectNewAnimals(animals) { const first = this.animalData === null; const animalIdsBefore = first ? new Set() : new Set(Object.keys(this.animalData)); const animalIdsAfter = new Set(animals.map(animal => animal.slug)); this.animalData = {}; animals.forEach(animal => this.animalData[animal.slug] = animal); if(!first) { const newAnimalIds = [...animalIdsAfter].filter(id => !animalIdsBefore.has(id)); if(newAnimalIds.length && this.getConfig("newAnimalPopup")) { const animal = this.animalData[newAnimalIds[0]]; const name = capitalizeFirstLetter(get_item_name(animal.animal)); const tier = animal.tier; const tierDots = `<span class="dot-green"></span> `.repeat(tier); const slug = animal.slug; const animalImage = `${IMAGE_URL}${animal.animal}_${Breeding.get_gender_label(animal.sexNumber)}_${animal.tier}.png`; const sexColor = animal.sex == "F" ? "pink" : "blue"; const sexImage = `${IMAGE_URL}${animal.sex=='F' ? 'female' : 'male'}.png`; const shiny = animal.shiny; const shinyDivStyle = shiny==0 ? "" : "background-color: rgba(255, 223, 0, 0.5);"; const foodAmount = animal.foodPerDay; const foodLabel = animal.food; const foodType = FOOD_MAP[animal.food]; const foodImage = `${IMAGE_URL}${foodType}.png`; const trait = animal.trait; const traitIcon = `${IMAGE_URL}${Breeding.getTraitDesc(trait, slug)[0]}.png`; const shinyLabel = (function() { switch(shiny) { case 0: return "No"; case 1: return "Shiny!"; case 2: return "Mega Shiny!"; default: return "Unknown"; } })(); const inactiveVar = `inactive_${animal.animal}_${animal.sex=='F' ? 'female' : 'male'}`; const inactiveCount = IdlePixelPlus.getVarOrDefault(inactiveVar, 0, "int").toLocaleString(); let extraButtonsHTML = ""; if(trait == "Fighter") { extraButtonsHTML += `<button onclick="IdlePixelPlus.sendMessage('ADD_FIGHT_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Make Fighter</button> `; extraButtonsHTML += `<button onclick="IdlePixelPlus.sendMessage('ADD_FIGHT_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.retryLastAnimalActivation(); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Make Fighter & Try Again</button> `; } const dialogHTML = ` <div id="breeding-overhaul-new-animal-dialog-backdrop"> <dialog id="breeding-overhaul-new-animal-dialog" open> <img id="breeding-overhaul-new-animal-image" src="${animalImage}" /> <div class="v-flex half-gap"> <h5><img src="${sexImage}" class="inline" style="height: 1em" /> T-${tier} ${name}</h5> <div><strong>Tier: </strong> ${tierDots}</div> <div><strong>Trait: </strong> <img src="${traitIcon}" class="inline" style="height: 1em" /> ${trait}</div> <div style="${shinyDivStyle}"><strong>Shiny: </strong> ${shinyLabel}</div> <div><strong>Food: </strong> <img src="${foodImage}" class="inline" style="height: 1em" /> ${foodAmount} ${foodLabel} / day</div> <div><strong>Inactive Remaining: </strong> <span id="breeding-overhaul-new-animal-dialog-inactive-count">${inactiveCount}</span></div> <div class="v-flex half-gap"> <button onclick="IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Keep</button> <button onclick="IdlePixelPlus.plugins.breedingoverhaul.retryLastAnimalActivation(); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Keep & Try Again</button> ${extraButtonsHTML} <button onclick="IdlePixelPlus.sendMessage('KILL_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Kill</button> <button onclick="IdlePixelPlus.sendMessage('KILL_ANIMAL=${slug}'); IdlePixelPlus.plugins.breedingoverhaul.retryLastAnimalActivation(); IdlePixelPlus.plugins.breedingoverhaul.closeNewAnimalDialog()">Kill & Try Again</button> </div> </div> </dialog> </div> `; this.closeNewAnimalDialog(); document.body.insertAdjacentHTML("afterbegin", dialogHTML); setTimeout(function() { const updatedInactiveCount = IdlePixelPlus.getVarOrDefault(inactiveVar, 0, "int").toLocaleString(); if(inactiveCount != updatedInactiveCount) { const inactiveCountSpan = document.getElementById("breeding-overhaul-new-animal-dialog-inactive-count"); if(inactiveCountSpan) { inactiveCountSpan.textContent = updatedInactiveCount; } } }, 50); } } } retryLastAnimalActivation() { if(typeof this.lastAnimalActivation === "string") { if(this.lastAnimalActivation.startsWith("SLAUGHTER_FOR_TIER_")) { // if number is more than we have, find new max to send const parts = this.lastAnimalActivation.split(/[~=]/g); const owned = IdlePixelPlus.getVarOrDefault(parts[1], 0, "int"); const attempted = parseInt(parts[3]); if(attempted > owned) { parts[3] = owned; } this.lastAnimalActivation = `${parts[0]}=${parts.slice(1).join("~")}`; } IdlePixelPlus.sendMessage(this.lastAnimalActivation); } } closeNewAnimalDialog() { const dialog = document.getElementById("breeding-overhaul-new-animal-dialog-backdrop"); if(dialog) { dialog.remove(); } } onMessageSent(message) { if(typeof message === "string") { if(message.startsWith("ACTIVATE_ANIMAL=") || message.startsWith("SLAUGHTER_FOR_TIER_")) { this.lastAnimalActivation = message; } } } onLogin() { // Intercept sent messages - used to track last animal action if(window.websocket) { const plugin = this; const originalSend = window.websocket.send; window.websocket.send = function(data) { plugin.onMessageSent(data); return originalSend.call(this, data); } } // Initialize new ui components this.initUI(); // override refresh function const original_refresh_animals_data = Breeding.refresh_animals_data; Breeding.refresh_animals_data = (...args) => { const animalArea = document.getElementById("breeding-chicken-area"); let raw = args[0] || null; let animals = this.parseAnimals(raw); // rejoin (sorted) for original function if(animals?.length) { raw = animals.map(animal => animal.raw).join("="); } // run original smitty function try { original_refresh_animals_data.apply(this, [raw]); } catch(err) { console.error("Error running original Breeding.refresh_animals_data: ", err); throw err; } // update ui try { this.updateAnimalData(animals); } catch(err) { console.error("Error running BreedingOverhaulPlugin.updateAnimalData: ", err); } // detect new animals try { this.detectNewAnimals(animals); } catch(err) { console.error("Error running BreedingOverhaulPlugin.detectNewAnimals: ", err); } } } onVariableSet(key, valueBefore, valueAfter) { // TODO return; switch(key) { case "is_in_breeding_fight": { console.log(`${key}: "${valueBefore}" -> "${valueAfter}"`); if(valueAfter == "1") { this.fight = {}; } else { this.fight = null; } break; } } } onMessageReceived(data) { // TODO return; try { const manual = window.var_large_chicken_beak_automated == "0"; const focusFirePeck = this.getConfig("focusFirePeck"); // requires manual since auto-mode functions server side if(manual && focusFirePeck) { if(data?.startsWith("REFRESH_BREEDING_FIGHTING=")) { data = data.substring("REFRESH_BREEDING_FIGHTING=".length).split(","); const pecksAvailable = []; for(let i = 0; i < data.length; i++) { const fighterData = data[i].split("~"); const hp = parseInt(fighterData[1]); const stamina = parseInt(fighterData[5]); const requiredStamina = parseInt(fighterData[6]); const special = fighterData[7]; console.log(`i=${i}, hp=${hp}, special=${special}, stamina=${stamina}, requiredStamina=${requiredStamina}`); if(special == "peck" && hp > 0 && stamina >= requiredStamina) { pecksAvailable.push(i); console.log(`${i} peck ready`); break; } } } } } catch(err) { console.error("Breeding Overhaul - onMessageReceived - Something went wrong while processing focus fire for special abilities. A recent game update may have broken this functionality."); } } } const plugin = new BreedingOverhaulPlugin(); IdlePixelPlus.registerPlugin(plugin); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址