blackhack-ui

UI components for blackhack

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greasyfork.org/scripts/571915/1796552/blackhack-ui.js

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name           blackhack-ui
// @namespace      brofist.io 1st-cheat (FOR ALL MODES)
// @version        1.11
// @description    UI components for blackhack (Scrollable Widgets & Fixed Coords)
// @author         CiNoP
// @license        GPL-3.0-only
// ==/UserScript==
 
/* blackhack-ui.js */
(function () {
	'use strict';
	const BH = window.BH = window.BH || {};
	BH.uiVer = 1.11;
 
	BH.createZoom = () => {
		let oW = window.innerWidth, oH = window.innerHeight;
		document.onkeydown = e => { if (e.keyCode === 48) { e.preventDefault(); window.innerWidth = oW; window.innerHeight = oH; } };
		document.onwheel = e => {
			e.preventDefault();
			const f = e.deltaY < 0 ? 1.1 : 1 / 1.1;
			window.innerWidth *= f; window.innerHeight *= f;
		};
	};
 
	BH.createUI = () => {
		const hack = window.hack, { clamp, round3, saveSettings, DEFAULTS } = BH;
		const isTwoPlayer = location.pathname.toLowerCase().includes("twoplayer");
		const panel = document.createElement('div');
		panel.id = 'hk-panel';
		panel.innerHTML = `
<style>
#hk-panel{position:fixed;top:8px;left:8px;z-index:999999;background:#181818;color:#ccc;font:12px/1.5 monospace;border:1px solid #333;border-radius:6px;width:320px;user-select:none;box-shadow:0 2px 12px rgba(0,0,0,.5)}
#hk-panel *{box-sizing:border-box}
.hk-tabs{display:flex;border-bottom:1px solid #333;font-size:10px}
.hk-tab{flex:1;padding:6px 2px;text-align:center;background:#111;color:#666;border:none;cursor:pointer;font:inherit;transition:background .15s,color .15s;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.hk-tab:first-child{border-radius:6px 0 0 0}.hk-tab:last-child{border-radius:0 6px 0 0}
.hk-tab.active{background:#2a2a2a;color:#fff}.hk-tab:hover:not(.active){background:#1e1e1e;color:#aaa}
.hk-pane{display:none;padding:10px 12px}.hk-pane.active{display:block}
.hk-row{display:flex;align-items:center;margin:4px 0}
.hk-row label{color:#aaa;white-space:nowrap;min-width:100px}
.hk-row input[type=number]{width:64px;padding:3px 4px;background:#0e0e0e;color:#fff;border:1px solid #444;border-radius:3px;text-align:center;font:inherit;outline:none;-moz-appearance:textfield}
.hk-row input[type=number]::-webkit-outer-spin-button,.hk-row input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.hk-row input[type=number]:focus{border-color:#777}
.hk-range{color:#555;font-size:10px;margin-left:6px;white-space:nowrap}
.hk-btn{width:100%;margin-top:6px;padding:5px 0;background:#333;color:#ddd;border:1px solid #555;border-radius:3px;cursor:pointer;font:inherit;transition:background .15s}
.hk-btn:hover:not(:disabled){background:#444}
.hk-btn:disabled{opacity:0.5;cursor:not-allowed}
.hk-btn.hk-reset{background:#2a1a1a;border-color:#553333}.hk-btn.hk-reset:hover{background:#3a2020}
.hk-sep{border:none;border-top:1px solid #2a2a2a;margin:6px 0}
.hk-foot{padding:4px 12px 8px;border-top:1px solid #2a2a2a}
.hk-ind-row{display:flex;justify-content:center;align-items:center;gap:8px;padding:6px 0 2px}
.hk-ind-dot{width:12px;height:12px;border-radius:50%;background:#0f0;transition:background .15s;box-shadow:0 0 4px rgba(0,0,0,.6)}
.hk-ind-gap{width:12px;height:12px;border-radius:50%;background:#555;box-shadow:0 0 4px rgba(0,0,0,.6)}
.hk-bl-add-row{display:flex;gap:6px;margin-bottom:8px}
#hk-bl-input{flex:1;padding:4px 8px;background:#0e0e0e;color:#fff;border:1px solid #444;border-radius:3px;font:inherit;outline:none}
#hk-bl-input:focus{border-color:#777}#hk-bl-input::placeholder{color:#555}
.hk-bl-addbtn{width:auto!important;margin:0!important;padding:4px 12px!important;flex-shrink:0}
#hk-bl-list{max-height:180px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#333 #181818}
#hk-bl-list::-webkit-scrollbar{width:5px}#hk-bl-list::-webkit-scrollbar-track{background:#181818}
#hk-bl-list::-webkit-scrollbar-thumb{background:#444;border-radius:3px}
.hk-bl-item{display:flex;align-items:center;padding:4px 2px;border-bottom:1px solid #222}.hk-bl-item:last-child{border-bottom:none}
.hk-bl-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;margin-right:8px;background:#444;transition:background .3s}.hk-bl-dot.hk-on{background:#0f0}
.hk-bl-name{flex:1;color:#ccc;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hk-bl-x{background:none;border:none;color:#555;cursor:pointer;font-size:18px;line-height:1;padding:0 4px;transition:color .15s}.hk-bl-x:hover{color:#f44}
.hk-bl-empty{color:#444;text-align:center;padding:16px 0;font-style:italic}
 
/* TP WARNING STYLES */
.hk-warn-2pa{color:#f44;text-align:center;padding:20px 10px;font-size:11px;}
 
.hk-tp-info{text-align:center;margin-bottom:10px;font-size:13px;}
.hk-tp-target{color:#0f0;font-weight:bold;}
 
/* WIDGETS TAB STYLES */
.hk-wg-cols{display:flex;gap:6px;margin-bottom:8px;align-items:flex-start}
.hk-wg-col{flex:1;display:flex;flex-direction:column;gap:4px;min-width:0}
.hk-wg-col-title{text-align:center;color:#aaa;margin-bottom:2px;border-bottom:1px solid #333;padding-bottom:2px;font-size:10px;white-space:nowrap;overflow:hidden}
.hk-wg-list{display:flex;flex-direction:column;gap:3px;max-height:350px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#444 #111}
.hk-wg-list::-webkit-scrollbar{width:4px}
.hk-wg-list::-webkit-scrollbar-thumb{background:#444;border-radius:2px}

/* SCROLLABLE BUTTON STYLES (AIMP BEHAVIOR) */
.hk-wg-btn{background:#222;border:1px solid #444;color:#ccc;cursor:pointer;padding:4px 5px;border-radius:3px;font-size:10px;width:100%;overflow:hidden;transition:background .15s;min-height:25px;flex-shrink:0;display:flex;align-items:center;justify-content:center}
.hk-wg-btn:hover{background:#333}
.hk-wg-btn-text{white-space:nowrap;margin:0 auto;display:inline-block}
.hk-wg-marquee{margin:0;animation:hk-aimp-scroll var(--scroll-time) linear infinite alternate}

@keyframes hk-aimp-scroll {
	0%, 15% { transform: translateX(0); }
	85%, 100% { transform: translateX(var(--scroll-dist)); }
}
</style>
 
<div class="hk-tabs">
	<button class="hk-tab active" data-t="0">Пар-тры</button>
	<button class="hk-tab" data-t="1">Визуал</button>
	<button class="hk-tab" data-t="2">Блоклист</button>
	<button class="hk-tab" data-t="3">ТП к др</button>
	<button class="hk-tab" data-t="4">ТП к видж</button>
</div>
 
<div class="hk-pane active" data-t="0">
	<div class="hk-row"><label>Множитель</label><input type="number" id="hk-mult" step="0.001"><span class="hk-range">-9 … 9</span></div>
	<div class="hk-row"><label>Грав. шкала</label><input type="number" id="hk-gs" step="0.001"><span class="hk-range">-10 … 10</span></div>
	<div class="hk-row"><label>Выс. прыжка</label><input type="number" id="hk-jh" step="0.001"><span class="hk-range">0.2 … 50</span></div>
	<hr class="hk-sep">
	<div class="hk-row"><label>Масса</label><input type="number" id="hk-mass" step="0.001"><span class="hk-range">-10 … 10</span></div>
	<div class="hk-row"><label>Сопротивление</label><input type="number" id="hk-damp" step="0.001"><span class="hk-range">0 … 1</span></div>
</div>
 
<div class="hk-pane" data-t="1">
	<div class="hk-row"><label>Все</label><input type="number" id="hk-aa" step="0.001"><span class="hk-range">0 … 1</span></div>
	<hr class="hk-sep">
	<div class="hk-row"><label>Тела</label><input type="number" id="hk-ac" step="0.001"><span class="hk-range">0 … 1</span></div>
	<div class="hk-row"><label>Яд</label><input type="number" id="hk-ap" step="0.001"><span class="hk-range">0 … 1</span></div>
	<div class="hk-row"><label>Функционал</label><input type="number" id="hk-af" step="0.001"><span class="hk-range">0 … 1</span></div>
	<button class="hk-btn" id="hk-al-apply">Применить</button>
</div>
 
<div class="hk-pane" data-t="2">
	<div class="hk-bl-add-row">
		<input type="text" id="hk-bl-input" placeholder="Никнейм игрока" maxlength="32">
		<button class="hk-btn hk-bl-addbtn" id="hk-bl-add">+</button>
	</div>
	<div id="hk-bl-list"></div>
	<div id="hk-bl-empty" class="hk-bl-empty">Список пуст</div>
</div>
 
<div class="hk-pane" data-t="3">
	<div id="hk-tp-warn" class="hk-warn-2pa" style="display:none;">Доступно только в Two Player!</div>
	<div id="hk-tp-content">
		<div class="hk-tp-info">Цель: <span class="hk-tp-target" id="hk-tp-target-name">Никто</span></div>
		<div style="color:#777;text-align:center;margin-bottom:8px;font-size:10px;">(Кликните по игроку в Kick меню)</div>
		<button class="hk-btn" id="hk-tp-btn">Включить следование</button>
	</div>
</div>
 
<div class="hk-pane" data-t="4">
	<div id="hk-wg-warn" class="hk-warn-2pa" style="display:none;">Доступно только в Two Player!</div>
	<div id="hk-wg-content">
		<button class="hk-btn" id="hk-wg-refresh" style="margin-bottom:8px;margin-top:0;">Обновить список</button>
		<div class="hk-wg-cols" id="hk-wg-cols">
			<div class="hk-wg-col" id="hk-wg-cps"><div class="hk-wg-col-title">Чекпоинты</div><div class="hk-wg-list"></div></div>
			<div class="hk-wg-col" id="hk-wg-btns"><div class="hk-wg-col-title">Кнопки</div><div class="hk-wg-list"></div></div>
			<div class="hk-wg-col" id="hk-wg-lvrs"><div class="hk-wg-col-title">Рычаги</div><div class="hk-wg-list"></div></div>
		</div>
		<hr class="hk-sep" id="hk-wg-sep">
		<div style="display:flex;gap:6px;" id="hk-wg-bottom-btns">
			<button class="hk-btn" id="hk-wg-spawn" style="margin-top:0;">ТП Спавн</button>
			<button class="hk-btn" id="hk-wg-door" style="margin-top:0;">ТП Дверь</button>
		</div>
	</div>
</div>
 
<div class="hk-foot">
	<div class="hk-ind-row">
		<span class="hk-ind-dot" id="hk-d-nc"></span><span class="hk-ind-dot" id="hk-d-im"></span>
		<span class="hk-ind-dot" id="hk-d-gn"></span><span class="hk-ind-gap"></span>
		<span class="hk-ind-dot" id="hk-d-lm"></span>
	</div>
	<button class="hk-btn hk-reset" id="hk-reset">Сброс настроек</button>
</div>`;
 
		document.body.appendChild(panel);
		const $ = id => panel.querySelector('#' + id);
 
		// Блокировка вкладок ТП вне 2па
		if (!isTwoPlayer) {
			$('hk-tp-warn').style.display = 'block';
			$('hk-tp-content').style.display = 'none';
			$('hk-wg-warn').style.display = 'block';
			$('hk-wg-content').style.display = 'none';
		}
 
		// ─── Widgets Tab Logic ───
		let lastMapRef = null;
 
		function refreshWidgets() {
			if (!isTwoPlayer || !hack.gp || !hack.gp.list) return;
 
			const cpsList = panel.querySelector('#hk-wg-cps .hk-wg-list');
			const btnsList = panel.querySelector('#hk-wg-btns .hk-wg-list');
			const lvrsList = panel.querySelector('#hk-wg-lvrs .hk-wg-list');
			cpsList.innerHTML = ''; btnsList.innerHTML = ''; lvrsList.innerHTML = '';
			
			let cpCount = 1;
			let sObj = null, dObj = null;
			const extractedObjects =[];
 
			const getPrecisePos = (item) => {
				let worldX = typeof item.getX === 'function' ? item.getX() : item.x;
				let worldY = typeof item.getY === 'function' ? item.getY() : item.y;
				if (item.targetShape) {
					worldX += (item.targetShape.x || 0);
					worldY += (item.targetShape.y || 0);
				}
				return { x: worldX, y: worldY };
			};
 
			hack.gp.list.forEach(obj => {
				if (!obj) return;
				let funcId = obj.id || "";
				let lowerId = funcId.toLowerCase();
				let foundShape = null;
 
				if (!lowerId.includes('spawn') && !lowerId.includes('door') && !lowerId.includes('checkpoint') && !lowerId.startsWith('button:') && !lowerId.startsWith('leaver:')) {
					if (obj.shapes && Array.isArray(obj.shapes)) {
						for (const sh of obj.shapes) {
							if (sh && sh.id) {
								const sid = sh.id.toLowerCase();
								if (sid.includes('checkpoint') || sid.startsWith('button:') || sid.startsWith('leaver:')) {
									funcId = sh.id; lowerId = sid; foundShape = sh; break;
								}
							}
						}
					}
				} else {
					foundShape = obj.shapes?.find(sh => sh && sh.id && sh.id.toLowerCase() === lowerId);
				}
 
				if (!funcId) return;
				obj.funcId = funcId; obj._lowerId = lowerId; obj.targetShape = foundShape;
 
				if (lowerId.includes('spawn')) {
					if (!sObj) sObj = obj;
				} else if (lowerId.includes('door')) {
					if (!dObj) dObj = obj;
				} else {
					extractedObjects.push(obj);
				}
			});
 
			if (sObj) {
				const sPos = getPrecisePos(sObj);
				extractedObjects.sort((a, b) => {
					const pA = getPrecisePos(a), pB = getPrecisePos(b);
					return Math.hypot(pA.x - sPos.x, pA.y - sPos.y) - Math.hypot(pB.x - sPos.x, pB.y - sPos.y);
				});
			}
 
			const makeBtn = (text, item, parent) => {
				const btn = document.createElement('button');
				btn.className = 'hk-wg-btn';
				btn.title = item.funcId || text; 
				
				let fullText = text;
				if (item._lowerId.startsWith('button:') || item._lowerId.startsWith('leaver:')) {
					const count = text.split(',').filter(x => x.trim()).length;
					fullText = `[${count}] ${text}`;
				}
				
				const span = document.createElement('span');
				span.className = 'hk-wg-btn-text';
				span.textContent = fullText;
				btn.appendChild(span);
				parent.appendChild(btn);

				// РЕАЛЬНОЕ ИЗМЕРЕНИЕ ШИРИНЫ ПОСЛЕ ДОБАВЛЕНИЯ В DOM
				requestAnimationFrame(() => {
					const btnWidth = btn.clientWidth - 10; // 10px это суммарный паддинг (5+5)
					const textWidth = span.scrollWidth;

					if (textWidth > btnWidth) {
						// Переключаем центровку на прилипание к левому краю для начала анимации
						btn.style.justifyContent = 'flex-start';
						span.style.margin = '0';
						
						const dist = btnWidth - textWidth;
						const time = Math.max(3, Math.abs(dist) / 20) + "s"; // 20px в сек
						
						span.style.setProperty('--scroll-dist', dist + 'px');
						span.style.setProperty('--scroll-time', time);
						span.classList.add('hk-wg-marquee');
					}
				});
				
				btn.onclick = () => { 
					const pos = getPrecisePos(item);
					if (hack.funcs?.handlers) hack.funcs.handlers.tpPlayer(pos.x, pos.y); 
				};
			};
 
			extractedObjects.forEach(obj => {
				if (obj._lowerId.includes('checkpoint')) makeBtn(`CP ${cpCount++}`, obj, cpsList);
				else if (obj._lowerId.startsWith('button:')) {
					const t = obj.funcId.substring(7); if (t) makeBtn(t, obj, btnsList);
				}
				else if (obj._lowerId.startsWith('leaver:')) {
					const t = obj.funcId.substring(7); if (t) makeBtn(t, obj, lvrsList);
				}
			});
 
			const btnSp = $('hk-wg-spawn'), btnDr = $('hk-wg-door');
			btnSp.onclick = () => { if (sObj) { const p = getPrecisePos(sObj); hack.funcs.handlers.tpPlayer(p.x, p.y); } };
			btnSp.disabled = !sObj;
			btnDr.onclick = () => { if (dObj) { const p = getPrecisePos(dObj); hack.funcs.handlers.tpPlayer(p.x, p.y); } };
			btnDr.disabled = !dObj;
		}
		$('hk-wg-refresh').addEventListener('click', refreshWidgets);
		hack._refreshWidgets = refreshWidgets;
 
		panel.querySelectorAll('.hk-tab').forEach(btn => {
			btn.addEventListener('click', () => {
				panel.querySelectorAll('.hk-tab').forEach(b => b.classList.remove('active'));
				panel.querySelectorAll('.hk-pane').forEach(p => p.classList.remove('active'));
				btn.classList.add('active');
				panel.querySelector(`.hk-pane[data-t="${btn.dataset.t}"]`).classList.add('active');
				if (btn.dataset.t === "4") {
					lastMapRef = window.mapData;
					refreshWidgets();
				}
			});
		});
 
		setInterval(() => {
			const isTabActive = panel.querySelector('.hk-tab[data-t="4"]').classList.contains('active');
			if (isTabActive && window.mapData !== lastMapRef) {
				lastMapRef = window.mapData;
				setTimeout(refreshWidgets, 100);
			}
		}, 1000);
 
		panel.addEventListener('keydown', e => e.stopPropagation());
		panel.addEventListener('keyup', e => e.stopPropagation());
 
		const readInput = (el, min, max) => round3(clamp(parseFloat(el.value) || 0, min, max));
 
		function populateFields() {
			$('hk-mult').value = hack.vars.mult.uiValue;
			$('hk-gs').value   = hack.vars.gravNoclipGravScale;
			$('hk-jh').value   = hack.vars.jumpHeight;
			$('hk-mass').value = hack.vars.playerMass;
			$('hk-damp').value = hack.vars.playerDamping;
			$('hk-aa').value   = hack.vars.layoutAlpha.collision;
			$('hk-ac').value   = hack.vars.layoutAlpha.collision;
			$('hk-ap').value   = hack.vars.layoutAlpha.poison;
			$('hk-af').value   = hack.vars.layoutAlpha.functional;
		}
		populateFields();
 
		const bindField = (id, min, max, setter) => {
			$(id).addEventListener('change', function () {
				const v = readInput(this, min, max); this.value = v; setter(v); saveSettings();
			});
		};
		bindField('hk-mult', -9, 9, v => { hack.vars.mult.uiValue = v; if (hack.vars.mult.enabled) hack.vars.mult.value = v; });
		bindField('hk-gs', -10, 10, v => { hack.vars.gravNoclipGravScale = v; });
		bindField('hk-jh', 0.2, 50, v => { hack.vars.jumpHeight = v; });
		bindField('hk-mass', -10, 10, v => { hack.vars.playerMass = v; });
		bindField('hk-damp', 0, 1, v => { hack.vars.playerDamping = v; });
 
		$('hk-aa').addEventListener('change', function () {
			const v = readInput(this, 0, 1); this.value = v;
			$('hk-ac').value = v; $('hk-ap').value = v; $('hk-af').value = v;
			hack.vars.layoutAlpha.collision = hack.vars.layoutAlpha.poison = hack.vars.layoutAlpha.functional = v;
			saveSettings();
		});
		bindField('hk-ac', 0, 1, v => { hack.vars.layoutAlpha.collision = v; });
		bindField('hk-ap', 0, 1, v => { hack.vars.layoutAlpha.poison = v; });
		bindField('hk-af', 0, 1, v => { hack.vars.layoutAlpha.functional = v; });
 
		$('hk-al-apply').addEventListener('click', () => {
			if (hack.vars.layoutMode) {
				const lp = hack.getLP();
				if (lp) hack.vars.layoutSavedPos = { x: lp.getX(), y: lp.getY() };
				if (hack.reloadMap()) hack.restoreAfterReload();
			}
		});
 
		const blList = $('hk-bl-list'), blEmpty = $('hk-bl-empty'), blInput = $('hk-bl-input');
		function refreshBL() {
			const names = hack.vars.blacklisted.names;
			blList.innerHTML = ''; blEmpty.style.display = names.length ? 'none' : 'block';
			for (const name of names) {
				const online = hack.isPlayerOnline(name);
				const item = document.createElement('div'); item.className = 'hk-bl-item';
				const dot = document.createElement('span'); dot.className = 'hk-bl-dot' + (online ? ' hk-on' : '');
				const nm = document.createElement('span'); nm.className = 'hk-bl-name'; nm.textContent = name;
				const x = document.createElement('button'); x.className = 'hk-bl-x'; x.textContent = '×';
				x.addEventListener('click', () => hack.funcs.handlers.blacklistRemove(name));
				item.append(dot, nm, x); blList.appendChild(item);
			}
		}
		hack._refreshBL = refreshBL; refreshBL(); setInterval(refreshBL, 2000);
 
		function addFromInput() {
			const n = blInput.value.trim(); if (!n) return;
			hack.funcs.handlers.blacklistAdd(n); blInput.value = '';
		}
		$('hk-bl-add').addEventListener('click', addFromInput);
		blInput.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); addFromInput(); } });
 
		function refreshTP() {
			if (!hack.vars) return;
			const targetEl = document.getElementById('hk-tp-target-name');
			if (!targetEl) return;
			if (hack.vars.tpTargetIndex !== null) {
				const p = hack.mode?.otherPlayers?.[hack.vars.tpTargetIndex];
				const isVisible = !!(p && p.myName === hack.vars.tpTargetName && p.gpData && p.reset <= 10);
				targetEl.textContent = hack.vars.tpTargetName + (isVisible ? "" : " (Вне зоны)");
				targetEl.style.color = isVisible ? "#0f0" : "#ffaa00";
			} else {
				targetEl.textContent = "Никто";
				targetEl.style.color = "#777";
			}
			const btn = document.getElementById('hk-tp-btn');
			if (btn) {
				btn.textContent = hack.vars.tpToPlayerEnabled ? 'Выключить следование' : 'Включить следование';
				btn.style.background = hack.vars.tpToPlayerEnabled ? '#533' : '#333';
			}
		}
		hack._refreshTP = refreshTP;
		setInterval(refreshTP, 500);
 
		$('hk-tp-btn').addEventListener('click', () => {
			if (!isTwoPlayer) return;
			hack.vars.tpToPlayerEnabled = !hack.vars.tpToPlayerEnabled;
			refreshTP();
		});
 
		$('hk-reset').addEventListener('click', () => {
			hack.vars.mult.uiValue = DEFAULTS.mult;
			if (hack.vars.mult.enabled) hack.vars.mult.value = DEFAULTS.mult;
			hack.vars.gravNoclipGravScale = DEFAULTS.gravScale;
			hack.vars.jumpHeight = DEFAULTS.jumpHeight;
			hack.vars.playerMass = DEFAULTS.mass;
			hack.vars.playerDamping = DEFAULTS.damping;
			hack.vars.layoutAlpha.collision = DEFAULTS.alphaCollision;
			hack.vars.layoutAlpha.poison = DEFAULTS.alphaPoison;
			hack.vars.layoutAlpha.functional = DEFAULTS.alphaFunctional;
			populateFields(); saveSettings();
		});
 
		document.addEventListener('keydown', e => {
			if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); panel.style.display = panel.style.display === 'none' ? '' : 'none'; }
		}, true);
	};
})();