娘化怪物换皮

替换怪物图标为自定义图片,同时在玩家死亡时更换人物模型为复活图像

当前为 2025-06-10 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         娘化怪物换皮
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  替换怪物图标为自定义图片,同时在玩家死亡时更换人物模型为复活图像
// @match        https://www.milkywayidle.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

// 感谢大佬VoltaX提供代码;
(function () {
  'use strict';
  const css = `
    .monster-skin-invisible{
      display: none;
    }
  `
  const InsertStyleSheet = (style) => {
      const s = new CSSStyleSheet();
      s.replaceSync(style);
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, s];
  };
  InsertStyleSheet(css);
  const monsterImageMap = {
    'fly': 'https://tupian.li/images/2025/06/04/683fc426239c5.png',
    'rat': 'https://tupian.li/images/2025/06/04/683fc47029152.png',
    'skunk': 'https://tupian.li/images/2025/06/04/683fa6997824a.png',
    'porcupine': 'https://tupian.li/images/2025/06/04/683fac1b9b65b.png',
    'slimy': 'https://tupian.li/images/2025/06/04/683fc49467b01.png',
    'frog': 'https://tupian.li/images/2025/06/01/683b2bbfd2a55.png',
    'snake': 'https://tupian.li/images/2025/06/01/683b2d2bea6cb.png',
    'swampy': 'https://tupian.li/images/2025/06/01/683b2e0597ec3.png',
    'alligator': 'https://tupian.li/images/2025/06/04/683f9c8ce135e.png',
    'sea_snail': 'https://tupian.li/images/2025/06/04/683fc5484dc24.png',
    'crab': 'https://tupian.li/images/2025/05/31/683b24df1d644.png',
    'aquahorse': 'https://tupian.li/images/2025/05/31/683b1fe9a0f68.png',
    'nom_nom': 'https://tupian.li/images/2025/05/31/683b2062365e6.png',
    'turtle': 'https://tupian.li/images/2025/05/31/683b2185a6149.png',
    'jungle_sprite': 'https://tupian.li/images/2025/05/28/6836a73f15139.png',
    'myconid': 'https://tupian.li/images/2025/05/28/6836a7bd9f025.png',
    'treant': 'https://tupian.li/images/2025/05/29/68380fd7d96a7.png',
    'centaur_archer': 'https://tupian.li/images/2025/05/29/68380ff4b0c1c.png',
    'gobo_stabby': 'https://tupian.li/images/2025/05/27/683558db30944.png',
    'gobo_slashy': 'https://tupian.li/images/2025/05/27/6835568b93282.png',
    'gobo_smashy': 'https://tupian.li/images/2025/05/27/6835569105822.png',
    'gobo_shooty': 'https://tupian.li/images/2025/05/27/68355b0cc3b6d.png',
    'gobo_boomy': 'https://tupian.li/images/2025/05/28/6836a114147dd.png',
    'eye': 'https://tupian.li/images/2025/05/26/6833d05055c33.png',
    'veyes': 'https://tupian.li/images/2025/05/26/6833d725e9851.png',
    'eyes': 'https://tupian.li/images/2025/05/26/6833d42325add.png',
    'novice_sorcerer': 'https://tupian.li/images/2025/05/15/682547e3837af.png',
    'ice_sorcerer': 'https://tupian.li/images/2025/05/15/68254b93d8115.png',
    'flame_sorcerer': 'https://tupian.li/images/2025/05/15/682554040216c.png',
    'elementalist': 'https://tupian.li/images/2025/05/15/68255648e29f6.png',
    'gummy_bear': 'https://tupian.li/images/2025/05/15/68255fdbc65e4.png',
    'panda': 'https://tupian.li/images/2025/05/26/6833ca3da317e.png',
    'black_bear': 'https://tupian.li/images/2025/05/26/6833cac48e339.png',
    'grizzly_bear': 'https://tupian.li/images/2025/05/26/6833cb092398a.png',
    'polar_bear': 'https://tupian.li/images/2025/05/26/6833cb613c407.png',
    'granite_golem': 'https://tupian.li/images/2025/05/12/6821653433b19.png',
    'magnetic_golem': 'https://tupian.li/images/2025/05/12/682161da65fb5.png',
    'stalactite_golem': 'https://tupian.li/images/2025/05/12/6821768be1f4c.png',
    'zombie': 'https://tupian.li/images/2025/05/22/682e86f600c9b.png',
    'vampire': 'https://tupian.li/images/2025/05/22/682e88479710a.png',
    'werewolf': 'https://tupian.li/images/2025/05/22/682e910cd2ae3.png',
    'abyssal_imp': 'https://tupian.li/images/2025/05/19/682b00a051ac3.png',
    'soul_hunter': 'https://tupian.li/images/2025/05/20/682be59cc6c89.png',
    'infernal_warlock': 'https://tupian.li/images/2025/05/20/682be69d6e6fb.png',
    // boss
    'giant_shoebill': 'https://tupian.li/images/2025/06/04/683f9f7508e6f.png',
    'marine_huntress': 'https://tupian.li/images/2025/05/31/683b249489376.png',
    'luna_empress': 'https://tupian.li/images/2025/05/29/68381033a00a0.png',
    'gobo_chieftain': 'https://tupian.li/images/2025/05/28/6836a01b42125.png',
    'the_watcher': 'https://tupian.li/images/2025/05/26/6833da1da9bd5.png',
    'chronofrost_sorcerer': 'https://tupian.li/images/2025/05/15/682559ecdc1b9.png',
    'red_panda': 'https://tupian.li/images/2025/05/19/682afebf033a5.png',
    'crystal_colossus': 'https://tupian.li/images/2025/05/13/6822afe43314a.png',
    'dusk_revenant': 'https://tupian.li/images/2025/05/23/6830019de887a.png',
    'demonic_overlord': 'https://tupian.li/images/2025/05/20/682c51856505a.png',
    //地下城1
    'butterjerry': 'https://tupian.li/images/2025/06/05/68410adee5214.png',
    'jackalope': 'https://tupian.li/images/2025/06/10/6847fdabbdcc1.png',
    'dodocamel': 'https://tupian.li/images/2025/06/06/684295d91f752.png',
    'manticore': 'https://tupian.li/images/2025/06/10/6847ff3b6f77b.png',
    'griffin': 'https://tupian.li/images/2025/06/10/6847f1e89826f.png',
    //地下城2
    'rabid_rabbit': 'https://tupian.li/images/2025/06/05/68410450af761.png',
    'zombie_bear': 'https://tupian.li/images/2025/06/05/684100cf8d239.png',
    'acrobat': 'https://tupian.li/images/2025/06/05/684104884c419.png',
    'juggler': 'https://tupian.li/images/2025/06/05/684101e1cb408.png',
    'magician': 'https://tupian.li/images/2025/06/05/684105731c385.png',
    'deranged_jester': 'https://tupian.li/images/2025/06/05/6841058d0e69d.png',
   //地下城3
    'enchanted_pawn': 'https://tupian.li/images/2025/05/23/683007f32f92d.png',
    'enchanted_knight': 'https://tupian.li/images/2025/05/23/6830041252057.png',
    'enchanted_bishop': 'https://tupian.li/images/2025/05/23/683004e1c3e59.png',
    'enchanted_rook': 'https://tupian.li/images/2025/05/23/6830099fb9494.png',
    'enchanted_queen': 'https://tupian.li/images/2025/05/23/68300ec809402.png',
    'enchanted_king': 'https://tupian.li/images/2025/05/23/683011ce17fb6.png',
   //地下城4
    //'squawker': 'https://tupian.li/images/2025/04/29/68106e8bbc823.png',
    //'anchor_shark': 'https://tupian.li/images/2025/04/29/68106e88f0b72.png',
    //'brine_marksman': 'https://tupian.li/images/2025/04/29/68106e87b9066.png',
    //'tidal_conjuror': 'https://tupian.li/images/2025/04/29/68106e83e8263.png',
    //'captain_fishhook': 'https://tupian.li/images/2025/04/29/68106e867adf5.png',
    //'the_kraken': 'https://tupian.li/images/2025/04/29/68106e84975c8.png',
  };


  const reviveImageUrl = 'https://tupian.li/images/2025/04/26/680cb8ea3d2b6.jpg'; // 玩家战败cg
  let isPlayerReplaced = false;
  let originalPlayerModel = null;

  const CreateCustomIconElement = (imgsrc, svg, monsterId) => { // 将创建自定义图标的功能独立出来
    const img = document.createElement('img');
    img.src = imgsrc;
    img.style.width = svg.getAttribute('width') || '100%';
    img.style.height = svg.getAttribute('height') || '100%';
    img.style.objectFit = 'contain';
    img.classList.add("replaced-monster-skin");
    img.setAttribute("data-monster-id", monsterId);
    return img;
  }
  // 负责修改在「交战#」一栏中出现的怪物名字和图标
  const ReplaceMonsterName = () => {
    document.querySelectorAll("div.CombatUnit_name__1SlO1").forEach(nameDiv => { // 从战斗单位的名字入手
      const iconWrapper = nameDiv.parentElement.querySelector(":scope div.CombatUnit_monsterIcon__2g3AZ"); // 获得怪物图标元素
      if(!iconWrapper) return;
      if(iconWrapper.children[0].localName === "svg"){
        const svg = iconWrapper.children[0];
        const monsterId = svg.getAttribute("aria-label").split("/").at(-1); // 获得怪物正式名
        if(svg.nextElementSibling && svg.nextElementSibling.dataset.monsterId === monsterId) return; // 如果之前已经加过自定义图标,并且这个自定义图标和怪物相符合,直接返回
        const imgsrc = monsterImageMap[monsterId];
        if(imgsrc){
          const img = CreateCustomIconElement(imgsrc, svg, monsterId);
          if(svg.nextElementSibling && svg.nextElementSibling.classList.contains("replaced-monster-skin")){ // 如果之前加过自定义图标,那么这个自定义图标过期,替换为新的自定义图标
            svg.nextElementSibling.replaceWith(img);
          }
          else svg.insertAdjacentElement("afterend", img); // 否则直接黏在<svg>后面
          svg.classList.add("monster-skin-invisible"); // 隐藏原<svg>图标
        }
      }
    });
  };
  // 负责修改出现在「战斗区域」一栏中的怪物名字和图标
  const replaceIcons = () => {
    document.querySelectorAll('div.SkillAction_skillAction__1esCp:not([modified]), div.SkillActionDetail_regularComponent__3oCgr:not([modified]), div.CombatMonsterTooltip_combatMonsterTooltip__3TWKx:not([modified])').forEach(div => {
      const name = div.children[0].textContent;
      div.setAttribute("modified", ""); // 已经修改过的图标之后不再检查
      for(const svg of [...div.querySelectorAll(":scope svg")]){
        const use = svg.children[0];
        const monsterId = use.href.baseVal.split("#").at(-1); // 获得怪物正式名
        const imgsrc = monsterImageMap[monsterId];
        if(imgsrc){
          const img = CreateCustomIconElement(imgsrc, svg, monsterId);
          svg.replaceWith(img);
        }
      }
    });
  };
  const ReplaceTasks = () => {
    document.querySelectorAll("div.RandomTask_name__1hl1b:not([checked])").forEach(div => {
      div.setAttribute("checked", "");
      const svg = div.children[0];
      if(svg.getAttribute("aria-label") !== "Combat") return;
      const taskTitle = div.childNodes[1].textContent;
      const taskTitleComp = taskTitle.split("-");
      const possibleMonsterName = taskTitleComp.at(-1).trim();
      console.log(possibleMonsterName);
    })
  }

  // 检查并替换玩家战败cg
  const checkReviveStatus = () => {
    const unit = document.querySelector('.CombatUnit_combatUnit__1m3XT');
    const reviveOverlay = unit?.querySelector('.CountdownOverlay_countdownOverlay__2QRmL');
    const modelContainer = unit?.querySelector('.CombatUnit_model__2qQML');

    if (unit && modelContainer) {
      if (reviveOverlay && !isPlayerReplaced) {
        // 保存原始内容
        originalPlayerModel = modelContainer.cloneNode(true);
        // 替换为复活图像
        modelContainer.innerHTML = `
          <div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;">
            <img src="${reviveImageUrl}" alt="复活中" style="max-height: 100px;" />
          </div>
        `;
        isPlayerReplaced = true;
      } else if (!reviveOverlay && isPlayerReplaced && originalPlayerModel) {
        // 恢复原模型
        modelContainer.replaceWith(originalPlayerModel);
        isPlayerReplaced = false;
        originalPlayerModel = null;
      }
    }
  };


  const ObserveHeader = () => {
    const header = document.querySelector("div.Header_displayName__1hN09:not([observing])");
    if(!header) return;
    header.setAttribute("observing", "");
    const ReplaceHeader = (mutlist, observer) => {
      observer.disconnect();
      const headerText = header.textContent;
      observer.observe(header, {childList: true, subtree: true, characterData: true});
    };
    const observer = new MutationObserver(ReplaceHeader)
    observer.observe(header, {childList: true, subtree: true, characterData: true});
    ReplaceHeader(null, observer);
  };
  const ReplaceQueuedActions = () => {
    document.querySelectorAll("div.QueuedActions_text__3iRiY:not([modified])").forEach(div => {
      div.setAttribute("modified", "");
      div.childNodes.forEach(node => {
        if(node.nodeType !== Node.TEXT_NODE) return;
      })
    });
  };

  // 启动监听器和定时器
  const observer = new MutationObserver((mutlist, observer) => {
    replaceIcons();
    checkReviveStatus();
    ReplaceMonsterName();
    ReplaceTasks();
    ReplaceQueuedActions();
    ObserveHeader();
  });

  observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class"] });

  // 初始延迟触发一次
  setTimeout(() => {
    replaceIcons();
    checkReviveStatus();
  }, 500);

  // 定时检查(确保复活状态不会漏)
  setInterval(checkReviveStatus, 1000);
})();