// ==UserScript== // @name 咕咕镇数据采集 // @namespace https://greasyfork.org/users/448113 // @version 1.4.19 // @description 咕咕镇数据采集,目前采集已关闭,兼作助手 // @author paraii // @match https://www.guguzhen.com/* // @grant GM_xmlhttpRequest // @connect www.guguzhen.com // @run-at document-body // @license MIT License // @downloadURL none // ==/UserScript== // @connect notes.orga.cat // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/js/tooltip.js // @require https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/js/popover.js (function() { 'use strict' //////////////////////////////////////////////////////////////////////////////////////////////////// // // common utilities // //////////////////////////////////////////////////////////////////////////////////////////////////// const g_modificationVersion = '2022-06-14 03:30:00'; const g_kfUser = document.getElementsByClassName('icon-user')[0].parentNode.innerText.split(' ')[1]; const g_autoTaskEnabledStorageKey = g_kfUser + '_autoTaskEnabled'; const g_keepPkRecordStorageKey = g_kfUser + '_keepPkRecord'; const g_amuletGroupsStorageKey = g_kfUser + '_amulet_groups'; const g_equipmentExpandStorageKey = g_kfUser + '_equipment_Expand'; const g_equipmentBGStorageKey = g_kfUser + '_equipment_BG'; const g_beachIgnoreStoreMysEquipStorageKey = g_kfUser + '_beach_ignoreStoreMysEquip'; const g_beachForceExpandStorageKey = g_kfUser + '_beach_forceExpand'; const g_beachBGStorageKey = g_kfUser + '_beach_BG'; const g_userDataStorageKeyConfig = [ g_kfUser, g_autoTaskEnabledStorageKey, g_keepPkRecordStorageKey, g_amuletGroupsStorageKey, g_equipmentExpandStorageKey, g_equipmentBGStorageKey, g_beachIgnoreStoreMysEquipStorageKey, g_beachForceExpandStorageKey, g_beachBGStorageKey ]; const g_userDataStorageKeyExtra = [ 'attribute', 'cardName', 'title', 'over', 'halo_max', 'beachcheck', 'dataReward', 'keepcheck' ]; const USER_STORAGE_RESERVED_SEPARATORS = /[:;,|=+*%!#$&?<>{}^`"\\\/\[\]\r\n\t\v\s]/; const USER_STORAGE_KEY_VALUE_SEPARATOR = ':'; console.log(g_kfUser) // perform a binary search. array must be sorted, but no matter in ascending or descending order. // in this manner, you must pass in a proper comparer function for it works properly, aka, if the // array was sorted in ascending order, then the comparer(a, b) should return a negative value // while a < b or a positive value while a > b; otherwise, if the array was sorted in descending // order, then the comparer(a, b) should return a positive value while a < b or a negative value // while a > b, and in both, if a equals b, the comparer(a, b) should return 0. if you pass nothing // or null / undefined value as comparer, then you must make sure about that the array was sorted // in ascending order. // // in this particular case, we just want to check whether the array contains the value or not, we // don't even need to point out the first place where the value appears (if the array actually // contains the value), so we perform a simplest binary search and return an index (may not the // first place where the value appears) or a negative value (means value not found) to indicate // the search result. function searchElement(array, value, fnComparer) { if (array?.length > 0) { fnComparer ??= ((a, b) => a < b ? -1 : (a > b ? 1 : 0)); let li = 0; let hi = array.length - 1; while (li <= hi) { let mi = ((li + hi) >> 1); let cr = fnComparer(value, array[mi]); if (cr == 0) { return mi; } else if (cr > 0) { li = mi + 1; } else { hi = mi - 1; } } } return -1; } // perform a binary insertion. the array and comparer must exactly satisfy as it in the searchElement // function. this operation behaves sort-stable, aka, the newer inserting element will be inserted // into the position after any existed equivalent elements. function insertElement(array, value, fnComparer) { if (array != null) { fnComparer ??= ((a, b) => a < b ? -1 : (a > b ? 1 : 0)); let li = 0; let hi = array.length - 1; while (li <= hi) { let mi = ((li + hi) >> 1); let cr = fnComparer(value, array[mi]); if (cr >= 0) { li = mi + 1; } else { hi = mi - 1; } } array.splice(li, 0, value); return li; } return -1; } // it's not necessary to have newArray been sorted, but the oldArray must be sorted since we are calling // searchElement. if there are some values should be ignored in newArray, the comparer(a, b) should be // implemented as return 0 whenever parameter a equals any of values that should be ignored. function findNewObjects(newArray, oldArray, fnComparer, findIndices) { // just in case, i discovered that sometimes if we use array.length directly in for(...) statement // (either some other statements too), the statement get chances to be executed incorrectly (or just // console.log can be effected?), don't know why, but we can use a temporary variable to handle this. let newObjects = []; let nl = (newArray?.length ?? 0); for (let i = 0; i < nl; i++) { if (searchElement(oldArray, newArray[i], fnComparer) < 0) { newObjects.push(findIndices ? i : newArray[i]); } } return newObjects; } function loadUserConfigData() { return JSON.parse(localStorage.getItem(g_kfUser)); } function saveUserConfigData(json) { localStorage.setItem(g_kfUser, JSON.stringify(json)); } function getPostData(functionPattern, dataPattern) { let data = null; let sc = document.getElementsByTagName('script'); for (let i = 0; i < sc.length; i++) { let func = sc[i].innerText.match(functionPattern); if (func != null) { data = func[0].match(dataPattern)[0]; break; } } return data; } // HTTP requests var g_httpRequests = []; function httpRequestRegister(request) { if (request != null) { g_httpRequests.push(request); } } function httpRequestAbortAll() { while (g_httpRequests.length > 0) { g_httpRequests.pop().abort(); } g_httpRequests = []; } function httpRequestClearAll() { g_httpRequests = []; } // read objects from bag and store with title filter const g_postMethod = 'POST' const g_readUrl = 'https://www.guguzhen.com/fyg_read.php' const g_postUrl = 'https://www.guguzhen.com/fyg_click.php' const g_postHeader = { 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8' , 'Cookie' : document.cookie }; const g_networkTimeoutMS = 120 * 1000; function beginReadObjects(bag, store, fnFurtherProcess, fnParams) { if (bag != null || store != null) { let request = GM_xmlhttpRequest({ method: g_postMethod, url: g_readUrl, headers: g_postHeader, data: 'f=7', onload: response => { let div = document.createElement('div'); div.innerHTML = response.responseText; if (bag != null) { bag.push(div.querySelectorAll('div.alert-danger > div.content > button.fyg_mp3')); } if (store != null) { store.push(div.querySelectorAll('div.alert-success > div.content > button.fyg_mp3')); } if (fnFurtherProcess != null) { fnFurtherProcess(fnParams); } } }); httpRequestRegister(request); } else if (fnFurtherProcess != null) { fnFurtherProcess(fnParams); } } function beginReadObjectIds(bagIds, storeIds, key, ignoreEmptyCell, fnFurtherProcess, fnParams) { function parseObjectIds() { if (bagIds != null) { objectIdParseNodes(bagIds.pop(), bagIds, key, ignoreEmptyCell); } if (storeIds != null) { objectIdParseNodes(storeIds.pop(), storeIds, key, ignoreEmptyCell); } if (fnFurtherProcess != null) { fnFurtherProcess(fnParams); } } if (bagIds != null || storeIds != null) { beginReadObjects(bagIds, storeIds, parseObjectIds, null); } else if (fnFurtherProcess != null) { fnFurtherProcess(fnParams); } } function objectIdParseNodes(nodes, ids, key, ignoreEmptyCell) { for (let node of nodes) { if (node.className?.endsWith('fyg_mp3')) { let id = node.getAttribute('onclick')?.match(/\d+/)[0]; if (id != undefined) { if (objectMatchTitle(node, key)) { ids.push(parseInt(id)); } } else if (!ignoreEmptyCell) { ids.push(-1); } } } } function objectMatchTitle(node, key){ return (!(key?.length > 0) || (node.getAttribute('data-original-title') ?? node.getAttribute('title'))?.indexOf(key) >= 0); } // we wait the response(s) of the previous batch of request(s) to send another batch of request(s) // rather than simply send them all within an inside foreach - which could cause too many requests // to server simultaneously, that can be easily treated as D.D.O.S attack and therefor leads server // to returns http status 503: Service Temporarily Unavailable // * caution * the parameter 'objects' is required been sorted by their indices in ascending order const g_ConcurrentRequestCount = { min : 1 , max : 8 , default : 4 }; const g_object_move_path = { bag2store : 0 , store2bag : 1 , bag2beach : 2 , beach2bag : 3 }; const g_object_move_data = [ null, null, null, null ]; var g_maxConcurrentRequests = g_ConcurrentRequestCount.default; var g_objectMoveRequestsCount = 0; var g_objectMoveTargetSiteFull = false; function beginMoveObjects(objects, path, fnFurtherProcess, fnParams) { if (!g_objectMoveTargetSiteFull && objects?.length > 0) { g_object_move_data[g_object_move_path.bag2store] ??= (getPostData(/puti\(id\)\{[\s\S]*\}/m, /data: ".*\+id\+.*"/)?.slice(7, -1) ?? ''); g_object_move_data[g_object_move_path.store2bag] ??= (getPostData(/puto\(id\)\{[\s\S]*\}/m, /data: ".*\+id\+.*"/)?.slice(7, -1) ?? ''); g_object_move_data[g_object_move_path.bag2beach] ??= (getPostData(/stdel\(id\)\{[\s\S]*\}/m, /data: ".*\+id\+.*"/)?.slice(7, -1) ?? ''); g_object_move_data[g_object_move_path.beach2bag] ??= (getPostData(/stpick\(id\)\{[\s\S]*\}/m, /data: ".*\+id\+.*"/)?.slice(7, -1) ?? ''); if (!(g_object_move_data[path]?.length > 0)) { if (!(g_object_move_data[g_object_move_path.store2bag]?.length > 0) && g_object_move_data[g_object_move_path.bag2store]?.length > 0) { g_object_move_data[g_object_move_path.store2bag] = g_object_move_data[g_object_move_path.bag2store].replace('c=21&', 'c=22&'); } if (!(g_object_move_data[g_object_move_path.bag2store]?.length > 0) && g_object_move_data[g_object_move_path.store2bag]?.length > 0) { g_object_move_data[g_object_move_path.bag2store] = g_object_move_data[g_object_move_path.store2bag].replace('c=22&', 'c=21&'); } if (!(g_object_move_data[g_object_move_path.bag2beach]?.length > 0) && g_object_move_data[g_object_move_path.beach2bag]?.length > 0) { g_object_move_data[g_object_move_path.bag2beach] = g_object_move_data[g_object_move_path.beach2bag].replace('c=1&', 'c=7&'); } if (!(g_object_move_data[g_object_move_path.beach2bag]?.length > 0) && g_object_move_data[g_object_move_path.bag2beach]?.length > 0) { g_object_move_data[g_object_move_path.beach2bag] = g_object_move_data[g_object_move_path.bag2beach].replace('c=7&', 'c=1&'); } } if (g_object_move_data[path].length > 0) { let ids = []; while (ids.length < g_maxConcurrentRequests && objects.length > 0) { let id = objects.pop(); if (id >= 0) { ids.push(id); } } if ((g_objectMoveRequestsCount = ids.length) > 0) { while (ids.length > 0) { let request = GM_xmlhttpRequest({ method: g_postMethod, url: g_postUrl, headers: g_postHeader, data: g_object_move_data[path].replace('"+id+"', ids.shift()), onload: response => { if (response.responseText != 'ok') { g_objectMoveTargetSiteFull = true; console.log(response.responseText); } if (--g_objectMoveRequestsCount == 0) { beginMoveObjects(objects, path, fnFurtherProcess, fnParams); } } }); httpRequestRegister(request); } return; } } } g_objectMoveTargetSiteFull = false; if (fnFurtherProcess != null) { fnFurtherProcess(fnParams); } } // read currently mounted role card and halo informations // roleInfo = [ roleId, roleName ] // haloInfo = [ haloPoints, haloSlots, [ haloItem1, haloItem2, … ] ] function beginReadRoleAndHalo(roleInfo, haloInfo, fnFurtherProcess, fnParams) { let asyncOperations = 0; let error = 0; let requestRole; let requestHalo; if (roleInfo != null) { asyncOperations++; requestRole = GM_xmlhttpRequest({ method: g_postMethod, url: g_readUrl, headers: g_postHeader, data: 'f=9', onload: response => { let div = document.createElement('div'); div.innerHTML = response.responseText; let role = g_roleMap.get(div.querySelector('div.text-info.fyg_f24.fyg_lh60')?.children[0]?.innerText); if (role != undefined) { roleInfo.push(role.id); roleInfo.push(role.name); } asyncOperations--; }, onerror : err => { error++; asyncOperations--; }, ontimeout : err => { error++; asyncOperations--; } }); } if (haloInfo != null) { asyncOperations++; requestHalo = GM_xmlhttpRequest({ method: g_postMethod, url: g_readUrl, headers: g_postHeader, data: 'f=5', onload: response => { let haloPS = response.responseText.match(/
]*>([^<]+)<[^>]*>\+(\d+)[^<]*<\/span><\/p>/g; while ((attr = regex.exec(content))?.length == 3) { this.buffCode += ((100 ** (g_amuletBuffTypeOrders.get(attr[1]) % 6)) * attr[2]); this.text += `${this.text.length > 0 ? ', ' : ''}${attr[1]} +${attr[2]} ${g_amuletBuffUnits[this.type]}`; } if (this.isValid()) { return this; } } } this.reset(); return null; }); this.fromAmulet = ((amulet) => { if (amulet?.isValid()) { this.id = amulet.id; this.type = amulet.type; this.level = amulet.level; this.enhancement = amulet.enhancement; this.buffCode = amulet.buffCode; this.text = amulet.text; } else { this.reset(); } return (this.isValid() ? this : null); }); this.getCode = (() => { if (this.isValid()) { return (this.type * AMULET_TYPE_ID_FACTOR + this.level * AMULET_LEVEL_ID_FACTOR + this.enhancement * AMULET_ENHANCEMENT_FACTOR + this.buffCode); } return -1; }); this.getBuff = (() => { let buffs = {}; if (this.isValid()) { let code = this.buffCode; let type = this.type * 6; g_amuletBuffTypeNames.slice(type, type + 6).forEach((buff) => { let v = (code % 100); if (v > 0) { buffs[buff] = v; } code = Math.trunc(code / 100); }); } return buffs; }); this.getTotalPoints = (() => { let points = 0; if (this.isValid()) { let code = this.buffCode; for(let i = 0; i < 6; i++) { points += (code % 100); code = Math.trunc(code / 100); } } return points; }); this.formatName = (() => { if (this.isValid()) { return `${g_amuletLevelNames[this.level]}${g_amuletTypeNames[this.type]} (+${this.enhancement})`; } return null; }); this.formatBuff = (() => { if (this.isValid()) { if (this.text?.length > 0) { return this.text; } this.text = ''; let buffs = this.getBuff(); for (let buff in buffs) { this.text += `${this.text.length > 0 ? ', ' : ''}${buff} +${buffs[buff]} ${g_amuletBuffUnits[this.type]}`; } } return this.text; }); this.formatBuffText = (() => { if (this.isValid()) { return this.formatName() + ' = ' + this.formatBuff(); } return null; }); this.formatShortMark = (() => { let text = this.formatBuff()?.replaceAll(/(\+)|( 点)|( %)/g, ''); if (text?.length > 0) { for (let buff in this.getBuff()) { text = text.replaceAll(buff, g_amuletBuffShortMarks[g_amuletBuffTypeOrders.get(buff)]); } return this.formatName() + ' = ' + text; } return null; }); this.compareTo = ((other, ascType) => { if (!this.isValid()) { return 1; } else if (!other?.isValid()) { return -1; } else if (this.id >= 0 && this.id == other.id) { return 0; } let delta = other.type - this.type; if (delta != 0) { return (ascType ? -delta : delta); } let tbuffs = this.formatBuffText().split(' = ')[1].replaceAll(/(\+)|( 点)|( %)/g, '').split(', '); let obuffs = other.formatBuffText().split(' = ')[1].replaceAll(/(\+)|( 点)|( %)/g, '').split(', '); let bl = Math.min(tbuffs.length, obuffs.length); for (let i = 0; i < bl; i++) { let tbuff = tbuffs[i].split(' '); let obuff = obuffs[i].split(' '); if ((delta = g_amuletBuffTypeOrders.get(tbuff[0]) - g_amuletBuffTypeOrders.get(obuff[0])) != 0 || (delta = parseInt(obuff[1]) - parseInt(tbuff[1])) != 0) { return delta; } } if ((delta = obuffs.length - tbuffs.length) != 0 || (delta = other.level - this.level) != 0 || (delta = other.enhancement - this.enhancement) != 0) { return delta; } return 0; }); } function AmuletGroup(persistenceString) { this.buffSummary = { 力量 : 0, 敏捷 : 0, 智力 : 0, 体魄 : 0, 精神 : 0, 意志 : 0, 物理攻击 : 0, 魔法攻击 : 0, 速度 : 0, 生命护盾回复效果 : 0, 最大生命值 : 0, 最大护盾值 : 0, 固定生命偷取 : 0, 固定反伤 : 0, 固定暴击几率 : 0, 固定技能几率 : 0, 物理防御效果 : 0, 魔法防御效果 : 0 }; this.name = null; this.items = []; this.isValid = (() => { return (this.items.length > 0 && amuletIsValidGroupName(this.name)); }); this.count = (() => { return this.items.length; }); this.clear = (() => { this.items = []; for (let buff in this.buffSummary) { this.buffSummary[buff] = 0; } }); this.add = ((amulet) => { if (amulet?.isValid()) { let buffs = amulet.getBuff(); for (let buff in buffs) { this.buffSummary[buff] += buffs[buff]; } return insertElement(this.items, amulet, (a, b) => a.compareTo(b, true)); } return -1; }); this.remove = ((amulet) => { if (this.isValid() && amulet?.isValid()) { let i = searchElement(this.items, amulet, (a, b) => a.compareTo(b, true)); if (i >= 0) { let buffs = amulet.getBuff(); for (let buff in buffs) { this.buffSummary[buff] -= buffs[buff]; } this.items.splice(i, 1); return true; } } return false; }); this.removeId = ((id) => { if (this.isValid()) { let i = this.items.findIndex((a) => a.id == id); if (i >= 0) { let amulet = this.items[i]; let buffs = amulet.getBuff(); for (let buff in buffs) { this.buffSummary[buff] -= buffs[buff]; } this.items.splice(i, 1); return amulet; } } return null; }); this.validate = ((amulets) => { if (this.isValid()) { let mismatch = 0; let al = this.items.length; let i = 0; if (amulets?.length > 0) { amulets = amulets.slice().sort((a, b) => a.type != b.type ? a.type - b.type : a.buffCode - b.buffCode); for ( ; amulets.length > 0 && i < al; i++) { let mi = searchElement(amulets, this.items[i], (a, b) => a.type != b.type ? a.type - b.type : a.buffCode - b.buffCode); if (mi >= 0) { // remove a matched amulet from the amulet pool can avoid one single amulet matches all // the equivalent objects in the group. // let's say two (or even more) AGI +5 apples in one group is fairly normal, if we just // have only one equivalent apple in the amulet pool and we don't remove it when the // first match happens, then the 2nd apple will get matched later, the consequence would // be we can never find the mismatch which should be encountered at the 2nd apple this.items[i].fromAmulet(amulets[mi]); amulets.splice(mi, 1); } else { mismatch++; } } } if (i > mismatch) { this.items.sort((a, b) => a.compareTo(b, true)); } if (i < al) { mismatch += (al - i); } return (mismatch == 0); } return false; }); this.findIndices = ((amulets) => { let indices; let al; if (this.isValid() && (al = (amulets?.length ?? 0)) > 0) { let items = this.items.slice().sort((a, b) => a.type != b.type ? a.type - b.type : a.buffCode - b.buffCode); for (let i = 0; items.length > 0 && i < al; i++) { let mi; if (amulets[i]?.id >= 0 && (mi = searchElement(items, amulets[i], (a, b) => a.type != b.type ? a.type - b.type : a.buffCode - b.buffCode)) >= 0) { // similar to the 'validate', remove the amulet from the search list when we found // a match item in first time to avoid the duplicate founding, e.g. say we need only // one AGI +5 apple in current group and we actually have 10 of AGI +5 apples in store, // if we found the first matched itme in store and record it's index but not remove it // from the temporary searching list, then we will continuously reach this kind of // founding and recording until all those 10 AGI +5 apples are matched and processed, // this obviously ain't the result what we expected (indices ??= []).push(i); items.splice(mi, 1); } } } return indices; }); this.parse = ((persistenceString) => { this.clear(); if (persistenceString?.length > 0) { let elements = persistenceString.split(AMULET_STORAGE_GROUPNAME_SEPARATOR); if (elements.length == 2) { let name = elements[0].trim(); if (amuletIsValidGroupName(name)) { let items = elements[1].split(AMULET_STORAGE_AMULET_SEPARATOR); let il = items.length; for (let i = 0; i < il; i++) { if (this.add((new Amulet()).fromCode(parseInt(items[i]))) < 0) { this.clear(); break; } } if (this.count() > 0) { this.name = name; } } } } return (this.count() > 0); }); this.formatBuffSummary = ((linePrefix, lineSuffix, lineSeparator) => { if (this.isValid()) { let str = ''; let nl = ''; g_amuletBuffTypeNames.forEach((buff) => { let v = this.buffSummary[buff]; if (v > 0) { str += `${nl}${linePrefix}${buff} +${v} ${g_amuletBuffUnits[Math.trunc(g_amuletBuffTypeOrders.get(buff) / 6)]}${lineSuffix}`; nl = lineSeparator; } }); return str; } return ''; }); this.formatItems = ((linePrefix, erroeLinePrefix, lineSuffix, errorLineSuffix, lineSeparator) => { if (this.isValid()) { let str = ''; let nl = ''; this.items.forEach((amulet) => { str += `${nl}${amulet.id < 0 ? erroeLinePrefix : linePrefix}${amulet.formatBuffText()}` + `${amulet.id < 0 ? errorLineSuffix : lineSuffix}`; nl = lineSeparator; }); return str; } return ''; }); this.getDisplayStringLineCount = (() => { if (this.isValid()) { let lines = 0; g_amuletBuffTypeNames.forEach((buff) => { if (this.buffSummary[buff] > 0) { lines++; } }); return lines + this.items.length; } return 0; }); this.formatPersistenceString = (() => { if (this.isValid()) { let codes = []; this.items.forEach((amulet) => { codes.push(amulet.getCode()); }); return `${this.name}${AMULET_STORAGE_GROUPNAME_SEPARATOR}${codes.join(AMULET_STORAGE_AMULET_SEPARATOR)}`; } return ''; }); this.parse(persistenceString); } function AmuletGroupCollection(persistenceString) { this.items = {}; this.itemCount = 0; this.count = (() => { return this.itemCount; }); this.contains = ((name) => { return (this.items[name] != undefined); }); this.add = ((item) => { if (item?.isValid()) { if (!this.contains(item.name)) { this.itemCount++; } this.items[item.name] = item; return true; } return false; }); this.remove = ((name) => { if (this.contains(name)) { delete this.items[name]; this.itemCount--; return true; } return false; }); this.clear = (() => { for (let name in this.items) { delete this.items[name]; } this.itemCount = 0; }); this.get = ((name) => { return this.items[name]; }); this.rename = ((oldName, newName) => { if (amuletIsValidGroupName(newName)) { let group = this.items[oldName]; if (this.remove(oldName)) { group.name = newName; return this.add(group); } } return false; }); this.toArray = (() => { let groups = []; for (let name in this.items) { groups.push(this.items[name]); } return groups; }); this.parse = ((persistenceString) => { this.clear(); if (persistenceString?.length > 0) { let groupStrings = persistenceString.split(AMULET_STORAGE_GROUP_SEPARATOR); let gl = groupStrings.length; for (let i = 0; i < gl; i++) { if (!this.add(new AmuletGroup(groupStrings[i]))) { this.clear(); break; } } } return (this.count() > 0); }); this.formatPersistenceString = (() => { let str = ''; let ns = ''; for (let name in this.items) { str += (ns + this.items[name].formatPersistenceString()); ns = AMULET_STORAGE_GROUP_SEPARATOR; } return str; }); this.parse(persistenceString); } function amuletIsValidGroupName(groupName) { return (groupName?.length > 0 && groupName.length < 32 && groupName.search(USER_STORAGE_RESERVED_SEPARATORS) < 0); } function amuletSaveGroups(groups) { if (groups?.count() > 0) { localStorage.setItem(g_amuletGroupsStorageKey, groups.formatPersistenceString()); } else { localStorage.removeItem(g_amuletGroupsStorageKey); } } function amuletLoadGroups() { return new AmuletGroupCollection(localStorage.getItem(g_amuletGroupsStorageKey)); } function amuletClearGroups() { localStorage.removeItem(g_amuletGroupsStorageKey); } function amuletSaveGroup(group) { if (group?.isValid()) { let groups = amuletLoadGroups(); if (groups.add(group)) { amuletSaveGroups(groups); } } } function amuletLoadGroup(groupName) { return amuletLoadGroups().get(groupName); } function amuletDeleteGroup(groupName) { let groups = amuletLoadGroups(); if (groups.remove(groupName)) { amuletSaveGroups(groups); } } function amuletCreateGroupFromArray(groupName, amulets) { if (amulets?.length > 0 && amuletIsValidGroupName(groupName)) { let group = new AmuletGroup(null); for (let amulet of amulets) { if (group.add(amulet) < 0) { group.clear(); break; } } if (group.count() > 0) { group.name = groupName; return group; } } return null; } function amuletNodesToArray(nodes, array, key) { if (array != null) { let amulet; for (let node of nodes) { if (objectMatchTitle(node, key) && (amulet ??= new Amulet()).fromNode(node)?.isValid()) { array.push(amulet); amulet = null; } } } return array; } function beginReadAmulets(bagAmulets, storeAmulets, key, fnFurtherProcess, fnParams) { function parseAmulets() { if (bagAmulets != null) { amuletNodesToArray(bagAmulets.pop(), bagAmulets, key); } if (storeAmulets != null) { amuletNodesToArray(storeAmulets.pop(), storeAmulets, key); } if (fnFurtherProcess != null) { fnFurtherProcess(fnParams); } } if (bagAmulets != null || storeAmulets != null) { beginReadObjects(bagAmulets, storeAmulets, parseAmulets, null); } else if (fnFurtherProcess != null) { fnFurtherProcess(fnParams); } } function beginMoveAmulets({ groupName, amulets, path, proc, params }) { let indices = amuletLoadGroup(groupName)?.findIndices(amulets)?.sort((a, b) => b - a); let ids; while (indices?.length > 0) { (ids ??= []).push(amulets[indices.pop()].id); } beginMoveObjects(ids, path, proc, params); } function beginLoadAmuletGroupFromStore(amulets, groupName, fnFurtherProcess, fnParams) { if (amulets?.length > 0) { let store = amuletNodesToArray(amulets, [], null); beginMoveAmulets({ groupName : groupName, amulets : store, path : g_object_move_path.store2bag, proc : fnFurtherProcess, params : fnParams }); } else { beginReadAmulets(null, amulets = [], null, beginMoveAmulets, { groupName : groupName, amulets : amulets, path : g_object_move_path.store2bag, proc : fnFurtherProcess, params : fnParams }); } } function beginUnloadAmuletGroupFromBag(amulets, groupName, fnFurtherProcess, fnParams) { if (amulets?.length > 0) { let bag = amuletNodesToArray(amulets, [], null); beginMoveAmulets({ groupName : groupName, amulets : bag, path : g_object_move_path.bag2store, proc : fnFurtherProcess, params : fnParams }); } else { beginReadAmulets(amulets, null, null, beginMoveAmulets, { groupName : groupName, amulets : amulets, path : g_object_move_path.bag2store, proc : fnFurtherProcess, params : fnParams }); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // // equipment utilities // //////////////////////////////////////////////////////////////////////////////////////////////////// // of cause we should calculate the property points to classify an equipment's quality level, // but for now let's get it work first ... const g_equipmentTipClassLevel = [ 'popover-primary', 'popover-info', 'popover-success', 'popover-warning', 'popover-danger' ]; function equipmentGetNodeLevel(node) { return g_equipmentTipClassLevel.indexOf(node?.getAttribute('data-tip-class')); } function equipmentInfoParseNode(node) { if (node?.className?.split(' ').length == 3 && node.innerText.indexOf('+') < 0) { let id = node.getAttribute('onclick')?.match(/\d+/)[0]; let attr = node.getAttribute('data-content')?.match(/>\s*\d+\s?%\s*(\d+)); let name = title?.substring(title.lastIndexOf('>') + 1).trim(); name = (g_equipMap.get(name)?.shortMark ?? g_equipMap.get(name.substring(2))?.shortMark); let mys = (node.getAttribute('data-content')?.match(/\[神秘属性\]/) == null ? 0 : 1); if (attr?.length > 0 && title?.length > 1 && lv?.length > 0 && name?.length > 0) { return [ name, lv[1], attr[0].match(/\d+/)[0], attr[1].match(/\d+/)[0], attr[2].match(/\d+/)[0], attr[3].match(/\d+/)[0], mys, id ]; } } return null; } function equipmentNodesToInfoArray(nodes, array) { if (array != null) { let nl = (nodes?.length ?? 0); for (let i = 0; i < nl; i++) { let e = equipmentInfoParseNode(nodes[i]); if (e != null) { array.push(e); } } } return array; } function equipmentInfoComparer(e1, e2) { let delta = g_equipMap.get(e1[0]).index - g_equipMap.get(e2[0]).index; for (let i = 1; delta == 0 && i < 7; i++) { delta = parseInt(e1[i]) - parseInt(e2[i]); } return delta; } function objectNodeComparer(e1, e2) { let eq1 = equipmentInfoParseNode(e1); if (eq1 != null) { e1.setAttribute('data-meta-index', g_equipMap.get(eq1[0]).index); } let eq2 = equipmentInfoParseNode(e2); if (eq2 != null) { e2.setAttribute('data-meta-index', g_equipMap.get(eq2[0]).index); } if (eq1 == null && eq2 == null) { return ((new Amulet()).fromNode(e1)?.compareTo((new Amulet()).fromNode(e2)) ?? 1); } else if (eq1 == null) { return 1; } else if (eq2 == null) { return -1; } return equipmentInfoComparer(eq1, eq2); } //////////////////////////////////////////////////////////////////////////////////////////////////// // // bag & store utilities // //////////////////////////////////////////////////////////////////////////////////////////////////// function beginClearBag(bag, key, fnFurtherProcess, fnParams) { function beginClearBagObjects(objects) { beginMoveObjects(objects, g_object_move_path.bag2store, fnFurtherProcess, fnParams); } let objects = []; if (bag?.length > 0) { objectIdParseNodes(bag, objects, key, true); beginClearBagObjects(objects); } else { beginReadObjectIds(objects, null, key, true, beginClearBagObjects, objects); } } function beginRestoreObjects(store, amulets, equips, fnFurtherProcess, fnParams) { function readStoreCompletion() { beginRestoreObjects(store.pop(), amulets, equips, fnFurtherProcess, fnParams); } if (store == null) { beginReadObjects(null, store = [], readStoreCompletion, null); } else { let ids = []; if (amulets?.length > 0) { let ams = amuletNodesToArray(store, [], null); for (let i = ams.length - 1; i >= 0; i--) { for (let j = amulets.length - 1; j >= 0; j--) { if (ams[i].compareTo(amulets[j]) == 0) { amulets.splice(j, 1); ids.unshift(ams[i].id); break; } } } } if (equips?.length > 0) { let eqs = equipmentNodesToInfoArray(store, []); for (let i = eqs.length - 1; i >= 0; i--) { for (let j = equips.length - 1; j >= 0; j--) { if (equipmentInfoComparer(eqs[i], equips[j]) == 0) { equips.splice(j, 1); ids.unshift(parseInt(eqs[i][7])); break; } } } } beginMoveObjects(ids, g_object_move_path.store2bag, fnFurtherProcess, fnParams); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // // generic popups // //////////////////////////////////////////////////////////////////////////////////////////////////// const g_genericPopupContainerId = 'generic-popup-container'; const g_genericPopupClass = 'generic-popup'; const g_genericPopupId = g_genericPopupClass; const g_genericPopupContentContainerId = 'generic-popup-content-container'; const g_genericPopupContentClass = 'generic-popup-content'; const g_genericPopupContentId = g_genericPopupContentClass; const g_genericPopupFixedContentId = 'generic-popup-content-fixed'; const g_genericPopupInformationTipsId = 'generic-popup-information-tips'; const g_genericPopupProgressClass = g_genericPopupClass; const g_genericPopupProgressId = 'generic-popup-progress'; const g_genericPopupProgressContentClass = 'generic-popup-content-progress'; const g_genericPopupProgressContentId = g_genericPopupProgressContentClass; const g_genericPopupTopLineDivClass = 'generic-popup-top-line-container'; const g_genericPopupTitleTextClass = 'generic-popup-title-text'; const g_genericPopupTitleTextId = g_genericPopupTitleTextClass; const g_genericPopupTitleButtonContainerId = 'generic-popup-title-button-container'; const g_genericPopupFootButtonContainerId = 'generic-popup-foot-button-container'; const g_genericPopupBackgroundColor = '#ebf2f9'; const g_genericPopupBackgroundColorAlt = '#dbe2e9'; const g_genericPopupBorderColor = '#3280fc'; const g_genericPopupTitleTextColor = '#ffffff'; const g_genericPopupStyle = ``; const g_genericPopupHTML = `${g_genericPopupStyle}
配置项 | 值 |
---|
装备名称 | 装备属性 |
---|
获得了
')) { let gain; let sp = '获得'; let regex = /x\s*(\d+)\s*([^<]+)<\/span>/g; while ((gain = regex.exec(response))?.length == 3) { gainText += `${sp}${gain[2].trim()}:${gain[1]}`; sp = ', '; } let lvlUp = response.match(/角色 \[ [^\s]+ \] 卡片等级提升!/g); if (lvlUp?.length > 0) { lvlUp.forEach((e) => { gainText += `${sp}${e}`; sp = ', '; }); } } return gainText; } function func0(time) { if (time == 0) { if (times[0] != 0) { GM_xmlhttpRequest({ method: g_postMethod, url: g_readUrl, headers: g_postHeader, data: 'f=12', onload: response => { let ap = response.responseText.match(/class="fyg_colpz03" style="font-size:32px;font-weight:900;">\d+)[0].match(/>\d+)[0].slice(1, -1); document.getElementsByClassName('fyg_colpz03')[0].innerText = ap; let rankp = response.responseText.match(/class="fyg_colpz02" style="font-size:32px;font-weight:900;">\d+%)[0].match(/\d+%/)[0]; document.getElementsByClassName('fyg_colpz02')[0].innerText = rankp; let div_sum = document.createElement('div'); div_sum.innerText = `贝壳总次数:经验总次数=${sum0}:${sum1}=${(sum0 == 0 || sum1 == 0) ? 'undefined' : (sum0 / sum1).toFixed(4)}`; dataReward.sumShell = sum0; dataReward.sumExp = sum1; localStorage.setItem('dataReward', JSON.stringify(dataReward)); document.getElementsByClassName('btn-outline-secondary')[0].parentNode.appendChild(div_sum); times[0] = 0; } }); } return; } GM_xmlhttpRequest({ method: g_postMethod, url: g_postUrl, headers: g_postHeader, data: gox_data, onload: response => { let gainText = parseGainResponse(response.responseText); if (gainText.length > 0) { let div_info = document.createElement('div'); div_info.innerText = gainText; document.getElementsByClassName('btn-outline-secondary')[0].parentNode.appendChild(div_info); if (gainText.indexOf('贝壳') != -1) { sum0 += 1; } if (gainText.indexOf('经验') != -1) { sum1 += 1; } func0(time - 1); } else { let div_info = document.createElement('div'); div_info.innerText = '段位进度不足或无法识别的应答信息'; document.getElementsByClassName('btn-outline-secondary')[0].parentNode.appendChild(div_info); func0(0); } } }); } function func1(time) { if (time == 0) { times[1] = 0; return; } let observerPk = new MutationObserver((mutationsList, observer) => { let isPk = 0; for (let mutation of mutationsList) { if (mutation.type == 'childList') { isPk = 1; } } if (isPk) { observerPk.disconnect(); func1(time - 1); } }); observerPk.observe(document.querySelector('#pk_text'), { characterData : true , childList : true }); jgjg(1); } function func2(time) { if (time == 0) { times[2] = 0; return; } let observerPk = new MutationObserver((mutationsList, observer) => { let isPk = 0; for (let mutation of mutationsList) { if (mutation.type == 'childList') { isPk = 1; } } if (isPk) { observerPk.disconnect(); func2(time - 1); } }); observerPk.observe(document.querySelector('#pk_text'), { characterData : true , childList : true }); jgjg(2); } func0(times[0]); let waitFor0 = setInterval(() => { if (times[0] == 0) { clearInterval(waitFor0); func1(times[1]); } }, 1000); let waitFor1 = setInterval(() => { if (times[0] == 0 && times[1] == 0) { clearInterval(waitFor1); func2(times[2]); } }, 1000); } else { alert('体力不足'); } } document.getElementsByClassName('btn-outline-secondary')[0].addEventListener('click', gobattle, false); } function selector_act() { var btnNum = $('.btn-group .btn-secondary').index(this); $('.btn-group .btn-secondary') .eq(btnNum) .css('background-color', 'rgb(135, 206, 250)') .siblings('.btn-group .btn-secondary') .css('background-color', 'rgb(255, 255, 255)'); } let btnselector = document.getElementsByClassName('btn-secondary'); for (let i = 0; i < btnselector.length; i++) { btnselector[i].addEventListener('click', selector_act, false); } } }, 1000); } }); taskObserver.observe(document.getElementsByClassName('panel panel-primary')[0], { childList : true, subtree : true, }); } } else if (window.location.pathname == '/fyg_wish.php') { let timer = setInterval(() => { let wishPoints = parseInt(document.getElementById('xys_dsn')?.innerText); if (!isNaN(wishPoints)) { clearInterval(timer); function getWishPoints() { let text = 'WISH'; for (let i = 7; i <= 13; i++) { text += (' ' + (document.getElementById('xyx_' + ('0' + i).slice(-2))?.innerText ?? '0')); } return text; } let div = document.createElement('div'); div.className = 'row'; div.innerHTML = '