- // ==UserScript==
- // @name DH2 Fixed
- // @namespace FileFace
- // @description Improve Diamond Hunt 2
- // @version 0.51.1
- // @author Zorbing
- // @grant none
- // @run-at document-start
- // @include http://www.diamondhunt.co/game.php
- // ==/UserScript==
-
- (function ()
- {
- 'use strict';
-
-
-
- /**
- * observer
- */
-
- let observedKeys = new Map();
- /**
- * Observes the given key for change
- *
- * @param {string} key The name of the variable
- * @param {Function} fn The function which is called on change
- */
- function observe(key, fn)
- {
- if (key instanceof Array)
- {
- for (let k of key)
- {
- observe(k, fn);
- }
- }
- else
- {
- if (!observedKeys.has(key))
- {
- observedKeys.set(key, new Set());
- }
- observedKeys.get(key).add(fn);
- }
- return fn;
- }
- function unobserve(key, fn)
- {
- if (key instanceof Array)
- {
- let ret = [];
- for (let k of key)
- {
- ret.push(unobserve(k, fn));
- }
- return ret;
- }
- if (!observedKeys.has(key))
- {
- return false;
- }
- return observedKeys.get(key).delete(fn);
- }
- function updateValue(key, newValue)
- {
- if (window[key] === newValue)
- {
- return false;
- }
-
- const oldValue = window[key];
- window[key] = newValue;
- (observedKeys.get(key) || []).forEach(fn => fn(key, oldValue, newValue));
- return true;
- }
-
-
-
- /**
- * global constants
- */
-
- const tierLevels = ['empty', 'sapphire', 'emerald', 'ruby', 'diamond'];
- const tierNames = ['Standard', 'Sapphire', 'Emerald', 'Ruby', 'Diamond'];
- const tierItemList = ['pickaxe', 'shovel', 'hammer', 'axe', 'rake', 'fishingRod'];
- const furnaceLevels = ['stone', 'bronze', 'iron', 'silver', 'gold'];
- const furnaceCapacity = [10, 30, 75, 150, 300];
- const ovenLevels = ['bronze', 'iron', 'silver', 'gold'];
- const maxOilStorageLevel = 4; // 7
- const oilStorageSize = [10e3, 50e3, 100e3, 300e3];
-
-
-
- /**
- * general functions
- */
-
- let styleElement = null;
- function addStyle(styleCode)
- {
- if (styleElement === null)
- {
- styleElement = document.createElement('style');
- document.head.appendChild(styleElement);
- }
- styleElement.innerHTML += styleCode;
- }
- function getBoundKey(key)
- {
- return 'bound' + key[0].toUpperCase() + key.substr(1);
- }
- function getTierKey(key, tierLevel)
- {
- return tierLevels[tierLevel] + key[0].toUpperCase() + key.substr(1);
- }
- function formatNumber(num)
- {
- return parseFloat(num).toLocaleString('en');
- }
- function formatNumbersInText(text)
- {
- return text.replace(/\d(?:[\d',\.]*\d)?/g, (numStr) =>
- {
- return formatNumber(parseInt(numStr.replace(/\D/g, ''), 10));
- });
- }
- function now()
- {
- return (new Date()).getTime();
- }
- function padLeft(num, padChar)
- {
- return (num < 10 ? padChar : '') + num;
- }
- // use time format established in DHQoL (https://greasyfork.org/scripts/16041-dhqol)
- function formatTimer(timer)
- {
- timer = parseInt(timer, 10);
- const hours = Math.floor(timer / 3600);
- const minutes = Math.floor((timer % 3600) / 60);
- const seconds = timer % 60;
- return padLeft(hours, '0') + ':' + padLeft(minutes, '0') + ':' + padLeft(seconds, '0');
- }
- const timeSteps = [
- {
- threshold: 1
- , name: 'second'
- , short: 'sec'
- , padp: 0
- }
- , {
- threshold: 60
- , name: 'minute'
- , short: 'min'
- , padp: 0
- }
- , {
- threshold: 3600
- , name: 'hour'
- , short: 'h'
- , padp: 1
- }
- , {
- threshold: 86400
- , name: 'day'
- , short: 'd'
- , padp: 2
- }
- ];
- function formatTime2NearestUnit(time, long = false)
- {
- let step = timeSteps[0];
- for (let i = timeSteps.length-1; i > 0; i--)
- {
- if (time >= timeSteps[i].threshold)
- {
- step = timeSteps[i];
- break;
- }
- }
- const factor = Math.pow(10, step.padp);
- const num = Math.round(time / step.threshold * factor) / factor;
- const unit = long ? step.name + (num === 1 ? '' : 's') : step.short;
- return num + ' ' + unit;
- }
- const storePrefix = 'dh2-';
- const store = {
- get: (key) =>
- {
- const value = localStorage.getItem(storePrefix + key);
- try
- {
- return JSON.parse(value);
- }
- catch (e) {}
- return value;
- }
- , has: (key) =>
- {
- return localStorage.hasOwnProperty(storePrefix + key);
- }
- , persist: (key, value) =>
- {
- localStorage.setItem(storePrefix + key, JSON.stringify(value));
- }
- , remove: (key) =>
- {
- localStorage.removeItem(storePrefix + key);
- }
- };
-
-
-
- /**
- * hide crafting recipes of lower tiers or of maxed machines
- */
-
- function setRecipeVisibility(key, visible)
- {
- const recipeRow = document.getElementById('crafting-' + key);
- if (recipeRow)
- {
- recipeRow.style.display = visible ? '' : 'none';
- }
- }
- function hideLeveledRecipes(max, getKey, init)
- {
- const keys2Observe = [];
- let maxLevel = 0;
- for (let i = max-1; i >= 0; i--)
- {
- const level = i+1;
- const key = getKey(i);
- const boundKey = getBoundKey(key);
- keys2Observe.push(key);
- keys2Observe.push(boundKey);
- if (window[key] > 0 || window[boundKey] > 0)
- {
- maxLevel = Math.max(maxLevel, level);
- }
-
- setRecipeVisibility(key, level > maxLevel);
- }
-
- if (init)
- {
- observe(keys2Observe, () => hideLeveledRecipes(max, getKey, false));
- }
- }
- function hideToolRecipe(key, init)
- {
- const emptyKey = getTierKey(key, 0);
- const keys2Observe = [emptyKey];
- let hasTool = window[emptyKey] > 0;
- for (let i = 0; i < tierLevels.length; i++)
- {
- const boundKey = getBoundKey(getTierKey(key, i));
- hasTool = hasTool || window[boundKey] > 0;
- keys2Observe.push(boundKey);
- }
-
- setRecipeVisibility(emptyKey, !hasTool);
-
- if (init)
- {
- observe(keys2Observe, () => hideToolRecipe(key, false));
- }
- }
- function hideRecipe(key, max, init)
- {
- const maxValue = typeof max === 'function' ? max() : max;
- const boundKey = getBoundKey(key);
- const unbound = parseInt(window[key], 10);
- const bound = parseInt(window[boundKey], 10);
-
- setRecipeVisibility(key, (bound + unbound) < maxValue);
-
- if (init)
- {
- observe([key, boundKey], () => hideRecipe(key, max, false));
- }
- }
- function hideCraftedRecipes()
- {
- function processRecipes(init)
- {
- // furnace
- hideLeveledRecipes(
- furnaceLevels.length
- , i => furnaceLevels[i] + 'Furnace'
- , init
- );
- // oil storage
- hideLeveledRecipes(
- 7
- , i => 'oilStorage' + (i+1)
- , init
- );
- // oven recipes
- hideLeveledRecipes(
- ovenLevels.length
- , i => ovenLevels[i] + 'Oven'
- , init
- );
- // tools
- hideToolRecipe('axe', init);
- hideToolRecipe('hammer', init);
- hideToolRecipe('shovel', init);
- hideToolRecipe('pickaxe', init);
- hideToolRecipe('fishingRod', init);
- // drills
- hideRecipe('drills', 10, init);
- // crushers
- hideRecipe('crushers', 10, init);
- // oil pipe
- hideRecipe('oilPipe', 1, init);
- // boats
- hideRecipe('rowBoat', 1, init);
- hideRecipe('canoe', 1, init);
- }
- processRecipes(true);
-
- const _processCraftingTab = window.processCraftingTab;
- window.processCraftingTab = () =>
- {
- const reinit = !!window.refreshLoadCraftingTable;
- _processCraftingTab();
-
- if (reinit)
- {
- processRecipes(true);
- }
- };
- }
-
-
-
- /**
- * improve item boxes
- */
-
- function hideNumberInItemBox(key, setVisibility)
- {
- const itemBox = document.getElementById('item-box-' + key);
- const numberElement = itemBox.lastElementChild;
- if (setVisibility)
- {
- numberElement.style.visibility = 'hidden';
- }
- else
- {
- numberElement.style.display = 'none';
- }
- }
- function addSpan2ItemBox(key)
- {
- hideNumberInItemBox(key);
-
- const itemBox = document.getElementById('item-box-' + key);
- const span = document.createElement('span');
- itemBox.appendChild(span);
- return span;
- }
- function setOilPerSecond(span, oil)
- {
- span.innerHTML = `+ ${formatNumber(oil)} L/s <img src="images/oil.png" class="image-icon-20" style="margin-top: -2px;">`;
- }
- function improveItemBoxes()
- {
- // show capacity of furnace
- for (let i = 0; i < furnaceLevels.length; i++)
- {
- const key = furnaceLevels[i] + 'Furnace';
- const capacitySpan = addSpan2ItemBox(getBoundKey(key));
- capacitySpan.className = 'capacity';
- capacitySpan.textContent = 'Capacity: ' + formatNumber(furnaceCapacity[i]);
- }
-
- // show oil cap of oil storage
- for (let i = 0; i < maxOilStorageLevel; i++)
- {
- const key = 'oilStorage' + (i+1);
- const capSpan = addSpan2ItemBox(getBoundKey(key));
- capSpan.className = 'oil-cap';
- capSpan.textContent = 'Oil cap: ' + formatNumber(oilStorageSize[i]);
- }
-
- // show oil per second
- const handheldOilSpan = addSpan2ItemBox('handheldOilPump');
- setOilPerSecond(handheldOilSpan, 1*window.miner);
- observe('miner', () => setOilPerSecond(handheldOilSpan, 1*window.miner));
- const oilPipeSpan = addSpan2ItemBox('boundOilPipe');
- setOilPerSecond(oilPipeSpan, 50);
-
- // show current tier
- hideNumberInItemBox('emptyAnvil', true);
- hideNumberInItemBox('farmer', true);
- hideNumberInItemBox('planter', true);
- hideNumberInItemBox('cooksBook', true);
- hideNumberInItemBox('cooksPage', true);
- for (let tierItem of tierItemList)
- {
- for (let i = 0; i < tierLevels.length; i++)
- {
- const key = getTierKey(tierItem, i);
- const toolKey = tierItem == 'rake' ? key : getBoundKey(key);
- const tierSpan = addSpan2ItemBox(toolKey);
- tierSpan.className = 'tier';
- tierSpan.textContent = tierNames[i];
- }
- }
-
- // show boat progress
- function setTransitText(span, isInTransit)
- {
- span.textContent = isInTransit ? 'In transit' : 'Ready';
- }
- const boatSpan = addSpan2ItemBox('boundRowBoat');
- setTransitText(boatSpan, window.rowBoatTimer > 0);
- observe('rowBoatTimer', () => setTransitText(boatSpan, window.rowBoatTimer > 0));
- const canoeSpan = addSpan2ItemBox('boundCanoe');
- setTransitText(canoeSpan, window.canoeTimer > 0);
- observe('canoeTimer', () => setTransitText(canoeSpan, window.canoeTimer > 0));
- }
-
-
-
- /**
- * fix wood cutting
- */
-
- function fixWoodcutting()
- {
- addStyle(`
- img.woodcutting-tree-img
- {
- border: 1px solid transparent;
- }
- `);
- }
-
-
-
- /**
- * fix chat
- */
-
- function isMuted(user)
- {
- // return window.mutedPeople.some((name) => user.indexOf(name) > -1);
- return window.mutedPeople.includes(user);
- }
- function handleScrolling(chatbox)
- {
- if (window.isAutoScrolling)
- {
- setTimeout(() => chatbox.scrollTop = chatbox.scrollHeight);
- }
- }
- const chatHistoryKey = 'chatHistory';
- const maxChatHistoryLength = 100;
- const TYPE_RELOAD = -1;
- const TYPE_NORMAL = 0;
- const TYPE_PM_FROM = 1;
- const TYPE_PM_TO = 2;
- const TYPE_SERVER_MSG = 3;
- const reloadedChatData = {
- timestamp: 0
- , username: ''
- , userlevel: 0
- , icon: 0
- , tag: 0
- , type: TYPE_RELOAD
- , msg: '[...]'
- };
- let chatHistory = [];
- function processChatData(username, icon, tag, msg, isPM)
- {
- let userlevel = 0;
- let type = tag == 5 ? TYPE_SERVER_MSG : TYPE_NORMAL;
- if (isPM == 1)
- {
- const match = msg.match(/^\s*\[(.+) ([A-Za-z0-9 ]+)\]: (.+?)\s*$/) || ['', '', username, msg];
- type = match[1] == 'Sent to' ? TYPE_PM_TO : TYPE_PM_FROM;
- username = match[2];
- msg = match[3];
- }
- else if (tag != 5)
- {
- const match = msg.match(/^\s*\((\d+)\): (.+?)\s*$/);
- if (match)
- {
- userlevel = match[1];
- msg = match[2];
- }
- else
- {
- userlevel = window.getGlobalLevel();
- }
- }
- const data = {
- timestamp: now()
- , username: username
- , userlevel: userlevel
- , icon: icon
- , tag: tag
- , type: type
- , msg: msg
- };
- return data;
- }
- function add2ChatHistory(data)
- {
- chatHistory.push(data);
- chatHistory = chatHistory.slice(-maxChatHistoryLength);
- store.persist(chatHistoryKey, chatHistory);
- }
- function getChatTab(username)
- {
- const chatTabs = document.getElementById('chat-tabs');
- let tab = chatTabs.querySelector('div.chat-tab[data-username="' + username + '"]');
- if (!tab)
- {
- tab = document.createElement('div');
- tab.className = 'chat-tab';
- tab.dataset.username = username;
- tab.dataset.new = 0;
- const filler = chatTabs.querySelector('.filler');
- if (filler)
- {
- chatTabs.insertBefore(tab, filler);
- }
- else
- {
- chatTabs.appendChild(tab);
- }
- }
- return tab;
- }
- const chatBoxId = 'div-chat';
- const generalChatId = 'div-chat-area';
- const pmChatPrefix = 'div-chat-pm-';
- const msgInputId = 'chat-input-text';
- function getChatDiv(username)
- {
- const id = username == '' ? generalChatId : pmChatPrefix + username;
- let div = document.getElementById(id);
- if (!div)
- {
- div = document.createElement('div');
- div.setAttribute('disabled', 'disabled');
- div.id = id;
- div.className = 'div-chat-area';
-
- const height = document.getElementById(generalChatId).style.height;
- div.style.height = height;
-
- const generalChat = document.getElementById(generalChatId);
- generalChat.parentNode.insertBefore(div, generalChat);
- }
- return div;
- }
- function changeChatTab(oldTab, newTab)
- {
- const oldChatDiv = getChatDiv(oldTab.dataset.username);
- oldChatDiv.classList.remove('selected');
- const newChatDiv = getChatDiv(newTab.dataset.username);
- newChatDiv.classList.add('selected');
-
- const toUsername = newTab.dataset.username;
- const newTextPlaceholder = toUsername == '' ? window.username + ':' : 'PM to ' + toUsername + ':';
- document.getElementById(msgInputId).placeholder = newTextPlaceholder;
-
- if (window.isAutoScrolling)
- {
- setTimeout(() => newChatDiv.scrollTop = newChatDiv.scrollHeight);
- }
- }
- const chatIcons = [
- null
- , { key: 'halloween2015', title: 'Halloween 2015' }
- , { key: 'christmas2015', title: 'Chirstmas 2015' }
- , { key: 'easter2016', title: 'Holiday' }
- , { key: 'halloween2016', title: 'Halloween 2016' }
- , { key: 'christmas2016', title: 'Chirstmas 2016' }
- , { key: 'dh1Max', title: 'Max Level in DH1' }
- , { key: 'hardcore', title: 'Hardcore Account' }
- , { key: 'quest', title: 'Questmaster' }
- ];
- const chatTags = [
- null
- , { key: 'donor', name: '' }
- , { key: 'contributor', name: 'Contributor' }
- , { key: 'mod', name: 'Moderator' }
- , { key: 'dev', name: 'Dev' }
- , { key: 'yell', name: 'Server Message' }
- ];
- function isPM(data)
- {
- return data.type == TYPE_PM_TO || data.type == TYPE_PM_FROM;
- }
- const locale = 'en-US';
- const localeOptions = {
- hour12: false
- , year: 'numeric'
- , month: 'long'
- , day: 'numeric'
- , hour: '2-digit'
- , minute: '2-digit'
- , second: '2-digit'
- };
- function add2Chat(data)
- {
- // username is 3-12 characters long
- let chatbox = getChatDiv('');
-
- const isThisPm = isPM(data);
- // don't mute pms (you can just ignore pm-tab if you like)
- if (!isThisPm && isMuted(data.username))
- {
- return;
- }
-
- const msgUsername = data.type == TYPE_PM_TO ? window.username : data.username;
- const historyIndex = chatHistory.indexOf(data);
- const historyPart = historyIndex == -1 ? [] : chatHistory.slice(0, historyIndex).reverse();
- const msgBeforeUser = historyPart.find(d => isThisPm && isPM(d) || !isThisPm && !isPM(d));
- const msgBeforeTime = historyPart.find(d => isThisPm && isPM(d) || !isThisPm && !isPM(d) && d.type != TYPE_RELOAD);
- let isSameUser = false;
- let isSameTime = false;
- if (msgBeforeUser)
- {
- const beforeUsername = msgBeforeUser.type == TYPE_PM_TO ? window.username : msgBeforeUser.username;
- isSameUser = beforeUsername === msgUsername;
- }
- if (msgBeforeTime)
- {
- isSameTime = Math.floor(data.timestamp / 1000 / 60) - Math.floor(msgBeforeTime.timestamp / 1000 / 60) === 0;
- }
-
- const d = new Date(data.timestamp);
- const hour = (d.getHours() < 10 ? '0' : '') + d.getHours();
- const minute = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
- const icon = chatIcons[data.icon] || { key: '', title: '' };
- const tag = chatTags[data.tag] || { key: '', name: '' };
- // thanks aguyd (https://greasyfork.org/forum/profile/aguyd) for the vulnerability warning
- const formattedMsg = data.msg.replace(/(https?:\/\/[^\s"<>]+)/g, '<a target="_blank" href="$1">$1</a>');
-
- const msgTitle = data.type == TYPE_RELOAD ? 'Chat loaded on ' + d.toLocaleString(locale, localeOptions) : '';
- let levelAppendix = data.type == TYPE_NORMAL ? ' (' + data.userlevel + ')' : '';
- let chatSegment = `<span class="chat-msg" data-type="${data.type}" data-tag="${tag.key}">`
- + `<span
- class="timestamp"
- title="${d.toLocaleString(locale, localeOptions)}"
- data-same-time="${isSameTime}">${hour}:${minute}</span>`
- + `<span class="user" data-name="${msgUsername}" data-same-user="${isSameUser}">`
- + `<span class="icon ${icon.key}" title="${icon.title}"></span>`
- + `<span
- class="name chat-tag-${tag.key}"
- title="${tag.name}">${msgUsername}${levelAppendix}:</span>`
- + `</span>`
- + `<span class="msg" title="${msgTitle}">${formattedMsg}</span>`
- + `</span>`;
-
- const chatTab = getChatTab(isThisPm ? data.username : '');
- if (!chatTab.classList.contains('selected'))
- {
- chatTab.dataset.new = parseInt(chatTab.dataset.new, 10) + 1;
- }
- if (isThisPm)
- {
- window.lastPMUser = data.username;
- chatbox = getChatDiv(data.username);
- }
-
- const tmp = document.createElement('templateWrapper');
- tmp.innerHTML = chatSegment;
- while (tmp.childNodes.length > 0)
- {
- chatbox.appendChild(tmp.childNodes[0]);
- }
-
- handleScrolling(chatbox);
- }
- function applyChatStyle()
- {
- addStyle(`
- span.chat-msg
- {
- display: flex;
- margin-bottom: 1px;
- }
- span.chat-msg:nth-child(2n)
- {
- background-color: hsla(0, 0%, 90%, 1);
- }
- .chat-msg[data-type="${TYPE_RELOAD}"]
- {
- font-size: 0.8rem;
- }
- .chat-msg .timestamp
- {
- display: none;
- }
- .chat-msg:not([data-type="${TYPE_RELOAD}"]) .timestamp
- {
- color: hsla(0, 0%, 50%, 1);
- display: inline-block;
- font-size: .9rem;
- margin: 0;
- margin-right: 5px;
- width: 2.5rem;
- }
- .chat-msg .timestamp[data-same-time="true"]
- {
- opacity: .1;
- }
-
- .chat-msg[data-type="${TYPE_PM_FROM}"] { color: purple; }
- .chat-msg[data-type="${TYPE_PM_TO}"] { color: purple; }
- .chat-msg[data-type="${TYPE_SERVER_MSG}"] { color: blue; }
- .chat-msg[data-tag="contributor"] { color: green; }
- .chat-msg[data-tag="mod"] { color: #669999; }
- .chat-msg[data-tag="dev"] { color: #666600; }
- .chat-msg:not([data-type="${TYPE_RELOAD}"]) .user
- {
- flex: 0 0 132px;
- margin-right: 5px;
- white-space: nowrap;
- }
- #${generalChatId} .chat-msg:not([data-type="${TYPE_RELOAD}"]) .user
- {
- flex-basis: 182px;
- padding-left: 22px;
- }
- .chat-msg .user[data-same-user="true"]:not([data-name=""])
- {
- opacity: 0;
- }
-
- .chat-msg .user .icon
- {
- margin-left: -22px;
- }
- .chat-msg .user .icon::before
- {
- background-size: 20px 20px;
- content: '';
- display: inline-block;
- margin-right: 2px;
- width: 20px;
- height: 20px;
- vertical-align: middle;
- }
- .chat-msg .user .icon.halloween2015::before { background-image: url('images/chat-icons/1.png'); }
- .chat-msg .user .icon.christmas2015::before { background-image: url('images/chat-icons/2.png'); }
- .chat-msg .user .icon.easter2016::before { background-image: url('images/chat-icons/3.png'); }
- .chat-msg .user .icon.halloween2016::before { background-image: url('images/chat-icons/4.png'); }
- .chat-msg .user .icon.christmas2016::before { background-image: url('images/chat-icons/5.png'); }
- .chat-msg .user .icon.dh1Max::before { background-image: url('images/chat-icons/6.png'); }
- .chat-msg .user .icon.hardcore::before { background-image: url('images/chat-icons/7.png'); }
- .chat-msg .user .icon.quest::before { background-image: url('images/chat-icons/8.png'); }
-
- .chat-msg .user .name
- {
- color: rgba(0, 0, 0, 0.7);
- cursor: pointer;
- }
- .chat-msg .user .name.chat-tag-donor::before
- {
- background-image: url('images/chat-icons/donor.png');
- background-size: 20px 20px;
- content: '';
- display: inline-block;
- height: 20px;
- width: 20px;
- vertical-align: middle;
- }
- .chat-msg .user .name.chat-tag-yell
- {
- cursor: pointer;
- }
- .chat-msg .user .name.chat-tag-yell::before
- {
- content: 'Server Message';
- }
- .chat-msg .user .name.chat-tag-contributor,
- .chat-msg .user .name.chat-tag-mod,
- .chat-msg .user .name.chat-tag-dev,
- .chat-msg .user .name.chat-tag-yell
- {
- color: white;
- display: inline-block;
- font-size: 10pt;
- margin-top: -1px;
- padding-bottom: 0;
- text-align: center;
- /* 2px border, 10 padding */
- width: calc(100% - 2*1px - 2*5px);
- }
-
- .chat-msg[data-type="${TYPE_RELOAD}"] .user > *,
- .chat-msg[data-type="${TYPE_PM_FROM}"] .user > .icon,
- .chat-msg[data-type="${TYPE_PM_TO}"] .user > .icon
- {
- display: none;
- }
-
- .chat-msg .msg
- {
- word-wrap: break-word;
- min-width: 0;
- }
-
- #div-chat .div-chat-area
- {
- width: 100%;
- height: 130px;
- display: none;
- }
- #div-chat .div-chat-area.selected
- {
- display: block;
- }
- #chat-tabs
- {
- display: flex;
- margin: 10px -6px -6px;
- flex-wrap: wrap;
- }
- #chat-tabs .chat-tab
- {
- background-color: gray;
- border-top: 1px solid black;
- border-right: 1px solid black;
- cursor: pointer;
- display: inline-block;
- font-weight: normal;
- padding: 0.3rem .6rem;
- }
- #chat-tabs .chat-tab.selected
- {
- background-color: transparent;
- border-top-color: transparent;
- }
- #chat-tabs .chat-tab.filler
- {
- background-color: hsla(0, 0%, 90%, 1);
- border-right: 0;
- box-shadow: inset 5px 5px 5px -5px rgba(0, 0, 0, 0.5);
- color: transparent;
- cursor: default;
- flex-grow: 1;
- }
- #chat-tabs .chat-tab::before
- {
- content: attr(data-username);
- }
- #chat-tabs .chat-tab:not(.filler)[data-username=""]::before
- {
- content: 'Server';
- }
- #chat-tabs .chat-tab::after
- {
- color: white;
- content: '(' attr(data-new) ')';
- font-size: .9rem;
- font-weight: bold;
- margin-left: .4rem;
- }
- #chat-tabs .chat-tab[data-new="0"]::after
- {
- color: inherit;
- font-weight: normal;
- }
- `);
- }
- function addIntelligentScrolling()
- {
- // add checkbox instead of button for toggling auto scrolling
- const btn = document.querySelector('input[value="Toggle Autoscroll"]');
- const checkboxId = 'chat-toggle-autoscroll';
- // create checkbox
- const toggleCheckbox = document.createElement('input');
- toggleCheckbox.type = 'checkbox';
- toggleCheckbox.id = checkboxId;
- toggleCheckbox.checked = true;
- // create label
- const toggleLabel = document.createElement('label');
- toggleLabel.htmlFor = checkboxId;
- toggleLabel.textContent = 'Autoscroll';
- btn.parentNode.insertBefore(toggleCheckbox, btn);
- btn.parentNode.insertBefore(toggleLabel, btn);
- btn.style.display = 'none';
-
- // add checkbox for intelligent scrolling
- const isCheckboxId = 'chat-toggle-intelligent-scroll';
- const intScrollCheckbox = document.createElement('input');
- intScrollCheckbox.type = 'checkbox';
- intScrollCheckbox.id = isCheckboxId;
- intScrollCheckbox.checked = true;
- // add label
- const intScrollLabel = document.createElement('label');
- intScrollLabel.htmlFor = isCheckboxId;
- intScrollLabel.textContent = 'Intelligent Scrolling';
- btn.parentNode.appendChild(intScrollCheckbox);
- btn.parentNode.appendChild(intScrollLabel);
-
- const chatArea = document.getElementById(generalChatId);
- function setAutoScrolling(value, full)
- {
- if (window.isAutoScrolling != value)
- {
- toggleCheckbox.checked = value;
- window.isAutoScrolling = value;
- window.scrollText(
- 'none'
- , value ? 'lime' : 'red'
- , (value ? 'En' : 'Dis') + 'abled' + (full ? ' Autoscroll' : '')
- );
- return true;
- }
- return false;
- }
- toggleCheckbox.addEventListener('change', function ()
- {
- setAutoScrolling(this.checked);
- if (this.checked && intScrollCheckbox.checked)
- {
- chatArea.scrollTop = chatArea.scrollHeight - chatArea.clientHeight;
- }
- });
-
- // does not consider pm tabs; may be changed in a future version?
- chatArea.addEventListener('scroll', () =>
- {
- if (intScrollCheckbox.checked)
- {
- const scrolled2Bottom = (chatArea.scrollTop + chatArea.clientHeight) >= chatArea.scrollHeight;
- setAutoScrolling(scrolled2Bottom, true);
- }
- });
- }
- function clickTab(newTab)
- {
- const oldTab = document.querySelector('#chat-tabs .chat-tab.selected');
- if (newTab == oldTab)
- {
- return;
- }
- oldTab.classList.remove('selected');
- newTab.classList.add('selected');
- newTab.dataset.new = 0;
-
- changeChatTab(oldTab, newTab);
- }
- function addChatTabs()
- {
- const chatBoxArea = document.getElementById(chatBoxId);
- const chatTabs = document.createElement('div');
- chatTabs.id = 'chat-tabs';
- chatTabs.addEventListener('click', (event) =>
- {
- const newTab = event.target;
- if (!newTab.classList.contains('chat-tab') || newTab.classList.contains('filler'))
- {
- return;
- }
-
- clickTab(newTab);
- });
- chatBoxArea.appendChild(chatTabs);
-
- const generalTab = getChatTab('');
- generalTab.classList.add('selected');
- const generalChatDiv = getChatDiv('');
- generalChatDiv.classList.add('selected');
- // works only if username length of 1 isn't allowed
- const fillerTab = getChatTab('f');
- fillerTab.classList.add('filler');
-
- const _sendChat = window.sendChat;
- window.sendChat = (inputEl) =>
- {
- let msg = inputEl.value;
- const selectedTab = document.querySelector('.chat-tab.selected');
- if (selectedTab.dataset.username != '' && msg[0] != '/')
- {
- inputEl.value = '/pm ' + selectedTab.dataset.username + ' ' + msg;
- }
- _sendChat(inputEl);
- };
- }
- function newAddToChatBox(username, icon, tag, msg, isPM)
- {
- const data = processChatData(username, icon, tag, msg, isPM);
- add2ChatHistory(data);
- add2Chat(data);
- }
- function newChat()
- {
- addChatTabs();
- applyChatStyle();
-
- window.addToChatBox = newAddToChatBox;
-
- const chatbox = document.getElementById(chatBoxId);
- chatbox.addEventListener('click', (event) =>
- {
- let target = event.target;
- while (target && target.id != chatBoxId && !target.classList.contains('user'))
- {
- target = target.parentElement;
- }
- if (target.id == chatBoxId)
- {
- return;
- }
-
- const username = target.dataset.name;
- if (username == window.username || username == '')
- {
- return;
- }
-
- const userTab = getChatTab(username);
- clickTab(userTab);
- document.getElementById(msgInputId).focus();
- });
- }
- function fixChat()
- {
- newChat();
- addIntelligentScrolling();
-
- const _enlargeChat = window.enlargeChat;
- const chatBoxArea = document.getElementById(chatBoxId);
- function setChatBoxHeight(height)
- {
- document.getElementById(generalChatId).style.height = height;
- const chatDivs = chatBoxArea.querySelectorAll('div[id^="' + pmChatPrefix + '"]');
- for (let i = 0; i < chatDivs.length; i++)
- {
- chatDivs[i].style.height = height;
- }
- }
- window.enlargeChat = (enlargeB) =>
- {
- _enlargeChat(enlargeB);
-
- const height = document.getElementById(generalChatId).style.height;
- store.persist('chat.height', height);
- setChatBoxHeight(height);
- };
- setChatBoxHeight(store.get('chat.height'));
-
- // TEMP >>> (due to a naming issue, migrate the data)
- const oldChatHistoryKey = 'chatHistory2';
- const oldChatHistory = store.get(oldChatHistoryKey);
- if (oldChatHistory != null)
- {
- store.persist(chatHistoryKey, oldChatHistory);
- store.remove(oldChatHistoryKey);
- }
- // TEMP <<<
-
- // load history
- chatHistory = store.get(chatHistoryKey) || chatHistory;
- // find index of last message which is not a pm
- const lastNotPM = chatHistory.slice(0).reverse().find((d) =>
- {
- return !isPM(d);
- });
- // insert a placeholder for a reloaded chat
- if (lastNotPM && lastNotPM.type != TYPE_RELOAD)
- {
- reloadedChatData.timestamp = (new Date()).getTime();
- chatHistory.push(reloadedChatData);
- }
- chatHistory.forEach(d => add2Chat(d));
- // reset the new counter for all tabs
- const tabs = document.querySelectorAll('.chat-tab');
- for (let i = 0; i < tabs.length; i++)
- {
- tabs[i].dataset.new = 0;
- }
- }
-
-
-
- /**
- * hopefully only temporary fixes
- */
-
- function temporaryFixes()
- {
- // fix grow time of some seeds
- const seeds = {
- 'limeLeafSeeds': '1 hour and 30 minutes'
- };
- for (let seedName in seeds)
- {
- const tooltip = document.getElementById('tooltip-' + seedName);
- tooltip.lastElementChild.lastChild.textContent = seeds[seedName];
- }
-
- // fix exhaustion timer and updating brewing and cooking recipes
- const _clientGameLoop = window.clientGameLoop;
- window.clientGameLoop = () =>
- {
- _clientGameLoop();
- if (document.getElementById('tab-sub-container-fight').style.display == 'none')
- {
- document.getElementById('combat-countdown').style.display = '';
- }
- if (document.getElementById('tab-container-combat').style.display != 'none')
- {
- window.combatNotFightingTick();
- }
- if (currentOpenTab == 'brewing')
- {
- window.processBrewingTab();
- }
- if (currentOpenTab == 'cooksBook')
- {
- window.processCooksBookTab();
- }
- };
-
- // fix elements of scrollText (e.g. when joining the game and receiving xp at that moment)
- const textEls = document.querySelectorAll('div.scroller');
- for (let i = 0; i < textEls.length; i++)
- {
- const scroller = textEls[i];
- if (scroller.style.position != 'absolute')
- {
- scroller.style.display = 'none';
- }
- }
-
- // fix style of tooltips
- addStyle(`
- body > div.tooltip > h2:first-child
- {
- margin-top: 0;
- font-size: 20pt;
- font-weight: normal;
- }
- `);
- }
-
-
-
- /**
- * improve timer
- */
-
- function improveTimer()
- {
- window.formatTime = (seconds) =>
- {
- return formatTimer(seconds);
- };
- window.formatTimeShort2 = (seconds) =>
- {
- return formatTimer(seconds);
- };
-
- addStyle(`
- #notif-smelting > span:not(.timer)
- {
- display: none;
- }
- `);
- const smeltingNotifBox = document.getElementById('notif-smelting');
- const smeltingTimerEl = document.createElement('span');
- smeltingTimerEl.className = 'timer';
- smeltingNotifBox.appendChild(smeltingTimerEl);
- function updateSmeltingTimer()
- {
- const totalTime = parseInt(window.smeltingPercD, 10);
- const elapsedTime = parseInt(window.smeltingPercN, 10);
- smeltingTimerEl.textContent = formatTimer(Math.max(totalTime - elapsedTime, 0));
- }
- observe('smeltingPercD', () => updateSmeltingTimer());
- observe('smeltingPercN', () => updateSmeltingTimer());
- updateSmeltingTimer();
-
- // add tree grow timer
- const treeInfo = {
- 1: {
- name: 'Normal tree'
- // 3h = 10800s
- , growTime: 3 * 60 * 60
- }
- , 2: {
- name: 'Oak tree'
- // 6h = 21600s
- , growTime: 6 * 60 * 60
- }
- , 3: {
- name: 'Willow tree'
- // 8h = 28800s
- , growTime: 8 * 60 * 60
- }
- };
- function updateTreeInfo(place, nameEl, timerEl, init)
- {
- const idKey = 'treeId' + place;
- const growTimerKey = 'treeGrowTimer' + place;
- const lockedKey = 'treeUnlocked' + place;
-
- const info = treeInfo[window[idKey]];
- if (!info)
- {
- const isLocked = place > 4 && window[lockedKey] == 0;
- nameEl.textContent = isLocked ? 'Locked' : 'Empty';
- timerEl.textContent = '';
- }
- else
- {
- nameEl.textContent = info.name;
- const remainingTime = info.growTime - parseInt(window[growTimerKey], 10);
- timerEl.textContent = remainingTime > 0 ? '(' + formatTimer(remainingTime) + ')' : 'Fully grown';
- }
-
- if (init)
- {
- observe(
- [idKey, growTimerKey, lockedKey]
- , () => updateTreeInfo(place, nameEl, timerEl, false)
- );
- }
- }
- for (let i = 0; i < 6; i++)
- {
- const treePlace = i+1;
- const treeContainer = document.getElementById('wc-div-tree-' + treePlace);
- treeContainer.style.position = 'relative';
- const infoEl = document.createElement('div');
- infoEl.setAttribute('style', 'position: absolute; top: 0; left: 0; right: 0; pointer-events: none; margin-top: 5px; color: white;');
- const treeName = document.createElement('div');
- treeName.style.fontSize = '1.2rem';
- infoEl.appendChild(treeName);
- const treeTimer = document.createElement('div');
- infoEl.appendChild(treeTimer);
- treeContainer.appendChild(infoEl);
-
- updateTreeInfo(treePlace, treeName, treeTimer, true);
- }
- }
-
-
-
- /**
- * improve smelting dialog
- */
-
- const smeltingRequirements = {
- 'glass': {
- sand: 1
- , oil: 10
- }
- , 'bronzeBar': {
- copper: 1
- , tin: 1
- , oil: 10
- }
- , 'ironBar': {
- iron: 1
- , oil: 100
- }
- , 'silverBar': {
- silver: 1
- , oil: 300
- }
- , 'goldBar': {
- gold: 1
- , oil: 1e3
- }
- };
- function improveSmelting()
- {
- const amountInput = document.getElementById('input-smelt-bars-amount');
- amountInput.type = 'number';
- amountInput.min = 0;
- amountInput.step = 5;
- function onValueChange(event)
- {
- smeltingValue = null;
- window.selectBar('', '', amountInput, document.getElementById('smelting-furnace-capacity').value);
- }
- amountInput.addEventListener('mouseup', onValueChange);
- amountInput.addEventListener('keyup', onValueChange);
- amountInput.setAttribute('onkeyup', '');
-
- const _selectBar = window.selectBar;
- let smeltingValue = null;
- window.selectBar = (bar, inputElement, inputBarsAmountEl, capacity) =>
- {
- const requirements = smeltingRequirements[bar];
- let maxAmount = capacity;
- for (let key in requirements)
- {
- maxAmount = Math.min(Math.floor(window[key] / requirements[key]), maxAmount);
- }
- const value = parseInt(amountInput.value, 10);
- if (value > maxAmount)
- {
- smeltingValue = value;
- amountInput.value = maxAmount;
- }
- else if (smeltingValue != null)
- {
- amountInput.value = Math.min(smeltingValue, maxAmount);
- if (smeltingValue <= maxAmount)
- {
- smeltingValue = null;
- }
- }
- return _selectBar(bar, inputElement, inputBarsAmountEl, capacity);
- };
-
- const _openFurnaceDialogue = window.openFurnaceDialogue;
- window.openFurnaceDialogue = (furnace) =>
- {
- if (smeltingBarType == 0)
- {
- amountInput.max = getFurnaceCapacity(furnace);
- }
- return _openFurnaceDialogue(furnace);
- };
- }
-
-
-
- /**
- * add chance to time calculator
- */
-
- /**
- * calculates the number of seconds until the event with the given chance happened at least once with the given
- * probability p (in percent)
- */
- function calcSecondsTillP(chancePerSecond, p)
- {
- return Math.round(Math.log(1 - p/100) / Math.log(1 - chancePerSecond));
- }
- function addChanceTooltip(headline, chancePerSecond, elId, targetEl)
- {
- // ensure element existence
- const tooltipElId = 'tooltip-chance-' + elId;
- let tooltipEl = document.getElementById(tooltipElId);
- if (!tooltipEl)
- {
- tooltipEl = document.createElement('div');
- tooltipEl.id = tooltipElId;
- tooltipEl.style.display = 'none';
- document.getElementById('tooltip-list').appendChild(tooltipEl);
- }
-
- // set elements content
- const percValues = [1, 10, 20, 50, 80, 90, 99];
- let percRows = '';
- for (let p of percValues)
- {
- percRows += `
- <tr>
- <td>${p}%</td>
- <td>${formatTime2NearestUnit(calcSecondsTillP(chancePerSecond, p), true)}</td>
- </tr>`;
- }
- tooltipEl.innerHTML = `<h2>${headline}</h2>
- <table class="chance">
- <tr>
- <th>Probability</th>
- <th>Time</th>
- </tr>
- ${percRows}
- </table>
- `;
-
- // ensure binded events to show the tooltip
- if (targetEl.dataset.tooltipId == null)
- {
- targetEl.setAttribute('data-tooltip-id', tooltipElId);
- window.$(targetEl).bind({
- mousemove: window.changeTooltipPosition
- , mouseenter: window.showTooltip
- , mouseleave: window.hideTooltip
- });
- }
- }
- function chance2TimeCalculator()
- {
- addStyle(`
- table.chance
- {
- border-spacing: 0;
- }
- table.chance th
- {
- border-bottom: 1px solid gray;
- }
- table.chance td:first-child
- {
- border-right: 1px solid gray;
- text-align: center;
- }
- table.chance th,
- table.chance td
- {
- padding: 4px 8px;
- }
- table.chance tr:nth-child(2n) td
- {
- background-color: white;
- }
- `);
-
- const _clicksShovel = window.clicksShovel;
- window.clicksShovel = () =>
- {
- _clicksShovel();
-
- const shovelChance = document.getElementById('dialogue-shovel-chance');
- const titleEl = shovelChance.parentElement;
- const chance = 1/window.getChanceOfDiggingSand();
- addChanceTooltip('One sand every:', chance, 'shovel', titleEl);
- };
-
- // depends on fishingXp
- const _clicksFishingRod = window.clicksFishingRod;
- window.clicksFishingRod = () =>
- {
- _clicksFishingRod();
-
- const fishList = ['shrimp', 'sardine', 'tuna', 'swordfish', 'shark'];
- for (let fish of fishList)
- {
- const rawFish = 'raw' + fish[0].toUpperCase() + fish.substr(1);
- const row = document.getElementById('dialogue-fishing-rod-tr-' + rawFish);
- const chance = row.cells[4].textContent
- .replace(/[^\d\/]/g, '')
- .split('/')
- .reduce((p, c) => p / parseInt(c, 10), 1)
- ;
- addChanceTooltip(`One raw ${fish} every:`, chance, rawFish, row);
- }
- };
- }
-
-
-
- /**
- * add tooltips for recipes
- */
-
- function updateRecipeTooltips(recipeKey, recipes)
- {
- const table = document.getElementById('table-' + recipeKey + '-recipe');
- const rows = table.rows;
- for (let i = 1; i < rows.length; i++)
- {
- const row = rows[i];
- const key = row.id.replace(recipeKey + '-', '');
- const recipe = recipes[key];
- const requirementCell = row.cells[3];
- requirementCell.title = recipe.recipe
- .map((name, i) =>
- {
- return formatNumber(recipe.recipeCost[i]) + ' '
- + name.replace(/[A-Z]/g, (match) => ' ' + match.toLowerCase())
- ;
- })
- .join(' + ')
- ;
- window.$(requirementCell).tooltip();
- }
- }
- function addRecipeTooltips()
- {
- const _processCraftingTab = window.processCraftingTab;
- window.processCraftingTab = () =>
- {
- const reinit = !!window.refreshLoadCraftingTable;
- _processCraftingTab();
-
- if (reinit)
- {
- updateRecipeTooltips('crafting', window.craftingRecipes);
- }
- };
-
- const _processBrewingTab = window.processBrewingTab;
- window.processBrewingTab = () =>
- {
- const reinit = !!window.refreshLoadBrewingTable;
- _processBrewingTab();
-
- if (reinit)
- {
- updateRecipeTooltips('brewing', window.brewingRecipes);
- }
- }
-
- const _processMagicTab = window.processMagicTab;
- window.processMagicTab = () =>
- {
- const reinit = !!window.refreshLoadCraftingTable;
- _processMagicTab();
-
- if (reinit)
- {
- updateRecipeTooltips('magic', window.magicRecipes);
- }
- }
- }
-
-
-
- /**
- * fix formatting of numbers
- */
-
- function prepareRecipeForTable(recipe)
- {
- // create a copy of the recipe to prevent requirement check from failing
- const newRecipe = JSON.parse(JSON.stringify(recipe));
- newRecipe.recipeCost = recipe.recipeCost.map(cost => formatNumber(cost));
- newRecipe.xp = formatNumber(recipe.xp);
- return newRecipe;
- }
- function fixNumberFormat()
- {
- const _addRecipeToBrewingTable = window.addRecipeToBrewingTable;
- window.addRecipeToBrewingTable = (brewingRecipe) =>
- {
- _addRecipeToBrewingTable(prepareRecipeForTable(brewingRecipe));
- };
-
- const _addRecipeToMagicTable = window.addRecipeToMagicTable;
- window.addRecipeToMagicTable = (magicRecipe) =>
- {
- _addRecipeToMagicTable(prepareRecipeForTable(magicRecipe));
- };
- }
-
-
-
- /**
- * style tweaks
- */
-
- function addTweakStyle(setting, style)
- {
- const prefix = 'body.' + setting;
- addStyle(
- style
- .replace(/(^\s*|,\s*|\}\s*)([^\{\},]+)(,|\s*\{)/g, '$1' + prefix + ' $2$3')
- );
- document.body.classList.add(setting);
- }
- function tweakStyle()
- {
- // tweak oil production/consumption
- addTweakStyle('tweak-oil', `
- span#oil-flow-values
- {
- position: relative;
- margin-left: .5em;
- }
- #oil-flow-values > span
- {
- font-size: 0px;
- position: absolute;
- top: -0.75rem;
- visibility: hidden;
- }
- #oil-flow-values > span > span
- {
- font-size: 1rem;
- visibility: visible;
- }
- #oil-flow-values > span:last-child
- {
- top: 0.75rem;
- }
- #oil-flow-values span[data-item-display="oilIn"]::before
- {
- content: '+';
- }
- #oil-flow-values span[data-item-display="oilOut"]::before
- {
- content: '-';
- }
- `);
-
- addTweakStyle('no-select', `
- table.tab-bar,
- span.item-box,
- div.farming-patch,
- div.farming-patch-locked,
- div.tab-sub-container-combat,
- table.top-links a
- {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- }
- `);
- }
-
-
-
- /**
- * init
- */
-
- function init()
- {
- temporaryFixes();
-
- hideCraftedRecipes();
- improveItemBoxes();
- fixWoodcutting();
- fixChat();
- improveTimer();
- improveSmelting();
- chance2TimeCalculator();
- addRecipeTooltips();
-
- fixNumberFormat();
- tweakStyle();
- }
- document.addEventListener('DOMContentLoaded', () =>
- {
- const _doCommand = window.doCommand;
- window.doCommand = (data) =>
- {
- const values = data.split('=')[1];
- if (data.startsWith('REFRESH_ITEMS='))
- {
- const itemDataValues = values.split(';');
- const itemArray = [];
- for (var i = 0; i < itemDataValues.length; i++)
- {
- const [key, newValue] = itemDataValues[i].split('~');
- if (updateValue(key, newValue))
- {
- itemArray.push(key);
- }
- }
-
- window.refreshItemValues(itemArray, false);
-
- if (window.firstLoadGame)
- {
- window.loadInitial();
- window.firstLoadGame = false;
- init();
- }
- else
- {
- window.clientGameLoop();
- }
- return;
- }
- else if (data.startsWith('CHAT='))
- {
- var parts = data.substr(5).split('~');
- return newAddToChatBox(parts[0], parts[1], parts[2], parts[3], 0);
- }
- return _doCommand(data);
- };
- });
-
-
-
- /**
- * fix web socket errors
- */
-
- function webSocketLoaded(event)
- {
- if (window.webSocket == null)
- {
- console.error('no webSocket instance found!');
- return;
- }
-
- const messageQueue = [];
- const _onMessage = webSocket.onmessage;
- webSocket.onmessage = (event) => messageQueue.push(event);
- document.addEventListener('DOMContentLoaded', () =>
- {
- messageQueue.forEach(event => onMessage(event));
- webSocket.onmessage = _onMessage;
- });
-
- const commandQueue = [];
- const _sendBytes = window.sendBytes;
- window.sendBytes = (command) => commandQueue.push(command);
- const _onOpen = webSocket.onopen;
- webSocket.onopen = (event) =>
- {
- window.sendBytes = _sendBytes;
- commandQueue.forEach(command => window.sendBytes(command));
- return _onOpen(event);
- };
- }
- function isWebSocketScript(script)
- {
- return script.src.includes('socket.js');
- }
- function fixWebSocketScript()
- {
- if (!document.head)
- {
- return;
- }
-
- const scripts = document.head.querySelectorAll('script');
- let found = false;
- for (let i = 0; i < scripts.length; i++)
- {
- if (isWebSocketScript(scripts[i]))
- {
- // does this work?
- scripts[i].onload = webSocketLoaded;
- return;
- }
- }
-
- // create an observer instance
- const mutationObserver = new MutationObserver((mutationList) =>
- {
- mutationList.forEach((mutation) =>
- {
- if (mutation.addedNodes.length === 0)
- {
- return;
- }
-
- for (let i = 0; i < mutation.addedNodes.length; i++)
- {
- const node = mutation.addedNodes[i];
- if (node.tagName == 'SCRIPT' && isWebSocketScript(node))
- {
- mutationObserver.disconnect();
- node.onload = webSocketLoaded;
- return;
- }
- }
- });
- });
- mutationObserver.observe(document.head, {
- childList: true
- });
- }
- fixWebSocketScript();
-
- // fix scrollText (e.g. when joining the game and receiving xp at that moment)
- window.mouseX = window.innerWidth / 2;
- window.mouseY = window.innerHeight / 2;
- })();