// ==UserScript==
// @name PCR图书馆辅助计算器
// @namespace http://tampermonkey.net/
// @version 3.1.0
// @description 辅助计算PCR手游的所需体力,总次数
// @author winrey,colin,hymbz
// @license MIT
// @icon https://pcredivewiki.tw/static/images/unit/icon_unit_108831.png
// @supportURL https://github.com/winrey/pcr-wiki-helper/issues
// @homepage https://github.com/winrey/pcr-wiki-helper
// @run-at document-start
// @connect cdn.jsdelivr.net
// @match *://pcredivewiki.tw/*
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_info
// @grant GM.getValue
// @grant GM.setClipboard
// @grant GM.setValue
// @grant GM.deleteValue
// @grant GM.info
// @grant GM_addStyle
// @require https://cdn.jsdelivr.net/npm/jquery@3.4.0/dist/jquery.min.js
// @require https://cdn.jsdelivr.net/gh/winrey/pcr-wiki-helper@eea66a67d2a0f3794d905fd6447b66329dc34d2e/js/solver.js
// @require https://cdn.jsdelivr.net/gh/winrey/pcr-wiki-helper@21de4a7a288c0cdf5d3cea248eee31301d36c105/js/html2canvas.min.js
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
const sleep = time => new Promise(r => setTimeout(r, time));
$(document).ready(function () {
GM_addStyle(`
.helper--calc-result-cell.helper--show-deleted-btn::after {
content: '\u2716';
position: absolute;
bottom: 70px;
background-color: #ff0000;
color: #fff;
line-height: 0.9rem;
border-radius: 30%;
padding: 3px;
opacity: 50%;
cursor: pointer;
z-index: 10000;
}
.helper--calc-result-cell.helper--show-deleted-btn.multiSelect-no::after {
content: '\u00A0\u00A0\u00A0';
}
.helper--calc-result-cell.helper--show-deleted-btn.multiSelect-yes::after {
content: '\u2714';
}
.Unique.helper-cell.mapDrop-item.mr-2.p-2.text-center{
background-color: rgba(255,193,7,.5);
border-radius: 0.7vw;
}
table.table-bordered.mapDrop-table.helper .result-cell-td{
vertical-align: middle;
text-align: justify;
text-align-last: justify;
}
.topToView{
z-index: 96000;
white-space: break-spaces;
}
div.ClpMeue{
visibility: hidden;
transition: all 0.3s;
position: relative;
top: 3rem;
opacity:0;
max-width: 0px;
}
button#toImage {
position: absolute;
font-size: 0.2375rem;
padding: 0.075rem 0.05rem;
bottom: 4.4rem;
}
button#toText{
position: absolute;
font-size: 0.2375rem;
padding: 0.075rem 0.05rem;
bottom: 2.8rem;
}
div.ClpMeue.active{
visibility: visible;
display: inline-block;
opacity:1;
}
.mapDrop-table .helper-oddTri {
right: .3rem;
top: 1.6rem;
color: black;
}
.mapDrop-table .mapDrop-item {
max-width: 106px;
}
.mapDrop-table .helper--calc-result-cell{
width: 70px;
height: 70px;
margin: 0 auto;
position: relative;
}
.mapDrop-table .helper--calc-result-cell.un--wanted{
opacity: 0.4;
}
.mapDrop-table .helper-block {
top: 1.6rem;
right: .12rem;
}
#helper--bottom-btn-group {
position: fixed;
right: calc(60px + 1% - 2px);
bottom: 90px;
overflow: visible;
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.helper--nav-to-level.helper--important::after {
content: "独";
color: #e60c0c;
font-size: .5em;
position: relative;
top: -0.8em;
right: 1em;
}
span.dropsProgress.hide{
display: none;
}
#helper--modal-content:not(.helper--drop) input[item-name], #helper--modal-content:not(.helper--drop) input[orig-item-name] {
display: none;
}
#helper--modal-content input[item-name], #helper--modal-content input[orig-item-name]{
width: 6em;
}
a.singleSelect{
display: none
}
a.singleSelect.ready{
display: inline
}
.switch-multiSelectBtnState {
display: none;
width: 70px;
height: 32px;
border: solid 2px #ddd;
border-radius: 30px;
background-color: #FFF;
position: relative;
padding-left: 2.6rem;
-webkit-transition: background-color 0.3s;
transition: background-color 0.3s;
-webkit-user-select: none;
font-size: 14px;
left: 5.1rem;
}
.switch-multiSelectBtnState.ready {
display: inline;
}
.switch-handler.ready{
display: inline-block;
}
.switch-multiSelectBtnState.selected-completedBtn{
pointer-events: none;
}
.switch-multiSelectBtnState::before {
right: 4rem;
content: '多选';
position: absolute;
width: 2rem;
font-size: 15px;
top: -0.1rem;
margin-right: -1rem;
transition: width 0s 0s,margin-right .3s ,top 0.3s,content 0s 1s;
}
.switch-multiSelectBtnState.active.selected-completedBtn::before {
right: 3rem;
content: '已选完成';
font-size: 14px;
top: -0.08rem;
margin-right: 0rem;
border-radius: 11%;
width: 4rem;
border: 2px outset #f5d68e;
pointer-events: all;
line-height: 15.9px;
transition: box-shadow 0.3s
}
.switch-multiSelectBtnState.selected-completedBtn:hover::before {
box-shadow: -0.7px 1px 5.1px #000;
}
.switch-handler {
position: relative;
left: 2.5rem;
top: 0.25rem;
width: 1rem;
height: 1rem;
background-color: #FFF;
border-radius: 100% 100%;
-webkit-box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.52);
box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.52);
-webkit-transition: all 0.3s;
transition: all 0.3s;
display: none;
}
.switch-multiSelectBtnState.active {
border-color: #4cd964;
background-color: #4cd964;
}
.switch-handler.ready.active {
left: 3.9rem;
}
.switch-handler::after {
color: #000000;
content: '关';
position: relative;
bottom: 0.25rem;
left: -0.6rem;
padding-left: 1rem;
-webkit-transition: color .3s 0.1s;
}
.switch-handler.active::after {
content: ' ';
color: #fff0;
position: relative;
left: -4rem;
}
.switch-handler.active::before {
content: '开';
color: #f5f5f5;
right: 1.4rem;
padding-right: 1.4rem;
}
.switch-handler::before {
content: '\u00A0\u00A0\u00A0';
color: #fff0;
width: 2.6rem;
bottom: 0.2rem;
-webkit-transition: color .3s .1s;
position: relative;
}
.form-control.active{
transition:border linear .2s,box-shadow linear .5s;
-moz-transition:border linear .2s,-moz-box-shadow linear .5s;
-webkit-transition:border linear .2s,-webkit-box-shadow linear .5s;
outline:none;
border-color: rgba(12, 255, 0, 0.75);
box-shadow:0 0 8px rgba(59, 224, 9, 0.75);
-moz-box-shadow:0 0 8px rgba(93,149,242,.5);
-webkit-box-shadow:0 0 8px rgba(93,149,242,3);
}
`);
/**
* @global
* @namespace onceItemchange
* @type {object[]}
* @property {string} onceItemchange[].id - 碎片id.
* @property {number} onceItemchange[].count - 碎片数量.
*/
const onceItemchange = [];
let vue;
/**
* 点击“存储队伍”按钮
*/
const saveTeamData = () => {
//findOnePCRelem(`.sticky-top>button.pcbtn.primary`, '儲存隊伍').click();
vue.saveTeam();
let d = document.querySelector('a[href="##"]');
d && d.click();
};
/**
* 自动切换到地图掉落模式
*
*/
async function autoSwitch2MapList() {
vue = document.querySelector('div.container').__vue__;
//findOnePCRelem(`.armory-function.p-1.pb-3>button`, '地圖掉落模式').click();
vue.isLoading = true;
await sleep(20);
vue.changeDisplayMode(3);
vue.pageSize = 1000;
await sleep(1000);
}
function selectNumInOnePage(num, event) {
const $select = $(
'#app > .main > .container > .item-box > .row.mb-3 > div:nth-child(3) > .row > div:nth-child(3) select'
);
if (num) {
const changeEvent = new Event('change');
$select.val(1000);
$select[0].dispatchEvent(changeEvent);
} else return $select.val();
}
function toPage(num) {
const $table = $('.mapDrop-table:not(.helper)');
const $pages = $($table.find('tr').toArray().pop());
const $frist = $($pages.find('li').toArray()[num || 1]);
$frist.children()[0].click();
}
async function getMapData() {
function rowParser($tr, page, index) {
function parseItem($item) {
const url = $($item.find('a')[0]).attr('href');
const name = $($item.find('img')[0]).attr('title');
const img = $($item.find('img')[0]).attr('src');
const requireItemID = img.match(/\d{6}/)[0]; //pcredivewiki.tw/static/images/equipment/icon_equipment_115221.png
const odd = parseInt($($item.find('h6.dropOdd')[0]).text()) / 100; // %不算在parseInt内
const count = parseInt(
(!/無需|溢/.test($($item.find('.py-1')[0]).text()) &&
$($item.find('.py-1')[0]).text()) ||
0
);
const id = /\d+/.exec(img)[0];
return { url, name, img, odd, count, id };
}
const children = $tr.children().map(function () {
return $(this);
});
const name = children[0].text();
const requirement = parseInt(children[1].text());
const items = $(children[2].children()[0])
.children()
.toArray()
.map(v => parseItem($(v)));
return { name: name, requirement: requirement, items: items, page: page, index: index };
}
function next($table) {
const $pages = $($table.find('tr').toArray().pop());
const $next = $($pages.find('li').toArray().pop());
if ($next.hasClass('disabled')) return false;
$next.children()[0].click();
return true;
}
let $table = $('.mapDrop-table:not(.helper)');
const data = [];
toPage(1);
let page = 1;
await sleep(20);
do {
await sleep(20);
$table = $('.mapDrop-table:not(.helper)');
//判断简易计算
let start = $table.find('thead>tr').length,
pageData = $table.find('tbody>tr'),
dataTd = $table.find('tbody>td');
pageData = pageData.toArray().map($);
pageData = pageData.slice(0, -1); // 最后一行是分页栏
if (start === 1) {
pageData = pageData.slice(start, -1); // 最后一行是分页栏
} else {
//pageData = pageData
// .filter(function (i, v) { return v !== this && v % 2 === 0 || false }.bind(pageData.length - 1)) //结果过滤偶数 //edit "Comments "by cool_delete
let paeD = (i, v) => ~~/建議:\s(\d+)/.exec($(dataTd.get(v)).text())[1]; //结果过滤0数
pageData = pageData.filter(paeD);
}
pageData = pageData.map((m, i) => rowParser(m, page, i));
data.push.apply(data, pageData);
page += 1;
} while (next($table));
toPage(1);
return data;
}
function getCost(name) {
if (name === '1-1') return 6;
if (name.startsWith('1-')) return 8;
if (name.startsWith('2-')) return 8;
if (name.startsWith('3-')) return 8;
if (name.startsWith('4-')) return 9;
if (name.startsWith('5-')) return 9;
if (name.startsWith('6-')) return 9;
return 10;
}
function calcResult(data) {
data = data.map(chan => {
const sum = (...arr) => [].concat(...arr).reduce((acc, val) => acc + val, 0);
chan.exception = sum(chan.items.map(v => v.count * v.odd));
chan.max = Math.max.apply(
null,
chan.items.map(v => v.count / v.odd)
);
chan.min = Math.min.apply(
null,
chan.items.filter(v => v.count).map(v => v.count / v.odd)
);
chan.effective = sum.apply(
null,
chan.items.map(v => (v.count ? v.odd : 0))
);
return chan;
});
const model = {
optimize: 'cost',
opType: 'min',
constraints: (() => {
const equis = {};
data.forEach(c => c.items.forEach(e => (equis[e.name] = { min: e.count })));
return equis;
})(),
variables: (() => {
const challs = {};
data.forEach(c => {
const cMap = {};
c.items.forEach(item => (cMap[item.name] = item.odd));
cMap.cost = getCost(c.name);
challs[c.name] = cMap;
});
return challs;
})(),
};
console.log('model', model);
const lp_result = solver.Solve(model);
console.log(lp_result);
for (let k in lp_result) {
if (!k.includes('-')) continue;
const target = data.find(c => c.name === k);
if (target) target.times = lp_result[k] || 0;
}
return {
total: lp_result.result,
map: data.sort((a, b) => b.times - a.times).sort((a, b) => b.effective - a.effective),
};
}
const BOUNS_KEY = '___bouns';
function askBouns() {
const bouns =
parseInt(
prompt('请输入目前倍数(N3或N2,非活动期可取消)').split('').reverse().join('') || '1'
) || 1;
sessionStorage.setItem(BOUNS_KEY, bouns);
return bouns;
}
function getBouns() {
let bouns = parseInt(sessionStorage.getItem('___bouns'));
if (!bouns) {
bouns = askBouns();
}
return bouns;
}
function showResult(data) {
const bouns = getBouns();
const table = genTable(data.map.filter(m => m.times));
const comment = $.parseHTML('说明');
const commentLines = [];
commentLines.push(
'推荐使用方法:按照列表顺序刷图,数量不要超过「适用」和「推荐」两者的最小值,完成后修改数量,重新根据新情景计算。'
);
commentLines.push('');
commentLines.push(
'注意:如果您尚缺好感,可考虑以30体/次为倍数单位扫荡刷图,能最大化获取发情蛋糕。'
);
commentLines.push('');
commentLines.push('---表头说明---');
commentLines.push(
'『章节』关卡编号。点击编号可以自动跳转到图书馆原表中关卡详细介绍。点击『章节』能切换排序'
);
commentLines.push(
'『独』标识。代表当前结果中仅有该图能出的装备碎片。赶进度的话刷满黄色碎片数。'
);
commentLines.push('『需求』关卡需求。图中所需装备总数。');
commentLines.push('『效率』装备效率。图中所有有效装备掉落的概率和。');
commentLines.push('『适用』有效次数。预计能保持「效率」不变的次数。');
commentLines.push(
'『推荐』推荐次数。假设概率固定,由考虑体力的线性规划算法计算出的总最优刷图次数。'
);
commentLines.push('『最大』最大次数。最近该图需要的最高次数。');
$(comment[0]).click(e => {
tips('说明', commentLines.join('\n'));
e.preventDefault();
e.stopPropagation();
});
const quickModifyBtn = $.parseHTML(`快速修改`);
$(quickModifyBtn[0]).click(async e => {
let modifyState = !document.querySelector('.singleSelect.ready');
[...document.querySelectorAll('span.dropsProgress')].reduce(
(t, i) => i.classList.toggle('hide', modifyState),
document.querySelector('span.dropsProgress')
);
//点击快速修改 如果找不到输入框就没法设置
vue.showFastStock()
document.querySelector('#popBox.modal.fade.show') &&
document.querySelector('#popBox.modal.fade.show').click();
document
.getElementById('helper--modal-content')
.classList.toggle('helper--drop', modifyState);
deleteItem(modifyState);
modifyState &&
document
.querySelector('span.switch-multiSelectBtnState')
.addEventListener(`click`, multiItemChange);
modifyState &&
document.querySelector('span.switch-handler').addEventListener(`click`, e => {
multiSelectState(
switchMultBtnState(
'active',
!document.querySelector('span.switch-multiSelectBtnState.active')
)
);
e.stopImmediatePropagation();
});
modifyState &&
(document.querySelector('.singleSelect.ready').parentElement.scrollLeft = 3000);
return false;
});
const reCalcBtn = $.parseHTML(
`重新计算`
);
const multipleBtn = $.parseHTML(
``
);
$(reCalcBtn[0]).click(() => {
TranslateRefresh_the_interface();
handleClickCalcBtn();
return false;
});
showModalByDom(
`总体力需求:${Math.round(
data.total / bouns
)} 当前倍率:${bouns} `,
comment,
quickModifyBtn,
reCalcBtn,
multipleBtn,
table
);
}
function createModal(...content) {
const containerStyle = `
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed;
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
transition: all ease-in-out 0.5s;
`;
const maskStyle = `
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
position: absolute;
z-index: 11000;
`;
const boxStyle = `
min-width: 80%;
z-index: 12000;
`;
const contentStyle = `
width: 100%;
margin-bottom: 10px;
max-height: 80vh;
overflow: scroll;
`;
const html = `
${content.join(
''
)}
`;
$('#app').after(html);
$('#helper--modal-close').click(() => hideModal());
$('#helper--modal-mask').click(() => hideModal());
$('#helper--modal-Clipboard').click(showToClpMeue);
$('button#toText').click(() => {
return showToClpMeue(), txtToClipboard();
});
$('button#toImage').click(() => {
return showToClpMeue(), clpSetImage();
});
}
const showToClpMeue = () => {
document.querySelector('div.ClpMeue').classList.toggle('active');
};
function genItemsGroup(items) {
const old = window.performance.now();
items = boundLocatStrong(items); // ${item.Unique?`唯一`:``}
const html = `
${items
.map(
item => `
`
)
.join('')}
`;
return html;
}
function boundLocatStrong(items) {
for (let item of items) {
try {
let p = ~~new RegExp('"equipment_id":' + item.id + ',"count":([^,]+),')
.exec(localStorage.itemList)[1]
.replace(/^\"|\"$/g, '');
item.information = `有` + p + ' 缺' + item.count;
item.has = p;
let c = `${item.count && (item.count += p)}`;
} catch (e) {
item.count = 0;
}
}
return items;
}
const changeItemCount = e => {
// 快速完成
const singleItem = () => {
const $this = $(e.target);
const count = $this[0].dataset.itemCount;
if (!count) return;
const ID = $this[0].dataset.item_id;
const name = $this[0].dataset.item_name;
if (confirm(`${name}的数量达到了${count}。页面刷新后自动计算`)) {
itemCountChage(ID, count);
GM.setValue(`mount`, `(()=>{ setTimeout(handleClickCalcBtn,2000) })()`);
location.reload();
}
};
const multiItem = () => {
e.target.classList.toggle(
`multiSelect-yes`,
!(e.target.classList[e.target.classList.length - 1] == `multiSelect-yes`)
);
let cls = document.querySelector('.switch-multiSelectBtnState').classList;
cls.toggle(
`selected-completedBtn`,
document.querySelectorAll('.multiSelect-yes').length != 0
);
};
(e.target.classList[e.target.classList.length - 1] == `helper--show-deleted-btn` &&
!singleItem()) ||
multiItem();
};
const multiItemChange = e => {
let cell = document.querySelectorAll('.multiSelect-yes');
if (
cell.length &&
confirm(`你目前选了${cell.length}个装备,开始修改,点击确定刷新页面自动计算`)
) {
for (let dom of [...cell]) {
itemCountChage(dom.dataset.itemId, dom.dataset.itemCount);
}
GM.setValue(`mount`, `(()=>{ setTimeout(handleClickCalcBtn,2000) })()`);
location.reload();
}
};
function itemCountChage(equipment_id, count) {
let p = new RegExp('"equipment_id":' + equipment_id + ',"count":([^,]+)', 'g');
let t = new RegExp(`\\d+`, 'g');
localStorage.setItem(
`itemList`,
localStorage.itemList.replace(p, (match, p1) => {
return match.substr(0, 30) + p1.replace(t, count); //match[match.length-1]match.length-3
})
);
}
function uniqueItem(mapData) {
let itmes = [];
for (let i = 0; i < mapData.length; i++) {
itmes.push(...mapData[i].items);
}
for (let t of itmes) {
itmes[t.name] = (itmes[t.name] && itmes[t.name] + 1) || 1;
}
for (let i = 0; i < mapData.length; i++) {
for (const item of mapData[i].items) {
if (item.count > 0 && itmes[item.name] < 2) {
mapData[i].IsuniqueItem = true;
item.Unique = true;
}
}
}
}
function sortColumn(e) {
//-1>a,b 1>b,a//greedy
let trList = [...e.target.closest('table').querySelectorAll(`tbody>tr`)];
const greedy = () => {
trList
.sort((a, b) => {
return (~~a.dataset.isUniqueItem && -1) || (~~a.dataset.isUniqueItem && 1) || 0;
})
.sort((a, b) => {
return (
(~~a.dataset.isUniqueItem &&
~~b.dataset.isUniqueItem &&
~~b.children[2].dataset.dropEffective - ~~a.children[2].dataset.dropEffective) ||
0
);
});
return 1;
};
const dropEffective = () => {
trList.sort((a, b) => {
return ~~b.children[2].dataset.dropEffective - ~~a.children[2].dataset.dropEffective || 0;
});
return 0;
};
e.target.dataset.sortType = (!~~e.target.dataset.sortType && greedy()) || dropEffective(); //切换状态保存
let tbody = e.target.closest('table').querySelector('tbody');
tbody.innerHTML = '';
for (let t of trList) {
tbody.appendChild(t);
}
}
async function txtToClipboard() {
const 数据条目 = '20', //(条)
trList = [
...document.querySelectorAll(
`table.table.table-bordered.mapDrop-table.helper>tbody tr:nth-child(-n+${数据条目})`
),
],
howMuchSpace = (sum = 12, a = []) => {
return sum < 1 && (sum = 0), (a.length = sum), a.fill(space, 0, sum).join(``);
},
surroundedByaBar = (text, Rows = 6, horizontal = text.length + 8) => {
Rows = (Rows & 1 && Rows) || Rows + 1; //only odd
let str = enter,
blank = (horizontal - text.length) / 2,
half = Math.ceil(Rows / 2);
for (let i = 1; i <= Rows; i++) {
str += '|';
for (let k = 0; k < horizontal; k++) {
((Rows === i || 1 === i) && (str += '-')) || i === half || (str += ' ');
//中间行k已到居中文本位置
i === half &&
((blank <= k &&
k < blank + text.length - 2 &&
((k = text.length + blank - 1), (str += text))) ||
(str += ' '));
}
str += '|' + enter;
}
str.substr(0, str.length - 2);
return str;
},
space = ' ',
enter = '\r\n',
title = `${howMuchSpace(17)}pcr简易装备库${howMuchSpace()}数据目:${trList.length}${enter}`;
let count = 0,
text = `\u200E ${howMuchSpace(3)}章节${howMuchSpace(6)}效率${howMuchSpace(
6
)}各效率${enter}`;
for (let t of trList) {
text += howMuchSpace(5);
for (let b = 1; b < 9; b += 2) {
let lent = t.childNodes[b].innerText.length;
text += t.childNodes[b].innerText + howMuchSpace(10 - lent);
}
text = text.trim();
text += enter;
count += 1;
}
//设置dom移除监听 负责在生成链接后设置粘贴板
document.querySelector('.wating').parentElement.parentElement.parentElement.addEventListener(
'DOMNodeRemoved',
async () => {
GM.setClipboard(
`7天内打开链接,装备、角色数据完整保留,但将于${(d =>
`${d.getMonth() + 1}月${d.getDate()}号`)(
new Date(new Date().getTime() + 7 * 86400000)
)}失效!${enter}请尽快打开链接:${surroundedByaBar(
vue.exportNotice || 'network error,copy Text below'
)}${enter}${howMuchSpace(4)}${enter}${howMuchSpace(
4
)}并点击储存队伍${enter}${enter}${enter}${howMuchSpace(
4
)}如果链接失效,可复制""(不含引号)内的字符"到文字汇入队伍的输入框${enter}"${vue.zipMyTeam()}"`
);
tips('备份成功', '复制完成,请尽快拷贝到其他地方保存');
},
{ once: true }
);
vue.isLoading = true;
backupTream();
}
function backupTream() {
let uuid = vue.uuid();
let armory = _.concat([vue.teamList], [vue.itemList]);
let teamList = JSON.stringify(armory);
teamList = vue.b64EncodeUnicode(teamList);
let url = '/static/php/mysqlAdd.php';
let data = { teamList: teamList, uuid: uuid };
$.ajax({
type: 'POST',
url: url,
data: data,
success: function (data) {
let res = data.toString().trim();
if (res == '200') {
vue.isLoading = false;
vue.exportNotice = 'https://pcredivewiki.tw/Armory?s=' + uuid;
}
},
});
}
async function clpSetImage() {
vue.isLoading = true;
await sleep(200);
let shareContent = document.querySelector(
'table.table.table-bordered.mapDrop-table.helper tbody'
);
let width = shareContent.offsetWidth;
let height = shareContent.offsetHeight;
let canvas = document.createElement('canvas');
let scale = 1;
canvas.width = width * scale;
canvas.height = height * scale;
const content = canvas.getContext('2d');
content.scale(scale, scale);
const opts = {
scale: scale,
width: width,
height: height,
canvas: canvas,
allowTaint: true,
width: width,
heigth: height,
y: window.pageYOffset + shareContent.getBoundingClientRect().top,
}; //局部元素带滚动时候 截图需要指定window的x,y 元素的getBoundingClientRect挺重要的
html2canvas(shareContent, opts).then(canvas => {
const content = canvas.getContext('2d');
content.mozImageSmoothingEnabled = false;
content.webkitImageSmoothingEnabled = false;
content.msImageSmoothingEnabled = false;
content.ImgSmoothingEnabled = false;
canvas.toBlob(blob => {
navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
vue.isLoading = false;
tips('图片复制成功', '复制完成,可直接复制到微信或qq。');
});
});
}
const deleteItem = switchOn => {
switchMultBtnState(`ready`, switchOn);
for (let i of $('table .p-2.text-center.mapDrop-item.mr-2>div.helper--calc-result-cell')) {
(~~i.dataset.itemCount && switchOn && !!$(i).addClass('helper--show-deleted-btn')) ||
$(i).removeClass('helper--show-deleted-btn');
}
!switchOn && multiSelectState();
};
const switchMultBtnState = (cls, switchOn = false) => {
let state = ['ready', 'active', 'selected-completedBtn'];
!switchOn &&
cls == state[1] &&
document.querySelector('a.singleSelect').classList.toggle(state[0], !switchOn);
if (
!switchOn &&
cls == state[0] &&
!state.forEach(i => {
document.querySelector('span.switch-multiSelectBtnState').classList.toggle(i, switchOn);
document.querySelector('span.switch-handler').classList.toggle(i, switchOn);
document.querySelector('a.singleSelect').classList.toggle(i, switchOn);
})
)
return switchOn;
if (
!switchOn &&
cls == (state.shift() && state)[0] &&
!state.forEach(i => {
document.querySelector('span.switch-multiSelectBtnState').classList.toggle(i, switchOn);
document.querySelector('span.switch-handler').classList.toggle(i, switchOn);
})
)
return switchOn;
document.querySelector('span.switch-multiSelectBtnState').classList.toggle(cls, switchOn);
document.querySelector('span.switch-handler').classList.toggle(cls, switchOn);
switchOn && cls == state[1]
? document.querySelector('a.singleSelect').classList.toggle(state[0], !switchOn)
: document.querySelector('a.singleSelect').classList.toggle(state[0], switchOn);
return switchOn;
};
const multiSelectState = (switchOn = false) => {
for (let i of $('table .p-2.text-center.mapDrop-item.mr-2>div.helper--calc-result-cell')) {
let c = ~~i.dataset.itemCount;
c && i.classList.toggle('multiSelect-no', switchOn);
c && !switchOn && i.classList.toggle('multiSelect-yes', switchOn);
}
};
const toDetailsTheMap = map => {
const onlineMap = `https://pcredivewiki.tw/Map`;
const genUri = () => {
/* 日后地图更新
打开https://pcredivewiki.tw/Map 打开控制台按下Exc 在console中输入
` $$('.btn.btn-info.p-3')
.map(el => (el.innerText.replace(/\d+\./,'')+'N'))
.reduce((sum, value) =>{return sum .push(value),sum},[]).join('","')
`
不含反引号 输出后模仿格式(注意前后引号!!)复制到下面maps中
*/
console.log(`如果地图更新的话看我,点右边的超链接`);
const levelsForMapUir = new Map();
const maps = [
'朱諾平原N',
'帕拉斯高原N',
'赫柏丘陵N',
'維斯塔溪谷N',
'刻瑞斯森林N',
'佛洛拉湖畔N',
'墨提斯大瀑布N',
'伊麗絲樹海N',
'弗麗嘉雪原N',
'洛麗泰海岸N',
'蓋奴亞荒漠N',
'波諾尼亞砂丘N',
'朵羅西亞溼地N',
'尤金尼亞熱地N',
'塔利亞火山N',
'泰美斯銀嶺N',
'菲得斯冰原N',
'法艾頓草原N',
'法艾頓草原‧南部N',
'卡斯塔利亞樹林‧西部N',
'卡斯塔利亞樹林‧東部N',
'馬提爾德岩峰‧南部N',
'馬提爾德岩峰‧北部N',
'雷蒂烏斯群峰‧西麓N',
'雷蒂烏斯群峰‧東麓N',
'佩特羅大森林‧西部N',
'佩特羅大森林‧東部N',
'迪茲塔爾河蝕岸‧北部N',
'迪茲塔爾河蝕岸‧南部N',
'弗泰拉斷崖‧北部N',
'弗泰拉斷崖‧南部N',
'法斯奇亞森林‧南部N',
'法斯奇亞森林‧東部N',
'迪克斯提亞岩崖‧西壁N',
'迪克斯提亞岩崖‧東壁N',
'維娜湖畔道‧南部N',
'維娜湖畔道‧北部N',
'拉圖斯斷崖‧南部N',
'拉圖斯斷崖‧北部N',
'スカプ山系・西麓N',
'スカプ山系・東麓N',
'ペクトス氷峰・西壁N',
'ペクトス氷峰・東壁N',
];
let i = 1;
for (let m of maps) {
levelsForMapUir.set(i, `https://pcredivewiki.tw/Map/Detail/${encodeURI(m)}`);
i += 1;
}
return levelsForMapUir;
};
const mapIndex = map.split('-');
const p = mapIndex.shift() >> 0;
const d = genUri();
(d.has(p) && GM.setValue(`toMap`, mapIndex.shift() >> 0) && unsafeWindow.open(d.get(p))) ||
alert(
`地图可能更新了,请按下F12 ,再按下Esc,找到‘如果地图更新的话看我,点右边的超链接’字样,按提示修改脚本`
);
};
function genTable(mapData) {
uniqueItem(mapData);
const bouns = getBouns(); //
const html = `
| 章节 |
效率 |
各次数 |
掉落一覽 |
${mapData
.map(
m => `
|
${m.name}
|
${Math.round(m.effective * 100)}% |
适用
${Math.ceil(m.min / bouns)}
推荐
${Math.ceil(m.times / bouns)}
最大
${Math.ceil(m.max / bouns)} |
${genItemsGroup(m.items)}
|
`
)
.join('')}
`.trim();
const table = $.parseHTML(html).pop(); // 0是一堆逗号,我也不造这是什么鬼
$(table)
.find('a.helper--nav-to-level')
.click(function (e) {
const $this = $(e.currentTarget);
const page = parseInt($this.attr('data-page'));
const index = parseInt($this.attr('data-index'));
//hideModal();
toDetailsTheMap($this.text());
/*
setTimeout(() => {
const $table = $(".mapDrop-table:not(.helper)");
const elem = $table.find("tr")[index];
elem.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center",
})
}, 200)
*/
});
$(table)
.find('.p-2.text-center.mapDrop-item.mr-2>div.helper--calc-result-cell')
.click(changeItemCount);
const Debounce = function (fn, delay = 500, immediate = false) {
typeof delay === 'boolean' && (immediate = delay);
let timer = null; // 闭包存储setTimeout状态
return function () {
let self = this; // 事件源this
let args = arguments; // 接收事件源的event
if (timer) clearTimeout(timer); // 存在就清除执行fn的定时器
if (immediate) {
// 立即执行
let callNow = !timer; // 执行fn的状态
timer = setTimeout(function () {
timer = null;
}, delay);
if (callNow) fn.call(self, ...args);
} else {
// 非立即执行
timer = setTimeout(function () {
// 或者使用箭头函数将this指向dom
fn.call(self, ...args);
}, delay);
}
};
};
const inputEntry = async e => {
// 只有回车触发更改
if (e.type !== 'blur' && e.keyCode != 13) {
return;
}
const itemName = e.target.getAttribute('item-name'),
id = e.target.parentElement.parentElement.children[0].dataset.item_id,
newNum = e.srcElement.valueAsNumber;
// 通过图书馆的快速修改功能来进行库存的修改
// const inputDoms=[...document.querySelectorAll(`#app table img[title="${itemName}"]`)]
// const Refresh_the_interface = () => {
// let i=inputDoms.shift().closest('div').querySelector('input')
// i.valueAsNumber = newNum;
// inputDoms.length&&
// window.requestAnimationFrame(Refresh_the_interface)||
// i.dispatchEvent(new KeyboardEvent("keyup", { key: "Enter", keyCode: 13 }))
// };
// window.requestAnimationFrame(Refresh_the_interface)
//卡顿原因自pcr的vue计算dom挤在一个宏任务了 无法优化
singleFragmentGlobalSave(id, newNum);
e.target.classList.toggle('active', 1);
// 在修改库存后,修改结果页的库存显示
// table.querySelectorAll(`input[item-name=${itemName}]`).forEach(dom => {
// })
// 在输入掉落数时同步所有相同装备下的 input 的 value
const c = [...table.querySelectorAll(`input[item-name=${itemName}]`)];
c.reduce((t, i) => {
i.value = newNum;
const itemSpanDom = i.closest('div').querySelector('span.text-center');
const title = itemSpanDom.getAttribute('title');
let totalNeed = itemSpanDom.getAttribute('data-total-need');
itemSpanDom.innerText = newNum < totalNeed ? `总需${totalNeed}` : '已满';
itemSpanDom.setAttribute('title', `有${newNum} 缺${Math.max(totalNeed - newNum, 0)}`);
i.closest('div')
.querySelector('img')
.setAttribute('title', `有${newNum} 缺${Math.max(totalNeed - newNum, 0)}`);
i.closest('div').querySelector('span.dropsProgress').innerText = `进度:${newNum}`;
}, c[0]);
};
const fnChanged = Debounce(Debounce(inputEntry, 300), 300);
table.querySelectorAll('input[item-name]').forEach(inputDom => {
inputDom.addEventListener('input', fnChanged);
inputDom.addEventListener('keyup', fnChanged);
inputDom.addEventListener('blur', fnChanged);
});
const deltaInputEntry = async e => {
// 只有回车触发更改
if (e.keyCode != 13) {
return;
}
// 修改上方总数量并触发修改事件 -> delegate to inputEntry()
const itemName = e.target.getAttribute('orig-item-name');
const delta = +e.srcElement.value;
const origInputDom = e.target.closest('div').querySelector('input[item-name]');
origInputDom.value = +origInputDom.value + delta;
origInputDom.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter', keyCode: 13 }));
e.target.value = '';
// 如有下个物品,跳转焦点
const nextItemDiv =
e.target.closest('div').nextElementSibling &&
e.target
.closest('div')
.nextElementSibling.querySelector('div')
.classList.contains('un--wanted') &&
e.target.closest('div').nextElementSibling;
if (nextItemDiv) {
nextItemDiv
.querySelector('input[orig-item-name]')
.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter', keyCode: 13 }));
} else {
(e.target.closest('div').nextElementSibling &&
!e.target
.closest('div')
.nextElementSibling.querySelector('input[orig-item-name]')
.focus()) ||
e.target.closest('tr').querySelector('input[orig-item-name]').focus();
}
};
table.querySelectorAll('input[orig-item-name]').forEach(inputDom => {
inputDom.addEventListener('input', deltaInputEntry);
inputDom.addEventListener('keyup', deltaInputEntry);
});
return table;
}
function singleFragmentGlobalSave(id, newNum) {
//直接存到local,vue中关闭再处理
itemCountChage(id, newNum);
onceItemchange.push({ id: id, count: newNum });
}
async function hideModal() {
document.querySelector('#popBox.modal.fade.show') &&
document.querySelector('#popBox.modal.fade.show').click();
$('#helper--modal').css('opacity', 0);
$('#helper--modal').css('pointer-events', 'none');
window.requestAnimationFrame(Refresh_the_interface);
}
const Refresh_the_interface = () => {
let i = onceItemchange.shift();
i && vue.changeStock(i.count, i.id);
onceItemchange.length && window.requestAnimationFrame(Refresh_the_interface);
};
const TranslateRefresh_the_interface = () => {
let i = onceItemchange.shift();
i && vue.changeStock(i.count, i.id);
onceItemchange.length && TranslateRefresh_the_interface();
};
function showModal(...content) {
$('#helper--modal').css('opacity', 1);
$('#helper--modal').css('pointer-events', '');
if (content && content.length) {
debugger;
$('#helper--modal-content').html(content.join(''));
}
}
async function showModalByDom(...dom) {
$('#helper--modal').css('opacity', 1);
$('#helper--modal').css('pointer-events', '');
if (dom.length) {
$('#helper--modal-content').html('');
for (let i in dom) $('#helper--modal-content').append(dom[i]);
}
document
.querySelector('table.table.table-bordered.mapDrop-table.helper th')
.addEventListener(`click`, sortColumn);
}
async function handleClickCalcBtn() {
await autoSwitch2MapList();
await sleep(300);
saveTeamData();
// 自动调整至旧版数量
//const tempDom = document.querySelector('button[title="設計圖數量為舊版數量"]');
//if(![...tempDom.classList].includes('active'))
// tempDom.click();
//await sleep(100);
document.getElementById('helper--modal-content').classList.remove('helper--drop');
// if (selectNumInOnePage() != '1000') {
// selectNumInOnePage(1000);
// }
await sleep(100);
const data = await getMapData();
console.log('data', data);
const result = calcResult(data);
console.log('result', result);
showResult(result);
vue.pageSize = 10;
vue.isLoading = false;
document.querySelector('#popBox.modal.fade.show') &&
document.querySelector('#popBox.modal.fade.show').click(),
changeBtnGroup();
}
async function handleFastModifyBtn() {
const $table = $('.mapDrop-table:not(.helper)');
if ($table && $table.find('thead button').length) {
$table.find('thead button')[0].click();
} else {
alert('现在还不是地图掉落页面呢~');
}
}
function btnFactory(content, colorRotate, onClick) {
const btn = $.parseHTML(`
`);
$(btn).click(onClick);
return btn;
}
/**
* 返回pcr的按钮element
*
* @param {String} css 按钮的父级或集合
* @param {?String} btnName 按钮的innerText!
* @returns:html元素
*/
function findOnePCRelem(css, btnName) {
try {
if (!btnName) {
return $(css);
}
return [...document.querySelectorAll(css)].filter(node => node.innerText === btnName).pop();
} catch (error) {
console.error('ccs路径错误');
}
}
function tips(title, text) {
vue.copyText('errrcolin');
vue.popMsg.title = title;
vue.popMsg.content = text;
let options = {
attributes: true,
attributeFilter: ['class'],
};
$('div#popBox')[0].classList.toggle('topToView', 1);
let mb = new MutationObserver(function (mutationRecord, observer) {
if (mutationRecord[0].target.classList.contains('show')) return;
observer.disconnect();
$('div#popBox')[0].classList.toggle('topToView', 0);
});
mb.observe($('div#popBox')[0], options);
$('div#popBox button').focus();
}
function createBtnGroup() {
const group = $.parseHTML(`
`);
const fastModifyBtn = btnFactory('快速
修改', 270, handleFastModifyBtn);
const bounsBtn = btnFactory('修改
倍数', 180, askBouns);
const calcBtn = btnFactory('计算
结果', 90, handleClickCalcBtn);
$(group).append(calcBtn);
$(group).append(fastModifyBtn);
$(group).append(bounsBtn);
$('#app .container').append(group);
}
function changeBtnGroup() {
const group = $('#helper--bottom-btn-group');
group.html('');
const fastModifyBtn = btnFactory('快速
修改', 188, handleFastModifyBtn);
const bounsBtn = btnFactory('修改
倍数', 216, askBouns);
const lastResultBtn = btnFactory('上次
结果', 144, () => showModal());
const calcBtn = btnFactory('重新
计算', 72, handleClickCalcBtn);
group.append(calcBtn);
group.append(bounsBtn);
group.append(fastModifyBtn);
group.append(lastResultBtn);
}
function appendName(mapName) {
document.querySelector('nav.navbar.navbar-expand-md.navbar-dark.fixed-top').style.visibility =
'hidden'; //隐藏导航条
document.querySelector('.float-right.pcbtn.mr-3') &&
!document.querySelector('.float-right.pcbtn.mr-3').click(); //显示魔物
const toName = (
name,
ElementFindByNameDotParent = document.querySelector('#H' + name.mapName).parentElement
) => {
ElementFindByNameDotParent.scrollIntoView({ block: 'center' }),
(ElementFindByNameDotParent.style.border = '3px solid #db1f77');
};
[...document.querySelectorAll('.item-title')].forEach(
ele => (ele.id = 'H' + ele.outerText.split('-').pop())
); //添加id方便toName
toName({ mapName });
}
createBtnGroup();
createModal();
(async () => {
try {
let before = await GM.getValue('mount', 0),
after = await GM.getValue('toMap', 0);
before && eval(before);
await sleep(2000);
unsafeWindow.location.href.includes('https://pcredivewiki.tw/Map/Detail') &&
after &&
appendName(after);
} catch (e) {
console.log(`错误: ` + e);
} finally {
await GM.deleteValue('mount');
await GM.deleteValue('toMap');
}
})();
});
})();