Greasy Fork

MWITools

Tools for MilkyWayIdle. Shows total action time. Shows market prices. Shows action number quick inputs. Shows how many actions are needed to reach certain skill level. Shows skill exp percentages. Shows total networth. Shows combat summary. Shows combat maps index. Shows item level on item icons. Shows how many ability books are needed to reach certain level. Shows market equipment filters.

< 脚本 MWITools 的反馈

评价:好评 - 脚本运行良好

§
发布于:2025-06-16

Like the tool!

Added some features:

function parseShorthandNumber(text) {
if (!text) return NaN;

const cleaned = text.trim().replace(/,/g, "").toUpperCase(); // Remove commas and normalize case

const match = cleaned.match(/^([\d.]+)([KMB])?$/);
if (!match) return parseFloat(cleaned); // Fallback if no suffix

const number = parseFloat(match[1]);
const suffix = match[2];

const multiplier = {
K: 1e3,
M: 1e6,
B: 1e9,
};

return suffix ? number * multiplier[suffix] : number;
}

async function showRequiredItems(panel, maxNum) {
const requiresDiv = panel.querySelector("div.SkillActionDetail_itemRequirements__3SPnA");
if (!requiresDiv) return;

const children = Array.from(requiresDiv.children);

const inventorySpans = panel.querySelectorAll("span.SkillActionDetail_inventoryCount__tHmPD");
const inputSpans = Array.from(
panel.querySelectorAll("span.SkillActionDetail_inputCount__1rdrn")
).filter(span => !span.innerText.includes("Required"));
const possibleCounts = [];

for (let i = 0; i < inventorySpans.length && i < inputSpans.length; i++) {
const invText = inventorySpans[i].innerText.trim().replace(/,/g, "");
const inputText = inputSpans[i].innerText.trim();

const invValue = parseShorthandNumber(invText);
const match = inputText.match(/\/\s*([\d,]+)/);
const inputValue = match ? parseInt(match[1].replace(/,/g, ""), 10) : NaN;

if (!isNaN(invValue) && !isNaN(inputValue) && inputValue > 0) {
const multipliedValue = inputValue * maxNum;
if (multipliedValue > invValue) possibleCounts.push(multipliedValue);
}
}

children.forEach((child, index) => {
if (child.className.includes("inputCount")) {
const match = child.textContent.trim().match(/\/\s*([\d,]+)/);
if (!match) return;

const value = parseInt(match[1].replace(/,/g, ""), 10); // Remove commas before parsing
if (isNaN(value)) return;

const multipliedValue = value * maxNum;

const newText = `Required: ${multipliedValue.toLocaleString()}`; // Optional formatting

const targetContainer = requiresDiv.children[index + 1];
if (!targetContainer) return;

// Keep only the first child (assumed to be a

), remove the rest
while (targetContainer.children.length > 1) {
targetContainer.removeChild(targetContainer.lastChild);
}
// Check if a child with the same className and newText already exists
const alreadyExists = Array.from(targetContainer.children).some(el =>
el.className === child.className && el.textContent.trim() === newText
);

if (!alreadyExists) {
const clone = child.cloneNode(true);
clone.textContent = newText;
clone.style.color = possibleCounts.includes(multipliedValue) ? "red" : "gold";
targetContainer.appendChild(clone);
}
}
});
showOutputEXP(panel, maxNum);
}
async function showOutputEXP(panel, amount) {

const outputDiv = panel.querySelector("div.SkillActionDetail_expGain__F5xHu");
if (!outputDiv) return;

const clone = outputDiv.cloneNode(true);
const parent = outputDiv.parentNode;
const svg = outputDiv.firstElementChild.outerHTML;

const value = parseFloat(outputDiv.textContent.replace(/,/g, ""), 10); // Remove commas before parsing
if (isNaN(value)) return;

const multipliedValue = value * amount;

const newText = `Outputs: ${multipliedValue.toLocaleString()}${svg}`; // Optional formatting

// Keep only the first child (assumed to be a
), remove the rest
while (parent.children.length > 2) {
parent.removeChild(parent.children[1]);
}

// Check if a child with the same className and newText already exists
const alreadyExists = Array.from(parent.children).some(el =>
el.className === clone.className && el.textContent.trim() === newText
);

if (!alreadyExists) {
clone.innerHTML = newText;
clone.style.color = "gold";
parent.insertBefore(clone, parent.children[parent.children.length - 1]);
}
}

async function addMaxLButton(quickInputButtonsDiv, panel, inputElem) {
const inventorySpans = panel.querySelectorAll("span.SkillActionDetail_inventoryCount__tHmPD");
const inputSpans = Array.from(
panel.querySelectorAll("span.SkillActionDetail_inputCount__1rdrn")
).filter(span => !span.innerText.includes("Required"));
const possibleCounts = [];
for (let i = 0; i < inventorySpans.length && i < inputSpans.length; i++) {
const invText = inventorySpans[i].innerText.trim().replace(/,/g, "");
const inputText = inputSpans[i].innerText.trim();

const invValue = parseShorthandNumber(invText);
const match = inputText.match(/\/\s*([\d,]+)/);
const inputValue = match ? parseInt(match[1].replace(/,/g, ""), 10) : NaN;

if (!isNaN(invValue) && !isNaN(inputValue) && inputValue > 0) {
possibleCounts.push(Math.floor(invValue / inputValue));
}
}

const m_value = possibleCounts.length ? Math.min(...possibleCounts) : 0;

const button = document.createElement("button");
button.style.backgroundColor = "white";
button.style.color = "black";
button.style.padding = "1px 6px 1px 6px";
button.style.margin = "1px";
button.innerText = "MAX_LOW";

button.onclick = () => {
reactInputTriggerHack(inputElem, m_value);
showRequiredItems(panel, m_value);
};

quickInputButtonsDiv.append(button);
}

async function addMaxHButton(quickInputButtonsDiv, panel, inputElem) {
const inventorySpans = panel.querySelectorAll("span.SkillActionDetail_inventoryCount__tHmPD");
const inputSpans = Array.from(
panel.querySelectorAll("span.SkillActionDetail_inputCount__1rdrn")
).filter(span => !span.innerText.includes("Required"));
const possibleCounts = [];

for (let i = 0; i < inventorySpans.length && i < inputSpans.length; i++) {
const invText = inventorySpans[i].innerText.trim().replace(/,/g, "");
const inputText = inputSpans[i].innerText.trim();

const invValue = parseShorthandNumber(invText);
const match = inputText.match(/\/\s*([\d,]+)/);
const inputValue = match ? parseInt(match[1].replace(/,/g, ""), 10) : NaN;

if (!isNaN(invValue) && !isNaN(inputValue) && inputValue > 0) {
possibleCounts.push(Math.floor(invValue / inputValue));
}
}

const m_value = possibleCounts.length ? Math.max(...possibleCounts) : 0;

const button = document.createElement("button");
button.style.backgroundColor = "white";
button.style.color = "black";
button.style.padding = "1px 6px 1px 6px";
button.style.margin = "1px";
button.innerText = "MAX_HIGH";

button.onclick = () => {
reactInputTriggerHack(inputElem, m_value);
showRequiredItems(panel, m_value);
};

quickInputButtonsDiv.append(button);
}

async function addGatherMax(quickInputButtonsDiv, panel, inputElem, actionName, effBuff, duration, exp) {

const skillHrid = initData_actionDetailMap[getActionHridFromItemName(actionName)].experienceGain.skillHrid;
let currentExp = null;
let currentLevel = null;
for (const skill of initData_characterSkills) {
if (skill.skillHrid === skillHrid) {
currentExp = skill.experience;
currentLevel = skill.level;
break;
}
}
const calculateNeedToLevel = (currentLevel, targetLevel, effBuff, duration, exp) => {
let needTotalTimeSec = 0;
let needTotalNumOfActions = 0;
for (let level = currentLevel; level < targetLevel; level++) {
let needExpToNextLevel = null;
if (level === currentLevel) {
needExpToNextLevel = initData_levelExperienceTable[level + 1] - currentExp;
} else {
needExpToNextLevel = initData_levelExperienceTable[level + 1] - initData_levelExperienceTable[level];
}
const extraLevelEffBuff = (level - currentLevel) * 0.01; // 升级过程中,每升一级,额外多1%效率
const needNumOfActionsToNextLevel = Math.round(needExpToNextLevel / exp);
needTotalNumOfActions += needNumOfActionsToNextLevel;
needTotalTimeSec += (needNumOfActionsToNextLevel / (effBuff + extraLevelEffBuff)) * duration;
}
return { numOfActions: needTotalNumOfActions, timeSec: needTotalTimeSec };
};
const m_value = calculateNeedToLevel(currentLevel, currentLevel + 1, effBuff, duration, exp).numOfActions;
const button = document.createElement("button");
button.style.backgroundColor = "white";
button.style.color = "black";
button.style.padding = "1px 6px 1px 6px";
button.style.margin = "1px";
button.innerText = m_value;
button.id = "maxForGather";

button.onclick = () => {
reactInputTriggerHack(inputElem, parseInt(button.innerText));
showRequiredItems(panel, parseInt(button.innerText));
};

quickInputButtonsDiv.append(button);
}
§
发布于:2025-06-16

basically added:

1. MAX_LOW and MAX_HIGH; this is give you either the lowest or highest possible crafting with the current items owned.
2. A button to use the "To reach level XXX need to do XXX times" times.
3. Added how much of each requirement is need for the given amount to produce.
4. Added how much the requested Produce outputs EXP.

发布留言

登录以发布留言。