The West - Custom World Order (Lightning Fast + UI Input)

Arrange worlds in custom order within "Lumile tale" section + add editable input in corner

  1. // ==UserScript==
  2. // @name The West - Custom World Order (Lightning Fast + UI Input)
  3. // @namespace https://www.the-west.ro/
  4. // @author honeydew (Texas)
  5. // @version 3.1
  6. // @description Arrange worlds in custom order within "Lumile tale" section + add editable input in corner
  7. // @match https://www.the-west.ro/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. 'use strict';
  13.  
  14. // 🎯 Load from localStorage or default to empty
  15. let PREFERRED_ORDER = JSON.parse(localStorage.getItem("preferredWorldOrder") || "[]");
  16.  
  17. const ALL_WORLDS_ID = "allWorlds";
  18. const LUMILE_TALE_TEXT = "Lumile tale";
  19.  
  20. let isProcessing = false;
  21.  
  22. function getWorldNumber(worldElement) {
  23. const match = worldElement.id?.match(/world_(\d+)/);
  24. return match ? parseInt(match[1], 10) : null;
  25. }
  26.  
  27. function arrangeWorldsInOrder() {
  28. if (isProcessing) return false;
  29. isProcessing = true;
  30.  
  31. try {
  32. const allWorlds = document.getElementById(ALL_WORLDS_ID);
  33. if (!allWorlds || allWorlds.offsetParent === null) return false;
  34.  
  35. const lumileTaleRow = Array.from(allWorlds.querySelectorAll(".worlds-row"))
  36. .find(row => row.querySelector("h2")?.textContent?.includes(LUMILE_TALE_TEXT));
  37.  
  38. if (!lumileTaleRow) return false;
  39.  
  40. const content = lumileTaleRow.querySelector(".row-content");
  41. if (!content) return false;
  42.  
  43. const worldElements = Array.from(content.querySelectorAll('[id^="world_"]'));
  44. if (worldElements.length === 0) return false;
  45.  
  46. const orderedWorlds = [];
  47. const remainingWorlds = [];
  48. const worldMap = new Map();
  49.  
  50. worldElements.forEach(element => {
  51. const worldNum = getWorldNumber(element);
  52. if (worldNum !== null) {
  53. worldMap.set(worldNum, element);
  54. }
  55. });
  56.  
  57. PREFERRED_ORDER.forEach(worldNum => {
  58. if (worldMap.has(worldNum)) {
  59. orderedWorlds.push(worldMap.get(worldNum));
  60. worldMap.delete(worldNum);
  61. }
  62. });
  63.  
  64. worldElements.forEach(element => {
  65. const worldNum = getWorldNumber(element);
  66. if (worldNum !== null && worldMap.has(worldNum)) {
  67. remainingWorlds.push(element);
  68. }
  69. });
  70.  
  71. const newOrder = [...orderedWorlds, ...remainingWorlds];
  72. const currentOrder = Array.from(content.children).filter(child =>
  73. child.id && child.id.startsWith('world_')
  74. );
  75.  
  76. let needsReordering = false;
  77. if (newOrder.length === currentOrder.length) {
  78. for (let i = 0; i < newOrder.length; i++) {
  79. if (newOrder[i] !== currentOrder[i]) {
  80. needsReordering = true;
  81. break;
  82. }
  83. }
  84. } else {
  85. needsReordering = true;
  86. }
  87.  
  88. if (needsReordering) {
  89. const referenceElement = Array.from(content.children)
  90. .find(child => !child.id || !child.id.startsWith('world_'));
  91.  
  92. newOrder.forEach((worldElement, index) => {
  93. if (index === 0) {
  94. content.insertBefore(worldElement, referenceElement || content.firstChild);
  95. } else {
  96. const previousWorld = newOrder[index - 1];
  97. content.insertBefore(worldElement, previousWorld.nextSibling);
  98. }
  99. });
  100.  
  101. const orderedNumbers = orderedWorlds.map(el => getWorldNumber(el)).filter(n => n !== null);
  102. const remainingNumbers = remainingWorlds.map(el => getWorldNumber(el)).filter(n => n !== null);
  103.  
  104. console.log(`[⚡] Worlds reordered! Priority: [${orderedNumbers.join(', ')}], Others: [${remainingNumbers.join(', ')}]`);
  105. return true;
  106. }
  107.  
  108. return false;
  109. } finally {
  110. isProcessing = false;
  111. }
  112. }
  113.  
  114. function instantArrangeWorlds() {
  115. requestAnimationFrame(() => {
  116. arrangeWorldsInOrder();
  117. });
  118. }
  119.  
  120. const observer = new MutationObserver((mutations) => {
  121. let shouldCheck = false;
  122.  
  123. for (const mutation of mutations) {
  124. if (mutation.type === 'childList') {
  125. for (const node of mutation.addedNodes) {
  126. if (node.nodeType === 1) {
  127. if (node.id === ALL_WORLDS_ID ||
  128. node.querySelector?.(`#${ALL_WORLDS_ID}`) ||
  129. node.id?.startsWith('world_') ||
  130. node.querySelector?.('[id^="world_"]') ||
  131. node.classList?.contains('worlds-row') ||
  132. node.classList?.contains('row-content')) {
  133. shouldCheck = true;
  134. break;
  135. }
  136. }
  137. }
  138. } else if (mutation.type === 'attributes') {
  139. const target = mutation.target;
  140. if (target.id === ALL_WORLDS_ID ||
  141. target.querySelector?.(`#${ALL_WORLDS_ID}`) ||
  142. (mutation.attributeName === 'style' && target.style?.display !== 'none')) {
  143. shouldCheck = true;
  144. }
  145. }
  146.  
  147. if (shouldCheck) break;
  148. }
  149.  
  150. if (shouldCheck) {
  151. instantArrangeWorlds();
  152. }
  153. });
  154.  
  155. observer.observe(document.body, {
  156. childList: true,
  157. subtree: true,
  158. attributes: true,
  159. attributeFilter: ['style', 'class']
  160. });
  161.  
  162. const events = ['click', 'keydown', 'focus', 'mousedown'];
  163. events.forEach(eventType => {
  164. document.addEventListener(eventType, (e) => {
  165. const target = e.target;
  166. if (target.closest?.('.menulink') ||
  167. target.closest?.('[data-menu]') ||
  168. target.closest?.('.ui-dialog') ||
  169. target.id?.includes('world') ||
  170. target.className?.includes('world')) {
  171. setTimeout(instantArrangeWorlds, 50);
  172. setTimeout(instantArrangeWorlds, 150);
  173. }
  174. }, true);
  175. });
  176.  
  177. let checkCount = 0;
  178. const periodicCheck = () => {
  179. checkCount++;
  180. const allWorlds = document.getElementById(ALL_WORLDS_ID);
  181. if (allWorlds && allWorlds.offsetParent !== null) {
  182. const content = allWorlds.querySelector('.worlds-row h2')?.textContent?.includes(LUMILE_TALE_TEXT)
  183. ? allWorlds.querySelector('.worlds-row .row-content')
  184. : null;
  185.  
  186. if (content) {
  187. const firstWorldElement = content.querySelector('[id^="world_"]');
  188. if (firstWorldElement) {
  189. const firstWorldNum = getWorldNumber(firstWorldElement);
  190. if (PREFERRED_ORDER.length > 0 &&
  191. (!PREFERRED_ORDER.includes(firstWorldNum) || firstWorldNum !== PREFERRED_ORDER[0])) {
  192. const hasPriorityWorlds = PREFERRED_ORDER.some(num =>
  193. content.querySelector(`#world_${num}`)
  194. );
  195. if (hasPriorityWorlds) {
  196. instantArrangeWorlds();
  197. }
  198. }
  199. }
  200. }
  201. }
  202.  
  203. const delay = Math.min(100 + (checkCount * 10), 500);
  204. setTimeout(periodicCheck, delay);
  205. };
  206.  
  207. function createCornerInputUI() {
  208. const wrapper = document.createElement('div');
  209. wrapper.style.position = 'fixed';
  210. wrapper.style.top = '10px';
  211. wrapper.style.right = '10px';
  212. wrapper.style.background = 'rgba(0,0,0,0.75)';
  213. wrapper.style.padding = '8px';
  214. wrapper.style.borderRadius = '6px';
  215. wrapper.style.zIndex = '9999';
  216. wrapper.style.fontSize = '12px';
  217. wrapper.style.color = 'white';
  218. wrapper.style.fontFamily = 'monospace';
  219.  
  220. const label = document.createElement('label');
  221. label.textContent = 'World order:';
  222. label.style.marginRight = '4px';
  223.  
  224. const input = document.createElement('input');
  225. input.type = 'text';
  226. input.value = PREFERRED_ORDER.join(', ');
  227. input.style.width = '160px';
  228. input.style.marginRight = '4px';
  229. input.style.borderRadius = '4px';
  230. input.style.border = 'none';
  231. input.style.padding = '2px 4px';
  232.  
  233. const button = document.createElement('button');
  234. button.textContent = 'Save';
  235. button.style.cursor = 'pointer';
  236. button.style.border = 'none';
  237. button.style.padding = '2px 6px';
  238. button.style.borderRadius = '4px';
  239. button.style.background = '#4CAF50';
  240. button.style.color = 'white';
  241.  
  242. button.onclick = () => {
  243. const newValue = input.value.trim();
  244. updatePreferredOrder(newValue);
  245. localStorage.setItem("preferredWorldOrder", JSON.stringify(PREFERRED_ORDER));
  246. instantArrangeWorlds();
  247. };
  248.  
  249. wrapper.appendChild(label);
  250. wrapper.appendChild(input);
  251. wrapper.appendChild(button);
  252. document.body.appendChild(wrapper);
  253. }
  254.  
  255. function updatePreferredOrder(input) {
  256. const list = input.split(',')
  257. .map(str => parseInt(str.trim(), 10))
  258. .filter(num => !isNaN(num));
  259.  
  260. if (list.length > 0) {
  261. PREFERRED_ORDER = list;
  262. console.log(`[⚙️] Updated preferred world order: [${PREFERRED_ORDER.join(', ')}]`);
  263. } else {
  264. console.warn('[⚠️] Invalid input. Could not update world order.');
  265. }
  266. }
  267.  
  268. function initialize() {
  269. console.log(`[⚡] Custom World Order script loaded! Priority order: [${PREFERRED_ORDER.join(', ')}]`);
  270. setTimeout(instantArrangeWorlds, 100);
  271. setTimeout(periodicCheck, 200);
  272. createCornerInputUI();
  273.  
  274. let lastUrl = location.href;
  275. setInterval(() => {
  276. if (location.href !== lastUrl) {
  277. lastUrl = location.href;
  278. setTimeout(instantArrangeWorlds, 300);
  279. }
  280. }, 1000);
  281. }
  282.  
  283. if (document.readyState === 'loading') {
  284. document.addEventListener('DOMContentLoaded', initialize);
  285. } else {
  286. initialize();
  287. }
  288.  
  289. setTimeout(initialize, 50);
  290. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址