// ==UserScript==
// @name Wod增强
// @namespace https://github.com/knight000/Wod_Script
// @description 提供如下功能:1.自动荣誉投票 2.自动减少地城探索时间 3.链接替换为新窗口打开 4.登录失效自动返回登录页面 5.显示技能类别提示
// @author knight000
// @match http*://*.world-of-dungeons.org/*
// @require https://cdn.jsdelivr.net/npm/[email protected]/dayjs.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/plugin/customParseFormat.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js
// @icon http://info.world-of-dungeons.org/wod/css/WOD.gif
// @require https://code.jquery.com/jquery-3.3.1.min.js
// @grant GM_addStyle
// @modifier Christophero
// @version 2023.12.21.1
// ==/UserScript==
const WOD_ENHANCE_CONFIG = "WOD_ENHANCE_CONFIG";
let funArr = [];
let $panelBody = null;
const baseParams = [
"session_hero_id",
"wod_post_id",
"wod_post_world",
"klasse_id",
"klasse_name",
"rasse_id",
"rasse_name",
"gruppe_id",
"gruppe_name",
"clan_id",
"clan_name",
"stufe",
"heldenname",
"spielername",
];
const attrEnMap = {
力量: "st",
体质: "ko",
智力: "in",
灵巧: "ge",
魅力: "ch",
敏捷: "sn",
感知: "wa",
意志: "wi",
};
let globalSteps = 0;
let globalCurSteps = 0;
(function () {
"use strict";
// 相关模块,注释掉来取消使用
addControlPanel();
funControl("autoVote", "自动投票", autoVote, false);
funControl("autoReduce", "加速探索", autoReduce, true);
funControl("replaceLink", "链接新窗口", replaceLink, true);
funControl("autoGotoLogin", "自动跳转登录", autoGotoLogin, true);
funControl("showSkillTypeTips", "技能类别提示", showSkillTypeTips, true);
funControl("forceDisplayTip", "手机强制提示", forceDisplayTip, true);
funControl("chaseWarning", "反抗军追击警告", chaseWarning, true);
funControl("easySelling", "出售增强", easySelling, true);
funControl("quickViewDrop", "地城掉落", quickViewDrop, true);
funControl("styleEnhance", "样式优化", styleEnhance, true);
funControl("calcNpcPrice", "交易物品总N价", calcNpcPrice, true);
funControl("resetSkillPoint", "技能重置", resetSkillPoint, false);
funControl("refreshSkill", "刷新技能数值", refreshSkill, false);
funControl("smartSelect", "批量框选", smartSelect, false);
funControl("cancelDungeon", "取消探险", cancelDungeon, true);
funControl("battlePreCheck", "耗材检查", battlePreCheck, true);
funControl("multiTombola", "彩票十连", multiTombola, true);
funControl("sibebarDungeon", "侧边选地城", sibebarDungeon, true);
funControl("easyAttrVal", "简易属性提升", easyAttrVal, false);
funControl("easyChangeSkill", "简易技能等级", easyChangeSkill, false);
funControl("importHeroTemplate", "导入英雄模版", importHeroTemplate, false);
funControl("batchDelProfile", "批量删除设置", batchDelProfile, true);
funControl("batchChangeOwner", "物品归属变更", batchChangeOwner, true);
funControl("autoCleanWarehouse", "自动清仓", autoCleanWarehouse, false);
funControl("viewFullScreen", "主体切换全屏", viewFullScreen, false);
})();
function addControlPanel() {
const $main = $(
`
<br>
<div class="spoilerbox">
<input type="button" class="spoilerbutton" value="+" onclick="this.value=this.value=='+'?'-':'+';">
<span class="spoilerheader"><b>Wod增强控制面板</b></span>
<div class="spoiler">
<div>
<button id="saveEnhanceConfig" class="button clickable" title="仅启用勾选功能">仅启用勾选功能</button><br>
</div>
</div>
</div>`
);
$("#gadgettable-left").append($main);
try {
$panelBody = $main.find(".spoiler>div");
} catch (error) {
console.log(error);
}
$("#saveEnhanceConfig").click(function () {
if (confirm("是否仅启用当前勾选功能?")) {
const config = loadConfig();
$panelBody.find("input:checkbox").each((i, e) => {
config[$(e).attr("name")] = $(e).is(":checked");
});
saveConfig(config);
alert("保存完毕");
}
});
}
function funControl(name, label, fun, defStatus) {
let config = loadConfig();
if (!config.hasOwnProperty(name)) {
config[name] = defStatus;
saveConfig(config);
}
const $chk = $(`<input type="checkbox" name="${name}">`);
$chk.prop("checked", config[name]);
$panelBody.append($chk).append(label).append("<br>");
if (config[name]) {
fun();
} else {
console.log(label + " 功能未启用");
}
}
function loadConfig() {
let configJson = localStorage.getItem(WOD_ENHANCE_CONFIG);
try {
return JSON.parse(configJson) || {};
} catch (ex) {
return {};
}
}
function saveConfig(config) {
localStorage.setItem(WOD_ENHANCE_CONFIG, JSON.stringify(config));
}
/**
* @function 自动投票模块
*/
function autoVote() {
const $voteBanner = $(".vote.banner > a");
const $voteReward = $(".vote.reward");
// 如果有$(".vote.banner")就表明是投票页面进行操作
if ($voteBanner.length != 0) {
// 移除链接,这样就不会弹出窗口了
$voteBanner.removeAttr("href");
for (let i = 0; i < $voteBanner.length; i++) {
console.log("[Wod增强]正在检测投票链接" + i);
let $voteSpan;
try {
// 把投票链接后的说明提取出来,以此判定是否已投票,正则把多余的"去除
$voteSpan = $voteReward[i]
.getElementsByTagName("span")[0]
.innerHTML.replace(/"/g, "");
} catch (ex) {
continue;
}
// 如果是5或者3荣誉就进行点击
if (($voteSpan && $voteSpan[0] == "5") || $voteSpan[0] == "3") {
$voteBanner[i].click();
break;
}
}
console.log("[Wod增强]全部投票完毕");
} else {
// 非投票页面检测未投票就打开投票窗口,如果默认设置拦截弹出窗口的话就打不开了
if ($("center:contains('点链接获5')").length != 0) {
window.open(
$("a[href^='/wod/spiel/rewards/vote.php?']:last").attr("href")
);
console.log("[Wod增强]非投票页面,未投票,已跳转");
} else {
console.log("[Wod增强]非投票页面,已投票");
}
}
}
/**
* @function 自动减少地域探索时间模块
*/
function autoReduce() {
let $reduce = $('[name="reduce_dungeon_time"]');
// 有这个按钮就按一下
if ($reduce.length) {
$reduce[0].click();
console.log("[Wod增强]已自动减少地域时间");
} else {
console.log("[Wod增强]无需自动减少地域时间");
}
}
/**
* @function 链接替换为新窗口打开模块
*/
function replaceLink() {
// 团队说明的快速链接
$("div.gadget.groupmsg.lang-cn a[target!='_blank']").attr("target", "_blank");
// 物品链接
$("a.item_unique[target!='_blank']").attr("target", "_blank");
// 拍卖详情
$("a:contains('详情')[target!='_blank']").attr("target", "_blank");
// 英雄详情
$("a.hero_active,a.hero_inactive").attr("target", "_blank");
console.log("[Wod增强]链接替换为新窗口打开");
}
/**
* @function 自动跳转登录页面
*/
function autoGotoLogin() {
if (document.title && document.title.endsWith(" OR cookies disabled.")) {
window.location = location.origin;
}
}
/**
* @function 显示技能类别提示
*/
function showSkillTypeTips() {
const genAuctionAnchor = (skillName) => {
let skillDisplayUrl = `/wod/spiel/hero/skill.php?name=${skillName}&IS_POPUP=1`;
return `<a href=\\"${skillDisplayUrl}\\" target=\\"_blank\\" onclick=\\"return wo(\\'${skillDisplayUrl}\\');\\">${skillName}</a>`;
};
// 添加全局技能类别查看
function renderSkillTips(skillMap) {
let $tds = $('td.content_table:contains("类别的所有技能")');
$tds.each(function (i) {
const type = $(this).find("i").text();
const skillList = skillMap[type];
if (!skillList) {
$(this).attr(
"onmouseover",
`wodToolTip(this, "<b>目前尚未收录 ${type} 类别的技能</b>")`
);
return;
}
let str = "<b>" + type + " 类别包含以下技能</b><br>";
str += skillList
.map((skillName) => genAuctionAnchor(skillName))
.join("<br>");
$(this).attr("onmouseover", `wodToolTip(this, "${str}")`);
$(this).click(function () {
window.open(
`https://www.christophero.xyz/skill?type=${encodeURIComponent(type)}`,
"_blank"
);
});
});
}
const SKILL_MAP_KEY = "skillMap";
const SKILL_MAP_VERSION = "skillMapVersion";
let skillMapStr = localStorage.getItem(SKILL_MAP_KEY);
let skillMapVer = localStorage.getItem(SKILL_MAP_VERSION);
let skillMap = {};
let nowTime = new Date().getTime();
if (
skillMapStr &&
skillMapVer &&
nowTime - skillMapVer < 7 * 24 * 60 * 60 * 1000
) {
skillMap = JSON.parse(skillMapStr);
renderSkillTips(skillMap);
} else {
// 获取技能信息
fetch("https://www.christophero.xyz/wod/skill/all", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
return response.json();
})
.then((res) => {
if (!(res && res.code === 200)) {
return;
}
const data = res.data;
for (let skill of data) {
if (!skillMap.hasOwnProperty(skill.type)) {
skillMap[skill.type] = [skill.name];
} else {
skillMap[skill.type].push(skill.name);
}
}
localStorage.setItem(SKILL_MAP_KEY, JSON.stringify(skillMap));
localStorage.setItem(SKILL_MAP_VERSION, nowTime);
renderSkillTips(skillMap);
});
}
}
/**
*
* @returns 返回是否是移动端
*/
function isMobile() {
const userAgentInfo = navigator.userAgent;
const mobileAgents = [
"Android",
"iPhone",
"SymbianOS",
"Windows Phone",
"iPad",
"iPod",
];
let mobileFlag = false;
//根据userAgent判断是否是手机
for (let v = 0; v < mobileAgents.length; v++) {
if (userAgentInfo.indexOf(mobileAgents[v]) > 0) {
mobileFlag = true;
break;
}
}
const screen_width = window.screen.width;
const screen_height = window.screen.height;
//根据屏幕分辨率判断是否是手机
if (screen_width < 500 && screen_height < 800) {
mobileFlag = true;
}
return mobileFlag;
}
/**
* 强制显示提示,重写了部分显示代码
*/
function forceDisplayTip() {
if (isMobile()) {
if (typeof GM_addStyle == "undefined") {
function GM_addStyle(styles) {
var S = document.createElement("style");
S.type = "text/css";
var T = "" + styles + "";
T = document.createTextNode(T);
S.appendChild(T);
document.body.appendChild(S);
return;
}
}
GM_addStyle(`
.tooltip {
min-width: 150px;
min-height: 90px;
}
`);
$('[src$="images/icons/inf.gif"]').click(function () {
return false;
});
window._wodTooltipSetSize = function (content) {
var offsetX, offsetY, screenWidth, screenHeight;
if (self.pageYOffset) {
offsetX = self.pageXOffset;
offsetY = self.pageYOffset;
} else if (
document.documentElement &&
document.documentElement.scrollTop
) {
offsetX = document.documentElement.scrollLeft;
offsetY = document.documentElement.scrollTop;
} else if (document.body) {
offsetX = document.body.scrollLeft;
offsetY = document.body.scrollTop;
}
if (self.innerHeight) {
screenWidth = self.innerWidth;
screenHeight = self.innerHeight;
} else if (
document.documentElement &&
document.documentElement.clientHeight
) {
screenWidth = document.documentElement.clientWidth;
screenHeight = document.documentElement.clientHeight;
} else if (document.body) {
screenWidth = document.body.clientWidth;
screenHeight = document.body.clientHeight;
}
if (typeof screenWidth == "undefined" || screenWidth <= 10)
screenWidth = 800;
if (typeof screenHeight == "undefined" || screenHeight <= 10)
screenHeight = 600;
var top;
var bottom;
var left;
var right;
if (
wodToolTipCurrentMouseY + wodToolTip_OffY + content.offsetHeight >
screenHeight - wodToolTip_ScrollBarWidth
) {
bottom = wodToolTip_OffY;
if (
content.offsetHeight <
screenHeight - 2 * wodToolTip_ScrollBarWidth
) {
top =
screenHeight -
content.offsetHeight -
wodToolTip_ScrollBarWidth -
wodToolTip_OffY;
} else top = wodToolTip_OffY;
} else {
top = wodToolTipCurrentMouseY + wodToolTip_OffY;
bottom =
screenHeight -
(wodToolTipCurrentMouseY +
wodToolTip_OffY +
content.offsetHeight +
wodToolTip_ScrollBarWidth);
}
if (
wodToolTipCurrentMouseX + wodToolTip_OffX + content.offsetWidth >
screenWidth - wodToolTip_ScrollBarWidth
) {
right = wodToolTip_OffX;
if (content.offsetWidth < screenWidth - 2 * wodToolTip_ScrollBarWidth) {
left =
screenWidth -
content.offsetWidth -
wodToolTip_ScrollBarWidth -
wodToolTip_OffX;
} else left = wodToolTip_OffX;
} else {
left = wodToolTipCurrentMouseX + wodToolTip_OffX;
right =
screenWidth -
(wodToolTipCurrentMouseX +
wodToolTip_OffX +
content.offsetWidth +
wodToolTip_ScrollBarWidth);
}
var is_absolut_position = isSafari2() || isIE6() || isChrome();
if (is_absolut_position) {
top += offsetY;
bottom -= offsetY;
left += offsetX;
right -= offsetX;
}
content.style.top = top + "px";
content.style.left = left + "px";
};
}
}
/**
* 反抗军追击警告
*/
function chaseWarning() {
const $dungeons = $(
'.gadget.nextdungeon a.menu, form[action^="/wod/spiel/dungeon/dungeon.php"] table.top .content_table>tbody>tr:visible>td:first-child'
);
if (!$dungeons.length) return;
const day = new Date().getDay();
$dungeons.each(function (i, e) {
const $dungeon = $(e);
const dungeonName = $dungeon.text().trim();
const chuyunfeiHauntDungeons = [
"发狂的炼金术士",
"格兰斯凯巴的生命之水",
"银刺",
"竞技比赛的前夜",
];
const guangtoHauntDungeons = ["尤里佛的一天", "冰冷大地"];
const liyunlongHauntDungeons = [
"阴影之心",
"图书馆的一夜",
"冰冷大地",
"血腥之手",
"尤里佛的一天",
"一场公平的决斗",
"笼罩缇琳的阴云",
"父债子偿",
];
if (
chuyunfeiHauntDungeons.includes(dungeonName) ||
guangtoHauntDungeons.includes(dungeonName) ||
liyunlongHauntDungeons.includes(dungeonName)
) {
$dungeon.css({ color: "#FF8C27" });
let tip = "";
if ("冰冷大地" == dungeonName) {
if ([0, 1, 3, 5].includes(day)) {
tip += "星期一三五七该地城3层房间2将与<b>强大的阿瑞露</b>为敌!<br>";
} else {
tip +=
"星期二四六该地城3层房间2将与阿瑞露一起对抗<b>顺劈骑士团</b>!<br>";
}
}
tip += "携带反抗军装备时,<br>";
if (chuyunfeiHauntDungeons.includes(dungeonName)) {
tip += "该地城有<b>林云</b>出没<br>";
}
if (guangtoHauntDungeons.includes(dungeonName)) {
tip += "该地城有<b>光头</b>出没<br>";
}
// 周日圣女放假
if (liyunlongHauntDungeons.includes(dungeonName)) {
tip += "除周日外该地城有<b>圣女</b>出没<br>";
}
tip +=
'查看<a href="https://www.christophero.xyz/itemList?categoryName=%E5%8D%A1%E6%B4%9B%E6%96%AF%E5%8F%8D%E6%8A%97%E5%86%9B%E6%94%B9%E8%89%AF%E5%85%B5%E5%99%A8" target="_blank" >反抗军武器</a><br>';
tip +=
'重点关注: 【<a href="/wod/spiel/hero/item.php?name=%E7%81%B0%E6%9A%AE%E4%B9%8B%E5%BD%A2&is_popup=1" target="_blank" >灰暮之形</a>】【<a href="/wod/spiel/hero/item.php?name=%E6%96%A9%E6%96%AD%E5%9B%A0%E6%9E%9C%E7%9A%84%E5%87%8C%E5%86%BD%E6%B3%95%E5%88%83&is_popup=1" target="_blank" >斩断因果的凌冽法刃</a>】';
$dungeon.mouseenter(function () {
wodToolTip(e, tip);
});
// $dungeon.parent().attr("onmouseover", `wodToolTip(this, "${tip}")`);
}
});
}
/**
* 增强出售操作,更方便移除物品
* @returns
*/
function easySelling() {
let pathName = location.pathname;
if (pathName !== "/wod/spiel/hero/items.php") {
return;
}
if (
!(
$('h1:contains("请确认")').length ||
$('p:contains("物品现在将以NPC价出售")').length
)
) {
return;
}
const $tip = $('p:contains("物品现在将以NPC价出售")');
$('p:contains("物品现在将以NPC价出售")~table').on(
"click",
"td>img:first-child",
function () {
const $a = $(this).nextAll("a:first");
const itemName = $a.text();
const params = $a.attr("href").split("?")[1];
const instanceId = new URLSearchParams(params).get("item_instance_id");
const $sellids = $('input[name="sellids"]');
if ($a.css("text-decoration-line") != "line-through") {
if (confirm(`是否将物品[${itemName}]从出售列表移除`)) {
let idArr = $sellids
.val()
.split(",")
.filter((id) => id);
idArr = idArr.filter((id) => id != instanceId);
$sellids.val(idArr.join(","));
$a.css({ "text-decoration-line": "line-through" });
$tip.text(`以下${idArr.length}件物品现在将以NPC价出售:`);
}
} else if ($a.css("text-decoration-line") != "none") {
if (confirm(`是否将物品[${itemName}]加入出售列表`)) {
let idArr = $sellids
.val()
.split(",")
.filter((id) => id);
idArr.push(instanceId);
$sellids.val(idArr.join(","));
$a.css({ "text-decoration-line": "none" });
$tip.text(`以下${idArr.length}件物品现在将以NPC价出售:`);
}
}
}
);
}
/**
* 查看地城掉落
* @returns
*/
function quickViewDrop() {
// 右下角地城添加快速链接
const $quickViewDropBtn = $(
'<button type="button" class="button clickable" title="查看地城掉落">查看地城掉落</button>'
);
const $curConfigAnchor = $(
"div.nextdungeon div[id^='CombatDungeonConfigSelector'], form[action^='/wod/spiel/quests/quests.php'] div[id^='CombatDungeonConfigSelector'], form[action^='/wod/spiel/dungeon/dungeon.php'] div[id^='CombatDungeonConfigSelector']"
);
$quickViewDropBtn.click(function () {
let url = "https://www.christophero.xyz/groupDungeon/";
let curDungeonName = "";
const $that = $(this);
if ($that.parents("div.nextdungeon").length) {
// 侧边
curDungeonName = $that
.parent()
.find(
'a[href^="/wod/spiel/dungeon/dungeon.php"],a[href^="/wod/spiel/quests/quests.php"]'
)
.text()
.trim();
} else if ($that.parents("tr[class^='row']").length) {
// 地城列表
curDungeonName = $that
.parents("tr[class^='row']:first")
.find("td:first")
.text()
.trim();
} else if (
$('h1:contains("任务: ")').length &&
$that.parents('form[action^="/wod/spiel/quests/quests.php"]')
) {
let res = null;
// 任务已经在执行了
if ($("#progressBar1_txt1").length) {
const reg = /^地城:\s*(.+)$/g;
curDungeonName = $that
.parents("form:first")
.find("h2:first")
.text()
.trim();
res = reg.exec(curDungeonName);
} else {
// 任务
const reg = /^\d+\.\s*(.+)$/g;
curDungeonName = $that
.parents("tr[class^='row']:first")
.find("h3:first")
.text()
.trim();
res = reg.exec(curDungeonName);
}
if (res) {
curDungeonName = res[1];
}
} else if (
$that.parents("table:first").prev(".progressBar_container").length
) {
curDungeonName = $that
.parents("table:first")
.prevAll("p:first")
.find("b:nth-child(2)")
.text()
.trim();
}
url += encodeURIComponent(curDungeonName);
url += `?groupId=${$('input[name="gruppe_id"]').val()}`;
window.open(url, "_blank");
});
$curConfigAnchor.after($quickViewDropBtn);
}
function calcNpcPrice() {
if (
![
"/wod/spiel/trade/exchange_details.php",
"/wod/module/search/select_item.php",
].includes(location.pathname)
)
return;
const $tradeTds = $('td[valign="top"][align="center"]:not([colspan])');
if (!$tradeTds.length) return;
$tradeTds.each((i, e) => {
let gold = parseInt($(e).find('input[name="gold"]').val());
if (isNaN(gold)) {
gold = parseInt(e.childNodes[0].textContent.trim());
}
const $tbody = $(e).find("tbody");
const $trs = $tbody.find("tr");
let ntotal = 0;
let promiseArr = [];
for (const tr of Array.from($trs)) {
let price = 0;
if (i == 1) {
promiseArr.push(
fetchNpcPrice(location.origin + $(tr).find("a").attr("href"), tr)
);
} else {
price = parseInt($(tr).find("td:eq(3)").text().trim());
}
if (price) ntotal += price;
}
Promise.all(promiseArr).then((priceArr) => {
ntotal += priceArr.reduce((t, c) => t + c, 0);
const total = ntotal + gold;
$tbody.append(
`<tr><td align="right"></td><td valign="top" nowrap>小计</td><td valign="top" nowrap></td><td nowrap>${ntotal} <img alt="" border="0" src="/wod/css//skins/skin-4/images/icons/lang/cn/gold.gif" title="金币"></td></tr>
<tr><td align="right"></td><td valign="top" nowrap>合计</td><td valign="top" nowrap></td><td nowrap>${total} <img alt="" border="0" src="/wod/css//skins/skin-4/images/icons/lang/cn/gold.gif" title="金币"></td></tr>`
);
});
});
}
function fetchNpcPrice(url, tr) {
return fetch(url, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"content-type": "application/x-www-form-urlencoded",
},
method: "GET",
}).then(async (response) => {
let price = 0;
if (response.status === 200) {
const text = await response.text();
const $priceTd = $(text).find(
'#details td:contains("NPC价/全新时NPC价") +td'
);
price = parseInt($priceTd[0].childNodes[0].textContent.trim());
if (isNaN(price)) price = 0;
}
$(tr).append(
`<td></td><td>${price} <img alt="" border="0" src="/wod/css//skins/skin-4/images/icons/lang/cn/gold.gif" title="金币"></td>`
);
return new Promise((resolve, reject) => {
resolve(price);
});
});
}
function resetSkillPoint() {
if (location.pathname !== "/wod/spiel/hero/skills.php") return;
$('<button class="button clickable" type="button">技能重置</button>')
.insertBefore('input[name="hide_all"],input[name="show_all"]')
.click(function () {
$('input[name^="undo["][src$="undo_steigern_enabled.gif"]').each(
(i, e) => {
let currentCnt = parseInt(
$(e)
.parents("tr:first")
.find('div[id^="skill_rang_"]')
.text()
.trim()
);
let skillId = $(e)
.prop("name")
.match(/undo\[(\d+)\]/)[1];
const deltaRang = -1 * currentCnt;
if (isAbleToChange(skillId, deltaRang)) {
advanceSkill(skillId, deltaRang);
hero["stufe"] = engineLevelGainedForEps(
hero["fc_ep"],
hero["fc_ep"] - hero["ep"]
);
if (hero["stufe"] < hero["stufe_orig"])
hero["stufe"] = hero["stufe_orig"];
updateSkills(skillId);
}
}
);
alert("技能已重置");
});
}
function insertCss(select, styles) {
if (document.styleSheets.length === 0) {
//如果没有style标签,则创建一个style标签
var style = document.createElement("style");
document.head.appendChild(style);
}
var styleSheet = document.styleSheets[document.styleSheets.length - 1]; //如果有style 标签.则插入到最后一个style标签中
var str = select + " {"; //插入的内容必须是字符串,所以得把obj转化为字符串
for (var prop in styles) {
str +=
prop.replace(/([A-Z])/g, function (item) {
//使用正则把大写字母替换成 '-小写字母'
return "-" + item.toLowerCase();
}) +
":" +
styles[prop] +
";";
}
str += "}";
styleSheet.insertRule(str, styleSheet.cssRules.length); //插入样式到最后一个style标签中的最后面
}
function styleEnhance() {
insertCss(".orders_top_row, .orders_top_row > *", {
"vertical-align": "unset",
});
}
function refreshSkill() {
if (
location.pathname !== "/wod/spiel/hero/skills.php" &&
!$('h1:contains("装备着的物品")').length
)
return;
const $btn = $(
'<button class="button clickable" type="button">刷新套装加成</button>'
).click(function () {
let $btn = $(this);
let baseUrl = location.origin + "/wod/spiel/hero/attributes.php?is_popup=1";
const searchParams = getBaseSearchParams();
searchParams.set("do_reset", "");
searchParams.set("levelup_warned", "0");
searchParams.set("geschlecht", "f");
searchParams.set("change_gender", "更改");
searchParams.set("amor_details", "关闭");
$btn.text("处理中请稍候...");
const heroId = searchParams.get("session_hero_id");
baseUrl += "&session_hero_id=" + heroId;
fetch(baseUrl, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
method: "POST",
body: searchParams.toString(),
}).then((resp) => {
$btn.text("处理完成刷新页面");
if (location.pathname == "/wod/spiel/hero/items.php") {
location.replace(
location.pathname +
"?menukey=hero_gear&view=gear&session_hero_id=" +
heroId
);
} else {
location.replace(location.href);
}
});
});
if (location.pathname === "/wod/spiel/hero/skills.php") {
$btn.insertBefore('input[name="hide_all"],input[name="show_all"]');
} else {
$btn.insertAfter('input[name="ok"][value="应用改动"]');
}
}
function smartSelect() {
// is dragging active
let isDragging = false;
// the rectangle to show while dragging
var rectangle = undefined;
// rectangle object specify the selected range
let rec = {
startX: 0,
startY: 0,
endX: 0,
endY: 0,
};
// rectangle object to draw the box upon dragging
let recDraw = {
startX: 0,
startY: 0,
endX: 0,
endY: 0,
style:
"position:absolute;border: 2px dotted black;background: white; opacity:0.5;z-index:200000000;font-size: 0px",
};
document.addEventListener("mousedown", (e) => {
// start dragging-mode if shift key is pressed
if (e.shiftKey || e.altKey) {
isDragging = true;
rec.startX = rec.endX = recDraw.startX = recDraw.endX = e.pageX;
rec.startY = rec.endY = recDraw.startY = recDraw.endY = e.pageY;
e.preventDefault();
}
});
document.addEventListener("mousemove", (e) => {
// show dragging rectangle is drag mode is activated
if ((e.shiftKey || e.altKey) && isDragging) {
rectangle.style.visibility = "visible";
e.preventDefault();
// Take care that the rectangle is displayed also with negative x or y values
if (e.pageX >= rec.startX && e.pageY >= rec.startY) {
//console.log("right bottom")
recDraw.endX = e.pageX;
recDraw.endY = e.pageY;
} else if (e.pageX < rec.startX && e.pageY >= rec.startY) {
//console.log("left bottom - negative X")
recDraw.startX = e.pageX;
recDraw.endX = rec.startX;
recDraw.endY = e.pageY;
} else if (e.pageX >= rec.startX && e.pageY < rec.startY) {
//console.log("right top - negative Y")
recDraw.endY = rec.startY;
recDraw.startY = e.pageY;
recDraw.endX = e.pageX;
} else if (e.pageX < rec.startX && e.pageY < rec.startY) {
//console.log("left top - negative X + negative Y")
recDraw.startY = e.pageY;
recDraw.startX = e.pageX;
recDraw.endX = rec.startX;
recDraw.endY = rec.startY;
}
drawRectangle();
}
});
document.addEventListener("mouseup", (e) => {
// stop dragging mode if mouse button is released
if (isDragging) {
rec.endX = e.pageX;
rec.endY = e.pageY;
isDragging = false;
rectangle.style.visibility = "hidden";
if (e.shiftKey) {
checkCheckboxes();
} else if (e.altKey) {
batchSelectOrEdit();
}
}
});
//////// Functions ////////
function drawRectangle() {
rectangle.style.left = `${recDraw.startX}px`;
rectangle.style.top = `${recDraw.startY}px`;
rectangle.style.width = `${recDraw.endX - recDraw.startX}px`;
rectangle.style.height = `${recDraw.endY - recDraw.startY}px`;
}
function checkCheckboxes() {
let left = rec.startX;
let right = rec.endX;
let top = rec.startY;
let bottom = rec.endY;
if (left > right) {
[left, right] = [right, left];
}
if (top > bottom) {
[top, bottom] = [bottom, top];
}
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach((ele) => {
const $ele = $(ele);
const eleOffset = $(ele).offset();
const eleTop = eleOffset.top;
const eleLeft = eleOffset.left;
const eleBottom = eleTop + $ele.height();
const eleRight = eleLeft + $ele.width();
const verticalCenter = (eleTop + eleBottom) / 2;
const horizonCenter = (eleLeft + eleRight) / 2;
// 中心点在矩形范围内就勾选
if (
left <= horizonCenter &&
horizonCenter <= right &&
top <= verticalCenter &&
verticalCenter <= bottom
) {
// toggle status of the checkbox in range
$(ele).click();
// ckbox.checked ? (ckbox.checked = false) : (ckbox.checked = true);
}
});
}
// 批量选择下拉框
function batchSelectOrEdit() {
let left = rec.startX;
let right = rec.endX;
let top = rec.startY;
let bottom = rec.endY;
if (left > right) {
[left, right] = [right, left];
}
if (top > bottom) {
[top, bottom] = [bottom, top];
}
const $eles = $("select:visible,input:visible");
const opts = [];
const boxedSels = [];
const boxedInputs = [];
$eles.each((i, ele) => {
const $ele = $(ele);
const eleOffset = $(ele).offset();
const eleTop = eleOffset.top;
const eleLeft = eleOffset.left;
const eleBottom = eleTop + $ele.height();
const eleRight = eleLeft + $ele.width();
const verticalCenter = (eleTop + eleBottom) / 2;
const horizonCenter = (eleLeft + eleRight) / 2;
// 中心点在矩形范围内就选择
if (
left <= horizonCenter &&
horizonCenter <= right &&
top <= verticalCenter &&
verticalCenter <= bottom
) {
if (ele.tagName == "SELECT") {
boxedSels.push(ele);
// 设置选项
$(ele)
.find("option")
.each((i, opt) => {
const text = opt.textContent;
const value = opt.value;
if (
opts.some((opt) => opt.text === text && opt.value === value)
) {
return;
}
opts.push({ text, value });
});
} else if (ele.tagName == "INPUT") {
boxedInputs.push(ele);
}
}
});
// 没有框选到任何下拉框则不弹出选项
if (!boxedSels.length && !boxedInputs.length) return;
// 展示批量选择框
const targetId = "ajax_dialog";
const innerHtml =
'<select id="batchSelect" style="visibility:hidden;"></select><input type="text" id="batchInput" style="visibility:hidden;">';
_ajaxCreateDialogDirect(
targetId,
innerHtml,
boxedSels.length > boxedInputs.length
? "批量选择下拉框选值"
: "批量修改输入框数值",
AJAX_YES | AJAX_CLOSE,
async function (event) {
if (event == "yes") {
if (boxedSels.length > boxedInputs.length) {
const selectedVal = $("#batchSelect").val();
boxedSels.forEach((select) => {
$(select).val(selectedVal).trigger("change");
});
} else {
const inputVal = $("#batchInput").val();
boxedInputs.forEach((input) => {
input.value = inputVal;
});
}
}
_ajaxStopWaiting(targetId);
_ajaxCloseModalDialog(targetId);
},
function () {
altDown = false;
document.querySelector(`#${targetId}_buttons button`).innerText =
"确定";
if (boxedSels.length > boxedInputs.length) {
const $batchSelect = $("#batchSelect");
for (let opt of opts) {
const $opt = $("<option></option>");
$opt.attr("value", opt.value).text(opt.text);
$batchSelect.append($opt);
}
$("#batchSelect").css("visibility", "visible");
} else {
$("#batchInput").css("visibility", "visible");
}
}
);
}
function initCheckboxSelector() {
// create div which is used for showing the rectangle and initialize it
rectangle = document.createElement("div");
rectangle.style.cssText = recDraw.style;
document.body.appendChild(rectangle);
}
initCheckboxSelector();
}
function cancelDungeon() {
const $nextDungeonDiv = $('.block_inner:contains("下一个地城:")');
if (!$nextDungeonDiv.length) return;
const $cancelDungeonBtn = $(
'<button id="cancelDungeon" class="button clickable" title="取消探险">取消探险</button>'
);
$nextDungeonDiv.append($cancelDungeonBtn);
$cancelDungeonBtn.click(function () {
if (!confirm("确定要取消当前探险吗?")) return;
let $btn = $(this);
let baseUrl = location.origin + "/wod/spiel/dungeon/dungeon.php?is_popup=1";
const searchParams = getBaseSearchParams();
const groupLv = searchParams.get("stufe");
searchParams.set("TABLE_DEFAULT_SORT_DIR", "DESC");
searchParams.set("TABLE_DEFAULT_SORT_COL", "7");
searchParams.set("TABLE_DEFAULT_PAGE", "1");
searchParams.set("TABLE_DEFAULT_PSNR[1]", "20");
searchParams.set("TABLE_DEFAULT_PSNR[2]", "20");
searchParams.set("TABLE_DATED_SORT_DIR", "ASC");
searchParams.set("TABLE_DATED_SORT_COL", "14");
searchParams.set("TABLE_DATED_PAGE", "1");
searchParams.set("dungeon_1name", "不可能存在的地城");
searchParams.set(
"profile_data_dungeon_1_profile_data",
"HogNB0ny8I/FjaOg6FXrzZvwI0A1hZgI77jjRYoCRE27wTAWqILHdEzHicsmJZl6X7ZRBEIW822E5+rsUoHjcPHJM1dTqbJE0c1Ad4c6gLXcUjcGy5WB8H0lm1qobBxf"
);
searchParams.set(
"callback_js_code_dungeon_1_callback_js_code",
"9hAUpnwetF8TrxnkIxgBdD27k4isavkaEEd/PwhJTLL8yf4MvBsSYedcxPRse51t2x6Aw2bcXKHHlyCszAStN2nnub0CncNJMDrZQePru5mpXW3It99S/D+JypTOewMQ/T9+eXLlhJZ7vK+j9IAKgKVS+EFJPGzy61GBgu60fBk="
);
searchParams.set("dungeon_1level", "99");
searchParams.set("dungeon_1level_to", "99");
searchParams.set("dungeon_1level_allowed", "99");
searchParams.set("dungeon_1level_allowed_to", "99");
searchParams.set("dungeon_1groupLevel", groupLv);
searchParams.set("dungeon_1profile_id", "0");
searchParams.set("dungeon_1is_open", "1");
searchParams.set("dungeon_2name", "不可能存在的地城");
searchParams.set(
"profile_data_dungeon_1_profile_data",
"HogNB0ny8I/FjaOg6FXrzZvwI0A1hZgI77jjRYoCRE26Yi3zTNw4kxlf3EBWtEk1b6aVuW+FUuN8kSMRggg8h3JkxoL2NsUxavZXWRdyOxUUEMX5AKRE3eAUHOs1WJk3"
);
searchParams.set(
"callback_js_code_dungeon_1_callback_js_code",
"hjeqjpM+qZ3O91mfrGQpvhGIqpJEtjGwgXlmEmXfIQzBrxMpR69Guzy7+7UjXgADJMzqeXDJhCUPVMr4x2KfpOKujjttFf22twLrAnOMemRyC0FkOn1zx6YBUufUkR7Ckg4pf2y/2z74zxDx8svUS6Kwihgpc54Xeb1xFKyGmQQ="
);
searchParams.set("dungeon_2level", "99");
searchParams.set("dungeon_2level_to", "99");
searchParams.set("dungeon_2level_allowed", "99");
searchParams.set("dungeon_2level_allowed_to", "99");
searchParams.set("dungeon_2groupLevel", groupLv);
searchParams.set("dungeon_2profile_id", "0");
searchParams.set("dungeon_2is_open", "1");
searchParams.set("unvisit", "取消探险");
$btn.text("取消中请稍候...");
fetch(baseUrl, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"content-type": "application/x-www-form-urlencoded",
},
method: "POST",
body: searchParams.toString(),
}).then((resp) => {
$btn.text("处理完成刷新页面");
location.reload();
});
});
}
/**
* 战斗耗材预检查
* @returns
*/
function battlePreCheck() {
const $nextDungeonDiv = $('.block_inner:contains("下一个地城:")');
if (!$nextDungeonDiv.length) return;
const $preCheckBtn = $(
'<button id="battlePreCheck" class="button clickable" title="耗材检查">耗材检查</button>'
);
$nextDungeonDiv.append($preCheckBtn);
$preCheckBtn.click(function () {
let $btn = $(this);
// 1. 获得团员列表
let baseUrl = location.origin + "/wod/spiel/dungeon/group.php?is_popup=1";
const searchParams = getBaseSearchParams();
$btn.text("获取团员列表中...");
fetch(baseUrl, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"content-type": "application/x-www-form-urlencoded",
},
method: "POST",
body: searchParams.toString(),
})
.then((resp) => {
return resp.text();
})
.then((text) => {
// 2. 获取所有人装备与耗材检查页面
$btn.text("分析成员设置中...");
$("#ajax_editor_container").show();
$("#ajax_editor_title").append("<span>耗材检查</span>");
$('<button class="button clickable" title="关闭">关闭</button>')
.appendTo($("#ajax_editor_buttons"))
.click(function () {
$("#ajax_editor_container").hide();
$(
"#ajax_editor_title, #ajax_editor_content, #ajax_editor_buttons"
).empty();
});
$btn.text("耗材检查");
const $doc = $(text);
const $heroRows = $doc.find(
"#smarttabs__members_inner .content_table [class^=row]"
);
const searchParams = new URLSearchParams();
const wodPostId = $doc.find('input[name="wod_post_id"]').val();
const heroId = $doc.find('input[name="sessionHeroId"]').val();
const playerId = $doc.find('input[name="session_player_id"]').val();
searchParams.set("wod_post_id", wodPostId);
searchParams.set("session_hero_id", heroId);
searchParams.set("session_player_id", playerId);
searchParams.set("ajax_class_name", "RenderHeroItemViewer");
searchParams.set("RenderHeroItemViewer_dialogStatus", "open");
searchParams.set("ajax", 1);
$heroRows.each(function () {
const $row = $(this);
const itemViewParams = $row
.find("[id^=HeroItemViewer]")
.attr("id")
.replace("HeroItemViewer", "");
searchParams.set("ajax_object_id", itemViewParams);
let renderUrl = location.origin + "/wod/ajax/render.php";
fetch(renderUrl, {
headers: {
accept: "*/*",
"content-type": "application/x-www-form-urlencoded",
"x-requested-with": "XMLHttpRequest",
},
method: "POST",
body: searchParams.toString(),
})
.then((resp) => {
return resp.text();
})
.then((text) => {
// 3. 将所有检查结果显示在页面上
const $render = $("<div></div>").append($(`<div>${text}</div>`));
const $errDiv = $render.find(
'div[id^="CombatConfigErrorViewer"]'
);
const $heroName = $render.find(
'a[href^="/wod/spiel/hero/profile.php?"]'
);
$("#ajax_editor_content")
.append($heroName)
.append($errDiv)
.append("<br>");
});
});
});
});
}
async function tombolaOnce(params, times) {
let response = await fetch(
`${location.origin}/wod/spiel/rewards/tombola.php?is_popup=1`,
// `${location.origin}?is_popup=1`,
{
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"content-type": "application/x-www-form-urlencoded",
},
body: params.toString(),
method: "POST",
mode: "cors",
credentials: "include",
}
);
let text = await response.text();
const $tombolaProgress = $("#tombolaProgress");
$tombolaProgress.val(parseInt($tombolaProgress.val()) + 1);
ajaxAlert(`目前进度:${$tombolaProgress.val()}/${times}`);
const $jq = $(text);
const $p = $jq.find(".content_block p:last");
if ($p.length) {
$("#tombolaDetailContainer").append($p);
} else {
$("#tombolaDetailContainer").append(
"<p>没有找到获奖信息,请检查彩票是否耗尽或者当日次数已达限制</p>"
);
}
return new Promise((resolve, reject) => {
resolve(text);
});
}
async function autoTombola(times) {
ajaxAlert(`即将开始兑换奖券...`);
$("#tombolaProgress").val(0);
$("#tombolaDetailContainer").empty();
const params = getAllHiddenParams();
params.set("exec", "和克劳斯.钱袋兑换一张奖券");
const promiseArr = [];
for (let i = 0; i < times; i++) {
promiseArr.push(tombolaOnce(params, times));
}
const respArr = await Promise.all(promiseArr);
_ajaxStopWaiting("ajax_editor");
ajaxAlert("兑换奖券完毕!");
setTimeout(function () {
_ajaxCloseModalDialog("ajax_dialog");
}, 800);
}
/**
* 彩票一键N连
* @returns
*/
function multiTombola() {
const $drawBtn = $('input[name="exec"][value="和克劳斯.钱袋兑换一张奖券"]');
if (!$drawBtn.length) return;
$(
'<input type="button" name="multiTombola" value="一键50连" class="button clickable">'
)
.click(function () {
if (confirm("是否进行一键50连?")) {
createTombolaDialog(50);
}
})
.insertAfter($drawBtn);
$(
'<input type="button" name="multiTombola" value="一键十连" class="button clickable">'
)
.click(function () {
if (confirm("是否进行一键十连?")) {
createTombolaDialog(10);
}
})
.insertAfter($drawBtn);
}
function createTombolaDialog(times) {
const targetId = "ajax_editor";
_ajaxCreateDialogDirect(
targetId,
null,
"抽奖记录",
AJAX_YES | AJAX_CLOSE,
async function (event) {
if (event == "yes") {
autoTombola(times);
} else {
_ajaxStopWaiting(targetId);
_ajaxCloseModalDialog("ajax_editor");
}
},
function () {
document.querySelector("#ajax_editor_buttons button").innerText =
"再来一次";
console.log("init");
}
);
_ajaxStartWaiting(targetId);
$("#ajax_editor_content")
.append('<input type="hidden" id="tombolaProgress">')
.append('<div id="tombolaDetailContainer"></div>');
autoTombola(times);
}
function getAllHiddenParams() {
const searchParams = new URLSearchParams();
$("input:hidden").each(function () {
const $this = $(this);
searchParams.set($this.attr("name"), $this.val());
});
return searchParams;
}
function getBaseSearchParams() {
const searchParams = new URLSearchParams();
$("input:hidden").each(function () {
const $this = $(this);
const key = $this.attr("name");
if (baseParams.includes(key)) {
searchParams.set($this.attr("name"), $this.val());
}
});
return searchParams;
}
function sibebarDungeon() {
const DUNGEON_LIST_KEY = "dungeonList";
const DUNGEON_LIST_VERSION = "dungeonListVersion";
// 每天更新一次地城信息
let dungeonListStr = localStorage.getItem(DUNGEON_LIST_KEY);
let dungeonListVer = localStorage.getItem(DUNGEON_LIST_VERSION);
let dungeonList = {};
const today = dayjs();
const todayStr = today.format("YYYYMMDD");
if (dungeonListStr && dungeonListVer && todayStr == dungeonListVer) {
dungeonList = JSON.parse(dungeonListStr);
renderDungeonSelector(dungeonList);
} else {
// 获取技能信息
fetch("https://www.christophero.xyz/wod/dungeon/listCommon", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
return response.json();
})
.then((res) => {
if (!(res && res.code === 200)) {
return;
}
const dungeonList = res.data;
localStorage.setItem(DUNGEON_LIST_KEY, JSON.stringify(dungeonList));
localStorage.setItem(DUNGEON_LIST_VERSION, todayStr);
renderDungeonSelector(dungeonList);
});
}
}
function renderDungeonSelector(dungeonList) {
// 将地城划分为常驻和限时地城
let normalList = [];
let limitList = [];
const now = dayjs();
const heroLv = parseInt($('input[name="stufe"]:hidden').val());
for (let d of dungeonList) {
if (d.type == "P" && d.minLevel <= heroLv && d.maxLevel >= heroLv) {
d.desc = "常";
normalList.push(d);
} else if (d.type == "L" && d.minLevel <= heroLv && d.maxLevel >= heroLv) {
const startTime = dayjs(d.startTime);
const endTime = dayjs(d.endTime);
if (startTime.isBefore(now) && endTime.isAfter(now)) {
d.desc = "今";
limitList.push(d);
} else if (
now.isBefore(startTime) &&
now.add(7, "hour").isAfter(startTime)
) {
d.desc = "明";
limitList.push(d);
}
}
}
// 将地城插入侧边栏
const $groupCrashBox = $(".gadget.group_cash_box");
const $fastDungeonContaner = $(`
<div class="gadget fast_dungeon lang-cn">
<div class="gadget_inner">
<div class="gadget_body" style="white-space: normal">
<div class="block">
<div class="block_body">
<div class="background"></div>
<div class="border-top"></div>
<div class="border-bottom"></div>
<div class="block_inner">
<div class="blockHeadline">
<a href="/wod/spiel/dungeon/dungeon.php?session_hero_id=115817">
<span class="font_Block_Headline">快捷地城</span>
</a>
</div>
<div class="blockParagraph fast_dungeon">
<div>
<label><input type="radio" name="fast_dungeon_type" checked value="normal" />常规</label>
<label><input type="radio" name="fast_dungeon_type" value="limit" />限时</label>
<button id="gotoTravel" class="button clickable" title="出发">出发</button>
</div>
<div>
<select name="fast_dungeon_select">
<option value="" selected="selected"> </option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`);
$groupCrashBox.before($fastDungeonContaner);
rebuildDungeonSelect("normal", normalList, limitList);
$fastDungeonContaner
.find('input[name="fast_dungeon_type"]')
.change(function () {
console.log($(this).val());
rebuildDungeonSelect($(this).val(), normalList, limitList);
});
$fastDungeonContaner.find("#gotoTravel").click(function () {
let dungeonId = $('select[name="fast_dungeon_select"]').val();
console.log(dungeonId);
if (!dungeonId) return;
gotoTravel(dungeonId);
});
}
function rebuildDungeonSelect(dungeonType, normalList, limitList) {
let list = dungeonType == "normal" ? normalList : limitList;
list = list.sort((d1, d2) => {
let timeDiff = dayjs(d1.startTime).diff(dayjs(d2.startTime));
let lvDiff = d2.minLevel - d1.minLevel;
return timeDiff || lvDiff;
});
const $select = $('select[name="fast_dungeon_select"]');
$select
.empty()
.append('<option value="" selected="selected"> </option>');
for (let dungeon of list) {
let name = "";
if (dungeon.name.startsWith("巨兽讨伐战-")) {
name = dungeon.name.replace("巨兽讨伐战-", "");
} else {
name = dungeon.name.length > 8 ? dungeon.name.substring(0, 4) + "..." +
dungeon.name.substring(dungeon.name.length - 4)
: dungeon.name;
}
$select.append(
$("<option> </option>")
.attr("value", dungeon.sysId)
.text(`${dungeon.desc}|${name}`)
);
}
}
function gotoTravel(sysId) {
let baseUrl = location.origin + "/wod/spiel/dungeon/dungeon.php?";
const searchParams = getBaseSearchParams();
const groupLv = searchParams.get("stufe");
searchParams.set("TABLE_DEFAULT_SORT_DIR", "DESC");
searchParams.set("TABLE_DEFAULT_SORT_COL", "7");
searchParams.set("TABLE_DEFAULT_PAGE", "1");
searchParams.set("TABLE_DEFAULT_PSNR[1]", "20");
searchParams.set("TABLE_DEFAULT_PSNR[2]", "20");
searchParams.set("TABLE_DATED_SORT_DIR", "ASC");
searchParams.set("TABLE_DATED_SORT_COL", "14");
searchParams.set("TABLE_DATED_PAGE", "1");
searchParams.set("dungeon_1name", "不可能存在的地城");
searchParams.set(
"profile_data_dungeon_1_profile_data",
"HogNB0ny8I/FjaOg6FXrzZvwI0A1hZgI77jjRYoCRE27wTAWqILHdEzHicsmJZl6X7ZRBEIW822E5+rsUoHjcPHJM1dTqbJE0c1Ad4c6gLXcUjcGy5WB8H0lm1qobBxf"
);
searchParams.set(
"callback_js_code_dungeon_1_callback_js_code",
"9hAUpnwetF8TrxnkIxgBdD27k4isavkaEEd/PwhJTLL8yf4MvBsSYedcxPRse51t2x6Aw2bcXKHHlyCszAStN2nnub0CncNJMDrZQePru5mpXW3It99S/D+JypTOewMQ/T9+eXLlhJZ7vK+j9IAKgKVS+EFJPGzy61GBgu60fBk="
);
searchParams.set("dungeon_1level", "99");
searchParams.set("dungeon_1level_to", "99");
searchParams.set("dungeon_1level_allowed", "99");
searchParams.set("dungeon_1level_allowed_to", "99");
searchParams.set("dungeon_1groupLevel", groupLv);
searchParams.set("dungeon_1profile_id", "0");
searchParams.set("dungeon_1is_open", "1");
searchParams.set(`visit[${sysId}]`, "探索");
searchParams.set("dungeon_2name", "不可能存在的地城");
searchParams.set(
"profile_data_dungeon_1_profile_data",
"HogNB0ny8I/FjaOg6FXrzZvwI0A1hZgI77jjRYoCRE26Yi3zTNw4kxlf3EBWtEk1b6aVuW+FUuN8kSMRggg8h3JkxoL2NsUxavZXWRdyOxUUEMX5AKRE3eAUHOs1WJk3"
);
searchParams.set(
"callback_js_code_dungeon_1_callback_js_code",
"hjeqjpM+qZ3O91mfrGQpvhGIqpJEtjGwgXlmEmXfIQzBrxMpR69Guzy7+7UjXgADJMzqeXDJhCUPVMr4x2KfpOKujjttFf22twLrAnOMemRyC0FkOn1zx6YBUufUkR7Ckg4pf2y/2z74zxDx8svUS6Kwihgpc54Xeb1xFKyGmQQ="
);
searchParams.set("dungeon_2level", "99");
searchParams.set("dungeon_2level_to", "99");
searchParams.set("dungeon_2level_allowed", "99");
searchParams.set("dungeon_2level_allowed_to", "99");
searchParams.set("dungeon_2groupLevel", groupLv);
searchParams.set("dungeon_2profile_id", "0");
searchParams.set("dungeon_2is_open", "1");
baseUrl += "session_hero_id=" + searchParams.get("session_hero_id");
ajaxAlert("切换中,请稍候...");
fetch(baseUrl, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
method: "POST",
body: searchParams.toString(),
})
.then((resp) => {
// location.replace(location.href);
return resp.text();
})
.then((html) => {
console.log(html);
let $curT = $("#gadgetNextdungeonTime");
if (!$curT.length) {
// 处理偶尔出现的刷新后英雄变化的问题
let [baseUrl, params] = location.href.split("?");
const urlParams = new URLSearchParams(params);
if (!urlParams.has("session_hero_id")) {
urlParams.set("session_hero_id", searchParams.get("session_hero_id"));
params = urlParams.toString();
}
location.replace(baseUrl + "?" + params);
return;
}
let $curB = $curT.prevAll("b:first");
let $curC = $curT.nextAll("div[id^=CombatDungeonConfigSelector]:first");
let $newT = $(html).find("#gadgetNextdungeonTime");
let $newB = $newT.prevAll("b:first");
let $newC = $newT.nextAll("div[id^=CombatDungeonConfigSelector]:first");
$curT.replaceWith($newT);
$curB.replaceWith($newB);
$curC.replaceWith($newC);
ajaxAlert("已切换!");
setTimeout(function () {
_ajaxCloseModalDialog("ajax_dialog");
}, 800);
});
}
/**
* 简易属性控制
*/
function easyAttrVal() {
const urls = ["/wod/spiel/hero/attributes.php"];
if (!urls.includes(location.pathname) || location.host.startsWith("zhao")) {
return;
}
// 添加便捷属性
$(
'<img src="/wod/css/skins/skin-7/images/icons/reset.gif" style="cursor:pointer;width:20px;height:20px;"/>'
)
.insertAfter(
$('input[name^="improve[at_"], img[src$="icons/steigern_disabled.gif"]')
)
.click(async function () {
const urlParams = getBaseSearchParams();
let targetVal = prompt("请输入目标数值", 10);
if (targetVal == null) return;
targetVal = parseNum(targetVal);
if (targetVal > 30) {
alert("目标数值不能大于30!");
return;
}
const attrName = $(this)
.parents("tr:eq(1)")
.find("td:first")
.text()
.trim();
ajaxAlert("正在处理,请稍候...");
await attrChangeBatch(urlParams, attrName, targetVal);
ajaxAlert("处理完成,即将刷新页面");
setTimeout(function () {
_ajaxCloseModalDialog("ajax_dialog");
location.replace(location.origin + "/wod/spiel/hero/attributes.php");
}, 800);
});
// 添加高手低手全13配置
const $attrTableTd = $(
'#main_content h1:contains("属性与特性") +table:first td:first'
);
$('<input type="button" value="成为高手" class="button clickable">')
.appendTo($attrTableTd)
.click(async function () {
ajaxAlert("正在处理,请稍候...");
await allAttrTo(urlParams, 2);
ajaxAlert("处理完成,即将刷新页面");
setTimeout(function () {
_ajaxCloseModalDialog("ajax_dialog");
location.replace(location.origin + "/wod/spiel/hero/attributes.php");
}, 800);
});
$('<input type="button" value="变成低手" class="button clickable">')
.appendTo($attrTableTd)
.click(async function () {
ajaxAlert("正在处理,请稍候...");
await allAttrTo(urlParams, 10);
ajaxAlert("处理完成,即将刷新页面");
setTimeout(function () {
_ajaxCloseModalDialog("ajax_dialog");
location.replace(location.origin + "/wod/spiel/hero/attributes.php");
}, 800);
});
$('<input type="button" value="八徽章" class="button clickable">')
.appendTo($attrTableTd)
.click(async function () {
ajaxAlert("正在处理,请稍候...");
await allAttrTo(urlParams, 13);
ajaxAlert("处理完成,即将刷新页面");
setTimeout(function () {
_ajaxCloseModalDialog("ajax_dialog");
location.replace(location.origin + "/wod/spiel/hero/attributes.php");
}, 800);
});
}
function easyChangeSkill() {
const urls = ["/wod/spiel/hero/skills.php"];
if (!urls.includes(location.pathname) || location.host.startsWith("zhao")) {
return;
}
const urlParams = getBaseSearchParams();
$(
'<img src="/wod/css/skins/skin-7/images/icons/reset.gif" style="cursor:pointer;width:20px;height:20px;"/>'
)
.insertAfter($('input[id^="button_steigern_"]'))
.click(async function () {
const urlParams = getBaseSearchParams();
let btnId = $(this).prev('input[id^="button_steigern_"]').attr("id");
const skillId = /button_steigern_(\d+)/.exec(btnId)[1];
const skillLv =
parseInt(
$(this).parents("tr:first").find("div[id^=skill_rang]").text().trim()
) || 0;
let targetVal = prompt("请输入目标技能等级", skillLv);
if (targetVal == null) return;
targetVal = parseNum(targetVal);
changeSkillBatch(skillId, skillLv, targetVal, true);
});
}
/**
* 快速变更技能等级
* @param {*} skillId
* @param {*} curLv
* @param {*} targetLv
* @param {*} force
* @returns
*/
function changeSkillBatch(skillId, curLv, targetLv, force) {
if (targetLv > 50 || targetLv < 0) {
alert("目标技能等级只能在0到50之间!");
return;
}
const diff = Math.abs(targetLv - curLv);
for (let i = 0; i < diff; i++) {
change_skill(skillId, curLv > targetLv ? "-" : "+", force);
}
}
/**
* 属性变更
* @param {*} urlParams
* @param {*} attrName
* @param {*} improve
* @returns
*/
async function attrChange(urlParams, attrName, improve) {
if (!improve) return;
const pname = `${improve ? "improve" : "undo"}[at_${attrEnMap[attrName]}]`;
const params = new URLSearchParams(urlParams.toString());
params.set(pname + ".x", 10);
params.set(pname + ".y", 10);
params.set("do_reset", "");
params.set("levelup_warned", "0");
params.set("geschlecht", "f");
params.set("amor_details", "关闭");
const url = `${
location.origin
}/wod/spiel/hero/attributes.php?is_popup=1&session_hero_id=${params.get(
"session_hero_id"
)}`;
const response = await fetch(url, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
body: params.toString(),
method: "POST",
});
globalCurSteps++;
ajaxAlert(`${attrName}变更进度: ${globalCurSteps}/${globalSteps}`);
return await response.text();
}
/**
* 属性批量变更
* @param {*} urlParams
* @param {*} attrName
* @param {*} targetVal
*/
async function attrChangeBatch(urlParams, attrName, targetVal) {
const curVal = getAttrVal(attrName);
await attrChangeBatchWithInitial(urlParams, attrName, curVal, targetVal);
}
/**
* 属性批量变更(手动指定初始属性)
* @param {*} urlParams
* @param {*} attrName
* @param {*} curVal
* @param {*} targetVal
*/
async function attrChangeBatchWithInitial(
urlParams,
attrName,
curVal,
targetVal
) {
globalCurSteps = 0;
globalSteps = Math.abs(targetVal - curVal);
let improve = true;
if (curVal > targetVal) {
improve = false;
}
for (let i = 0; i < globalSteps; i++) {
await attrChange(urlParams, attrName, improve);
}
}
/**
* 获得角色属性数值
* @param {*} attr
* @returns
*/
function getAttrVal(attr, $context) {
$context = $context || $(document);
let val = $context
.find(`td:contains("${attr}")`)
.next()
.find("td:eq(1)")[0]
.childNodes[0].textContent.trim();
val = parseInt(val);
return val;
}
function parseNum(numStr) {
let num = 1;
try {
num = parseInt(numStr);
} catch (ex) {
num = 1;
}
if (isNaN(num) || num < 0) {
num = 1;
}
return num;
}
// 导入英雄模版
function importHeroTemplate() {
// 只在技能页面生效
const urls = ["/wod/spiel/hero/skills.php"];
if (!urls.includes(location.pathname) || location.host.startsWith("zhao")) {
return;
}
// 技能列表页面添加导入英雄模版按钮
const $btn = $(
'<button class="button clickable" type="button">导入英雄模版</button>'
)
.insertBefore('input[name="hide_all"]')
.click(async function () {
const urlParams = getBaseSearchParams();
// 1. 获取人物卡
// 2. 分析人物卡
// 3. 获取8属性数值
// 4. 比较属性差异,升级或者降级属性
// 5. 比较技能差异,升级或者降级技能
createInputHeroCardDialog(urlParams, analyseHeroCard);
});
}
function createInputHeroCardDialog(urlParams, callback) {
const targetId = "ajax_editor";
const innerHtml =
'<div id="heroCardContainer" contentEditable="true" style="height: 100%;"></div>';
_ajaxCreateDialogDirect(
targetId,
innerHtml,
"请在下方输入人物卡,不要有多余文本",
AJAX_YES | AJAX_CLOSE,
async function (event) {
if (event == "yes") {
let heroCard = document.getElementById("heroCardContainer").textContent;
callback(urlParams, heroCard);
} else {
_ajaxStopWaiting(targetId);
_ajaxCloseModalDialog("ajax_editor");
}
},
function () {
console.log("init");
document.querySelector("#ajax_editor_buttons button").innerText = "导入";
}
);
}
async function analyseHeroCard(urlParams, heroCard) {
try {
heroCard = heroCard
.replace(/\[\d+\]/g, "")
.replace(/:g\w:/g, "")
.replace(/\[test\]/g, "")
.replace(/\[clone\]/g, "")
.replace(/\[/g, "<")
.replace(/\]/g, ">")
.replace(/[\r\n]/g, "")
.replace(
/<skill:\s*(.+?)\s*>/g,
(fullHtml, skillName) => `<skill>${skillName}</skill>`
)
.replace(
/<item:\s*(.+?)\s*>/g,
(fullHtml, itemName) => `<item>${itemName}</item>`
)
.replace(/color=orange/g, "orange");
} catch (error) {
ajaxAlert("浏览器版本过低,不能使用replaceAll函数");
return;
}
let $heroCard;
try {
$heroCard = $(heroCard);
} catch (error) {
ajaxAlert("人物卡格式可能存在问题,不能正确分析");
return;
}
const attrNameList = Object.keys(attrEnMap);
ajaxAlert("1. 分析人物卡属性清单");
// 获取属性清单
const targetAttrMap = {};
$heroCard
.find(attrNameList.map((attr) => `orange:contains("${attr}")`).join(","))
.each(function (i, e) {
const attrVal = $(e).parent().next().text();
const attrName = e.textContent;
targetAttrMap[attrName] = parseInt(attrVal);
});
ajaxAlert("2. 分析人物卡技能清单");
// 获取技能等级一览
const skillLvMap = {};
$heroCard.find("skill").each(function (i, e) {
const skillLv = $(e).parent().next().text();
const skillName = e.textContent;
skillLvMap[skillName] = parseInt(skillLv);
});
ajaxAlert("3. 分析人物卡装备清单");
// 获取装备物品一览
const equipItems = [];
$heroCard.find("item").each((i, e) => equipItems.push(e.textContent));
console.log(targetAttrMap);
console.log(skillLvMap);
console.log(equipItems);
if (confirm("是否使属性保持一致(注意不会使用重置点进行属性回退)?")) {
const currentAttrMap = await getHeroAttrs(urlParams);
ajaxAlert("4. 开始进行属性变更");
// 变更属性
for (let attrName of attrNameList) {
await attrChangeBatchWithInitial(
urlParams,
attrName,
currentAttrMap[attrName],
targetAttrMap[attrName]
);
}
}
ajaxAlert("5. 开始变更当前技能等级");
// 变更页面上的技能等级
for (let skillName of Object.keys(skillLvMap)) {
const $skillAnchor = $(
`a[href^="/wod/spiel/hero/skill.php"]:contains("${skillName}")`
);
if (!$skillAnchor.length) continue;
const $skillRang = $skillAnchor
.parent("td:first")
.next()
.find('[id^="skill_rang_"]');
if (!$skillRang.length) continue;
const skillId = $skillRang.attr("id").replace("skill_rang_", "");
const skillLv = parseInt($skillRang.text().trim()) || 0;
changeSkillBatch(skillId, skillLv, skillLvMap[skillName], true);
}
ajaxAlert("处理完毕!");
setTimeout(function () {
_ajaxStopWaiting("ajax_editor");
}, 800);
}
async function getHeroAttrs(urlParams) {
ajaxAlert("正在获取英雄当前属性");
const shId = urlParams.get("session_hero_id");
const url =
location.origin +
"/wod/spiel/hero/attributes.php?is_popup=1&session_hero_id=" +
shId;
const response = await fetch(url, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
body: urlParams.toString(),
method: "POST",
});
const text = await response.text();
const $context = $(text);
const attrMap = {
力量: getAttrVal("力量", $context),
体质: getAttrVal("体质", $context),
智力: getAttrVal("智力", $context),
灵巧: getAttrVal("灵巧", $context),
魅力: getAttrVal("魅力", $context),
敏捷: getAttrVal("敏捷", $context),
感知: getAttrVal("感知", $context),
意志: getAttrVal("意志", $context),
};
console.log(attrMap);
ajaxAlert(
`英雄当前属性已获取:<br>
力量:${attrMap["力量"]}<br>
体质:${attrMap["体质"]}<br>
智力:${attrMap["智力"]}<br>
灵巧:${attrMap["灵巧"]}<br>
魅力:${attrMap["魅力"]}<br>
敏捷:${attrMap["敏捷"]}<br>
感知:${attrMap["感知"]}<br>
意志:${attrMap["意志"]}`
);
return attrMap;
}
/**
* 批量删除设置
*/
function batchDelProfile() {
// 只在技能页面生效
const urls = ["/wod/spiel/hero/skillconfig.php"];
if (!urls.includes(location.pathname)) {
return;
}
$('<input type="button" value="批量删除">')
.insertAfter('input[value="删除"]')
.click(function () {
const urlParams = getBaseSearchParams();
urlParams.set("SELECTED_TAB", $('input[name="SELECTED_TAB"]').val());
urlParams.set("SELECTED_LVL", $('input[name="SELECTED_LVL"]').val());
urlParams.set("SELECTED_DUEL", $('input[name="SELECTED_DUEL"]').val());
urlParams.set("fig_type", $('input[name="fig_type"]').val());
urlParams.set("fig_id", $('input[name="fig_id"]').val());
urlParams.set("world", $('input[name="world"]').val());
urlParams.set("orig_profile", $('input[name="orig_profile"]').val());
urlParams.set("is_popup", "1");
urlParams.set("action", "delete");
createDelProfileDialog(urlParams);
});
}
function createDelProfileDialog(urlParams) {
const targetId = "ajax_editor";
const innerHtml =
'<div id="delProfileContainer" style="height: 100%;"></div>';
_ajaxCreateDialogDirect(
targetId,
innerHtml,
"请勾选需要删除的设置(默认和决斗不在此列)",
AJAX_YES | AJAX_CLOSE,
async function (event) {
if (event == "yes") {
console.log($('input:checked[id^="profile_"]'));
const profileList = $('input:checked[id^="profile_"]')
.map((i, e) => e.value)
.get();
if (!profileList.length) {
ajaxAlert(`没有勾选任何设置!`);
setTimeout(function () {
_ajaxStopWaiting(targetId);
});
return;
}
if (confirm(`是否删除这${profileList.length}条设置?`)) {
globalCurSteps = 0;
globalSteps = profileList.length;
let promiseArr = [];
for (const profileId of profileList) {
promiseArr.push(delProfile(urlParams, profileId));
}
ajaxAlert(`删除进度:${globalCurSteps}/${globalSteps}`);
Promise.all(promiseArr).then((responseArr) => {
_ajaxStopWaiting(targetId);
ajaxAlert(`删除完成,即将刷新页面`);
setTimeout(function () {
location.replace(
location.origin + "/wod/spiel/hero/skillconfig.php"
);
}, 800);
});
} else {
setTimeout(function () {
_ajaxStopWaiting(targetId);
});
}
} else {
_ajaxStopWaiting(targetId);
_ajaxCloseModalDialog("ajax_editor");
}
},
function () {
console.log("init");
document.querySelector("#ajax_editor_buttons button").innerText =
"开始删除";
const $container = $("#delProfileContainer");
for (let option of THE_ORDERS.profileDropdown.options) {
const userObj = option.userObject;
if (userObj.active || userObj.id < 1) continue;
$container.append(
`<input type="checkbox" value="${userObj.id}" id="profile_${userObj.id}"/><label for="profile_${userObj.id}">${userObj.name}</label><br>`
);
}
}
);
}
async function delProfile(urlParams, profileId) {
const shId = urlParams.get("session_hero_id");
const url =
location.origin +
"/wod/spiel/hero/skillconfig.php?is_popup=1&session_hero_id=" +
shId;
urlParams.set("profile", profileId);
const response = await fetch(url, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
body: urlParams.toString(),
method: "POST",
});
globalCurSteps++;
ajaxAlert(`删除进度:${globalCurSteps}/${globalSteps}`);
return response.text();
}
function batchChangeOwner() {
// 只在物品列表页面生效
const urls = ["/wod/spiel/hero/items.php"];
if (!urls.includes(location.pathname)) {
return;
}
// 没找到拥有者则退出
let $itemOwnerSelect = $(
'select[name="item_4owner"],select[name="item_5owner"],select[name="item_6owner"]'
);
if (!$itemOwnerSelect.length) {
return;
}
const $changeOwnerBtn = $(
'<button type="button" class="button clickable" title="变更物品归属">变更物品归属</button>'
)
.insertAfter($('input[value="应用改动"]'))
.click(function () {
createChangeOwnerDialog($itemOwnerSelect);
});
}
function createChangeOwnerDialog($itemOwnerSelect) {
const pageSize = 20;
let idx = $itemOwnerSelect
.prop("name")
.replace("item_", "")
.replace("owner", "");
const urlParams = getBaseSearchParams();
urlParams.set("ok", "应用改动");
urlParams.set("IS_POPUP", 1);
urlParams.set(
"pay_from_group_cash_box",
$('input[name="pay_from_group_cash_box"]').val()
);
urlParams.set("put_purchases_to", $('input[name="put_purchases_to"]').val());
urlParams.set("view", $('input[name="view"]').val());
urlParams.set("common_cellar", $('input[name="common_cellar"]').val());
urlParams.set("marketview", $('input[name="marketview"]').val());
urlParams.set("ITEMS_KELLER_SORT_DIR", "ASC");
urlParams.set("ITEMS_KELLER_SORT_COL", 2);
urlParams.set("ITEMS_KELLER_PAGE", 1);
urlParams.set(`item_${idx}name`, $(`input[name="item_${idx}name"]`).val());
urlParams.set(
`profile_data_item_${idx}_profile_data`,
$(`input[name="profile_data_item_${idx}_profile_data"]`).val()
);
urlParams.set(
`callback_js_code_item_${idx}_callback_js_code`,
$(`input[name="callback_js_code_item_${idx}_callback_js_code"]`).val()
);
urlParams.set(`item_${idx}hero_class`, 0);
urlParams.set(`item_${idx}hero_race`, 0);
urlParams.set(`item_${idx}location`, "");
urlParams.set(`item_${idx}unique`, "");
urlParams.set(`item_${idx}bonus_attr`, "NULL");
urlParams.set(`item_${idx}item_class`, 0);
urlParams.set(`item_${idx}any_skill`, 0);
urlParams.set(`item_${idx}skill`, "");
urlParams.set(`item_${idx}any_skillclass`, 0);
urlParams.set(`item_${idx}set`, 0);
urlParams.set(`item_${idx}item_condition`, 0);
urlParams.set(`item_${idx}sockets`, "NULL");
urlParams.set(`item_${idx}item_conditionMax`, 6);
urlParams.set(`item_${idx}usage_item`, "");
urlParams.set(`item_${idx}hero_level_enabled_posted`, 1);
urlParams.set(`item_${idx}hero_level`, 40);
urlParams.set(`item_${idx}hero_level_stored`, 40);
urlParams.set(`item_${idx}group_item`, "");
urlParams.set(`item_${idx}attribute_name`, "eff_at_st");
urlParams.set(`item_${idx}attribute_value`, "");
urlParams.set(`item_${idx}profile_id`, 0);
urlParams.set(`item_${idx}is_open`, 1);
urlParams.set(`ITEMS_KELLER_JPNR[1]:`, 1);
urlParams.set(`ITEMS_KELLER_PSNR[1]`, pageSize);
urlParams.set(`dummy`, "");
urlParams.set(`ITEMS_KELLER_JPNR[2]:`, 1);
urlParams.set(`ITEMS_KELLER_PSNR[2]`, pageSize);
$itemOwnerSelect = $itemOwnerSelect.clone().attr("id", "owner-select");
$itemOwnerSelect[0].removeChild($itemOwnerSelect.find(".option_")[0]);
$itemOwnerSelect[0].removeChild(
$itemOwnerSelect.find(
`option[value="${urlParams.get("session_hero_id")}"]`
)[0]
);
const targetId = "ajax_dialog";
const innerHtml = $itemOwnerSelect.prop("outerHTML");
_ajaxCreateDialogDirect(
targetId,
innerHtml,
"请选择需要转移物品归属的拥有者",
AJAX_YES | AJAX_CLOSE,
async function (event) {
if (event == "yes") {
let targetVal = prompt("请输入转移物品数量", 1000);
if (targetVal == null) return;
targetVal = parseNum(targetVal);
if (targetVal < 0) {
alert("目标数值不能小于0");
return;
}
urlParams.set(`item_${idx}owner`, $("#owner-select").val());
let goLagerArr = [];
let goBackArr = [];
let remainCnt = targetVal;
while (remainCnt) {
const lastGoLagerArr = [...goLagerArr];
goLagerArr = await changItemsOwner(urlParams, goLagerArr, goBackArr);
goBackArr = lastGoLagerArr;
if (remainCnt <= 0 || goLagerArr.length == 0) break;
remainCnt -= goLagerArr.length;
ajaxAlert(`处理进度:${targetVal - remainCnt}/${targetVal}`);
}
await changItemsOwner(urlParams, [], goBackArr);
ajaxAlert("处理完成!");
} else {
_ajaxStopWaiting(targetId);
_ajaxCloseModalDialog(targetId);
}
},
function () {
document.querySelector(`#${targetId}_buttons button`).innerText = "确定";
}
);
}
async function changItemsOwner(urlParams, goLagerArr, goBackArr) {
const params = new URLSearchParams(urlParams.toString());
const shId = urlParams.get("session_hero_id");
const goWhere = urlParams.get("backWhere");
for (let id of goLagerArr) {
params.set(id, "go_lager");
}
for (let id of goBackArr) {
params.set(id, goWhere);
}
const url =
location.origin +
"/wod/spiel/hero/items.php?is_popup=1&session_hero_id=" +
shId;
const response = await fetch(url, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
body: params.toString(),
method: "POST",
});
const text = await response.text();
const $context = $(text);
const $firstOption = $context.find(
'select[name^=EquipItem] option[value^="-go"]:first'
);
if ($firstOption.length) {
const backWhere = $firstOption.val().replace("-", "");
urlParams.set("backWhere", backWhere);
}
return $context
.find("select[name^=EquipItem]")
.filter((i, e) => $(e).find("option[value*=go_group]").length)
.map((i, e) => e.name)
.get();
}
function viewFullScreen() {
const $fcBtn = $(
'<button type="button" class="button" style="position: absolute;right: 0;top: 0;">切换全屏</button>'
);
const $main = $("#main_content");
GM_addStyle(`div#main_content:fullscreen {overflow: scroll;}`);
$fcBtn.prependTo($main).click(function () {
const isFullscreen =
document.fullScreen ||
document.mozFullScreen ||
document.webkitIsFullScreen;
if (isFullscreen) {
exitFullScreen($main[0]);
} else {
fullScreen($main[0]);
}
});
}
function fullScreen(el) {
let rfs =
el.requestFullScreen ||
el.webkitRequestFullScreen ||
el.mozRequestFullScreen ||
el.msRequestFullScreen;
$(el).css("overflow", "scroll");
if (typeof rfs != "undefined" && rfs) {
rfs.call(el);
return;
}
if (typeof window.ActiveXObject != "undefined") {
const wscript = new ActiveXObject("WScript.Shell");
if (wscript) {
wscript.SendKeys("{F11}");
}
}
}
// 定义退出全屏
function exitFullScreen() {
const el = document;
const cfs =
el.cancelFullScreen ||
el.webkitCancelFullScreen ||
el.mozCancelFullScreen ||
el.exitFullScreen;
$(el).css("overflow", "unset");
if (typeof cfs != "undefined" && cfs) {
cfs.call(el);
return;
}
if (typeof window.ActiveXObject != "undefined") {
const wscript = new ActiveXObject("WScript.Shell");
if (wscript != null) {
wscript.SendKeys("{F11}");
}
}
}
let totalPage = 0;
let currentPage = 0;
function autoCleanWarehouse() {
// 只在物品列表页面生效
const urls = ["/wod/spiel/hero/items.php"];
if (!urls.includes(location.pathname)) {
return;
}
const view = $('input[name="view"]').val();
if (!["cellar", "groupcellar_2"].includes(view)) return;
let pagePrefix = "GROUPCELLAR";
if (view === "groupcellar_2") {
pagePrefix = "GROUPCELLAR";
} else if (view === "cellar") {
pagePrefix = "KELLER";
}
const $cleanWarehouseBtn = $(
'<button type="button" class="button clickable" title="按指定规则自动清仓">自动清仓</button>'
)
.insertAfter($('input[value="应用改动"]'))
.click(async function () {
// 1. 获取第一页内容,并且获得总页数
// 2. 遍历所有页,获得物品列表,目前只清理耗材,生成类似下面的数据结构
// {冰晶:[{cnt: 30, id: 10}, {cnt: 45, id: 12}], 硫磺: [{cnt: 30, id: 21}, {cnt: 45, id: 22}]}
// 3. 将物品按剩余次数从小向下排序,增加合计,然后按照预设的清仓数量从小到大进行标记
// 4. 将所有标记的数据提取到列表中,然后200个一组依次出售
ajaxAlert(`处理开始,请稍候...`);
totalPage = 1;
currentPage = 0;
let firstPage = await fetchConsumablesPage(1, pagePrefix);
let coll = {};
let $doc = $(firstPage);
totalPage = $doc
.find(`input[name^="ITEMS_${pagePrefix}_PAGE"]:last`)
.val()
.trim();
buildColl(coll, firstPage);
const promiseArr = [];
for (let pageIndex = 2; pageIndex <= totalPage; pageIndex++) {
promiseArr.push(fetchConsumablesPage(pageIndex, pagePrefix));
}
Promise.all(promiseArr).then(async (textArr) => {
for (let text of textArr) {
buildColl(coll, text);
}
console.log(coll);
ajaxAlert(`已分析全部数据,开始出售物品!`);
const settings = loadCleanWarehouseSettings();
if (!settings) {
ajaxAlert(`没有预设清仓列表,请设置后重试`);
return;
}
let keepMap = settings["normal"];
let sellList = [];
for (let name of Object.keys(keepMap)) {
if (!coll[name]) continue;
const curTotal = coll[name].total || 0;
const max = keepMap[name];
if (curTotal <= max) continue;
const sellCnt = curTotal - max;
let markedCnt = 0;
for (let item of coll[name].list) {
markedCnt += item.cnt;
if (markedCnt <= sellCnt) {
sellList.push(item);
} else {
break;
}
}
}
console.log(sellList);
const chunkSellList = _.chunk(sellList, 200);
for (let i = 0; i < chunkSellList.length; i++) {
let list = chunkSellList[i];
ajaxAlert(`出售进度:${i}/${chunkSellList.length}`);
await sellItems(
list.map((i) => i.id),
pagePrefix
);
}
ajaxAlert(`处理完成!`);
});
});
}
/**
* 根据类型和页数获取消耗品列表
* @param {*} pageIndex
* @param {*} type
*/
async function fetchConsumablesPage(pageIndex, pagePrefix) {
const pageSize = 200;
let idx = 5;
if (pagePrefix === "GROUPCELLAR") {
idx = 5;
} else if (pagePrefix === "KELLER") {
idx = 4;
}
const urlParams = getBaseSearchParams();
const shId = urlParams.get("session_hero_id");
urlParams.set("IS_POPUP", 1);
urlParams.set(
"pay_from_group_cash_box",
$('input[name="pay_from_group_cash_box"]').val()
);
urlParams.set("put_purchases_to", $('input[name="put_purchases_to"]').val());
urlParams.set("view", $('input[name="view"]').val());
urlParams.set("common_cellar", $('input[name="common_cellar"]').val());
urlParams.set("marketview", $('input[name="marketview"]').val());
urlParams.set(`ITEMS_${pagePrefix}_SORT_DIR`, "ASC");
urlParams.set(`ITEMS_${pagePrefix}_SORT_COL`, 2);
urlParams.set(`ITEMS_${pagePrefix}_PAGE`, 1);
urlParams.set(`item_${idx}name`, $(`input[name="item_${idx}name"]`).val());
urlParams.set(
`profile_data_item_${idx}_profile_data`,
$(`input[name="profile_data_item_${idx}_profile_data"]`).val()
);
urlParams.set(
`callback_js_code_item_${idx}_callback_js_code`,
$(`input[name="callback_js_code_item_${idx}_callback_js_code"]`).val()
);
urlParams.set(`item_${idx}hero_class`, 0);
urlParams.set(`item_${idx}hero_race`, 0);
urlParams.set(`item_${idx}location`, "");
urlParams.set(`item_${idx}unique`, "");
urlParams.set(`item_${idx}bonus_attr`, "NULL");
urlParams.set(`item_${idx}item_class`, 0);
urlParams.set(`item_${idx}any_skill`, 0);
urlParams.set(`item_${idx}skill`, "");
urlParams.set(`item_${idx}any_skillclass`, 0);
urlParams.set(`item_${idx}set`, 0);
urlParams.set(`item_${idx}item_condition`, 0);
urlParams.set(`item_${idx}sockets`, "NULL");
urlParams.set(`item_${idx}item_conditionMax`, 6);
urlParams.set(`item_${idx}usage_item`, "yes");
urlParams.set(`item_${idx}hero_level_enabled_posted`, 1);
urlParams.set(`item_${idx}hero_level`, 40);
urlParams.set(`item_${idx}hero_level_stored`, 40);
urlParams.set(`item_${idx}group_item`, "no");
urlParams.set(`item_${idx}attribute_name`, "eff_at_st");
urlParams.set(`item_${idx}attribute_value`, "");
urlParams.set(`item_${idx}profile_id`, 0);
urlParams.set(`item_${idx}is_open`, 1);
urlParams.set(`dummy`, "");
urlParams.set(`ITEMS_${pagePrefix}_PAGE[${pageIndex}]:`, pageIndex);
urlParams.set(`ITEMS_${pagePrefix}_JPNR[1]:`, 1);
urlParams.set(`ITEMS_${pagePrefix}_PSNR[1]`, pageSize);
urlParams.set(`ITEMS_${pagePrefix}_JPNR[2]:`, 1);
urlParams.set(`ITEMS_${pagePrefix}_PSNR[2]`, pageSize);
const url =
location.origin +
"/wod/spiel/hero/items.php?is_popup=1&session_hero_id=" +
shId;
const response = await fetch(url, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
body: urlParams.toString(),
method: "POST",
});
const text = await response.text();
currentPage++;
ajaxAlert(`目前进度:${currentPage}/${totalPage}`);
return text;
}
/**
* 构建物品映射
* @param {*} coll
* @param {*} text
*/
function buildColl(coll, text) {
let $doc = $(text);
$doc.find('a[href^="/wod/spiel/hero/item.php?"]').each(function (i, e) {
const $e = $(e);
const name = $e.text();
const text = $e.parent().text();
const reResult = text.match(/(.+)\((\d+)\/(\d+)\)/);
if (!reResult) {
console.error(text);
return;
}
const url = $e.attr("href");
const searchParams = new URLSearchParams(url.split("?")[1]);
const itemId = searchParams.get("item_instance_id");
const count = parseInt(reResult[2]);
const max = parseInt(reResult[3]);
console.log(
`【${name}】: ${reResult[2]}/${reResult[3]} with ID: ${itemId}`
);
if (!coll[name]) {
coll[name] = {
total: count,
list: [{ cnt: count, id: itemId, name, max }],
};
} else {
coll[name].total += count;
coll[name].list.push({ cnt: count, id: itemId, name, max });
}
});
}
/**
* 出售物品
* @param {*} itemIds
* @param {*} pagePrefix
* @returns
*/
async function sellItems(itemIds, pagePrefix) {
const pageSize = 200;
let idx = 5;
if (pagePrefix === "GROUPCELLAR") {
idx = 5;
} else if (pagePrefix === "KELLER") {
idx = 4;
}
const urlParams = getBaseSearchParams();
const shId = urlParams.get("session_hero_id");
urlParams.set("IS_POPUP", 1);
urlParams.set(
"pay_from_group_cash_box",
$('input[name="pay_from_group_cash_box"]').val()
);
urlParams.set("put_purchases_to", $('input[name="put_purchases_to"]').val());
urlParams.set("view", $('input[name="view"]').val());
urlParams.set("common_cellar", $('input[name="common_cellar"]').val());
urlParams.set("marketview", $('input[name="marketview"]').val());
urlParams.set(`ITEMS_${pagePrefix}_SORT_DIR`, "ASC");
urlParams.set(`ITEMS_${pagePrefix}_SORT_COL`, 2);
urlParams.set(`ITEMS_${pagePrefix}_PAGE`, 1);
urlParams.set(`item_${idx}name`, "不可能存在的物品");
urlParams.set(
`profile_data_item_${idx}_profile_data`,
$(`input[name="profile_data_item_${idx}_profile_data"]`).val()
);
urlParams.set(
`callback_js_code_item_${idx}_callback_js_code`,
$(`input[name="callback_js_code_item_${idx}_callback_js_code"]`).val()
);
urlParams.set(`item_${idx}hero_class`, 0);
urlParams.set(`item_${idx}hero_race`, 0);
urlParams.set(`item_${idx}location`, "");
urlParams.set(`item_${idx}unique`, "");
urlParams.set(`item_${idx}bonus_attr`, "NULL");
urlParams.set(`item_${idx}item_class`, 0);
urlParams.set(`item_${idx}any_skill`, 0);
urlParams.set(`item_${idx}skill`, "");
urlParams.set(`item_${idx}any_skillclass`, 0);
urlParams.set(`item_${idx}set`, 0);
urlParams.set(`item_${idx}item_condition`, 0);
urlParams.set(`item_${idx}sockets`, "NULL");
urlParams.set(`item_${idx}item_conditionMax`, 6);
urlParams.set(`item_${idx}usage_item`, "yes");
urlParams.set(`item_${idx}hero_level_enabled_posted`, 1);
urlParams.set(`item_${idx}hero_level`, 40);
urlParams.set(`item_${idx}hero_level_stored`, 40);
urlParams.set(`item_${idx}group_item`, "no");
urlParams.set(`item_${idx}attribute_name`, "eff_at_st");
urlParams.set(`item_${idx}attribute_value`, "");
urlParams.set(`item_${idx}profile_id`, 0);
urlParams.set(`item_${idx}is_open`, 1);
urlParams.set(`dummy`, "");
urlParams.set(`ITEMS_${pagePrefix}_PAGE[1]:`, 1);
urlParams.set(`ITEMS_${pagePrefix}_JPNR[1]:`, 1);
urlParams.set(`ITEMS_${pagePrefix}_PSNR[1]`, pageSize);
urlParams.set(`ITEMS_${pagePrefix}_JPNR[2]:`, 1);
urlParams.set(`ITEMS_${pagePrefix}_PSNR[2]`, pageSize);
urlParams.set(`sellids`, itemIds.join(","));
urlParams.set(`sellconfirm`, " OK ");
const url =
location.origin +
"/wod/spiel/hero/items.php?is_popup=1&session_hero_id=" +
shId;
const response = await fetch(url, {
headers: {
accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"content-type": "application/x-www-form-urlencoded",
},
body: urlParams.toString(),
method: "POST",
});
const text = await response.text();
return text;
}
const CLEAN_WARE_HOUSE_KEY = "cleanWareHouseSettings";
/**
* 加载设置
*/
function loadCleanWarehouseSettings() {
let settings = {};
const textSettings = localStorage.getItem(CLEAN_WARE_HOUSE_KEY);
if (!textSettings) {
settings = JSON.parse(JSON.stringify(defaultMap));
} else {
settings = JSON.parse(textSettings);
}
return settings;
}