Kxs Client - Survev.io Client

A client to enhance the survev.io in-game experience with many features, as well as future features.

当前为 2025-03-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Kxs Client - Survev.io Client
  3. // @namespace https://github.com/Kisakay/KxsClient
  4. // @version 1.1.10
  5. // @description A client to enhance the survev.io in-game experience with many features, as well as future features.
  6. // @author Kisakay x SoyAlguien
  7. // @license AGPL-3.0
  8. // @run-at document-end
  9. // @icon https://kxs.rip/assets/KysClientLogo.png
  10. // @match *://survev.io/*
  11. // @match *://66.179.254.36/*
  12. // @match *://expandedwater.online/*
  13. // @match *://localhost:3000/*
  14. // @match *://surviv.wf/*
  15. // @match *://resurviv.biz/*
  16. // @match *://82.67.125.203/*
  17. // @match *://leia-uwu.github.io/survev/*
  18. // @match *://50v50.online/*
  19. // @match *://eu-comp.net/*
  20. // @match *://survev.leia-is.gay/*
  21. // @grant GM_xmlhttpRequest
  22. // @grant GM_info
  23. // @grant GM.getValue
  24. // @grant GM.setValue
  25. // ==/UserScript==
  26. ;
  27. /******/ (() => { // webpackBootstrap
  28. /******/ "use strict";
  29. /******/ var __webpack_modules__ = ({
  30.  
  31. /***/ 272:
  32. /***/ ((module) => {
  33.  
  34. module.exports = /*#__PURE__*/JSON.parse('{"base_url":"https://kxs.rip","fileName":"KxsClient.user.js","match":["*://survev.io/*","*://66.179.254.36/*","*://expandedwater.online/*","*://localhost:3000/*","*://surviv.wf/*","*://resurviv.biz/*","*://82.67.125.203/*","*://leia-uwu.github.io/survev/*","*://50v50.online/*","*://eu-comp.net/*","*://survev.leia-is.gay/*"],"grant":["GM_xmlhttpRequest","GM_info","GM.getValue","GM.setValue"]}');
  35.  
  36. /***/ }),
  37.  
  38. /***/ 330:
  39. /***/ ((module) => {
  40.  
  41. module.exports = /*#__PURE__*/JSON.parse('{"name":"kxsclient","version":"1.1.10","main":"index.js","namespace":"https://github.com/Kisakay/KxsClient","icon":"https://kxs.rip/assets/KysClientLogo.png","placeholder":"Kxs Client - Survev.io Client","scripts":{"test":"echo \\"Error: no test specified\\" && exit 1","commits":"oco --yes; npm version patch; git push;"},"keywords":[],"author":"Kisakay x SoyAlguien","license":"AGPL-3.0","description":"A client to enhance the survev.io in-game experience with many features, as well as future features.","devDependencies":{"@types/tampermonkey":"^5.0.4","ts-loader":"^9.5.1","typescript":"^5.7.2","webpack":"^5.97.1","webpack-cli":"^5.1.4"}}');
  42.  
  43. /***/ })
  44.  
  45. /******/ });
  46. /************************************************************************/
  47. /******/ // The module cache
  48. /******/ var __webpack_module_cache__ = {};
  49. /******/
  50. /******/ // The require function
  51. /******/ function __webpack_require__(moduleId) {
  52. /******/ // Check if module is in cache
  53. /******/ var cachedModule = __webpack_module_cache__[moduleId];
  54. /******/ if (cachedModule !== undefined) {
  55. /******/ return cachedModule.exports;
  56. /******/ }
  57. /******/ // Create a new module (and put it into the cache)
  58. /******/ var module = __webpack_module_cache__[moduleId] = {
  59. /******/ // no module.id needed
  60. /******/ // no module.loaded needed
  61. /******/ exports: {}
  62. /******/ };
  63. /******/
  64. /******/ // Execute the module function
  65. /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  66. /******/
  67. /******/ // Return the exports of the module
  68. /******/ return module.exports;
  69. /******/ }
  70. /******/
  71. /************************************************************************/
  72. /******/ /* webpack/runtime/define property getters */
  73. /******/ (() => {
  74. /******/ // define getter functions for harmony exports
  75. /******/ __webpack_require__.d = (exports, definition) => {
  76. /******/ for(var key in definition) {
  77. /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
  78. /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
  79. /******/ }
  80. /******/ }
  81. /******/ };
  82. /******/ })();
  83. /******/
  84. /******/ /* webpack/runtime/hasOwnProperty shorthand */
  85. /******/ (() => {
  86. /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
  87. /******/ })();
  88. /******/
  89. /************************************************************************/
  90. var __webpack_exports__ = {};
  91.  
  92. // EXPORTS
  93. __webpack_require__.d(__webpack_exports__, {
  94. f: () => (/* binding */ kxs_logo)
  95. });
  96.  
  97. // UNUSED EXPORTS: background_song
  98.  
  99. ;// ./src/ButtonManager.ts
  100. class MenuButton {
  101. constructor(params) {
  102. var _a;
  103. this.isEnabled = (_a = params.initialState) !== null && _a !== void 0 ? _a : false;
  104. this.button = this.createButton(params);
  105. }
  106. createButton(params) {
  107. const button = document.createElement("button");
  108. // Set initial text
  109. this.updateButtonText(button, params.text);
  110. // Set styles
  111. Object.assign(button.style, {
  112. backgroundColor: this.getBackgroundColor(params.color),
  113. border: "none",
  114. color: "#fff",
  115. padding: "10px",
  116. borderRadius: "5px",
  117. width: "100%",
  118. marginBottom: "10px",
  119. fontSize: "14px",
  120. cursor: "pointer",
  121. });
  122. // Set click handler
  123. button.onclick = () => {
  124. this.isEnabled = !this.isEnabled;
  125. params.onClick();
  126. if (params.updateText !== false) {
  127. this.updateButtonText(button, params.text);
  128. this.updateButtonColor(button, params.color);
  129. }
  130. };
  131. return button;
  132. }
  133. updateButtonText(button, baseText) {
  134. button.textContent = `${baseText} ${this.isEnabled ? "✅" : "❌"}`;
  135. }
  136. getBackgroundColor(color) {
  137. if (color)
  138. return color;
  139. return this.isEnabled ? "#4CAF50" : "#FF0000";
  140. }
  141. updateButtonColor(button, color) {
  142. button.style.backgroundColor = this.getBackgroundColor(color);
  143. }
  144. getElement() {
  145. return this.button;
  146. }
  147. setState(enabled) {
  148. this.isEnabled = enabled;
  149. this.updateButtonColor(this.button);
  150. }
  151. }
  152. class MenuManager {
  153. constructor(menu) {
  154. this.buttons = {};
  155. this.menu = menu;
  156. }
  157. addToggleButton(params) {
  158. const button = new MenuButton({
  159. text: params.text,
  160. initialState: params.initialState,
  161. color: params.color,
  162. onClick: params.onClick,
  163. updateText: params.updateText,
  164. });
  165. this.buttons[params.id] = button;
  166. this.menu.appendChild(button.getElement());
  167. return button;
  168. }
  169. addButton(params) {
  170. var _a;
  171. const button = document.createElement("button");
  172. // Set initial text
  173. button.textContent = params.text;
  174. // Set styles
  175. Object.assign(button.style, {
  176. backgroundColor: (_a = params.color) !== null && _a !== void 0 ? _a : "#007BFF", // Default color
  177. border: "none",
  178. color: "#fff",
  179. padding: "10px",
  180. borderRadius: "5px",
  181. width: "100%",
  182. marginBottom: "10px",
  183. fontSize: "14px",
  184. cursor: "pointer",
  185. });
  186. // Set click handler
  187. button.onclick = params.onClick;
  188. this.menu.appendChild(button);
  189. }
  190. getButton(id) {
  191. return this.buttons[id];
  192. }
  193. }
  194.  
  195.  
  196. ;// ./src/DiscordTracking.ts
  197. var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  198. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  199. return new (P || (P = Promise))(function (resolve, reject) {
  200. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  201. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  202. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  203. step((generator = generator.apply(thisArg, _arguments || [])).next());
  204. });
  205. };
  206. const stuff_emojis = {
  207. main_weapon: "🔫",
  208. secondary_weapon: "🔫",
  209. grenades: "💣",
  210. melees: "🔪",
  211. soda: "🥤",
  212. medkit: "🩹",
  213. bandage: "🩹",
  214. pills: "💊",
  215. backpack: "🎒",
  216. chest: "📦",
  217. helmet: "⛑️"
  218. };
  219. class WebhookValidator {
  220. static isValidWebhookUrl(url) {
  221. return this.DISCORD_WEBHOOK_REGEX.test(url);
  222. }
  223. static isWebhookAlive(webhookUrl) {
  224. return __awaiter(this, void 0, void 0, function* () {
  225. try {
  226. // First check if the URL format is valid
  227. if (!this.isValidWebhookUrl(webhookUrl)) {
  228. throw new Error("Invalid webhook URL format");
  229. }
  230. // Test the webhook with a GET request (Discord allows GET on webhooks)
  231. const response = yield fetch(webhookUrl, {
  232. method: "GET",
  233. headers: {
  234. "Content-Type": "application/json",
  235. },
  236. });
  237. // Discord returns 200 for valid webhooks
  238. return response.status === 200;
  239. }
  240. catch (error) {
  241. console.error("Error validating webhook:", error);
  242. return false;
  243. }
  244. });
  245. }
  246. static testWebhook(webhookUrl) {
  247. return __awaiter(this, void 0, void 0, function* () {
  248. try {
  249. if (!webhookUrl) {
  250. return {
  251. isValid: false,
  252. message: "Please enter a webhook URL",
  253. };
  254. }
  255. if (!this.isValidWebhookUrl(webhookUrl)) {
  256. return {
  257. isValid: false,
  258. message: "Invalid Discord webhook URL format",
  259. };
  260. }
  261. const isAlive = yield this.isWebhookAlive(webhookUrl);
  262. return {
  263. isValid: isAlive,
  264. message: isAlive
  265. ? "Webhook is valid and working!"
  266. : "Webhook is not responding or has been deleted",
  267. };
  268. }
  269. catch (error) {
  270. return {
  271. isValid: false,
  272. message: "Error testing webhook connection",
  273. };
  274. }
  275. });
  276. }
  277. }
  278. WebhookValidator.DISCORD_WEBHOOK_REGEX = /^https:\/\/discord\.com\/api\/webhooks\/\d+\/[\w-]+$/;
  279. class DiscordTracking {
  280. constructor(kxsClient, webhookUrl) {
  281. this.kxsClient = kxsClient;
  282. this.webhookUrl = webhookUrl;
  283. }
  284. setWebhookUrl(webhookUrl) {
  285. this.webhookUrl = webhookUrl;
  286. }
  287. validateCurrentWebhook() {
  288. return __awaiter(this, void 0, void 0, function* () {
  289. return WebhookValidator.isWebhookAlive(this.webhookUrl);
  290. });
  291. }
  292. sendWebhookMessage(message) {
  293. return __awaiter(this, void 0, void 0, function* () {
  294. if (!WebhookValidator.isValidWebhookUrl(this.webhookUrl)) {
  295. return;
  296. }
  297. this.kxsClient.nm.showNotification("Sending Discord message...", "info", 2300);
  298. try {
  299. const response = yield fetch(this.webhookUrl, {
  300. method: "POST",
  301. headers: {
  302. "Content-Type": "application/json",
  303. },
  304. body: JSON.stringify(message),
  305. });
  306. if (!response.ok) {
  307. throw new Error(`Discord Webhook Error: ${response.status}`);
  308. }
  309. }
  310. catch (error) {
  311. console.error("Error sending Discord message:", error);
  312. }
  313. });
  314. }
  315. getEmbedColor(isWin) {
  316. return isWin ? 0x2ecc71 : 0xe74c3c; // Green for victory, red for defeat
  317. }
  318. trackGameEnd(result) {
  319. return __awaiter(this, void 0, void 0, function* () {
  320. const title = result.isWin
  321. ? "🏆 VICTORY ROYALE!"
  322. : `${result.position} - Game Over`;
  323. const embed = {
  324. title,
  325. description: `${result.username}'s Match`,
  326. color: this.getEmbedColor(result.isWin),
  327. fields: [
  328. {
  329. name: "💀 Eliminations",
  330. value: result.kills.toString(),
  331. inline: true,
  332. },
  333. ],
  334. };
  335. if (result.duration) {
  336. embed.fields.push({
  337. name: "⏱️ Duration",
  338. value: result.duration,
  339. inline: true,
  340. });
  341. }
  342. if (result.damageDealt) {
  343. embed.fields.push({
  344. name: "💥 Damage Dealt",
  345. value: Math.round(result.damageDealt).toString(),
  346. inline: true,
  347. });
  348. }
  349. if (result.damageTaken) {
  350. embed.fields.push({
  351. name: "💢 Damage Taken",
  352. value: Math.round(result.damageTaken).toString(),
  353. inline: true,
  354. });
  355. }
  356. if (result.username) {
  357. embed.fields.push({
  358. name: "📝 Username",
  359. value: result.username,
  360. inline: true,
  361. });
  362. }
  363. if (result.stuff) {
  364. for (const [key, value] of Object.entries(result.stuff)) {
  365. if (value) {
  366. embed.fields.push({
  367. name: `${stuff_emojis[key]} ${key.replace("_", " ").toUpperCase()}`,
  368. value,
  369. inline: true,
  370. });
  371. }
  372. }
  373. }
  374. const message = {
  375. username: result.username,
  376. content: result.isWin ? "🎉 New Victory!" : "Match Ended",
  377. embeds: [embed],
  378. };
  379. yield this.sendWebhookMessage(message);
  380. });
  381. }
  382. }
  383.  
  384.  
  385. ;// ./src/ClientMainMenu.ts
  386. var ClientMainMenu_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  387. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  388. return new (P || (P = Promise))(function (resolve, reject) {
  389. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  390. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  391. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  392. step((generator = generator.apply(thisArg, _arguments || [])).next());
  393. });
  394. };
  395.  
  396.  
  397. class KxsMainClientMenu {
  398. constructor(kxsClient) {
  399. this.kxsClient = kxsClient;
  400. this.menu = document.createElement("div");
  401. this.setupKeyListeners();
  402. // this.initMenu();
  403. }
  404. initMenu() {
  405. this.menu.id = "kxsMenu";
  406. Object.assign(this.menu.style, {
  407. backgroundColor: "rgba(0, 0, 0, 0.8)",
  408. padding: "15px",
  409. marginLeft: "15px",
  410. borderRadius: "10px",
  411. boxShadow: "0 4px 10px rgba(0, 0, 0, 0.6)",
  412. zIndex: "10001",
  413. width: "250px",
  414. fontFamily: "Arial, sans-serif",
  415. color: "#fff",
  416. maxHeight: "400px",
  417. overflowY: "auto",
  418. });
  419. const title = document.createElement("h2");
  420. title.textContent = "Kxs Client";
  421. title.style.margin = "0 0 10px";
  422. title.style.textAlign = "center";
  423. title.style.fontSize = "18px";
  424. title.style.color = "#FFAE00";
  425. this.menu.appendChild(title);
  426. window.onload = () => {
  427. const savedBackground = localStorage.getItem("backgroundImage");
  428. if (savedBackground) {
  429. const backgroundElement = document.getElementById("background");
  430. if (backgroundElement) {
  431. backgroundElement.style.backgroundImage = `url(${savedBackground})`;
  432. }
  433. }
  434. };
  435. const startRowTop = document.getElementById("start-row-top");
  436. if (startRowTop) {
  437. startRowTop.appendChild(this.menu);
  438. }
  439. this.menuManager = new MenuManager(this.menu);
  440. this.menuManager.addToggleButton({
  441. id: "fps",
  442. text: "Show FPS",
  443. initialState: this.kxsClient.isFpsVisible,
  444. onClick: () => {
  445. this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible;
  446. this.kxsClient.updateFpsVisibility();
  447. this.kxsClient.updateLocalStorage();
  448. },
  449. });
  450. this.menuManager.addToggleButton({
  451. id: "ping",
  452. text: `Show Ping`,
  453. initialState: this.kxsClient.isPingVisible,
  454. onClick: () => {
  455. this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible;
  456. this.kxsClient.updatePingVisibility();
  457. this.kxsClient.updateLocalStorage();
  458. },
  459. });
  460. this.menuManager.addToggleButton({
  461. id: "kills",
  462. text: `Show Kills`,
  463. initialState: this.kxsClient.isKillsVisible,
  464. onClick: () => {
  465. this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible;
  466. this.kxsClient.updateKillsVisibility();
  467. this.kxsClient.updateLocalStorage();
  468. },
  469. });
  470. this.menuManager.addToggleButton({
  471. id: "uncapFps",
  472. text: `Uncap FPS`,
  473. initialState: this.kxsClient.isFpsUncapped,
  474. onClick: () => {
  475. this.kxsClient.updateLocalStorage();
  476. this.kxsClient.toggleFpsUncap();
  477. },
  478. });
  479. this.menuManager.addButton({
  480. id: "hideShow",
  481. text: "👀 Hide/Show Menu [P]",
  482. color: "#6F42C1",
  483. onClick: () => this.toggleMenuVisibility(),
  484. });
  485. this.menuManager.addButton({
  486. id: "background",
  487. text: `🎨 Change Background`,
  488. color: "#007BFF",
  489. onClick: () => {
  490. const backgroundElement = document.getElementById("background");
  491. if (!backgroundElement) {
  492. alert("Element with id 'background' not found.");
  493. return;
  494. }
  495. const choice = prompt("Enter '1' to provide a URL or '2' to upload a local image:");
  496. if (choice === "1") {
  497. const newBackgroundUrl = prompt("Enter the URL of the new background image:");
  498. if (newBackgroundUrl) {
  499. backgroundElement.style.backgroundImage = `url(${newBackgroundUrl})`;
  500. this.kxsClient.saveBackgroundToLocalStorage(newBackgroundUrl);
  501. alert("Background updated successfully!");
  502. }
  503. }
  504. else if (choice === "2") {
  505. const fileInput = document.createElement("input");
  506. fileInput.type = "file";
  507. fileInput.accept = "image/*";
  508. fileInput.onchange = (event) => {
  509. var _a, _b;
  510. const file = (_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0];
  511. if (file) {
  512. const reader = new FileReader();
  513. reader.onload = () => {
  514. backgroundElement.style.backgroundImage = `url(${reader.result})`;
  515. this.kxsClient.saveBackgroundToLocalStorage(file);
  516. alert("Background updated successfully!");
  517. };
  518. reader.readAsDataURL(file);
  519. }
  520. };
  521. fileInput.click();
  522. }
  523. },
  524. });
  525. this.menuManager.addButton({
  526. id: "webhook",
  527. text: `🕸️ Change Discord Webhook`,
  528. color: "#007BFF",
  529. onClick: () => ClientMainMenu_awaiter(this, void 0, void 0, function* () {
  530. const choice = prompt("enter the new discord webhook url:");
  531. if (choice) {
  532. const result = yield WebhookValidator.testWebhook(choice);
  533. if (result.isValid) {
  534. this.kxsClient.discordWebhookUrl = choice;
  535. this.kxsClient.discordTracker.setWebhookUrl(choice);
  536. this.kxsClient.updateLocalStorage();
  537. alert(result.message);
  538. }
  539. else {
  540. alert(result.message);
  541. }
  542. }
  543. }),
  544. });
  545. }
  546. setupKeyListeners() {
  547. document.addEventListener("keydown", (event) => {
  548. if (event.key.toLowerCase() === "p") {
  549. this.toggleMenuVisibility();
  550. }
  551. });
  552. }
  553. toggleMenuVisibility() {
  554. var _a;
  555. const isVisible = ((_a = this.menu) === null || _a === void 0 ? void 0 : _a.style.display) !== "none";
  556. this.menu.style.display = isVisible ? "none" : "block";
  557. }
  558. }
  559.  
  560.  
  561. ;// ./src/Ping.ts
  562. class PingTest {
  563. constructor(selectedServer) {
  564. this.ptcDataBuf = new ArrayBuffer(1);
  565. this.test = {
  566. region: selectedServer.region,
  567. url: `wss://${selectedServer.url}/ptc`,
  568. ping: 0, // Initialize to 0 instead of 9999
  569. ws: null,
  570. sendTime: 0,
  571. retryCount: 0,
  572. isConnecting: false,
  573. };
  574. }
  575. startPingTest() {
  576. if (this.test.isConnecting)
  577. return; // Prevent multiple connection attempts
  578. if (this.test.ws)
  579. return; // Don't create new connection if one exists
  580. this.test.isConnecting = true;
  581. try {
  582. const ws = new WebSocket(this.test.url);
  583. ws.binaryType = "arraybuffer";
  584. ws.onopen = () => {
  585. this.test.ws = ws;
  586. this.test.isConnecting = false;
  587. this.test.retryCount = 0;
  588. this.sendPing();
  589. };
  590. ws.onmessage = (event) => {
  591. if (this.test.sendTime === 0)
  592. return;
  593. const elapsed = Date.now() - this.test.sendTime;
  594. this.test.ping = Math.min(Math.round(elapsed), 999); // Cap at 999ms
  595. // Schedule next ping
  596. setTimeout(() => this.sendPing(), 250);
  597. };
  598. ws.onerror = (error) => {
  599. console.error("WebSocket error:", error);
  600. this.handleConnectionError();
  601. };
  602. ws.onclose = () => {
  603. this.test.ws = null;
  604. this.test.isConnecting = false;
  605. if (this.test.retryCount < 3) {
  606. setTimeout(() => this.startPingTest(), 1000);
  607. }
  608. };
  609. }
  610. catch (error) {
  611. console.error("Failed to create WebSocket:", error);
  612. this.handleConnectionError();
  613. }
  614. }
  615. handleConnectionError() {
  616. this.test.ping = 0;
  617. this.test.isConnecting = false;
  618. this.test.retryCount++;
  619. if (this.test.ws) {
  620. this.test.ws.close();
  621. this.test.ws = null;
  622. }
  623. if (this.test.retryCount < 3) {
  624. setTimeout(() => this.startPingTest(), 1000);
  625. }
  626. }
  627. sendPing() {
  628. if (!this.test.ws || this.test.ws.readyState !== WebSocket.OPEN) {
  629. this.handleConnectionError();
  630. return;
  631. }
  632. try {
  633. this.test.sendTime = Date.now();
  634. this.test.ws.send(this.ptcDataBuf);
  635. }
  636. catch (error) {
  637. console.error("Failed to send ping:", error);
  638. this.handleConnectionError();
  639. }
  640. }
  641. getPingResult() {
  642. return {
  643. region: this.test.region,
  644. ping: this.test.ping || 0,
  645. };
  646. }
  647. }
  648. class PingManager {
  649. constructor() {
  650. this.currentServer = null;
  651. this.pingTest = null;
  652. }
  653. startPingTest() {
  654. const currentUrl = window.location.href;
  655. const isSpecialUrl = /\/#\w+/.test(currentUrl);
  656. const teamSelectElement = document.getElementById("team-server-select");
  657. const mainSelectElement = document.getElementById("server-select-main");
  658. const region = isSpecialUrl && teamSelectElement
  659. ? teamSelectElement.value
  660. : mainSelectElement
  661. ? mainSelectElement.value
  662. : null;
  663. if (!region || region === this.currentServer)
  664. return;
  665. this.currentServer = region;
  666. this.resetPing();
  667. const servers = [
  668. { region: "NA", url: "usr.mathsiscoolfun.com:8001" },
  669. { region: "EU", url: "eur.mathsiscoolfun.com:8001" },
  670. { region: "Asia", url: "asr.mathsiscoolfun.com:8001" },
  671. { region: "SA", url: "sa.mathsiscoolfun.com:8001" },
  672. ];
  673. const selectedServer = servers.find((server) => region.toUpperCase() === server.region.toUpperCase());
  674. if (selectedServer) {
  675. this.pingTest = new PingTest(selectedServer);
  676. this.pingTest.startPingTest();
  677. }
  678. }
  679. resetPing() {
  680. var _a;
  681. if ((_a = this.pingTest) === null || _a === void 0 ? void 0 : _a.test.ws) {
  682. this.pingTest.test.ws.close();
  683. this.pingTest.test.ws = null;
  684. }
  685. this.pingTest = null;
  686. }
  687. getPingResult() {
  688. var _a;
  689. return ((_a = this.pingTest) === null || _a === void 0 ? void 0 : _a.getPingResult()) || { region: "", ping: 0 };
  690. }
  691. }
  692.  
  693.  
  694. ;// ./src/ClientHUD.ts
  695.  
  696. class KxsClientHUD {
  697. constructor(kxsClient) {
  698. this.healthAnimations = [];
  699. this.lastHealthValue = 100;
  700. this.kxsClient = kxsClient;
  701. this.frameCount = 0;
  702. this.fps = 0;
  703. this.kills = 0;
  704. this.isMenuVisible = true;
  705. this.pingManager = new PingManager();
  706. if (this.kxsClient.isPingVisible) {
  707. this.initCounter("ping", "Ping", "45ms");
  708. }
  709. if (this.kxsClient.isFpsVisible) {
  710. this.initCounter("fps", "FPS", "60");
  711. }
  712. if (this.kxsClient.isKillsVisible) {
  713. this.initCounter("kills", "Kills", "0");
  714. }
  715. this.setupWeaponBorderHandler();
  716. this.startUpdateLoop();
  717. this.escapeMenu();
  718. this.initFriendDetector();
  719. if (this.kxsClient.isKillFeedBlint) {
  720. if (document.readyState === 'loading') {
  721. document.addEventListener('DOMContentLoaded', this.initKillFeed);
  722. }
  723. else {
  724. this.initKillFeed();
  725. }
  726. }
  727. }
  728. initFriendDetector() {
  729. // Initialize friends list
  730. let all_friends = this.kxsClient.all_friends.split(',') || [];
  731. if (all_friends.length >= 1) {
  732. // Create a cache for detected friends
  733. // Structure will be: { "friendName": timestamp }
  734. const friendsCache = {};
  735. // Cache duration in milliseconds (4 minutes = 240000 ms)
  736. const cacheDuration = 4 * 60 * 1000;
  737. // Select the element containing kill feeds
  738. const killfeedContents = document.querySelector('#ui-killfeed-contents');
  739. if (killfeedContents) {
  740. // Keep track of last seen content for each div
  741. const lastSeenContent = {
  742. "ui-killfeed-0": "",
  743. "ui-killfeed-1": "",
  744. "ui-killfeed-2": "",
  745. "ui-killfeed-3": "",
  746. "ui-killfeed-4": "",
  747. "ui-killfeed-5": ""
  748. };
  749. // Function to check if a friend is in the text with cache management
  750. const checkForFriends = (text, divId) => {
  751. // If the text is identical to the last seen, ignore
  752. // @ts-ignore
  753. if (text === lastSeenContent[divId])
  754. return;
  755. // Update the last seen content
  756. // @ts-ignore
  757. lastSeenContent[divId] = text;
  758. // Ignore empty messages
  759. if (!text.trim())
  760. return;
  761. // Current timestamp
  762. const currentTime = Date.now();
  763. // Check if a friend is mentioned
  764. for (let friend of all_friends) {
  765. if (friend !== "" && text.includes(friend)) {
  766. // Check if the friend is in the cache and if the cache is still valid
  767. // @ts-ignore
  768. const lastSeen = friendsCache[friend];
  769. if (!lastSeen || (currentTime - lastSeen > cacheDuration)) {
  770. // Update the cache
  771. // @ts-ignore
  772. friendsCache[friend] = currentTime;
  773. // Display notification
  774. this.kxsClient.nm.showNotification(`[FriendDetector] ${friend} is in this game`, "info", 2300);
  775. }
  776. break;
  777. }
  778. }
  779. };
  780. // Function to check all kill feeds
  781. const checkAllKillfeeds = () => {
  782. all_friends = this.kxsClient.all_friends.split(',') || [];
  783. for (let i = 0; i <= 5; i++) {
  784. const divId = `ui-killfeed-${i}`;
  785. const killDiv = document.getElementById(divId);
  786. if (killDiv) {
  787. const textElement = killDiv.querySelector('.killfeed-text');
  788. if (textElement && textElement.textContent) {
  789. checkForFriends(textElement.textContent, divId);
  790. }
  791. }
  792. }
  793. };
  794. // Observe style or text changes in the entire container
  795. const observer = new MutationObserver(() => {
  796. checkAllKillfeeds();
  797. });
  798. // Start observing with a configuration that detects all changes
  799. observer.observe(killfeedContents, {
  800. childList: true, // Observe changes to child elements
  801. subtree: true, // Observe the entire tree
  802. characterData: true, // Observe text changes
  803. attributes: true // Observe attribute changes (like style/opacity)
  804. });
  805. // Check current content immediately
  806. checkAllKillfeeds();
  807. console.log("Friend detector initialized with 4-minute cache");
  808. }
  809. else {
  810. console.warn("Killfeed-contents element not found");
  811. }
  812. }
  813. }
  814. initKillFeed() {
  815. this.applyCustomStyles();
  816. this.setupObserver();
  817. }
  818. escapeMenu() {
  819. const customStyles = `
  820. .ui-game-menu-desktop {
  821. background: linear-gradient(135deg, rgba(25, 25, 35, 0.95) 0%, rgba(15, 15, 25, 0.98) 100%) !important;
  822. border: 1px solid rgba(255, 255, 255, 0.1) !important;
  823. border-radius: 12px !important;
  824. box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important;
  825. padding: 20px !important;
  826. backdrop-filter: blur(10px) !important;
  827. max-width: 350px !important;
  828. /* max-height: 80vh !important; */ /* Optionnel: Limiter la hauteur maximale */
  829. margin: auto !important;
  830. box-sizing: border-box !important;
  831. overflow-y: auto !important; /* Permettre le défilement vertical si nécessaire */
  832. }
  833.  
  834. .ui-game-menu-desktop::-webkit-scrollbar {
  835. width: 8px !important;
  836. }
  837. .ui-game-menu-desktop::-webkit-scrollbar-track {
  838. background: rgba(25, 25, 35, 0.5) !important;
  839. border-radius: 10px !important;
  840. }
  841. .ui-game-menu-desktop::-webkit-scrollbar-thumb {
  842. background-color: #4287f5 !important;
  843. border-radius: 10px !important;
  844. border: 2px solid rgba(25, 25, 35, 0.5) !important;
  845. }
  846. .ui-game-menu-desktop::-webkit-scrollbar-thumb:hover {
  847. background-color: #5a9eff !important;
  848. }
  849.  
  850. .ui-game-menu-desktop {
  851. scrollbar-width: thin !important;
  852. scrollbar-color: #4287f5 rgba(25, 25, 35, 0.5) !important;
  853. }
  854.  
  855. .kxs-header {
  856. display: flex;
  857. align-items: center;
  858. justify-content: flex-start;
  859. margin-bottom: 20px;
  860. padding: 10px;
  861. border-bottom: 2px solid rgba(255, 255, 255, 0.1);
  862. }
  863.  
  864. .kxs-logo {
  865. width: 30px;
  866. height: 30px;
  867. margin-right: 10px;
  868. border-radius: 6px;
  869. }
  870.  
  871. .kxs-title {
  872. font-size: 20px;
  873. font-weight: 700;
  874. color: #ffffff;
  875. text-transform: uppercase;
  876. text-shadow: 0 0 10px rgba(66, 135, 245, 0.5);
  877. font-family: 'Arial', sans-serif;
  878. letter-spacing: 2px;
  879. }
  880.  
  881. .kxs-title span {
  882. color: #4287f5;
  883. }
  884. .btn-game-menu {
  885. background: linear-gradient(135deg, rgba(66, 135, 245, 0.1) 0%, rgba(66, 135, 245, 0.2) 100%) !important;
  886. border: 1px solid rgba(66, 135, 245, 0.3) !important;
  887. border-radius: 8px !important;
  888. color: #ffffff !important;
  889. transition: all 0.3s ease !important;
  890. margin: 5px 0 !important;
  891. padding: 12px !important;
  892. font-weight: 600 !important;
  893. width: 100% !important;
  894. text-align: center !important;
  895. display: block !important;
  896. box-sizing: border-box !important;
  897. }
  898.  
  899. .btn-game-menu:hover {
  900. background: linear-gradient(135deg, rgba(66, 135, 245, 0.2) 0%, rgba(66, 135, 245, 0.3) 100%) !important;
  901. transform: translateY(-2px) !important;
  902. box-shadow: 0 4px 12px rgba(66, 135, 245, 0.2) !important;
  903. }
  904.  
  905. .slider-container {
  906. background: rgba(66, 135, 245, 0.1) !important;
  907. border-radius: 8px !important;
  908. padding: 10px 15px !important;
  909. margin: 10px 0 !important;
  910. width: 100% !important;
  911. box-sizing: border-box !important;
  912. }
  913.  
  914. .slider-text {
  915. color: #ffffff !important;
  916. font-size: 14px !important;
  917. margin-bottom: 8px !important;
  918. text-align: center !important;
  919. }
  920.  
  921. .slider {
  922. -webkit-appearance: none !important;
  923. width: 100% !important;
  924. height: 6px !important;
  925. border-radius: 3px !important;
  926. background: rgba(66, 135, 245, 0.3) !important;
  927. outline: none !important;
  928. margin: 10px 0 !important;
  929. }
  930.  
  931. .slider::-webkit-slider-thumb {
  932. -webkit-appearance: none !important;
  933. width: 16px !important;
  934. height: 16px !important;
  935. border-radius: 50% !important;
  936. background: #4287f5 !important;
  937. cursor: pointer !important;
  938. transition: all 0.3s ease !important;
  939. }
  940.  
  941. .slider::-webkit-slider-thumb:hover {
  942. transform: scale(1.2) !important;
  943. box-shadow: 0 0 10px rgba(66, 135, 245, 0.5) !important;
  944. }
  945.  
  946. .btns-game-double-row {
  947. display: flex !important;
  948. justify-content: center !important;
  949. gap: 10px !important;
  950. margin-bottom: 10px !important;
  951. width: 100% !important;
  952. }
  953.  
  954. .btn-game-container {
  955. flex: 1 !important;
  956. }
  957. `;
  958. const addCustomStyles = () => {
  959. const styleElement = document.createElement('style');
  960. styleElement.textContent = customStyles;
  961. document.head.appendChild(styleElement);
  962. };
  963. const addKxsHeader = () => {
  964. const menuContainer = document.querySelector('#ui-game-menu');
  965. if (!menuContainer)
  966. return;
  967. const header = document.createElement('div');
  968. header.className = 'kxs-header';
  969. const title = document.createElement('span');
  970. title.className = 'kxs-title';
  971. title.innerHTML = '<span>Kxs</span> CLIENT';
  972. header.appendChild(title);
  973. menuContainer.insertBefore(header, menuContainer.firstChild);
  974. };
  975. if (document.querySelector('#ui-game-menu')) {
  976. addCustomStyles();
  977. addKxsHeader();
  978. }
  979. }
  980. handleMessage(element) {
  981. if (element instanceof HTMLElement && element.classList.contains('killfeed-div')) {
  982. const killfeedText = element.querySelector('.killfeed-text');
  983. if (killfeedText instanceof HTMLElement) {
  984. if (killfeedText.textContent && killfeedText.textContent.trim() !== '') {
  985. if (!killfeedText.hasAttribute('data-glint')) {
  986. killfeedText.setAttribute('data-glint', 'true');
  987. element.style.opacity = '1';
  988. setTimeout(() => {
  989. element.style.opacity = '0';
  990. }, 5000);
  991. }
  992. }
  993. else {
  994. element.style.opacity = '0';
  995. }
  996. }
  997. }
  998. }
  999. setupObserver() {
  1000. const killfeedContents = document.getElementById('ui-killfeed-contents');
  1001. if (killfeedContents) {
  1002. const observer = new MutationObserver((mutations) => {
  1003. mutations.forEach((mutation) => {
  1004. if (mutation.target instanceof HTMLElement &&
  1005. mutation.target.classList.contains('killfeed-text')) {
  1006. const parentDiv = mutation.target.closest('.killfeed-div');
  1007. if (parentDiv) {
  1008. this.handleMessage(parentDiv);
  1009. }
  1010. }
  1011. mutation.addedNodes.forEach((node) => {
  1012. if (node instanceof HTMLElement) {
  1013. this.handleMessage(node);
  1014. }
  1015. });
  1016. });
  1017. });
  1018. observer.observe(killfeedContents, {
  1019. childList: true,
  1020. subtree: true,
  1021. characterData: true,
  1022. attributes: true,
  1023. attributeFilter: ['style', 'class']
  1024. });
  1025. killfeedContents.querySelectorAll('.killfeed-div').forEach(this.handleMessage);
  1026. }
  1027. }
  1028. applyCustomStyles() {
  1029. const customStyles = document.createElement('style');
  1030. if (this.kxsClient.isKillFeedBlint) {
  1031. customStyles.innerHTML = `
  1032. @import url('https://fonts.googleapis.com/css2?family=Oxanium:wght@600&display=swap');
  1033. .killfeed-div {
  1034. position: absolute !important;
  1035. padding: 5px 10px !important;
  1036. background: rgba(0, 0, 0, 0.7) !important;
  1037. border-radius: 5px !important;
  1038. transition: opacity 0.5s ease-out !important;
  1039. }
  1040. .killfeed-text {
  1041. font-family: 'Oxanium', sans-serif !important;
  1042. font-weight: bold !important;
  1043. font-size: 16px !important;
  1044. text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5) !important;
  1045. background: linear-gradient(90deg,
  1046. rgb(255, 0, 0),
  1047. rgb(255, 127, 0),
  1048. rgb(255, 255, 0),
  1049. rgb(0, 255, 0),
  1050. rgb(0, 0, 255),
  1051. rgb(75, 0, 130),
  1052. rgb(148, 0, 211),
  1053. rgb(255, 0, 0));
  1054. background-size: 200%;
  1055. -webkit-background-clip: text;
  1056. -webkit-text-fill-color: transparent;
  1057. animation: glint 3s linear infinite;
  1058. }
  1059. @keyframes glint {
  1060. 0% {
  1061. background-position: 200% 0;
  1062. }
  1063. 100% {
  1064. background-position: -200% 0;
  1065. }
  1066. }
  1067. .killfeed-div .killfeed-text:empty {
  1068. display: none !important;
  1069. }
  1070. `;
  1071. }
  1072. else {
  1073. customStyles.innerHTML = `
  1074. .killfeed-div {
  1075. position: absolute;
  1076. padding: 5px 10px;
  1077. background: rgba(0, 0, 0, 0.7);
  1078. border-radius: 5px;
  1079. transition: opacity 0.5s ease-out;
  1080. }
  1081. .killfeed-text {
  1082. font-family: inherit;
  1083. font-weight: normal;
  1084. font-size: inherit;
  1085. color: inherit;
  1086. text-shadow: none;
  1087. background: none;
  1088. }
  1089. .killfeed-div .killfeed-text:empty {
  1090. display: none;
  1091. }
  1092. `;
  1093. }
  1094. document.head.appendChild(customStyles);
  1095. }
  1096. handleResize() {
  1097. const viewportWidth = window.innerWidth;
  1098. const viewportHeight = window.innerHeight;
  1099. for (const name of ['fps', 'kills', 'ping']) {
  1100. const counterContainer = document.getElementById(`${name}CounterContainer`);
  1101. if (!counterContainer)
  1102. continue;
  1103. const counter = this.kxsClient.counters[name];
  1104. if (!counter)
  1105. continue;
  1106. const rect = counterContainer.getBoundingClientRect();
  1107. const savedPosition = this.getSavedPosition(name);
  1108. let newPosition = this.calculateSafePosition(savedPosition, rect.width, rect.height, viewportWidth, viewportHeight);
  1109. this.applyPosition(counterContainer, newPosition);
  1110. this.savePosition(name, newPosition);
  1111. }
  1112. }
  1113. calculateSafePosition(currentPosition, elementWidth, elementHeight, viewportWidth, viewportHeight) {
  1114. let { left, top } = currentPosition;
  1115. if (left + elementWidth > viewportWidth) {
  1116. left = viewportWidth - elementWidth;
  1117. }
  1118. if (left < 0) {
  1119. left = 0;
  1120. }
  1121. if (top + elementHeight > viewportHeight) {
  1122. top = viewportHeight - elementHeight;
  1123. }
  1124. if (top < 0) {
  1125. top = 0;
  1126. }
  1127. return { left, top };
  1128. }
  1129. getSavedPosition(name) {
  1130. const savedPosition = localStorage.getItem(`${name}CounterPosition`);
  1131. if (savedPosition) {
  1132. try {
  1133. return JSON.parse(savedPosition);
  1134. }
  1135. catch (_a) {
  1136. return this.kxsClient.defaultPositions[name];
  1137. }
  1138. }
  1139. return this.kxsClient.defaultPositions[name];
  1140. }
  1141. applyPosition(element, position) {
  1142. element.style.left = `${position.left}px`;
  1143. element.style.top = `${position.top}px`;
  1144. }
  1145. savePosition(name, position) {
  1146. localStorage.setItem(`${name}CounterPosition`, JSON.stringify(position));
  1147. }
  1148. startUpdateLoop() {
  1149. var _a;
  1150. const now = performance.now();
  1151. const delta = now - this.kxsClient.lastFrameTime;
  1152. this.frameCount++;
  1153. if (delta >= 1000) {
  1154. this.fps = Math.round((this.frameCount * 1000) / delta);
  1155. this.frameCount = 0;
  1156. this.kxsClient.lastFrameTime = now;
  1157. this.kills = this.kxsClient.getKills();
  1158. if (this.kxsClient.isFpsVisible && this.kxsClient.counters.fps) {
  1159. this.kxsClient.counters.fps.textContent = `FPS: ${this.fps}`;
  1160. }
  1161. if (this.kxsClient.isKillsVisible && this.kxsClient.counters.kills) {
  1162. this.kxsClient.counters.kills.textContent = `Kills: ${this.kills}`;
  1163. }
  1164. if (this.kxsClient.isPingVisible &&
  1165. this.kxsClient.counters.ping &&
  1166. this.pingManager) {
  1167. const result = this.pingManager.getPingResult();
  1168. this.kxsClient.counters.ping.textContent = `PING: ${result.ping} ms`;
  1169. }
  1170. }
  1171. this.pingManager.startPingTest();
  1172. if (this.kxsClient.animationFrameCallback) {
  1173. this.kxsClient.animationFrameCallback(() => this.startUpdateLoop());
  1174. }
  1175. this.updateUiElements();
  1176. this.updateBoostBars();
  1177. this.updateHealthBars();
  1178. (_a = this.kxsClient.kill_leader) === null || _a === void 0 ? void 0 : _a.update(this.kills);
  1179. }
  1180. initCounter(name, label, initialText) {
  1181. const counter = document.createElement("div");
  1182. counter.id = `${name}Counter`;
  1183. const counterContainer = document.createElement("div");
  1184. counterContainer.id = `${name}CounterContainer`;
  1185. Object.assign(counterContainer.style, {
  1186. position: "absolute",
  1187. left: `${this.kxsClient.defaultPositions[name].left}px`,
  1188. top: `${this.kxsClient.defaultPositions[name].top}px`,
  1189. zIndex: "10000",
  1190. });
  1191. Object.assign(counter.style, {
  1192. color: "white",
  1193. backgroundColor: "rgba(0, 0, 0, 0.2)",
  1194. borderRadius: "5px",
  1195. fontFamily: "Arial, sans-serif",
  1196. padding: "5px 10px",
  1197. pointerEvents: "auto",
  1198. cursor: "move",
  1199. width: `${this.kxsClient.defaultSizes[name].width}px`,
  1200. height: `${this.kxsClient.defaultSizes[name].height}px`,
  1201. display: "flex",
  1202. alignItems: "center",
  1203. justifyContent: "center",
  1204. textAlign: "center",
  1205. resize: "both",
  1206. overflow: "hidden",
  1207. });
  1208. counter.textContent = `${label}: ${initialText}`;
  1209. counterContainer.appendChild(counter);
  1210. const uiTopLeft = document.getElementById("ui-top-left");
  1211. if (uiTopLeft) {
  1212. uiTopLeft.appendChild(counterContainer);
  1213. }
  1214. const adjustFontSize = () => {
  1215. const { width, height } = counter.getBoundingClientRect();
  1216. const size = Math.min(width, height) * 0.4;
  1217. counter.style.fontSize = `${size}px`;
  1218. };
  1219. new ResizeObserver(adjustFontSize).observe(counter);
  1220. counter.addEventListener("mousedown", (event) => {
  1221. if (event.button === 1) {
  1222. this.resetCounter(name, label, initialText);
  1223. event.preventDefault();
  1224. }
  1225. });
  1226. this.kxsClient.makeDraggable(counterContainer, `${name}CounterPosition`);
  1227. this.kxsClient.counters[name] = counter;
  1228. }
  1229. resetCounter(name, label, initialText) {
  1230. const counter = this.kxsClient.counters[name];
  1231. const container = document.getElementById(`${name}CounterContainer`);
  1232. if (!counter || !container)
  1233. return;
  1234. // Reset only this counter's position and size
  1235. Object.assign(container.style, {
  1236. left: `${this.kxsClient.defaultPositions[name].left}px`,
  1237. top: `${this.kxsClient.defaultPositions[name].top}px`,
  1238. });
  1239. Object.assign(counter.style, {
  1240. width: `${this.kxsClient.defaultSizes[name].width}px`,
  1241. height: `${this.kxsClient.defaultSizes[name].height}px`,
  1242. fontSize: "18px",
  1243. });
  1244. counter.textContent = `${label}: ${initialText}`;
  1245. // Clear the saved position for this counter only
  1246. localStorage.removeItem(`${name}CounterPosition`);
  1247. }
  1248. updateBoostBars() {
  1249. const boostCounter = document.querySelector("#ui-boost-counter");
  1250. if (boostCounter) {
  1251. const boostBars = boostCounter.querySelectorAll(".ui-boost-base .ui-bar-inner");
  1252. let totalBoost = 0;
  1253. const weights = [25, 25, 40, 10];
  1254. boostBars.forEach((bar, index) => {
  1255. const width = parseFloat(bar.style.width);
  1256. if (!isNaN(width)) {
  1257. totalBoost += width * (weights[index] / 100);
  1258. }
  1259. });
  1260. const averageBoost = Math.round(totalBoost);
  1261. let boostDisplay = boostCounter.querySelector(".boost-display");
  1262. if (!boostDisplay) {
  1263. boostDisplay = document.createElement("div");
  1264. boostDisplay.classList.add("boost-display");
  1265. Object.assign(boostDisplay.style, {
  1266. position: "absolute",
  1267. bottom: "75px",
  1268. right: "335px",
  1269. color: "#FF901A",
  1270. backgroundColor: "rgba(0, 0, 0, 0.4)",
  1271. padding: "5px 10px",
  1272. borderRadius: "5px",
  1273. fontFamily: "Arial, sans-serif",
  1274. fontSize: "14px",
  1275. zIndex: "10",
  1276. textAlign: "center",
  1277. });
  1278. boostCounter.appendChild(boostDisplay);
  1279. }
  1280. boostDisplay.textContent = `AD: ${averageBoost}%`;
  1281. }
  1282. }
  1283. setupWeaponBorderHandler() {
  1284. const weaponContainers = Array.from(document.getElementsByClassName("ui-weapon-switch"));
  1285. weaponContainers.forEach((container) => {
  1286. if (container.id === "ui-weapon-id-4") {
  1287. container.style.border = "3px solid #2f4032";
  1288. }
  1289. else {
  1290. container.style.border = "3px solid #FFFFFF";
  1291. }
  1292. });
  1293. const weaponNames = Array.from(document.getElementsByClassName("ui-weapon-name"));
  1294. const WEAPON_COLORS = {
  1295. ORANGE: '#FFAE00',
  1296. BLUE: '#007FFF',
  1297. GREEN: '#0f690d',
  1298. RED: '#FF0000',
  1299. BLACK: '#000000',
  1300. OLIVE: '#808000',
  1301. ORANGE_RED: '#FF4500',
  1302. PURPLE: '#800080',
  1303. TEAL: '#008080',
  1304. BROWN: '#A52A2A',
  1305. PINK: '#FFC0CB',
  1306. DEFAULT: '#FFFFFF'
  1307. };
  1308. const WEAPON_COLOR_MAPPING = {
  1309. ORANGE: ['CZ-3A1', 'G18C', 'M9', 'M93R', 'MAC-10', 'MP5', 'P30L', 'DUAL P30L', 'UMP9', 'VECTOR', 'VSS', 'FLAMETHROWER'],
  1310. BLUE: ['AK-47', 'OT-38', 'OTS-38', 'M39 EMR', 'DP-28', 'MOSIN-NAGANT', 'SCAR-H', 'SV-98', 'M1 GARAND', 'PKP PECHENEG', 'AN-94', 'BAR M1918', 'BLR 81', 'SVD-63', 'M134', 'WATER GUN', 'GROZA', 'GROZA-S'],
  1311. GREEN: ['FAMAS', 'M416', 'M249', 'QBB-97', 'MK 12 SPR', 'M4A1-S', 'SCOUT ELITE', 'L86A2'],
  1312. RED: ['M870', 'MP220', 'SAIGA-12', 'SPAS-12', 'USAS-12', 'SUPER 90', 'LASR GUN', 'M1100'],
  1313. BLACK: ['DEAGLE 50', 'RAINBOW BLASTER'],
  1314. OLIVE: ['AWM-S', 'MK 20 SSR'],
  1315. ORANGE_RED: ['FLARE GUN'],
  1316. PURPLE: ['MODEL 94', 'PEACEMAKER', 'VECTOR (.45 ACP)', 'M1911', 'M1A1', 'MK45G'],
  1317. TEAL: ['M79'],
  1318. BROWN: ['POTATO CANNON', 'SPUD GUN'],
  1319. PINK: ['HEART CANNON'],
  1320. DEFAULT: []
  1321. };
  1322. weaponNames.forEach((weaponNameElement) => {
  1323. const weaponContainer = weaponNameElement.closest(".ui-weapon-switch");
  1324. const observer = new MutationObserver(() => {
  1325. var _a, _b, _c;
  1326. const weaponName = ((_b = (_a = weaponNameElement.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || '';
  1327. const colorKey = (((_c = Object.entries(WEAPON_COLOR_MAPPING)
  1328. .find(([_, weapons]) => weapons.includes(weaponName))) === null || _c === void 0 ? void 0 : _c[0]) || 'DEFAULT');
  1329. if (weaponContainer && weaponContainer.id !== "ui-weapon-id-4") {
  1330. weaponContainer.style.border = `3px solid ${WEAPON_COLORS[colorKey]}`;
  1331. }
  1332. });
  1333. observer.observe(weaponNameElement, { childList: true, characterData: true, subtree: true });
  1334. });
  1335. }
  1336. updateUiElements() {
  1337. const currentUrl = window.location.href;
  1338. const isSpecialUrl = /\/#\w+/.test(currentUrl);
  1339. const playerOptions = document.getElementById("player-options");
  1340. const teamMenuContents = document.getElementById("team-menu-contents");
  1341. const startMenuContainer = document.querySelector("#start-menu .play-button-container");
  1342. if (!playerOptions)
  1343. return;
  1344. if (isSpecialUrl &&
  1345. teamMenuContents &&
  1346. playerOptions.parentNode !== teamMenuContents) {
  1347. teamMenuContents.appendChild(playerOptions);
  1348. }
  1349. else if (!isSpecialUrl &&
  1350. startMenuContainer &&
  1351. playerOptions.parentNode !== startMenuContainer) {
  1352. const firstChild = startMenuContainer.firstChild;
  1353. startMenuContainer.insertBefore(playerOptions, firstChild);
  1354. }
  1355. const teamMenu = document.getElementById("team-menu");
  1356. if (teamMenu) {
  1357. teamMenu.style.height = "355px";
  1358. }
  1359. const menuBlocks = document.querySelectorAll(".menu-block");
  1360. menuBlocks.forEach((block) => {
  1361. block.style.maxHeight = "355px";
  1362. });
  1363. //scalable?
  1364. }
  1365. updateMenuButtonText() {
  1366. const hideButton = document.getElementById("hideMenuButton");
  1367. hideButton.textContent = this.isMenuVisible
  1368. ? "Hide Menu [P]"
  1369. : "Show Menu [P]";
  1370. }
  1371. updateHealthBars() {
  1372. const healthBars = document.querySelectorAll("#ui-health-container");
  1373. healthBars.forEach((container) => {
  1374. var _a, _b;
  1375. const bar = container.querySelector("#ui-health-actual");
  1376. if (bar) {
  1377. const currentHealth = Math.round(parseFloat(bar.style.width));
  1378. let percentageText = container.querySelector(".health-text");
  1379. // Create or update percentage text
  1380. if (!percentageText) {
  1381. percentageText = document.createElement("span");
  1382. percentageText.classList.add("health-text");
  1383. Object.assign(percentageText.style, {
  1384. width: "100%",
  1385. textAlign: "center",
  1386. marginTop: "5px",
  1387. color: "#333",
  1388. fontSize: "20px",
  1389. fontWeight: "bold",
  1390. position: "absolute",
  1391. zIndex: "10",
  1392. });
  1393. container.appendChild(percentageText);
  1394. }
  1395. // Check for health change
  1396. if (currentHealth !== this.lastHealthValue) {
  1397. const healthChange = currentHealth - this.lastHealthValue;
  1398. if (healthChange !== 0) {
  1399. this.showHealthChangeAnimation(container, healthChange);
  1400. }
  1401. this.lastHealthValue = currentHealth;
  1402. }
  1403. if (this.kxsClient.isHealthWarningEnabled) {
  1404. (_a = this.kxsClient.healWarning) === null || _a === void 0 ? void 0 : _a.update(currentHealth);
  1405. }
  1406. else {
  1407. (_b = this.kxsClient.healWarning) === null || _b === void 0 ? void 0 : _b.hide();
  1408. }
  1409. percentageText.textContent = `${currentHealth}%`;
  1410. // Update animations
  1411. this.updateHealthAnimations();
  1412. }
  1413. });
  1414. }
  1415. showHealthChangeAnimation(container, change) {
  1416. const animation = document.createElement("div");
  1417. const isPositive = change > 0;
  1418. Object.assign(animation.style, {
  1419. position: "absolute",
  1420. color: isPositive ? "#2ecc71" : "#e74c3c",
  1421. fontSize: "24px",
  1422. fontWeight: "bold",
  1423. fontFamily: "Arial, sans-serif",
  1424. textShadow: "2px 2px 4px rgba(0,0,0,0.3)",
  1425. pointerEvents: "none",
  1426. zIndex: "100",
  1427. opacity: "1",
  1428. top: "50%",
  1429. right: "-80px", // Position à droite de la barre de vie
  1430. transform: "translateY(-50%)", // Centre verticalement
  1431. whiteSpace: "nowrap", // Empêche le retour à la ligne
  1432. });
  1433. // Check if change is a valid number before displaying it
  1434. if (!isNaN(change)) {
  1435. animation.textContent = `${isPositive ? "+" : ""}${change} HP`;
  1436. }
  1437. else {
  1438. // Skip showing animation if change is NaN
  1439. return;
  1440. }
  1441. container.appendChild(animation);
  1442. this.healthAnimations.push({
  1443. element: animation,
  1444. startTime: performance.now(),
  1445. duration: 1500, // Animation duration in milliseconds
  1446. value: change,
  1447. });
  1448. }
  1449. updateHealthAnimations() {
  1450. const currentTime = performance.now();
  1451. this.healthAnimations = this.healthAnimations.filter(animation => {
  1452. const elapsed = currentTime - animation.startTime;
  1453. const progress = Math.min(elapsed / animation.duration, 1);
  1454. if (progress < 1) {
  1455. // Update animation position and opacity
  1456. // Maintenant l'animation se déplace horizontalement vers la droite
  1457. const translateX = progress * 20; // Déplacement horizontal
  1458. Object.assign(animation.element.style, {
  1459. transform: `translateY(-50%) translateX(${translateX}px)`,
  1460. opacity: String(1 - progress),
  1461. });
  1462. return true;
  1463. }
  1464. else {
  1465. // Remove completed animation
  1466. animation.element.remove();
  1467. return false;
  1468. }
  1469. });
  1470. }
  1471. }
  1472.  
  1473.  
  1474. ;// ./src/intercept.ts
  1475. function intercept(link, targetUrl) {
  1476. const open = XMLHttpRequest.prototype.open;
  1477. XMLHttpRequest.prototype.open = function (method, url) {
  1478. if (url.includes(link)) {
  1479. arguments[1] = targetUrl;
  1480. }
  1481. open.apply(this, arguments);
  1482. };
  1483. const originalFetch = window.fetch;
  1484. window.fetch = function (url, options) {
  1485. if (url.includes(link)) {
  1486. url = targetUrl;
  1487. }
  1488. return originalFetch.apply(this, arguments);
  1489. };
  1490. }
  1491.  
  1492.  
  1493. ;// ./src/HealthWarning.ts
  1494. class HealthWarning {
  1495. constructor(kxsClient) {
  1496. this.offsetX = 20; // Distance depuis le curseur
  1497. this.offsetY = 20;
  1498. this.warningElement = null;
  1499. this.kxsClient = kxsClient;
  1500. this.createWarningElement();
  1501. this.initMouseTracking();
  1502. }
  1503. createWarningElement() {
  1504. const warning = document.createElement("div");
  1505. const uiTopLeft = document.getElementById("ui-top-left");
  1506. warning.style.cssText = `
  1507. position: fixed;
  1508. background: rgba(0, 0, 0, 0.8);
  1509. border: 2px solid #ff0000;
  1510. border-radius: 5px;
  1511. padding: 10px 15px;
  1512. color: #ff0000;
  1513. font-family: Arial, sans-serif;
  1514. font-size: 14px;
  1515. z-index: 9999;
  1516. display: none;
  1517. backdrop-filter: blur(5px);
  1518. transition: transform 0.1s ease;
  1519. pointer-events: none;
  1520. `;
  1521. const content = document.createElement("div");
  1522. content.style.cssText = `
  1523. display: flex;
  1524. align-items: center;
  1525. gap: 8px;
  1526. `;
  1527. const icon = document.createElement("div");
  1528. icon.innerHTML = `
  1529. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  1530. <circle cx="12" cy="12" r="10"></circle>
  1531. <line x1="12" y1="8" x2="12" y2="12"></line>
  1532. <line x1="12" y1="16" x2="12.01" y2="16"></line>
  1533. </svg>
  1534. `;
  1535. const text = document.createElement("span");
  1536. text.textContent = "LOW HP!";
  1537. if (uiTopLeft) {
  1538. content.appendChild(icon);
  1539. content.appendChild(text);
  1540. warning.appendChild(content);
  1541. uiTopLeft.appendChild(warning);
  1542. }
  1543. this.warningElement = warning;
  1544. this.addPulseAnimation();
  1545. }
  1546. initMouseTracking() {
  1547. document.addEventListener("mousemove", (e) => {
  1548. if (!this.warningElement || this.warningElement.style.display === "none")
  1549. return;
  1550. const x = e.clientX + this.offsetX;
  1551. const y = e.clientY + this.offsetY;
  1552. // Empêcher l'alerte de sortir de l'écran
  1553. const rect = this.warningElement.getBoundingClientRect();
  1554. const maxX = window.innerWidth - rect.width;
  1555. const maxY = window.innerHeight - rect.height;
  1556. const finalX = Math.min(Math.max(0, x), maxX);
  1557. const finalY = Math.min(Math.max(0, y), maxY);
  1558. this.warningElement.style.transform = `translate(${finalX}px, ${finalY}px)`;
  1559. });
  1560. }
  1561. addPulseAnimation() {
  1562. const keyframes = `
  1563. @keyframes pulse {
  1564. 0% { opacity: 1; }
  1565. 50% { opacity: 0.5; }
  1566. 100% { opacity: 1; }
  1567. }
  1568. `;
  1569. const style = document.createElement("style");
  1570. style.textContent = keyframes;
  1571. document.head.appendChild(style);
  1572. if (this.warningElement) {
  1573. this.warningElement.style.animation = "pulse 1.5s infinite";
  1574. }
  1575. }
  1576. show(health) {
  1577. if (!this.warningElement)
  1578. return;
  1579. this.warningElement.style.display = "block";
  1580. const span = this.warningElement.querySelector("span");
  1581. if (span) {
  1582. span.textContent = `LOW HP: ${health}%`;
  1583. }
  1584. }
  1585. hide() {
  1586. if (!this.warningElement)
  1587. return;
  1588. this.warningElement.style.display = "none";
  1589. }
  1590. update(health) {
  1591. if (health <= 30 && health > 0) {
  1592. this.show(health);
  1593. }
  1594. else {
  1595. this.hide();
  1596. }
  1597. }
  1598. }
  1599.  
  1600.  
  1601. ;// ./src/KillLeaderTracking.ts
  1602. class KillLeaderTracker {
  1603. constructor(kxsClient) {
  1604. this.offsetX = 20;
  1605. this.offsetY = 20;
  1606. this.lastKnownKills = 0;
  1607. this.wasKillLeader = false;
  1608. this.MINIMUM_KILLS_FOR_LEADER = 3;
  1609. this.kxsClient = kxsClient;
  1610. this.warningElement = null;
  1611. this.encouragementElement = null;
  1612. this.killLeaderKillCount = 0;
  1613. this.createEncouragementElement();
  1614. this.initMouseTracking();
  1615. }
  1616. createEncouragementElement() {
  1617. const encouragement = document.createElement("div");
  1618. encouragement.style.cssText = `
  1619. position: fixed;
  1620. background: rgba(0, 255, 0, 0.1);
  1621. border: 2px solid #00ff00;
  1622. border-radius: 5px;
  1623. padding: 10px 15px;
  1624. color: #00ff00;
  1625. font-family: Arial, sans-serif;
  1626. font-size: 14px;
  1627. z-index: 9999;
  1628. display: none;
  1629. backdrop-filter: blur(5px);
  1630. transition: all 0.3s ease;
  1631. pointer-events: none;
  1632. box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
  1633. `;
  1634. const content = document.createElement("div");
  1635. content.style.cssText = `
  1636. display: flex;
  1637. align-items: center;
  1638. gap: 8px;
  1639. `;
  1640. const icon = document.createElement("div");
  1641. icon.innerHTML = `
  1642. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  1643. <path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"/>
  1644. </svg>
  1645. `;
  1646. const text = document.createElement("span");
  1647. text.textContent = "Nice Kill!";
  1648. content.appendChild(icon);
  1649. content.appendChild(text);
  1650. encouragement.appendChild(content);
  1651. document.body.appendChild(encouragement);
  1652. this.encouragementElement = encouragement;
  1653. this.addEncouragementAnimation();
  1654. }
  1655. initMouseTracking() {
  1656. document.addEventListener("mousemove", (e) => {
  1657. this.updateElementPosition(this.warningElement, e);
  1658. this.updateElementPosition(this.encouragementElement, e);
  1659. });
  1660. }
  1661. updateElementPosition(element, e) {
  1662. if (!element || element.style.display === "none")
  1663. return;
  1664. const x = e.clientX + this.offsetX;
  1665. const y = e.clientY + this.offsetY;
  1666. const rect = element.getBoundingClientRect();
  1667. const maxX = window.innerWidth - rect.width;
  1668. const maxY = window.innerHeight - rect.height;
  1669. const finalX = Math.min(Math.max(0, x), maxX);
  1670. const finalY = Math.min(Math.max(0, y), maxY);
  1671. element.style.transform = `translate(${finalX}px, ${finalY}px)`;
  1672. }
  1673. addEncouragementAnimation() {
  1674. const keyframes = `
  1675. @keyframes encouragementPulse {
  1676. 0% { transform: scale(1); opacity: 1; }
  1677. 50% { transform: scale(1.1); opacity: 0.8; }
  1678. 100% { transform: scale(1); opacity: 1; }
  1679. }
  1680. @keyframes fadeInOut {
  1681. 0% { opacity: 0; transform: translateY(20px); }
  1682. 10% { opacity: 1; transform: translateY(0); }
  1683. 90% { opacity: 1; transform: translateY(0); }
  1684. 100% { opacity: 0; transform: translateY(-20px); }
  1685. }
  1686. `;
  1687. const style = document.createElement("style");
  1688. style.textContent = keyframes;
  1689. document.head.appendChild(style);
  1690. if (this.encouragementElement) {
  1691. this.encouragementElement.style.animation = "fadeInOut 3s forwards";
  1692. }
  1693. }
  1694. showEncouragement(killsToLeader, isDethrone = false, noKillLeader = false) {
  1695. if (!this.encouragementElement)
  1696. return;
  1697. let message;
  1698. if (isDethrone) {
  1699. message = "Oh no! You've been dethroned!";
  1700. this.encouragementElement.style.borderColor = "#ff0000";
  1701. this.encouragementElement.style.color = "#ff0000";
  1702. this.encouragementElement.style.background = "rgba(255, 0, 0, 0.1)";
  1703. }
  1704. else if (noKillLeader) {
  1705. const killsNeeded = this.MINIMUM_KILLS_FOR_LEADER - this.lastKnownKills;
  1706. message = `Nice Kill! Get ${killsNeeded} more kills to become the first Kill Leader!`;
  1707. }
  1708. else {
  1709. message =
  1710. killsToLeader <= 0
  1711. ? "You're the Kill Leader! 👑"
  1712. : `Nice Kill! ${killsToLeader} more to become Kill Leader!`;
  1713. }
  1714. const span = this.encouragementElement.querySelector("span");
  1715. if (span)
  1716. span.textContent = message;
  1717. this.encouragementElement.style.display = "block";
  1718. this.encouragementElement.style.animation = "fadeInOut 3s forwards";
  1719. setTimeout(() => {
  1720. if (this.encouragementElement) {
  1721. this.encouragementElement.style.display = "none";
  1722. // Reset colors
  1723. this.encouragementElement.style.borderColor = "#00ff00";
  1724. this.encouragementElement.style.color = "#00ff00";
  1725. this.encouragementElement.style.background = "rgba(0, 255, 0, 0.1)";
  1726. }
  1727. }, 7000);
  1728. }
  1729. isKillLeader() {
  1730. const killLeaderNameElement = document.querySelector("#ui-kill-leader-name");
  1731. return this.kxsClient.getPlayerName() === (killLeaderNameElement === null || killLeaderNameElement === void 0 ? void 0 : killLeaderNameElement.textContent);
  1732. }
  1733. update(myKills) {
  1734. if (!this.kxsClient.isKillLeaderTrackerEnabled)
  1735. return;
  1736. const killLeaderElement = document.querySelector("#ui-kill-leader-count");
  1737. this.killLeaderKillCount = parseInt((killLeaderElement === null || killLeaderElement === void 0 ? void 0 : killLeaderElement.textContent) || "0", 10);
  1738. if (myKills > this.lastKnownKills) {
  1739. if (this.killLeaderKillCount === 0) {
  1740. // Pas encore de kill leader, encourager le joueur à atteindre 3 kills
  1741. this.showEncouragement(0, false, true);
  1742. }
  1743. else if (this.killLeaderKillCount < this.MINIMUM_KILLS_FOR_LEADER) {
  1744. // Ne rien faire si le kill leader n'a pas atteint le minimum requis
  1745. return;
  1746. }
  1747. else if (this.isKillLeader()) {
  1748. this.showEncouragement(0);
  1749. this.wasKillLeader = true;
  1750. }
  1751. else {
  1752. const killsNeeded = this.killLeaderKillCount + 1 - myKills;
  1753. this.showEncouragement(killsNeeded);
  1754. }
  1755. }
  1756. else if (this.wasKillLeader && !this.isKillLeader()) {
  1757. // Détroné
  1758. this.showEncouragement(0, true);
  1759. this.wasKillLeader = false;
  1760. }
  1761. this.lastKnownKills = myKills;
  1762. }
  1763. }
  1764.  
  1765.  
  1766. ;// ./src/GridSystem.ts
  1767. class GridSystem {
  1768. constructor() {
  1769. this.gridSize = 20; // Size of each grid cell
  1770. this.snapThreshold = 15; // Distance in pixels to trigger snap
  1771. this.gridVisible = false;
  1772. this.magneticEdges = true;
  1773. this.gridContainer = this.createGridOverlay();
  1774. this.setupKeyBindings();
  1775. }
  1776. createGridOverlay() {
  1777. const container = document.createElement("div");
  1778. container.id = "grid-overlay";
  1779. Object.assign(container.style, {
  1780. position: "fixed",
  1781. top: "0",
  1782. left: "0",
  1783. width: "100%",
  1784. height: "100%",
  1785. pointerEvents: "none",
  1786. zIndex: "9999",
  1787. display: "none",
  1788. opacity: "0.2",
  1789. });
  1790. // Create vertical lines
  1791. for (let x = this.gridSize; x < window.innerWidth; x += this.gridSize) {
  1792. const vLine = document.createElement("div");
  1793. Object.assign(vLine.style, {
  1794. position: "absolute",
  1795. left: `${x}px`,
  1796. top: "0",
  1797. width: "1px",
  1798. height: "100%",
  1799. backgroundColor: "#4CAF50",
  1800. });
  1801. container.appendChild(vLine);
  1802. }
  1803. // Create horizontal lines
  1804. for (let y = this.gridSize; y < window.innerHeight; y += this.gridSize) {
  1805. const hLine = document.createElement("div");
  1806. Object.assign(hLine.style, {
  1807. position: "absolute",
  1808. left: "0",
  1809. top: `${y}px`,
  1810. width: "100%",
  1811. height: "1px",
  1812. backgroundColor: "#4CAF50",
  1813. });
  1814. container.appendChild(hLine);
  1815. }
  1816. document.body.appendChild(container);
  1817. return container;
  1818. }
  1819. setupKeyBindings() {
  1820. document.addEventListener("keydown", (e) => {
  1821. if (e.key === "g" && e.altKey) {
  1822. this.toggleGrid();
  1823. }
  1824. });
  1825. }
  1826. toggleGrid() {
  1827. this.gridVisible = !this.gridVisible;
  1828. this.gridContainer.style.display = this.gridVisible ? "block" : "none";
  1829. }
  1830. snapToGrid(element, x, y) {
  1831. const rect = element.getBoundingClientRect();
  1832. const elementWidth = rect.width;
  1833. const elementHeight = rect.height;
  1834. // Snap to grid
  1835. let snappedX = Math.round(x / this.gridSize) * this.gridSize;
  1836. let snappedY = Math.round(y / this.gridSize) * this.gridSize;
  1837. // Edge snapping
  1838. if (this.magneticEdges) {
  1839. const screenEdges = {
  1840. left: 0,
  1841. right: window.innerWidth - elementWidth,
  1842. center: (window.innerWidth - elementWidth) / 2,
  1843. top: 0,
  1844. bottom: window.innerHeight - elementHeight,
  1845. middle: (window.innerHeight - elementHeight) / 2,
  1846. };
  1847. // Snap to horizontal edges
  1848. if (Math.abs(x - screenEdges.left) < this.snapThreshold) {
  1849. snappedX = screenEdges.left;
  1850. }
  1851. else if (Math.abs(x - screenEdges.right) < this.snapThreshold) {
  1852. snappedX = screenEdges.right;
  1853. }
  1854. else if (Math.abs(x - screenEdges.center) < this.snapThreshold) {
  1855. snappedX = screenEdges.center;
  1856. }
  1857. // Snap to vertical edges
  1858. if (Math.abs(y - screenEdges.top) < this.snapThreshold) {
  1859. snappedY = screenEdges.top;
  1860. }
  1861. else if (Math.abs(y - screenEdges.bottom) < this.snapThreshold) {
  1862. snappedY = screenEdges.bottom;
  1863. }
  1864. else if (Math.abs(y - screenEdges.middle) < this.snapThreshold) {
  1865. snappedY = screenEdges.middle;
  1866. }
  1867. }
  1868. return { x: snappedX, y: snappedY };
  1869. }
  1870. highlightNearestGridLine(x, y) {
  1871. if (!this.gridVisible)
  1872. return;
  1873. // Remove existing highlights
  1874. const highlights = document.querySelectorAll(".grid-highlight");
  1875. highlights.forEach((h) => h.remove());
  1876. // Create highlight for nearest vertical line
  1877. const nearestX = Math.round(x / this.gridSize) * this.gridSize;
  1878. if (Math.abs(x - nearestX) < this.snapThreshold) {
  1879. const vHighlight = document.createElement("div");
  1880. Object.assign(vHighlight.style, {
  1881. position: "absolute",
  1882. left: `${nearestX}px`,
  1883. top: "0",
  1884. width: "2px",
  1885. height: "100%",
  1886. backgroundColor: "#FFD700",
  1887. zIndex: "10000",
  1888. pointerEvents: "none",
  1889. });
  1890. vHighlight.classList.add("grid-highlight");
  1891. this.gridContainer.appendChild(vHighlight);
  1892. }
  1893. // Create highlight for nearest horizontal line
  1894. const nearestY = Math.round(y / this.gridSize) * this.gridSize;
  1895. if (Math.abs(y - nearestY) < this.snapThreshold) {
  1896. const hHighlight = document.createElement("div");
  1897. Object.assign(hHighlight.style, {
  1898. position: "absolute",
  1899. left: "0",
  1900. top: `${nearestY}px`,
  1901. width: "100%",
  1902. height: "2px",
  1903. backgroundColor: "#FFD700",
  1904. zIndex: "10000",
  1905. pointerEvents: "none",
  1906. });
  1907. hHighlight.classList.add("grid-highlight");
  1908. this.gridContainer.appendChild(hHighlight);
  1909. }
  1910. }
  1911. }
  1912.  
  1913.  
  1914. ;// ./src/StatsParser.ts
  1915. class StatsParser {
  1916. static cleanNumber(str) {
  1917. return parseInt(str.replace(/[^\d.-]/g, "")) || 0;
  1918. }
  1919. /**
  1920. * Extract the full duration string including the unit
  1921. */
  1922. static extractDuration(str) {
  1923. const match = str.match(/(\d+\s*[smh])/i);
  1924. return match ? match[1].trim() : "0s";
  1925. }
  1926. static parse(statsText, rankContent) {
  1927. let stats = {
  1928. username: "Player",
  1929. kills: 0,
  1930. damageDealt: 0,
  1931. damageTaken: 0,
  1932. duration: "",
  1933. position: "#unknown",
  1934. };
  1935. // Handle developer format
  1936. const devPattern = /Developer.*?Kills(\d+).*?Damage Dealt(\d+).*?Damage Taken(\d+).*?Survived(\d+\s*[smh])/i;
  1937. const devMatch = statsText.match(devPattern);
  1938. if (devMatch) {
  1939. return {
  1940. username: "Player",
  1941. kills: this.cleanNumber(devMatch[1]),
  1942. damageDealt: this.cleanNumber(devMatch[2]),
  1943. damageTaken: this.cleanNumber(devMatch[3]),
  1944. duration: devMatch[4].trim(), // Keep the full duration string with unit
  1945. position: rankContent.replace("##", "#"),
  1946. };
  1947. }
  1948. // Handle template format
  1949. const templatePattern = /%username%.*?Kills%kills_number%.*?Dealt%number_dealt%.*?Taken%damage_taken%.*?Survived%duration%/;
  1950. const templateMatch = statsText.match(templatePattern);
  1951. if (templateMatch) {
  1952. const parts = statsText.split(/Kills|Dealt|Taken|Survived/);
  1953. if (parts.length >= 5) {
  1954. return {
  1955. username: parts[0].trim(),
  1956. kills: this.cleanNumber(parts[1]),
  1957. damageDealt: this.cleanNumber(parts[2]),
  1958. damageTaken: this.cleanNumber(parts[3]),
  1959. duration: this.extractDuration(parts[4]), // Extract full duration with unit
  1960. position: rankContent.replace("##", "#"),
  1961. };
  1962. }
  1963. }
  1964. // Generic parsing as fallback
  1965. const usernameMatch = statsText.match(/^([^0-9]+)/);
  1966. if (usernameMatch) {
  1967. stats.username = usernameMatch[1].trim();
  1968. }
  1969. const killsMatch = statsText.match(/Kills[^0-9]*(\d+)/i);
  1970. if (killsMatch) {
  1971. stats.kills = this.cleanNumber(killsMatch[1]);
  1972. }
  1973. const dealtMatch = statsText.match(/Dealt[^0-9]*(\d+)/i);
  1974. if (dealtMatch) {
  1975. stats.damageDealt = this.cleanNumber(dealtMatch[1]);
  1976. }
  1977. const takenMatch = statsText.match(/Taken[^0-9]*(\d+)/i);
  1978. if (takenMatch) {
  1979. stats.damageTaken = this.cleanNumber(takenMatch[1]);
  1980. }
  1981. // Extract survival time with unit
  1982. const survivalMatch = statsText.match(/Survived[^0-9]*(\d+\s*[smh])/i);
  1983. if (survivalMatch) {
  1984. stats.duration = survivalMatch[1].trim();
  1985. }
  1986. stats.position = rankContent.replace("##", "#");
  1987. return stats;
  1988. }
  1989. }
  1990.  
  1991.  
  1992. ;// ./src/UpdateChecker.ts
  1993. var UpdateChecker_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  1994. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  1995. return new (P || (P = Promise))(function (resolve, reject) {
  1996. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  1997. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  1998. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  1999. step((generator = generator.apply(thisArg, _arguments || [])).next());
  2000. });
  2001. };
  2002. const packageInfo = __webpack_require__(330);
  2003. const config = __webpack_require__(272);
  2004. class UpdateChecker {
  2005. constructor(kxsClient) {
  2006. this.remoteScriptUrl = config.base_url + "/download/latest-dev.js";
  2007. this.kxsClient = kxsClient;
  2008. if (this.kxsClient.isAutoUpdateEnabled) {
  2009. this.checkForUpdate();
  2010. }
  2011. }
  2012. downloadScript() {
  2013. return UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2014. return new Promise((resolve, reject) => {
  2015. GM.xmlHttpRequest({
  2016. method: "GET",
  2017. url: this.remoteScriptUrl,
  2018. headers: {
  2019. "Cache-Control": "no-cache, no-store, must-revalidate",
  2020. "Pragma": "no-cache",
  2021. "Expires": "0"
  2022. },
  2023. nocache: true,
  2024. responseType: "blob",
  2025. onload: (response) => {
  2026. if (response.status === 200) {
  2027. const blob = new Blob([response.response], { type: 'application/javascript' });
  2028. const downloadUrl = window.URL.createObjectURL(blob);
  2029. const downloadLink = document.createElement('a');
  2030. downloadLink.href = downloadUrl;
  2031. downloadLink.download = 'KxsClient.user.js';
  2032. document.body.appendChild(downloadLink);
  2033. downloadLink.click();
  2034. document.body.removeChild(downloadLink);
  2035. window.URL.revokeObjectURL(downloadUrl);
  2036. resolve();
  2037. }
  2038. else {
  2039. reject(new Error("Error downloading script: " + response.statusText));
  2040. }
  2041. },
  2042. onerror: (error) => {
  2043. reject(new Error("Error during script download: " + error));
  2044. }
  2045. });
  2046. });
  2047. });
  2048. }
  2049. getNewScriptVersion() {
  2050. return UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2051. return new Promise((resolve, reject) => {
  2052. GM.xmlHttpRequest({
  2053. method: "GET",
  2054. url: this.remoteScriptUrl,
  2055. headers: {
  2056. "Cache-Control": "no-cache, no-store, must-revalidate",
  2057. "Pragma": "no-cache",
  2058. "Expires": "0"
  2059. },
  2060. nocache: true,
  2061. onload: (response) => {
  2062. if (response.status === 200) {
  2063. const scriptContent = response.responseText;
  2064. const versionMatch = scriptContent.match(/\/\/\s*@version\s+([\d.]+)/);
  2065. if (versionMatch && versionMatch[1]) {
  2066. resolve(versionMatch[1]);
  2067. }
  2068. else {
  2069. reject(new Error("Script version was not found in the file."));
  2070. }
  2071. }
  2072. else {
  2073. reject(new Error("Error retrieving remote script: " + response.statusText));
  2074. }
  2075. },
  2076. onerror: (error) => {
  2077. reject(new Error("Error during remote script request: " + error));
  2078. }
  2079. });
  2080. });
  2081. });
  2082. }
  2083. checkForUpdate() {
  2084. return UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2085. const localScriptVersion = yield this.getCurrentScriptVersion();
  2086. const hostedScriptVersion = yield this.getNewScriptVersion();
  2087. this.hostedScriptVersion = hostedScriptVersion;
  2088. if (localScriptVersion !== hostedScriptVersion) {
  2089. this.displayUpdateNotification();
  2090. }
  2091. else {
  2092. this.kxsClient.nm.showNotification("Client is up to date", "success", 2300);
  2093. }
  2094. });
  2095. }
  2096. displayUpdateNotification() {
  2097. const modal = document.createElement("div");
  2098. modal.style.position = "fixed";
  2099. modal.style.top = "50%";
  2100. modal.style.left = "50%";
  2101. modal.style.transform = "translate(-50%, -50%)";
  2102. modal.style.backgroundColor = "rgb(250, 250, 250)";
  2103. modal.style.borderRadius = "10px";
  2104. modal.style.padding = "20px";
  2105. modal.style.width = "400px";
  2106. modal.style.boxShadow = "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)";
  2107. modal.style.border = "1px solid rgb(229, 229, 229)";
  2108. const header = document.createElement("div");
  2109. header.style.display = "flex";
  2110. header.style.alignItems = "center";
  2111. header.style.marginBottom = "15px";
  2112. const title = document.createElement("h3");
  2113. title.textContent = "Download Update";
  2114. title.style.margin = "0";
  2115. title.style.fontSize = "16px";
  2116. title.style.fontWeight = "600";
  2117. header.appendChild(title);
  2118. const closeButton = document.createElement("button");
  2119. closeButton.innerHTML = "×";
  2120. closeButton.style.marginLeft = "auto";
  2121. closeButton.style.border = "none";
  2122. closeButton.style.background = "none";
  2123. closeButton.style.fontSize = "20px";
  2124. closeButton.style.cursor = "pointer";
  2125. closeButton.style.padding = "0 5px";
  2126. closeButton.onclick = () => modal.remove();
  2127. header.appendChild(closeButton);
  2128. const content = document.createElement("div");
  2129. content.innerHTML = `A new version of KxsClient is available!<br>
  2130. Locale: ${this.getCurrentScriptVersion()} | On web: ${this.hostedScriptVersion}<br>
  2131. Click the button below to update now.`;
  2132. content.style.marginBottom = "20px";
  2133. content.style.color = "rgb(75, 85, 99)";
  2134. const updateButton = document.createElement("button");
  2135. updateButton.textContent = "Update Now";
  2136. updateButton.style.backgroundColor = "rgb(59, 130, 246)";
  2137. updateButton.style.color = "white";
  2138. updateButton.style.padding = "8px 16px";
  2139. updateButton.style.borderRadius = "6px";
  2140. updateButton.style.border = "none";
  2141. updateButton.style.cursor = "pointer";
  2142. updateButton.style.width = "100%";
  2143. updateButton.onclick = () => UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2144. try {
  2145. yield this.downloadScript();
  2146. this.kxsClient.nm.showNotification("Download started", "success", 2300);
  2147. modal.remove();
  2148. }
  2149. catch (error) {
  2150. this.kxsClient.nm.showNotification("Download failed: " + error.message, "info", 5000);
  2151. }
  2152. });
  2153. modal.appendChild(header);
  2154. modal.appendChild(content);
  2155. modal.appendChild(updateButton);
  2156. document.body.appendChild(modal);
  2157. }
  2158. getCurrentScriptVersion() {
  2159. return packageInfo.version;
  2160. }
  2161. }
  2162.  
  2163.  
  2164. ;// ./src/DiscordRichPresence.ts
  2165. const DiscordRichPresence_packageInfo = __webpack_require__(330);
  2166. class DiscordWebSocket {
  2167. constructor(kxsClient, token) {
  2168. this.ws = null;
  2169. this.heartbeatInterval = 0;
  2170. this.sequence = null;
  2171. this.isAuthenticated = false;
  2172. this.kxsClient = kxsClient;
  2173. }
  2174. connect() {
  2175. if (this.kxsClient.discordToken === ""
  2176. || this.kxsClient.discordToken === null
  2177. || this.kxsClient.discordToken === undefined) {
  2178. return;
  2179. }
  2180. this.ws = new WebSocket('wss://gateway.discord.gg/?v=9&encoding=json');
  2181. this.ws.onopen = () => {
  2182. console.log('WebSocket connection established');
  2183. };
  2184. this.ws.onmessage = (event) => {
  2185. const data = JSON.parse(event.data);
  2186. this.handleMessage(data);
  2187. };
  2188. this.ws.onerror = (error) => {
  2189. this.kxsClient.nm.showNotification('WebSocket error: ' + error.type, 'error', 5000);
  2190. };
  2191. this.ws.onclose = () => {
  2192. this.kxsClient.nm.showNotification('Disconnected from Discord gateway', 'info', 5000);
  2193. clearInterval(this.heartbeatInterval);
  2194. this.isAuthenticated = false;
  2195. };
  2196. }
  2197. identify() {
  2198. const payload = {
  2199. op: 2,
  2200. d: {
  2201. token: this.kxsClient.discordToken,
  2202. properties: {
  2203. $os: 'linux',
  2204. $browser: 'chrome',
  2205. $device: 'chrome'
  2206. },
  2207. presence: {
  2208. activities: [{
  2209. name: "KxsClient",
  2210. type: 0,
  2211. application_id: "1321193265533550602",
  2212. assets: {
  2213. large_image: "mp:app-assets/1321193265533550602/1322173537326338058.png?size=512",
  2214. large_text: "KxsClient v" + DiscordRichPresence_packageInfo.version,
  2215. }
  2216. }],
  2217. status: 'online',
  2218. afk: false
  2219. }
  2220. }
  2221. };
  2222. this.send(payload);
  2223. }
  2224. handleMessage(data) {
  2225. switch (data.op) {
  2226. case 10: // Hello
  2227. const { heartbeat_interval } = data.d;
  2228. this.startHeartbeat(heartbeat_interval);
  2229. this.identify();
  2230. this.kxsClient.nm.showNotification('Started Discord RPC', 'success', 3000);
  2231. break;
  2232. case 11: // Heartbeat ACK
  2233. console.log('Heartbeat acknowledged');
  2234. break;
  2235. case 0: // Dispatch
  2236. this.sequence = data.s;
  2237. if (data.t === 'READY') {
  2238. this.isAuthenticated = true;
  2239. this.kxsClient.nm.showNotification('Connected to Discord gateway', 'success', 2500);
  2240. }
  2241. break;
  2242. }
  2243. }
  2244. startHeartbeat(interval) {
  2245. this.heartbeatInterval = setInterval(() => {
  2246. this.send({
  2247. op: 1,
  2248. d: this.sequence
  2249. });
  2250. }, interval);
  2251. }
  2252. send(data) {
  2253. var _a;
  2254. if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
  2255. this.ws.send(JSON.stringify(data));
  2256. }
  2257. }
  2258. disconnect() {
  2259. if (this.ws) {
  2260. clearInterval(this.heartbeatInterval);
  2261. this.ws.close();
  2262. }
  2263. }
  2264. }
  2265.  
  2266.  
  2267. ;// ./src/NotificationManager.ts
  2268. class NotificationManager {
  2269. constructor() {
  2270. this.notifications = [];
  2271. this.NOTIFICATION_HEIGHT = 65; // Height + margin
  2272. this.NOTIFICATION_MARGIN = 10;
  2273. this.addGlobalStyles();
  2274. }
  2275. static getInstance() {
  2276. if (!NotificationManager.instance) {
  2277. NotificationManager.instance = new NotificationManager();
  2278. }
  2279. return NotificationManager.instance;
  2280. }
  2281. addGlobalStyles() {
  2282. const styleSheet = document.createElement("style");
  2283. styleSheet.textContent = `
  2284. @keyframes slideIn {
  2285. 0% { transform: translateX(-120%); opacity: 0; }
  2286. 50% { transform: translateX(10px); opacity: 0.8; }
  2287. 100% { transform: translateX(0); opacity: 1; }
  2288. }
  2289. @keyframes slideOut {
  2290. 0% { transform: translateX(0); opacity: 1; }
  2291. 50% { transform: translateX(10px); opacity: 0.8; }
  2292. 100% { transform: translateX(-120%); opacity: 0; }
  2293. }
  2294. @keyframes slideLeft {
  2295. from { transform-origin: right; transform: scaleX(1); }
  2296. to { transform-origin: right; transform: scaleX(0); }
  2297. }
  2298. @keyframes bounce {
  2299. 0%, 100% { transform: scale(1); }
  2300. 50% { transform: scale(1.1); }
  2301. }
  2302. `;
  2303. document.head.appendChild(styleSheet);
  2304. }
  2305. updateNotificationPositions() {
  2306. this.notifications.forEach((notification, index) => {
  2307. const topPosition = 20 + (index * this.NOTIFICATION_HEIGHT);
  2308. notification.style.top = `${topPosition}px`;
  2309. });
  2310. }
  2311. removeNotification(notification) {
  2312. const index = this.notifications.indexOf(notification);
  2313. if (index > -1) {
  2314. this.notifications.splice(index, 1);
  2315. this.updateNotificationPositions();
  2316. }
  2317. }
  2318. getIconConfig(type) {
  2319. const configs = {
  2320. success: {
  2321. color: '#4CAF50',
  2322. svg: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
  2323. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
  2324. </svg>`
  2325. },
  2326. error: {
  2327. color: '#F44336',
  2328. svg: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
  2329. <path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>
  2330. </svg>`
  2331. },
  2332. info: {
  2333. color: '#FFD700',
  2334. svg: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
  2335. <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
  2336. </svg>`
  2337. }
  2338. };
  2339. return configs[type];
  2340. }
  2341. showNotification(message, type, duration = 5000) {
  2342. const notification = document.createElement("div");
  2343. // Base styles
  2344. Object.assign(notification.style, {
  2345. position: "fixed",
  2346. top: "20px",
  2347. left: "20px",
  2348. padding: "12px 20px",
  2349. backgroundColor: "#333333",
  2350. color: "white",
  2351. zIndex: "9999",
  2352. minWidth: "200px",
  2353. borderRadius: "4px",
  2354. display: "flex",
  2355. alignItems: "center",
  2356. gap: "10px",
  2357. transform: "translateX(-120%)",
  2358. opacity: "0",
  2359. boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)"
  2360. });
  2361. // Create icon
  2362. const icon = document.createElement("div");
  2363. Object.assign(icon.style, {
  2364. width: "20px",
  2365. height: "20px",
  2366. display: "flex",
  2367. alignItems: "center",
  2368. justifyContent: "center",
  2369. animation: "bounce 0.5s ease-in-out"
  2370. });
  2371. const iconConfig = this.getIconConfig(type);
  2372. icon.style.color = iconConfig.color;
  2373. icon.innerHTML = iconConfig.svg;
  2374. // Create message
  2375. const messageDiv = document.createElement("div");
  2376. messageDiv.textContent = message;
  2377. messageDiv.style.flex = "1";
  2378. // Create progress bar
  2379. const progressBar = document.createElement("div");
  2380. Object.assign(progressBar.style, {
  2381. height: "4px",
  2382. backgroundColor: "#e6f3ff",
  2383. width: "100%",
  2384. position: "absolute",
  2385. bottom: "0",
  2386. left: "0",
  2387. animation: `slideLeft ${duration}ms linear forwards`
  2388. });
  2389. // Assemble notification
  2390. notification.appendChild(icon);
  2391. notification.appendChild(messageDiv);
  2392. notification.appendChild(progressBar);
  2393. document.body.appendChild(notification);
  2394. // Add to stack and update positions
  2395. this.notifications.push(notification);
  2396. this.updateNotificationPositions();
  2397. // Entrance animation
  2398. requestAnimationFrame(() => {
  2399. notification.style.transition = "all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55)";
  2400. notification.style.animation = "slideIn 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards";
  2401. });
  2402. // Exit animation and cleanup
  2403. setTimeout(() => {
  2404. notification.style.animation = "slideOut 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards";
  2405. setTimeout(() => {
  2406. this.removeNotification(notification);
  2407. notification.remove();
  2408. }, 500);
  2409. }, duration);
  2410. }
  2411. }
  2412.  
  2413.  
  2414. ;// ./src/ClientSecondaryMenu.ts
  2415.  
  2416. class KxsLegacyClientSecondaryMenu {
  2417. constructor(kxsClient) {
  2418. this.kxsClient = kxsClient;
  2419. this.isClientMenuVisible = false;
  2420. this.isDragging = false;
  2421. this.dragOffset = { x: 0, y: 0 };
  2422. this.sections = [];
  2423. this.menu = document.createElement("div");
  2424. this.boundShiftListener = this.handleShiftPress.bind(this);
  2425. this.boundMouseDownListener = this.handleMouseDown.bind(this);
  2426. this.boundMouseMoveListener = this.handleMouseMove.bind(this);
  2427. this.boundMouseUpListener = this.handleMouseUp.bind(this);
  2428. this.initMenu();
  2429. this.addShiftListener();
  2430. this.addDragListeners();
  2431. }
  2432. handleShiftPress(event) {
  2433. if (event.key === "Shift" && event.location == 2) {
  2434. this.clearMenu();
  2435. this.toggleMenuVisibility();
  2436. this.loadOption();
  2437. }
  2438. }
  2439. handleMouseDown(e) {
  2440. if (e.target instanceof HTMLElement && !e.target.matches("input, select, button")) {
  2441. this.isDragging = true;
  2442. const rect = this.menu.getBoundingClientRect();
  2443. this.dragOffset = {
  2444. x: e.clientX - rect.left,
  2445. y: e.clientY - rect.top,
  2446. };
  2447. this.menu.style.cursor = "grabbing";
  2448. }
  2449. }
  2450. handleMouseMove(e) {
  2451. if (!this.isDragging)
  2452. return;
  2453. e.preventDefault();
  2454. const newX = e.clientX - this.dragOffset.x;
  2455. const newY = e.clientY - this.dragOffset.y;
  2456. const maxX = window.innerWidth - this.menu.offsetWidth;
  2457. const maxY = window.innerHeight - this.menu.offsetHeight;
  2458. this.menu.style.left = `${Math.max(0, Math.min(newX, maxX))}px`;
  2459. this.menu.style.top = `${Math.max(0, Math.min(newY, maxY))}px`;
  2460. }
  2461. handleMouseUp() {
  2462. this.isDragging = false;
  2463. this.menu.style.cursor = "move";
  2464. }
  2465. initMenu() {
  2466. this.menu.id = "kxsMenuIG";
  2467. this.applyMenuStyles();
  2468. this.createHeader();
  2469. document.body.appendChild(this.menu);
  2470. }
  2471. loadOption() {
  2472. let HUD = this.addSection("HUD");
  2473. this.addOption(HUD, {
  2474. label: "Use Legacy Menu",
  2475. value: this.kxsClient.isLegaySecondaryMenu,
  2476. type: "toggle",
  2477. onChange: (value) => {
  2478. this.kxsClient.isLegaySecondaryMenu = !this.kxsClient.isLegaySecondaryMenu;
  2479. this.kxsClient.updateLocalStorage();
  2480. this.kxsClient.secondaryMenu = new KxsClientSecondaryMenu(this.kxsClient);
  2481. this.destroy();
  2482. },
  2483. });
  2484. this.addOption(HUD, {
  2485. label: "Show Ping",
  2486. value: this.kxsClient.isPingVisible,
  2487. type: "toggle",
  2488. onChange: (value) => {
  2489. this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible;
  2490. this.kxsClient.updatePingVisibility();
  2491. this.kxsClient.updateLocalStorage();
  2492. },
  2493. });
  2494. this.addOption(HUD, {
  2495. label: "Show FPS",
  2496. value: this.kxsClient.isFpsVisible,
  2497. type: "toggle",
  2498. onChange: (value) => {
  2499. this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible;
  2500. this.kxsClient.updateFpsVisibility();
  2501. this.kxsClient.updateLocalStorage();
  2502. },
  2503. });
  2504. this.addOption(HUD, {
  2505. label: "Show Kills",
  2506. value: this.kxsClient.isKillsVisible,
  2507. type: "toggle",
  2508. onChange: (value) => {
  2509. this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible;
  2510. this.kxsClient.updateKillsVisibility();
  2511. this.kxsClient.updateLocalStorage();
  2512. },
  2513. });
  2514. this.addOption(HUD, {
  2515. label: "Kill Feed Blint Text",
  2516. value: this.kxsClient.isKillFeedBlint,
  2517. type: "toggle",
  2518. onChange: (value) => {
  2519. this.kxsClient.isKillFeedBlint = !this.kxsClient.isKillFeedBlint;
  2520. this.kxsClient.updateLocalStorage();
  2521. },
  2522. });
  2523. let musicSection = this.addSection("Music");
  2524. this.addOption(musicSection, {
  2525. label: "Death sound",
  2526. value: this.kxsClient.isDeathSoundEnabled,
  2527. type: "toggle",
  2528. onChange: (value) => {
  2529. this.kxsClient.isDeathSoundEnabled = !this.kxsClient.isDeathSoundEnabled;
  2530. this.kxsClient.updateLocalStorage();
  2531. },
  2532. });
  2533. this.addOption(musicSection, {
  2534. label: "Win sound",
  2535. value: this.kxsClient.isWinSoundEnabled,
  2536. type: "toggle",
  2537. onChange: (value) => {
  2538. this.kxsClient.isWinSoundEnabled = !this.kxsClient.isWinSoundEnabled;
  2539. this.kxsClient.updateLocalStorage();
  2540. },
  2541. });
  2542. let pluginsSection = this.addSection("Plugins");
  2543. this.addOption(pluginsSection, {
  2544. label: "Webhook URL",
  2545. value: this.kxsClient.discordWebhookUrl || "",
  2546. type: "input",
  2547. onChange: (value) => {
  2548. value = value.toString().trim();
  2549. this.kxsClient.discordWebhookUrl = value;
  2550. this.kxsClient.discordTracker.setWebhookUrl(value);
  2551. this.kxsClient.updateLocalStorage();
  2552. },
  2553. });
  2554. this.addOption(pluginsSection, {
  2555. label: "Heal Warning",
  2556. value: this.kxsClient.isHealthWarningEnabled,
  2557. type: "toggle",
  2558. onChange: (value) => {
  2559. this.kxsClient.isHealthWarningEnabled = !this.kxsClient.isHealthWarningEnabled;
  2560. this.kxsClient.updateLocalStorage();
  2561. },
  2562. });
  2563. this.addOption(pluginsSection, {
  2564. label: "Update Checker",
  2565. value: this.kxsClient.isAutoUpdateEnabled,
  2566. type: "toggle",
  2567. onChange: (value) => {
  2568. this.kxsClient.isAutoUpdateEnabled = !this.kxsClient.isAutoUpdateEnabled;
  2569. this.kxsClient.updateLocalStorage();
  2570. },
  2571. });
  2572. this.addOption(HUD, {
  2573. label: `Spotify Player`,
  2574. value: this.kxsClient.isSpotifyPlayerEnabled,
  2575. type: "toggle",
  2576. onChange: () => {
  2577. this.kxsClient.isSpotifyPlayerEnabled = !this.kxsClient.isSpotifyPlayerEnabled;
  2578. this.kxsClient.updateLocalStorage();
  2579. this.kxsClient.toggleSpotifyMenu();
  2580. },
  2581. });
  2582. this.addOption(pluginsSection, {
  2583. label: `Uncap FPS`,
  2584. value: this.kxsClient.isFpsUncapped,
  2585. type: "toggle",
  2586. onChange: () => {
  2587. this.kxsClient.isFpsUncapped = !this.kxsClient.isFpsUncapped;
  2588. this.kxsClient.toggleFpsUncap();
  2589. this.kxsClient.updateLocalStorage();
  2590. },
  2591. });
  2592. this.addOption(pluginsSection, {
  2593. label: `Winning Animation`,
  2594. value: this.kxsClient.isWinningAnimationEnabled,
  2595. type: "toggle",
  2596. onChange: () => {
  2597. this.kxsClient.isWinningAnimationEnabled = !this.kxsClient.isWinningAnimationEnabled;
  2598. this.kxsClient.updateLocalStorage();
  2599. },
  2600. });
  2601. this.addOption(pluginsSection, {
  2602. label: `Rich Presence (Account token required)`,
  2603. value: this.kxsClient.discordToken || "",
  2604. type: "input",
  2605. onChange: (value) => {
  2606. value = value.toString().trim();
  2607. this.kxsClient.discordToken = this.kxsClient.parseToken(value);
  2608. this.kxsClient.discordRPC.disconnect();
  2609. this.kxsClient.discordRPC.connect();
  2610. this.kxsClient.updateLocalStorage();
  2611. },
  2612. });
  2613. this.addOption(pluginsSection, {
  2614. label: `Kill Leader Tracking`,
  2615. value: this.kxsClient.isKillLeaderTrackerEnabled,
  2616. type: "toggle",
  2617. onChange: (value) => {
  2618. this.kxsClient.isKillLeaderTrackerEnabled = !this.kxsClient.isKillLeaderTrackerEnabled;
  2619. this.kxsClient.updateLocalStorage();
  2620. },
  2621. });
  2622. this.addOption(pluginsSection, {
  2623. label: `Friends Detector (separe with ',')`,
  2624. value: this.kxsClient.all_friends,
  2625. type: "input",
  2626. onChange: (value) => {
  2627. this.kxsClient.all_friends = value;
  2628. this.kxsClient.updateLocalStorage();
  2629. },
  2630. });
  2631. }
  2632. clearMenu() {
  2633. this.sections.forEach((section) => {
  2634. if (section.element) {
  2635. section.element.remove();
  2636. }
  2637. });
  2638. this.sections = [];
  2639. }
  2640. applyMenuStyles() {
  2641. Object.assign(this.menu.style, {
  2642. backgroundColor: "rgba(30, 30, 30, 0.95)",
  2643. padding: "15px",
  2644. borderRadius: "10px",
  2645. boxShadow: "0 4px 15px rgba(0, 0, 0, 0.7)",
  2646. zIndex: "10001",
  2647. width: "300px",
  2648. fontFamily: "Arial, sans-serif",
  2649. color: "#fff",
  2650. maxHeight: "500px",
  2651. overflowY: "auto",
  2652. position: "fixed",
  2653. top: "15%",
  2654. left: "10%",
  2655. cursor: "move",
  2656. display: "none",
  2657. });
  2658. }
  2659. createHeader() {
  2660. const title = document.createElement("h2");
  2661. title.textContent = "KxsClient alpha";
  2662. Object.assign(title.style, {
  2663. margin: "0 0 10px",
  2664. textAlign: "center",
  2665. fontSize: "18px",
  2666. color: "#FFAE00",
  2667. });
  2668. const subtitle = document.createElement("p");
  2669. subtitle.textContent = "reset with tab";
  2670. Object.assign(subtitle.style, {
  2671. margin: "0 0 10px",
  2672. textAlign: "center",
  2673. fontSize: "12px",
  2674. color: "#ccc",
  2675. });
  2676. this.menu.appendChild(title);
  2677. this.menu.appendChild(subtitle);
  2678. }
  2679. addSection(title) {
  2680. const section = {
  2681. title,
  2682. options: [],
  2683. };
  2684. const sectionElement = document.createElement("div");
  2685. sectionElement.className = "menu-section";
  2686. const sectionTitle = document.createElement("h3");
  2687. sectionTitle.textContent = title;
  2688. Object.assign(sectionTitle.style, {
  2689. margin: "15px 0 10px",
  2690. fontSize: "16px",
  2691. color: "#4CAF50",
  2692. });
  2693. sectionElement.appendChild(sectionTitle);
  2694. this.menu.appendChild(sectionElement);
  2695. // Stocker la référence à l'élément DOM
  2696. section.element = sectionElement;
  2697. this.sections.push(section);
  2698. return section;
  2699. }
  2700. addOption(section, option) {
  2701. section.options.push(option);
  2702. const optionDiv = document.createElement("div");
  2703. Object.assign(optionDiv.style, {
  2704. display: "flex",
  2705. justifyContent: "space-between",
  2706. alignItems: "center",
  2707. marginBottom: "8px",
  2708. padding: "4px",
  2709. borderRadius: "4px",
  2710. backgroundColor: "rgba(255, 255, 255, 0.1)",
  2711. });
  2712. const label = document.createElement("span");
  2713. label.textContent = option.label;
  2714. label.style.color = "#fff";
  2715. const valueElement = option.type === "toggle"
  2716. ? this.createToggleElement(option)
  2717. : this.createInputElement(option);
  2718. optionDiv.appendChild(label);
  2719. optionDiv.appendChild(valueElement);
  2720. // Utiliser la référence stockée à l'élément de section
  2721. if (section.element) {
  2722. section.element.appendChild(optionDiv);
  2723. }
  2724. }
  2725. createToggleElement(option) {
  2726. const toggle = document.createElement("div");
  2727. toggle.style.cursor = "pointer";
  2728. toggle.style.color = option.value ? "#4CAF50" : "#ff4444";
  2729. toggle.textContent = String(option.value);
  2730. toggle.addEventListener("click", () => {
  2731. var _a;
  2732. const newValue = !option.value;
  2733. option.value = newValue;
  2734. toggle.textContent = String(newValue);
  2735. toggle.style.color = newValue ? "#4CAF50" : "#ff4444";
  2736. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, newValue);
  2737. });
  2738. return toggle;
  2739. }
  2740. createInputElement(option) {
  2741. const input = document.createElement("input");
  2742. input.type = "text";
  2743. input.value = String(option.value);
  2744. Object.assign(input.style, {
  2745. backgroundColor: "rgba(255, 255, 255, 0.1)",
  2746. border: "none",
  2747. borderRadius: "3px",
  2748. color: "#FFAE00",
  2749. padding: "2px 5px",
  2750. width: "60px",
  2751. textAlign: "right",
  2752. });
  2753. input.addEventListener("change", () => {
  2754. var _a;
  2755. option.value = input.value;
  2756. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, input.value);
  2757. });
  2758. return input;
  2759. }
  2760. addShiftListener() {
  2761. window.addEventListener("keydown", this.boundShiftListener);
  2762. }
  2763. addDragListeners() {
  2764. this.menu.addEventListener("mousedown", this.boundMouseDownListener);
  2765. window.addEventListener("mousemove", this.boundMouseMoveListener);
  2766. window.addEventListener("mouseup", this.boundMouseUpListener);
  2767. }
  2768. toggleMenuVisibility() {
  2769. this.isClientMenuVisible = !this.isClientMenuVisible;
  2770. this.kxsClient.nm.showNotification(this.isClientMenuVisible ? "Opening menu..." : "Closing menu...", "info", 1400);
  2771. this.menu.style.display = this.isClientMenuVisible ? "block" : "none";
  2772. }
  2773. destroy() {
  2774. // Remove event listeners
  2775. window.removeEventListener("keydown", this.boundShiftListener);
  2776. this.menu.removeEventListener("mousedown", this.boundMouseDownListener);
  2777. window.removeEventListener("mousemove", this.boundMouseMoveListener);
  2778. window.removeEventListener("mouseup", this.boundMouseUpListener);
  2779. // Remove all section elements and clear sections array
  2780. this.sections.forEach(section => {
  2781. if (section.element) {
  2782. // Remove all option elements within the section
  2783. const optionElements = section.element.querySelectorAll("div");
  2784. optionElements.forEach(element => {
  2785. // Remove event listeners from toggle and input elements
  2786. const interactive = element.querySelector("div, input");
  2787. if (interactive) {
  2788. interactive.replaceWith(interactive.cloneNode(true));
  2789. }
  2790. element.remove();
  2791. });
  2792. section.element.remove();
  2793. }
  2794. });
  2795. this.sections = [];
  2796. // Remove the menu from DOM
  2797. this.menu.remove();
  2798. // Reset instance variables
  2799. this.isClientMenuVisible = false;
  2800. this.isDragging = false;
  2801. this.dragOffset = { x: 0, y: 0 };
  2802. this.menu = null;
  2803. // Clear references
  2804. this.kxsClient = null;
  2805. this.boundShiftListener = null;
  2806. this.boundMouseDownListener = null;
  2807. this.boundMouseMoveListener = null;
  2808. this.boundMouseUpListener = null;
  2809. }
  2810. }
  2811.  
  2812.  
  2813. ;// ./src/ClientSecondaryMenuRework.ts
  2814.  
  2815.  
  2816. class KxsClientSecondaryMenu {
  2817. constructor(kxsClient) {
  2818. this.searchTerm = '';
  2819. this.shiftListener = (event) => {
  2820. if (event.key === "Shift" && event.location == 2) {
  2821. this.clearMenu();
  2822. this.toggleMenuVisibility();
  2823. this.loadOption();
  2824. // Ensure options are displayed after loading
  2825. this.filterOptions();
  2826. }
  2827. };
  2828. this.mouseMoveListener = (e) => {
  2829. if (this.isDragging) {
  2830. const x = e.clientX - this.dragOffset.x;
  2831. const y = e.clientY - this.dragOffset.y;
  2832. this.menu.style.transform = 'none';
  2833. this.menu.style.left = `${x}px`;
  2834. this.menu.style.top = `${y}px`;
  2835. }
  2836. };
  2837. this.mouseUpListener = () => {
  2838. this.isDragging = false;
  2839. this.menu.style.cursor = "grab";
  2840. };
  2841. this.kxsClient = kxsClient;
  2842. this.isClientMenuVisible = false;
  2843. this.isDragging = false;
  2844. this.dragOffset = { x: 0, y: 0 };
  2845. this.sections = [];
  2846. this.allOptions = [];
  2847. this.activeCategory = "ALL";
  2848. this.menu = document.createElement("div");
  2849. this.initMenu();
  2850. this.addShiftListener();
  2851. this.addDragListeners();
  2852. }
  2853. initMenu() {
  2854. this.menu.id = "kxsMenuIG";
  2855. this.applyMenuStyles();
  2856. this.createHeader();
  2857. this.createGridContainer();
  2858. document.body.appendChild(this.menu);
  2859. }
  2860. applyMenuStyles() {
  2861. Object.assign(this.menu.style, {
  2862. backgroundColor: "rgba(17, 24, 39, 0.95)",
  2863. padding: "20px",
  2864. borderRadius: "12px",
  2865. boxShadow: "0 4px 20px rgba(0, 0, 0, 0.8)",
  2866. zIndex: "10001",
  2867. width: "800px",
  2868. fontFamily: "'Segoe UI', Arial, sans-serif",
  2869. color: "#fff",
  2870. maxHeight: "80vh",
  2871. overflowY: "auto",
  2872. overflowX: "hidden", // Prevent horizontal scrolling
  2873. position: "fixed",
  2874. top: "10%",
  2875. left: "50%",
  2876. transform: "translateX(-50%)",
  2877. display: "none",
  2878. boxSizing: "border-box", // Include padding in width calculation
  2879. });
  2880. }
  2881. createHeader() {
  2882. const header = document.createElement("div");
  2883. header.style.marginBottom = "20px";
  2884. header.innerHTML = `
  2885. <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; width: 100%; box-sizing: border-box;">
  2886. <div style="display: flex; align-items: center; gap: 10px;">
  2887. <img src="${kxs_logo}"
  2888. alt="Logo" style="width: 24px; height: 24px;">
  2889. <span style="font-size: 20px; font-weight: bold;">KXS CLIENT</span>
  2890. </div>
  2891. <div style="display: flex; gap: 10px;">
  2892. <button style="
  2893. padding: 6px;
  2894. background: none;
  2895. border: none;
  2896. color: white;
  2897. cursor: pointer;
  2898. font-size: 18px;
  2899. ">×</button>
  2900. </div>
  2901. </div>
  2902. <div style="display: flex; flex-direction: column; gap: 10px; margin-bottom: 15px; width: 100%; box-sizing: border-box;">
  2903. <div style="display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 5px;">
  2904. ${["ALL", "HUD", "SERVER", "MECHANIC"].map(cat => `
  2905. <button class="category-btn" data-category="${cat}" style="
  2906. padding: 6px 16px;
  2907. background: ${this.activeCategory === cat ? '#3B82F6' : 'rgba(55, 65, 81, 0.8)'};
  2908. border: none;
  2909. border-radius: 6px;
  2910. color: white;
  2911. cursor: pointer;
  2912. font-size: 14px;
  2913. transition: background 0.2s;
  2914. ">${cat}</button>
  2915. `).join('')}
  2916. </div>
  2917. <div style="display: flex; width: 100%; box-sizing: border-box;">
  2918. <div style="position: relative; width: 100%; box-sizing: border-box;">
  2919. <input type="text" id="kxsSearchInput" placeholder="Search options..." style="
  2920. width: 100%;
  2921. padding: 8px 12px 8px 32px;
  2922. background: rgba(55, 65, 81, 0.8);
  2923. border: none;
  2924. border-radius: 6px;
  2925. color: white;
  2926. font-size: 14px;
  2927. outline: none;
  2928. box-sizing: border-box;
  2929. ">
  2930. <div style="
  2931. position: absolute;
  2932. left: 10px;
  2933. top: 50%;
  2934. transform: translateY(-50%);
  2935. width: 14px;
  2936. height: 14px;
  2937. ">
  2938. <svg fill="#ffffff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  2939. <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
  2940. </svg>
  2941. </div>
  2942. </div>
  2943. </div>
  2944. </div>
  2945. `;
  2946. header.querySelectorAll('.category-btn').forEach(btn => {
  2947. btn.addEventListener('click', (e) => {
  2948. const category = e.target.dataset.category;
  2949. if (category) {
  2950. this.setActiveCategory(category);
  2951. }
  2952. });
  2953. });
  2954. const closeButton = header.querySelector('button');
  2955. closeButton === null || closeButton === void 0 ? void 0 : closeButton.addEventListener('click', () => {
  2956. this.toggleMenuVisibility();
  2957. });
  2958. const searchInput = header.querySelector('#kxsSearchInput');
  2959. searchInput === null || searchInput === void 0 ? void 0 : searchInput.addEventListener('input', (e) => {
  2960. this.searchTerm = e.target.value.toLowerCase();
  2961. this.filterOptions();
  2962. });
  2963. this.menu.appendChild(header);
  2964. }
  2965. clearMenu() {
  2966. const gridContainer = document.getElementById('kxsMenuGrid');
  2967. if (gridContainer) {
  2968. gridContainer.innerHTML = '';
  2969. }
  2970. // Reset search term when clearing menu
  2971. this.searchTerm = '';
  2972. const searchInput = document.getElementById('kxsSearchInput');
  2973. if (searchInput) {
  2974. searchInput.value = '';
  2975. }
  2976. }
  2977. loadOption() {
  2978. // Clear existing options to avoid duplicates
  2979. this.allOptions = [];
  2980. let HUD = this.addSection("HUD", 'HUD');
  2981. let MECHANIC = this.addSection("MECHANIC", 'MECHANIC');
  2982. let SERVER = this.addSection("SERVER", 'SERVER');
  2983. this.addOption(HUD, {
  2984. label: "Show Ping",
  2985. value: this.kxsClient.isPingVisible,
  2986. category: "HUD",
  2987. icon: '<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><defs><style>.a{fill:none;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;}</style></defs><path class="a" d="M34.6282,24.0793a14.7043,14.7043,0,0,0-22.673,1.7255"></path><path class="a" d="M43.5,20.5846a23.8078,23.8078,0,0,0-39,0"></path><path class="a" d="M43.5,20.5845,22.0169,29.0483a5.5583,5.5583,0,1,0,6.2116,8.7785l.0153.0206Z"></path></g></svg>',
  2988. type: "toggle",
  2989. onChange: (value) => {
  2990. this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible;
  2991. this.kxsClient.updatePingVisibility();
  2992. this.kxsClient.updateLocalStorage();
  2993. },
  2994. });
  2995. this.addOption(HUD, {
  2996. label: "Show FPS",
  2997. value: this.kxsClient.isFpsVisible,
  2998. category: "HUD",
  2999. type: "toggle",
  3000. icon: '<svg fill="#000000" viewBox="0 0 24 24" id="60fps" data-name="Flat Line" xmlns="http://www.w3.org/2000/svg" class="icon flat-line"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><rect id="primary" x="10.5" y="8.5" width="14" height="7" rx="1" transform="translate(5.5 29.5) rotate(-90)" style="fill: none; stroke: #000000; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></rect><path id="primary-2" data-name="primary" d="M3,12H9a1,1,0,0,1,1,1v5a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V6A1,1,0,0,1,4,5h6" style="fill: none; stroke: #000000; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path></g></svg>',
  3001. onChange: (value) => {
  3002. this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible;
  3003. this.kxsClient.updateFpsVisibility();
  3004. this.kxsClient.updateLocalStorage();
  3005. },
  3006. });
  3007. this.addOption(HUD, {
  3008. label: "Show Kills",
  3009. value: this.kxsClient.isKillsVisible,
  3010. type: "toggle",
  3011. category: "HUD",
  3012. icon: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M14.7245 11.2754L16 12.4999L10.0129 17.8218C8.05054 19.5661 5.60528 20.6743 3 20.9999L3.79443 19.5435C4.6198 18.0303 5.03249 17.2737 5.50651 16.5582C5.92771 15.9224 6.38492 15.3113 6.87592 14.7278C7.42848 14.071 8.0378 13.4615 9.25644 12.2426L12 9.49822M11.5 8.99787L17.4497 3.04989C18.0698 2.42996 19.0281 2.3017 19.7894 2.73674C20.9027 3.37291 21.1064 4.89355 20.1997 5.80024L19.8415 6.15847C19.6228 6.3771 19.3263 6.49992 19.0171 6.49992H18L16 8.49992V8.67444C16 9.16362 16 9.40821 15.9447 9.63839C15.8957 9.84246 15.8149 10.0375 15.7053 10.2165C15.5816 10.4183 15.4086 10.5913 15.0627 10.9372L14.2501 11.7498L11.5 8.99787Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>',
  3013. onChange: (value) => {
  3014. this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible;
  3015. this.kxsClient.updateKillsVisibility();
  3016. this.kxsClient.updateLocalStorage();
  3017. },
  3018. });
  3019. this.addOption(HUD, {
  3020. label: "Use Legacy Menu",
  3021. value: this.kxsClient.isLegaySecondaryMenu,
  3022. type: "toggle",
  3023. category: 'HUD',
  3024. icon: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M4 12H20M4 8H20M4 16H12" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>',
  3025. onChange: (value) => {
  3026. this.kxsClient.isLegaySecondaryMenu = !this.kxsClient.isLegaySecondaryMenu;
  3027. this.kxsClient.updateLocalStorage();
  3028. this.kxsClient.secondaryMenu = new KxsLegacyClientSecondaryMenu(this.kxsClient);
  3029. this.destroy();
  3030. },
  3031. });
  3032. this.addOption(MECHANIC, {
  3033. label: "Death sound",
  3034. value: this.kxsClient.isDeathSoundEnabled,
  3035. type: "toggle",
  3036. icon: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M19 21C19 21.5523 18.5523 22 18 22H14H10H6C5.44771 22 5 21.5523 5 21V18.75C5 17.7835 4.2165 17 3.25 17C2.55964 17 2 16.4404 2 15.75V11C2 5.47715 6.47715 1 12 1C17.5228 1 22 5.47715 22 11V15.75C22 16.4404 21.4404 17 20.75 17C19.7835 17 19 17.7835 19 18.75V21ZM17 20V18.75C17 16.9358 18.2883 15.4225 20 15.075V11C20 6.58172 16.4183 3 12 3C7.58172 3 4 6.58172 4 11V15.075C5.71168 15.4225 7 16.9358 7 18.75V20H9V18C9 17.4477 9.44771 17 10 17C10.5523 17 11 17.4477 11 18V20H13V18C13 17.4477 13.4477 17 14 17C14.5523 17 15 17.4477 15 18V20H17ZM11 12.5C11 13.8807 8.63228 15 7.25248 15C5.98469 15 5.99206 14.055 6.00161 12.8306V12.8305C6.00245 12.7224 6.00331 12.6121 6.00331 12.5C6.00331 11.1193 7.12186 10 8.50166 10C9.88145 10 11 11.1193 11 12.5ZM17.9984 12.8306C17.9975 12.7224 17.9967 12.6121 17.9967 12.5C17.9967 11.1193 16.8781 10 15.4983 10C14.1185 10 13 11.1193 13 12.5C13 13.8807 15.3677 15 16.7475 15C18.0153 15 18.0079 14.055 17.9984 12.8306Z" fill="#000000"></path> </g></svg>',
  3037. category: "MECHANIC",
  3038. onChange: (value) => {
  3039. this.kxsClient.isDeathSoundEnabled = !this.kxsClient.isDeathSoundEnabled;
  3040. this.kxsClient.updateLocalStorage();
  3041. },
  3042. });
  3043. this.addOption(HUD, {
  3044. label: "Win sound",
  3045. value: this.kxsClient.isWinSoundEnabled,
  3046. type: "toggle",
  3047. icon: '<svg fill="#000000" version="1.1" id="Trophy_x5F_cup" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M190.878,111.272c31.017-11.186,53.254-40.907,53.254-75.733l-0.19-8.509h-48.955V5H64.222v22.03H15.266l-0.19,8.509 c0,34.825,22.237,64.546,53.254,75.733c7.306,18.421,22.798,31.822,41.878,37.728v20c-0.859,15.668-14.112,29-30,29v18h-16v35H195 v-35h-16v-18c-15.888,0-29.141-13.332-30-29v-20C168.08,143.094,183.572,129.692,190.878,111.272z M195,44h30.563 c-0.06,0.427-0.103,1.017-0.171,1.441c-3.02,18.856-14.543,34.681-30.406,44.007C195.026,88.509,195,44,195,44z M33.816,45.441 c-0.068-0.424-0.111-1.014-0.171-1.441h30.563c0,0-0.026,44.509,0.013,45.448C48.359,80.122,36.837,64.297,33.816,45.441z M129.604,86.777l-20.255,13.52l6.599-23.442L96.831,61.77l24.334-0.967l8.44-22.844l8.44,22.844l24.334,0.967L143.26,76.856 l6.599,23.442L129.604,86.777z"></path> </g></svg>',
  3048. category: "HUD",
  3049. onChange: (value) => {
  3050. this.kxsClient.isWinSoundEnabled = !this.kxsClient.isWinSoundEnabled;
  3051. this.kxsClient.updateLocalStorage();
  3052. },
  3053. });
  3054. this.addOption(SERVER, {
  3055. label: "Webhook URL",
  3056. value: this.kxsClient.discordWebhookUrl || "",
  3057. icon: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M12.52 3.046a3 3 0 0 0-2.13 5.486 1 1 0 0 1 .306 1.38l-3.922 6.163a2 2 0 1 1-1.688-1.073l3.44-5.405a5 5 0 1 1 8.398-2.728 1 1 0 1 1-1.97-.348 3 3 0 0 0-2.433-3.475zM10 6a2 2 0 1 1 3.774.925l3.44 5.405a5 5 0 1 1-1.427 8.5 1 1 0 0 1 1.285-1.532 3 3 0 1 0 .317-4.83 1 1 0 0 1-1.38-.307l-3.923-6.163A2 2 0 0 1 10 6zm-5.428 6.9a1 1 0 0 1-.598 1.281A3 3 0 1 0 8.001 17a1 1 0 0 1 1-1h8.266a2 2 0 1 1 0 2H9.9a5 5 0 1 1-6.61-5.698 1 1 0 0 1 1.282.597Z" fill="#000000"></path> </g></svg>',
  3058. category: "SERVER",
  3059. type: "input",
  3060. onChange: (value) => {
  3061. value = value.toString().trim();
  3062. this.kxsClient.discordWebhookUrl = value;
  3063. this.kxsClient.discordTracker.setWebhookUrl(value);
  3064. this.kxsClient.updateLocalStorage();
  3065. },
  3066. });
  3067. this.addOption(MECHANIC, {
  3068. label: "Heal Warning",
  3069. value: this.kxsClient.isHealthWarningEnabled,
  3070. type: "toggle",
  3071. category: "MECHANIC",
  3072. icon: '<svg viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>health</title> <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="add" fill="#000000" transform="translate(42.666667, 64.000000)"> <path d="M365.491733,234.665926 C339.947827,276.368766 302.121072,321.347032 252.011468,369.600724 L237.061717,383.7547 C234.512147,386.129148 231.933605,388.511322 229.32609,390.901222 L213.333333,405.333333 C205.163121,398.070922 197.253659,390.878044 189.604949,383.7547 L174.655198,369.600724 C124.545595,321.347032 86.7188401,276.368766 61.174934,234.665926 L112.222458,234.666026 C134.857516,266.728129 165.548935,301.609704 204.481843,339.08546 L213.333333,347.498667 L214.816772,346.115558 C257.264819,305.964102 290.400085,268.724113 314.444476,234.665648 L365.491733,234.665926 Z M149.333333,58.9638831 L213.333333,186.944 L245.333333,122.963883 L269.184,170.666667 L426.666667,170.666667 L426.666667,213.333333 L247.850667,213.333333 L213.333333,282.36945 L149.333333,154.368 L119.851392,213.333333 L3.55271368e-14,213.333333 L3.55271368e-14,170.666667 L93.4613333,170.666667 L149.333333,58.9638831 Z M290.133333,0 C353.756537,0 405.333333,51.5775732 405.333333,115.2 C405.333333,126.248908 404.101625,137.626272 401.63821,149.33209 L357.793994,149.332408 C360.62486,138.880112 362.217829,128.905378 362.584434,119.422244 L362.666667,115.2 C362.666667,75.1414099 330.192075,42.6666667 290.133333,42.6666667 C273.651922,42.6666667 258.124715,48.1376509 245.521279,58.0219169 L241.829932,61.1185374 L213.366947,86.6338354 L184.888885,61.1353673 C171.661383,49.2918281 154.669113,42.6666667 136.533333,42.6666667 C96.4742795,42.6666667 64,75.1409461 64,115.2 C64,125.932203 65.6184007,137.316846 68.8727259,149.332605 L25.028457,149.33209 C22.5650412,137.626272 21.3333333,126.248908 21.3333333,115.2 C21.3333333,51.5767968 72.9101302,0 136.533333,0 C166.046194,0 192.966972,11.098031 213.350016,29.348444 C233.716605,11.091061 260.629741,0 290.133333,0 Z" id="Combined-Shape"> </path> </g> </g> </g></svg>',
  3073. onChange: (value) => {
  3074. this.kxsClient.isHealthWarningEnabled = !this.kxsClient.isHealthWarningEnabled;
  3075. this.kxsClient.updateLocalStorage();
  3076. },
  3077. });
  3078. this.addOption(SERVER, {
  3079. label: "Update Checker",
  3080. value: this.kxsClient.isAutoUpdateEnabled,
  3081. type: "toggle",
  3082. icon: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M18.4721 16.7023C17.3398 18.2608 15.6831 19.3584 13.8064 19.7934C11.9297 20.2284 9.95909 19.9716 8.25656 19.0701C6.55404 18.1687 5.23397 16.6832 4.53889 14.8865C3.84381 13.0898 3.82039 11.1027 4.47295 9.29011C5.12551 7.47756 6.41021 5.96135 8.09103 5.02005C9.77184 4.07875 11.7359 3.77558 13.6223 4.16623C15.5087 4.55689 17.1908 5.61514 18.3596 7.14656C19.5283 8.67797 20.1052 10.5797 19.9842 12.5023M19.9842 12.5023L21.4842 11.0023M19.9842 12.5023L18.4842 11.0023" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M12 8V12L15 15" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>',
  3083. category: "SERVER",
  3084. onChange: (value) => {
  3085. this.kxsClient.isAutoUpdateEnabled = !this.kxsClient.isAutoUpdateEnabled;
  3086. this.kxsClient.updateLocalStorage();
  3087. },
  3088. });
  3089. this.addOption(MECHANIC, {
  3090. label: `Uncap FPS`,
  3091. value: this.kxsClient.isFpsUncapped,
  3092. type: "toggle",
  3093. icon: '<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> <title>ic_fluent_fps_960_24_filled</title> <desc>Created with Sketch.</desc> <g id="🔍-Product-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="ic_fluent_fps_960_24_filled" fill="#000000" fill-rule="nonzero"> <path d="M11.75,15 C12.9926407,15 14,16.0073593 14,17.25 C14,18.440864 13.0748384,19.4156449 11.9040488,19.4948092 L11.75,19.5 L11,19.5 L11,21.25 C11,21.6296958 10.7178461,21.943491 10.3517706,21.9931534 L10.25,22 C9.87030423,22 9.55650904,21.7178461 9.50684662,21.3517706 L9.5,21.25 L9.5,15.75 C9.5,15.3703042 9.78215388,15.056509 10.1482294,15.0068466 L10.25,15 L11.75,15 Z M18,15 C19.1045695,15 20,15.8954305 20,17 C20,17.4142136 19.6642136,17.75 19.25,17.75 C18.8703042,17.75 18.556509,17.4678461 18.5068466,17.1017706 L18.5,17 C18.5,16.7545401 18.3231248,16.5503916 18.0898756,16.5080557 L18,16.5 L17.375,16.5 C17.029822,16.5 16.75,16.779822 16.75,17.125 C16.75,17.4387982 16.9812579,17.6985831 17.2826421,17.7432234 L17.375,17.75 L17.875,17.75 C19.0486051,17.75 20,18.7013949 20,19.875 C20,20.9975788 19.1295366,21.91685 18.0267588,21.9946645 L17.875,22 L17.25,22 C16.1454305,22 15.25,21.1045695 15.25,20 C15.25,19.5857864 15.5857864,19.25 16,19.25 C16.3796958,19.25 16.693491,19.5321539 16.7431534,19.8982294 L16.75,20 C16.75,20.2454599 16.9268752,20.4496084 17.1601244,20.4919443 L17.25,20.5 L17.875,20.5 C18.220178,20.5 18.5,20.220178 18.5,19.875 C18.5,19.5612018 18.2687421,19.3014169 17.9673579,19.2567766 L17.875,19.25 L17.375,19.25 C16.2013949,19.25 15.25,18.2986051 15.25,17.125 C15.25,16.0024212 16.1204634,15.08315 17.2232412,15.0053355 L17.375,15 L18,15 Z M7.75,15 C8.16421356,15 8.5,15.3357864 8.5,15.75 C8.5,16.1296958 8.21784612,16.443491 7.85177056,16.4931534 L7.75,16.5 L5.5,16.4990964 L5.5,18.0020964 L7.25,18.002809 C7.66421356,18.002809 8,18.3385954 8,18.752809 C8,19.1325047 7.71784612,19.4462999 7.35177056,19.4959623 L7.25,19.502809 L5.5,19.5020964 L5.5,21.2312276 C5.5,21.6109234 5.21784612,21.9247186 4.85177056,21.974381 L4.75,21.9812276 C4.37030423,21.9812276 4.05650904,21.6990738 4.00684662,21.3329982 L4,21.2312276 L4,15.75 C4,15.3703042 4.28215388,15.056509 4.64822944,15.0068466 L4.75,15 L7.75,15 Z M11.75,16.5 L11,16.5 L11,18 L11.75,18 C12.1642136,18 12.5,17.6642136 12.5,17.25 C12.5,16.8703042 12.2178461,16.556509 11.8517706,16.5068466 L11.75,16.5 Z M5,3 C6.65685425,3 8,4.34314575 8,6 L7.99820112,6.1048763 L8,6.15469026 L8,10 C8,11.5976809 6.75108004,12.9036609 5.17627279,12.9949073 L5,13 L4.7513884,13 C3.23183855,13 2,11.7681615 2,10.2486116 C2,9.69632685 2.44771525,9.2486116 3,9.2486116 C3.51283584,9.2486116 3.93550716,9.63465179 3.99327227,10.1319905 L4,10.2486116 C4,10.6290103 4.28267621,10.9433864 4.64942945,10.9931407 L4.7513884,11 L5,11 C5.51283584,11 5.93550716,10.6139598 5.99327227,10.1166211 L6,10 L5.99991107,8.82932572 C5.68715728,8.93985718 5.35060219,9 5,9 C3.34314575,9 2,7.65685425 2,6 C2,4.34314575 3.34314575,3 5,3 Z M12.2512044,3 C13.7707542,3 15.0025928,4.23183855 15.0025928,5.7513884 C15.0025928,6.30367315 14.5548775,6.7513884 14.0025928,6.7513884 C13.489757,6.7513884 13.0670856,6.36534821 13.0093205,5.86800953 L13.0025928,5.7513884 C13.0025928,5.37098974 12.7199166,5.05661365 12.3531633,5.00685929 L12.2512044,5 L12.0025928,5 C11.489757,5 11.0670856,5.38604019 11.0093205,5.88337887 L11.0025928,6 L11.0026817,7.17067428 C11.3154355,7.06014282 11.6519906,7 12.0025928,7 C13.659447,7 15.0025928,8.34314575 15.0025928,10 C15.0025928,11.6568542 13.659447,13 12.0025928,13 C10.3457385,13 9.0025928,11.6568542 9.0025928,10 L9.00441213,9.89453033 L9.0025928,9.84530974 L9.0025928,6 C9.0025928,4.40231912 10.2515128,3.09633912 11.82632,3.00509269 L12.0025928,3 L12.2512044,3 Z M19,3 C20.5976809,3 21.9036609,4.24891996 21.9949073,5.82372721 L22,6 L22,10 C22,11.6568542 20.6568542,13 19,13 C17.4023191,13 16.0963391,11.75108 16.0050927,10.1762728 L16,10 L16,6 C16,4.34314575 17.3431458,3 19,3 Z M12.0025928,9 C11.450308,9 11.0025928,9.44771525 11.0025928,10 C11.0025928,10.5522847 11.450308,11 12.0025928,11 C12.5548775,11 13.0025928,10.5522847 13.0025928,10 C13.0025928,9.44771525 12.5548775,9 12.0025928,9 Z M19,5 C18.4871642,5 18.0644928,5.38604019 18.0067277,5.88337887 L18,6 L18,10 C18,10.5522847 18.4477153,11 19,11 C19.5128358,11 19.9355072,10.6139598 19.9932723,10.1166211 L20,10 L20,6 C20,5.44771525 19.5522847,5 19,5 Z M5,5 C4.44771525,5 4,5.44771525 4,6 C4,6.55228475 4.44771525,7 5,7 C5.55228475,7 6,6.55228475 6,6 C6,5.44771525 5.55228475,5 5,5 Z" id="🎨Color"> </path> </g> </g> </g></svg>',
  3094. category: 'MECHANIC',
  3095. onChange: () => {
  3096. this.kxsClient.isFpsUncapped = !this.kxsClient.isFpsUncapped;
  3097. this.kxsClient.toggleFpsUncap();
  3098. this.kxsClient.updateLocalStorage();
  3099. },
  3100. });
  3101. this.addOption(HUD, {
  3102. label: `Winning Animation`,
  3103. value: this.kxsClient.isWinningAnimationEnabled,
  3104. icon: '<svg fill="#000000" height="200px" width="200px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 448.881 448.881" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <path d="M189.82,138.531c-8.92,0-16.611,6.307-18.353,15.055l-11.019,55.306c-3.569,20.398-7.394,40.53-9.946,59.652h-0.513 c-2.543-19.122-5.35-37.474-9.176-57.615l-11.85-62.35c-1.112-5.832-6.206-10.048-12.139-10.048H95.819 c-5.854,0-10.909,4.114-12.099,9.853l-12.497,60.507c-4.332,21.159-8.414,41.805-11.213,60.413h-0.513 c-2.8-17.332-6.369-39.511-10.196-59.901l-10.024-54.643c-1.726-9.403-9.922-16.23-19.479-16.23c-6.05,0-11.774,2.77-15.529,7.52 c-3.755,4.751-5.133,10.965-3.733,16.851l32.747,137.944c1.322,5.568,6.299,9.503,12.022,9.503h22.878 c5.792,0,10.809-4.028,12.061-9.689l14.176-64.241c4.083-17.334,6.883-33.648,9.946-53.019h0.507 c2.037,19.627,4.845,35.685,8.157,53.019l12.574,63.96c1.136,5.794,6.222,9.97,12.125,9.97h22.325 c5.638,0,10.561-3.811,11.968-9.269l35.919-139.158c1.446-5.607,0.225-11.564-3.321-16.136 C201.072,141.207,195.612,138.531,189.82,138.531z"></path> <path d="M253.516,138.531c-10.763,0-19.495,8.734-19.495,19.503v132.821c0,10.763,8.732,19.495,19.495,19.495 c10.771,0,19.503-8.732,19.503-19.495V158.034C273.019,147.265,264.287,138.531,253.516,138.531z"></path> <path d="M431.034,138.531c-9.861,0-17.847,7.995-17.847,17.847v32.373c0,25.748,0.761,48.945,3.313,71.637h-0.763 c-7.652-19.379-17.847-40.786-28.041-58.891l-32.14-56.704c-2.193-3.865-6.299-6.26-10.747-6.26h-25.818 c-6.827,0-12.357,5.529-12.357,12.357v141.615c0,9.86,7.987,17.847,17.847,17.847c9.853,0,17.84-7.987,17.84-17.847v-33.905 c0-28.042-0.514-52.258-1.532-74.941l0.769-0.256c8.406,20.141,19.627,42.318,29.823,60.671l33.174,59.909 c2.177,3.927,6.321,6.369,10.809,6.369h21.159c6.828,0,12.357-5.53,12.357-12.357V156.378 C448.881,146.526,440.894,138.531,431.034,138.531z"></path> </g> </g></svg>',
  3105. category: "HUD",
  3106. type: "toggle",
  3107. onChange: () => {
  3108. this.kxsClient.isWinningAnimationEnabled = !this.kxsClient.isWinningAnimationEnabled;
  3109. this.kxsClient.updateLocalStorage();
  3110. },
  3111. });
  3112. this.addOption(HUD, {
  3113. label: `Spotify Player`,
  3114. value: this.kxsClient.isSpotifyPlayerEnabled,
  3115. icon: '<svg fill="#000000" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>spotify</title> <path d="M24.849 14.35c-3.206-1.616-6.988-2.563-10.991-2.563-2.278 0-4.484 0.306-6.58 0.881l0.174-0.041c-0.123 0.040-0.265 0.063-0.412 0.063-0.76 0-1.377-0.616-1.377-1.377 0-0.613 0.401-1.132 0.954-1.311l0.010-0.003c5.323-1.575 14.096-1.275 19.646 2.026 0.426 0.258 0.706 0.719 0.706 1.245 0 0.259-0.068 0.502-0.186 0.712l0.004-0.007c-0.29 0.345-0.721 0.563-1.204 0.563-0.273 0-0.529-0.070-0.752-0.192l0.008 0.004zM24.699 18.549c-0.201 0.332-0.561 0.55-0.971 0.55-0.225 0-0.434-0.065-0.61-0.178l0.005 0.003c-2.739-1.567-6.021-2.49-9.518-2.49-1.925 0-3.784 0.28-5.539 0.801l0.137-0.035c-0.101 0.032-0.217 0.051-0.337 0.051-0.629 0-1.139-0.51-1.139-1.139 0-0.509 0.333-0.939 0.793-1.086l0.008-0.002c1.804-0.535 3.878-0.843 6.023-0.843 3.989 0 7.73 1.064 10.953 2.925l-0.106-0.056c0.297 0.191 0.491 0.52 0.491 0.894 0 0.227-0.071 0.437-0.192 0.609l0.002-0.003zM22.899 22.673c-0.157 0.272-0.446 0.452-0.777 0.452-0.186 0-0.359-0.057-0.502-0.154l0.003 0.002c-2.393-1.346-5.254-2.139-8.299-2.139-1.746 0-3.432 0.261-5.020 0.745l0.122-0.032c-0.067 0.017-0.145 0.028-0.224 0.028-0.512 0-0.927-0.415-0.927-0.927 0-0.432 0.296-0.795 0.696-0.898l0.006-0.001c1.581-0.47 3.397-0.74 5.276-0.74 3.402 0 6.596 0.886 9.366 2.44l-0.097-0.050c0.302 0.15 0.506 0.456 0.506 0.809 0 0.172-0.048 0.333-0.132 0.469l0.002-0.004zM16 1.004c0 0 0 0-0 0-8.282 0-14.996 6.714-14.996 14.996s6.714 14.996 14.996 14.996c8.282 0 14.996-6.714 14.996-14.996v0c-0.025-8.272-6.724-14.971-14.993-14.996h-0.002z"></path> </g></svg>',
  3116. category: "HUD",
  3117. type: "toggle",
  3118. onChange: () => {
  3119. this.kxsClient.isSpotifyPlayerEnabled = !this.kxsClient.isSpotifyPlayerEnabled;
  3120. this.kxsClient.updateLocalStorage();
  3121. this.kxsClient.toggleSpotifyMenu();
  3122. },
  3123. });
  3124. this.addOption(HUD, {
  3125. label: "Kill Feed Blint Text",
  3126. value: this.kxsClient.isKillFeedBlint,
  3127. icon: `<svg fill="#000000" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title></title> <g data-name="Layer 2" id="Layer_2"> <path d="M18,11a1,1,0,0,1-1,1,5,5,0,0,0-5,5,1,1,0,0,1-2,0,5,5,0,0,0-5-5,1,1,0,0,1,0-2,5,5,0,0,0,5-5,1,1,0,0,1,2,0,5,5,0,0,0,5,5A1,1,0,0,1,18,11Z"></path> <path d="M19,24a1,1,0,0,1-1,1,2,2,0,0,0-2,2,1,1,0,0,1-2,0,2,2,0,0,0-2-2,1,1,0,0,1,0-2,2,2,0,0,0,2-2,1,1,0,0,1,2,0,2,2,0,0,0,2,2A1,1,0,0,1,19,24Z"></path> <path d="M28,17a1,1,0,0,1-1,1,4,4,0,0,0-4,4,1,1,0,0,1-2,0,4,4,0,0,0-4-4,1,1,0,0,1,0-2,4,4,0,0,0,4-4,1,1,0,0,1,2,0,4,4,0,0,0,4,4A1,1,0,0,1,28,17Z"></path> </g> </g></svg>`,
  3128. category: "HUD",
  3129. type: "toggle",
  3130. onChange: () => {
  3131. this.kxsClient.isKillFeedBlint = !this.kxsClient.isKillFeedBlint;
  3132. this.kxsClient.updateLocalStorage();
  3133. },
  3134. });
  3135. this.addOption(SERVER, {
  3136. label: `Rich Presence (Account token required)`,
  3137. value: this.kxsClient.discordToken || "",
  3138. icon: '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M18.59 5.88997C17.36 5.31997 16.05 4.89997 14.67 4.65997C14.5 4.95997 14.3 5.36997 14.17 5.69997C12.71 5.47997 11.26 5.47997 9.83001 5.69997C9.69001 5.36997 9.49001 4.95997 9.32001 4.65997C7.94001 4.89997 6.63001 5.31997 5.40001 5.88997C2.92001 9.62997 2.25001 13.28 2.58001 16.87C4.23001 18.1 5.82001 18.84 7.39001 19.33C7.78001 18.8 8.12001 18.23 8.42001 17.64C7.85001 17.43 7.31001 17.16 6.80001 16.85C6.94001 16.75 7.07001 16.64 7.20001 16.54C10.33 18 13.72 18 16.81 16.54C16.94 16.65 17.07 16.75 17.21 16.85C16.7 17.16 16.15 17.42 15.59 17.64C15.89 18.23 16.23 18.8 16.62 19.33C18.19 18.84 19.79 18.1 21.43 16.87C21.82 12.7 20.76 9.08997 18.61 5.88997H18.59ZM8.84001 14.67C7.90001 14.67 7.13001 13.8 7.13001 12.73C7.13001 11.66 7.88001 10.79 8.84001 10.79C9.80001 10.79 10.56 11.66 10.55 12.73C10.55 13.79 9.80001 14.67 8.84001 14.67ZM15.15 14.67C14.21 14.67 13.44 13.8 13.44 12.73C13.44 11.66 14.19 10.79 15.15 10.79C16.11 10.79 16.87 11.66 16.86 12.73C16.86 13.79 16.11 14.67 15.15 14.67Z" fill="#000000"></path> </g></svg>',
  3139. category: "SERVER",
  3140. type: "input",
  3141. onChange: (value) => {
  3142. value = value.toString().trim();
  3143. this.kxsClient.discordToken = this.kxsClient.parseToken(value);
  3144. this.kxsClient.discordRPC.disconnect();
  3145. this.kxsClient.discordRPC.connect();
  3146. this.kxsClient.updateLocalStorage();
  3147. },
  3148. });
  3149. this.addOption(MECHANIC, {
  3150. label: `Kill Leader Tracking`,
  3151. icon: '<svg fill="#000000" viewBox="-4 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>crown</title> <path d="M12 10.938c-1.375 0-2.5-1.125-2.5-2.5 0-1.406 1.125-2.5 2.5-2.5s2.5 1.094 2.5 2.5c0 1.375-1.125 2.5-2.5 2.5zM2.031 9.906c1.094 0 1.969 0.906 1.969 2 0 1.125-0.875 2-1.969 2-1.125 0-2.031-0.875-2.031-2 0-1.094 0.906-2 2.031-2zM22.031 9.906c1.094 0 1.969 0.906 1.969 2 0 1.125-0.875 2-1.969 2-1.125 0-2.031-0.875-2.031-2 0-1.094 0.906-2 2.031-2zM4.219 23.719l-1.656-9.063c0.5-0.094 0.969-0.375 1.344-0.688 1.031 0.938 2.344 1.844 3.594 1.844 1.5 0 2.719-2.313 3.563-4.25 0.281 0.094 0.625 0.188 0.938 0.188s0.656-0.094 0.938-0.188c0.844 1.938 2.063 4.25 3.563 4.25 1.25 0 2.563-0.906 3.594-1.844 0.375 0.313 0.844 0.594 1.344 0.688l-1.656 9.063h-15.563zM3.875 24.5h16.25v1.531h-16.25v-1.531z"></path> </g></svg>',
  3152. category: "MECHANIC",
  3153. value: this.kxsClient.isKillLeaderTrackerEnabled,
  3154. type: "toggle",
  3155. onChange: (value) => {
  3156. this.kxsClient.isKillLeaderTrackerEnabled = !this.kxsClient.isKillLeaderTrackerEnabled;
  3157. this.kxsClient.updateLocalStorage();
  3158. },
  3159. });
  3160. this.addOption(MECHANIC, {
  3161. label: `Friends Detector (separe with ',')`,
  3162. icon: '<svg fill="#000000" viewBox="0 -6 44 44" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M42.001,32.000 L14.010,32.000 C12.908,32.000 12.010,31.104 12.010,30.001 L12.010,28.002 C12.010,27.636 12.211,27.300 12.532,27.124 L22.318,21.787 C19.040,18.242 19.004,13.227 19.004,12.995 L19.010,7.002 C19.010,6.946 19.015,6.891 19.024,6.837 C19.713,2.751 24.224,0.007 28.005,0.007 C28.006,0.007 28.008,0.007 28.009,0.007 C31.788,0.007 36.298,2.749 36.989,6.834 C36.998,6.889 37.003,6.945 37.003,7.000 L37.006,12.994 C37.006,13.225 36.970,18.240 33.693,21.785 L43.479,27.122 C43.800,27.298 44.000,27.634 44.000,28.000 L44.000,30.001 C44.000,31.104 43.103,32.000 42.001,32.000 ZM31.526,22.880 C31.233,22.720 31.039,22.425 31.008,22.093 C30.978,21.761 31.116,21.436 31.374,21.226 C34.971,18.310 35.007,13.048 35.007,12.995 L35.003,7.089 C34.441,4.089 30.883,2.005 28.005,2.005 C25.126,2.006 21.570,4.091 21.010,7.091 L21.004,12.997 C21.004,13.048 21.059,18.327 24.636,21.228 C24.895,21.438 25.033,21.763 25.002,22.095 C24.972,22.427 24.778,22.722 24.485,22.882 L14.010,28.596 L14.010,30.001 L41.999,30.001 L42.000,28.595 L31.526,22.880 ZM18.647,2.520 C17.764,2.177 16.848,1.997 15.995,1.997 C13.116,1.998 9.559,4.083 8.999,7.083 L8.993,12.989 C8.993,13.041 9.047,18.319 12.625,21.220 C12.884,21.430 13.022,21.755 12.992,22.087 C12.961,22.419 12.767,22.714 12.474,22.874 L1.999,28.588 L1.999,29.993 L8.998,29.993 C9.550,29.993 9.997,30.441 9.997,30.993 C9.997,31.545 9.550,31.993 8.998,31.993 L1.999,31.993 C0.897,31.993 -0.000,31.096 -0.000,29.993 L-0.000,27.994 C-0.000,27.629 0.200,27.292 0.521,27.117 L10.307,21.779 C7.030,18.234 6.993,13.219 6.993,12.988 L6.999,6.994 C6.999,6.939 7.004,6.883 7.013,6.829 C7.702,2.744 12.213,-0.000 15.995,-0.000 C15.999,-0.000 16.005,-0.000 16.010,-0.000 C17.101,-0.000 18.262,0.227 19.369,0.656 C19.885,0.856 20.140,1.435 19.941,1.949 C19.740,2.464 19.158,2.720 18.647,2.520 Z"></path> </g></svg>',
  3163. category: "MECHANIC",
  3164. value: this.kxsClient.all_friends,
  3165. type: "input",
  3166. onChange: (value) => {
  3167. this.kxsClient.all_friends = value;
  3168. this.kxsClient.updateLocalStorage();
  3169. },
  3170. });
  3171. }
  3172. createOptionCard(option, container) {
  3173. const optionCard = document.createElement("div");
  3174. Object.assign(optionCard.style, {
  3175. background: "rgba(31, 41, 55, 0.8)",
  3176. borderRadius: "10px",
  3177. padding: "16px",
  3178. display: "flex",
  3179. flexDirection: "column",
  3180. alignItems: "center",
  3181. gap: "12px",
  3182. minHeight: "150px",
  3183. });
  3184. const iconContainer = document.createElement("div");
  3185. Object.assign(iconContainer.style, {
  3186. width: "48px",
  3187. height: "48px",
  3188. borderRadius: "50%",
  3189. display: "flex",
  3190. alignItems: "center",
  3191. justifyContent: "center",
  3192. marginBottom: "8px"
  3193. });
  3194. iconContainer.innerHTML = option.icon || '';
  3195. const title = document.createElement("div");
  3196. title.textContent = option.label;
  3197. title.style.fontSize = "16px";
  3198. title.style.textAlign = "center";
  3199. const control = option.type === "toggle"
  3200. ? this.createToggleButton(option)
  3201. : this.createInputElement(option);
  3202. optionCard.appendChild(iconContainer);
  3203. optionCard.appendChild(title);
  3204. optionCard.appendChild(control);
  3205. container.appendChild(optionCard);
  3206. }
  3207. setActiveCategory(category) {
  3208. this.activeCategory = category;
  3209. this.filterOptions();
  3210. // Update button styles
  3211. this.menu.querySelectorAll('.category-btn').forEach(btn => {
  3212. const btnCategory = btn.dataset.category;
  3213. btn.style.background =
  3214. btnCategory === category ? '#3B82F6' : 'rgba(55, 65, 81, 0.8)';
  3215. });
  3216. }
  3217. filterOptions() {
  3218. const gridContainer = document.getElementById('kxsMenuGrid');
  3219. if (gridContainer) {
  3220. // Clear existing content
  3221. gridContainer.innerHTML = '';
  3222. // Get unique options based on category and search term
  3223. const displayedOptions = new Set();
  3224. this.sections.forEach(section => {
  3225. if (this.activeCategory === 'ALL' || section.category === this.activeCategory) {
  3226. section.options.forEach(option => {
  3227. // Create a unique key for each option
  3228. const optionKey = `${option.label}-${option.category}`;
  3229. // Check if option matches search term
  3230. const matchesSearch = this.searchTerm === '' ||
  3231. option.label.toLowerCase().includes(this.searchTerm) ||
  3232. option.category.toLowerCase().includes(this.searchTerm);
  3233. if (!displayedOptions.has(optionKey) && matchesSearch) {
  3234. displayedOptions.add(optionKey);
  3235. this.createOptionCard(option, gridContainer);
  3236. }
  3237. });
  3238. }
  3239. });
  3240. // Show a message if no options match the search
  3241. if (displayedOptions.size === 0 && this.searchTerm !== '') {
  3242. const noResultsMsg = document.createElement('div');
  3243. noResultsMsg.textContent = `No results found for "${this.searchTerm}"`;
  3244. noResultsMsg.style.gridColumn = '1 / -1';
  3245. noResultsMsg.style.textAlign = 'center';
  3246. noResultsMsg.style.padding = '20px';
  3247. noResultsMsg.style.color = '#9CA3AF';
  3248. gridContainer.appendChild(noResultsMsg);
  3249. }
  3250. }
  3251. }
  3252. createGridContainer() {
  3253. const gridContainer = document.createElement("div");
  3254. Object.assign(gridContainer.style, {
  3255. display: "grid",
  3256. gridTemplateColumns: "repeat(3, 1fr)",
  3257. gap: "16px",
  3258. padding: "16px",
  3259. gridAutoRows: "minmax(150px, auto)",
  3260. overflowY: "auto",
  3261. overflowX: "hidden", // Prevent horizontal scrolling
  3262. maxHeight: "calc(3 * 150px + 2 * 16px)",
  3263. width: "100%",
  3264. boxSizing: "border-box" // Include padding in width calculation
  3265. });
  3266. gridContainer.id = "kxsMenuGrid";
  3267. this.menu.appendChild(gridContainer);
  3268. }
  3269. addOption(section, option) {
  3270. section.options.push(option);
  3271. // Store all options for searching
  3272. this.allOptions.push(option);
  3273. }
  3274. addSection(title, category = "ALL") {
  3275. const section = {
  3276. title,
  3277. options: [],
  3278. category
  3279. };
  3280. const sectionElement = document.createElement("div");
  3281. sectionElement.className = "menu-section";
  3282. sectionElement.style.display = this.activeCategory === "ALL" || this.activeCategory === category ? "block" : "none";
  3283. section.element = sectionElement;
  3284. this.sections.push(section);
  3285. this.menu.appendChild(sectionElement);
  3286. return section;
  3287. }
  3288. createToggleButton(option) {
  3289. const btn = document.createElement("button");
  3290. Object.assign(btn.style, {
  3291. width: "100%",
  3292. padding: "8px",
  3293. background: option.value ? "#059669" : "#DC2626",
  3294. border: "none",
  3295. borderRadius: "6px",
  3296. color: "white",
  3297. cursor: "pointer",
  3298. transition: "background 0.2s",
  3299. fontSize: "14px",
  3300. fontWeight: "bold"
  3301. });
  3302. btn.textContent = option.value ? "ENABLED" : "DISABLED";
  3303. btn.addEventListener("click", () => {
  3304. var _a;
  3305. const newValue = !option.value;
  3306. option.value = newValue;
  3307. btn.textContent = newValue ? "ENABLED" : "DISABLED";
  3308. btn.style.background = newValue ? "#059669" : "#DC2626";
  3309. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, newValue);
  3310. });
  3311. return btn;
  3312. }
  3313. addShiftListener() {
  3314. window.addEventListener("keydown", (event) => {
  3315. if (event.key === "Shift" && event.location == 2) {
  3316. this.clearMenu();
  3317. this.toggleMenuVisibility();
  3318. this.loadOption();
  3319. // Ensure options are displayed after loading
  3320. this.filterOptions();
  3321. }
  3322. });
  3323. }
  3324. createInputElement(option) {
  3325. const input = document.createElement("input");
  3326. input.type = "text";
  3327. input.value = String(option.value);
  3328. Object.assign(input.style, {
  3329. width: "100%",
  3330. padding: "8px",
  3331. background: "rgba(55, 65, 81, 0.8)",
  3332. border: "none",
  3333. borderRadius: "6px",
  3334. color: "#FFAE00",
  3335. fontSize: "14px"
  3336. });
  3337. input.addEventListener("change", () => {
  3338. var _a;
  3339. option.value = input.value;
  3340. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, input.value);
  3341. });
  3342. return input;
  3343. }
  3344. addDragListeners() {
  3345. this.menu.addEventListener('mousedown', (e) => {
  3346. if (e.target instanceof HTMLElement &&
  3347. !e.target.matches("input, select, button, svg, path")) {
  3348. this.isDragging = true;
  3349. const rect = this.menu.getBoundingClientRect();
  3350. this.dragOffset = {
  3351. x: e.clientX - rect.left,
  3352. y: e.clientY - rect.top
  3353. };
  3354. this.menu.style.cursor = "grabbing";
  3355. }
  3356. });
  3357. document.addEventListener('mousemove', (e) => {
  3358. if (this.isDragging) {
  3359. const x = e.clientX - this.dragOffset.x;
  3360. const y = e.clientY - this.dragOffset.y;
  3361. this.menu.style.transform = 'none';
  3362. this.menu.style.left = `${x}px`;
  3363. this.menu.style.top = `${y}px`;
  3364. }
  3365. });
  3366. document.addEventListener('mouseup', () => {
  3367. this.isDragging = false;
  3368. this.menu.style.cursor = "grab";
  3369. });
  3370. }
  3371. toggleMenuVisibility() {
  3372. this.isClientMenuVisible = !this.isClientMenuVisible;
  3373. this.kxsClient.nm.showNotification(this.isClientMenuVisible ? "Opening menu..." : "Closing menu...", "info", 1400);
  3374. this.menu.style.display = this.isClientMenuVisible ? "block" : "none";
  3375. // If opening the menu, make sure to display options
  3376. if (this.isClientMenuVisible) {
  3377. this.filterOptions();
  3378. }
  3379. }
  3380. destroy() {
  3381. // Remove global event listeners
  3382. window.removeEventListener("keydown", this.shiftListener);
  3383. document.removeEventListener('mousemove', this.mouseMoveListener);
  3384. document.removeEventListener('mouseup', this.mouseUpListener);
  3385. // Remove all event listeners from menu elements
  3386. const removeAllListeners = (element) => {
  3387. var _a;
  3388. const clone = element.cloneNode(true);
  3389. (_a = element.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(clone, element);
  3390. };
  3391. // Clean up all buttons and inputs in the menu
  3392. this.menu.querySelectorAll('button, input').forEach(element => {
  3393. removeAllListeners(element);
  3394. });
  3395. // Remove the menu from DOM
  3396. this.menu.remove();
  3397. // Clear all sections
  3398. this.sections.forEach(section => {
  3399. if (section.element) {
  3400. removeAllListeners(section.element);
  3401. section.element.remove();
  3402. delete section.element;
  3403. }
  3404. section.options = [];
  3405. });
  3406. this.sections = [];
  3407. // Reset all class properties
  3408. this.isClientMenuVisible = false;
  3409. this.isDragging = false;
  3410. this.dragOffset = { x: 0, y: 0 };
  3411. this.activeCategory = "ALL";
  3412. // Clear references
  3413. this.menu = null;
  3414. this.kxsClient = null;
  3415. }
  3416. }
  3417.  
  3418.  
  3419. ;// ./src/KxsClient.ts
  3420. var KxsClient_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  3421. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  3422. return new (P || (P = Promise))(function (resolve, reject) {
  3423. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  3424. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  3425. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  3426. step((generator = generator.apply(thisArg, _arguments || [])).next());
  3427. });
  3428. };
  3429.  
  3430.  
  3431.  
  3432.  
  3433.  
  3434.  
  3435.  
  3436.  
  3437.  
  3438.  
  3439. class KxsClient {
  3440. constructor() {
  3441. this.deathObserver = null;
  3442. this.config = __webpack_require__(272);
  3443. this.menu = document.createElement("div");
  3444. this.lastFrameTime = performance.now();
  3445. this.isFpsUncapped = this.getFpsUncappedFromLocalStorage();
  3446. this.isFpsVisible = true;
  3447. this.isPingVisible = true;
  3448. this.isKillsVisible = true;
  3449. this.isDeathSoundEnabled = true;
  3450. this.isWinSoundEnabled = true;
  3451. this.isHealthWarningEnabled = true;
  3452. this.isAutoUpdateEnabled = true;
  3453. this.isWinningAnimationEnabled = true;
  3454. this.isKillLeaderTrackerEnabled = true;
  3455. this.isLegaySecondaryMenu = false;
  3456. this.isKillFeedBlint = false;
  3457. this.isSpotifyPlayerEnabled = false;
  3458. this.discordToken = null;
  3459. this.counters = {};
  3460. this.all_friends = '';
  3461. this.defaultPositions = {
  3462. fps: { left: 20, top: 160 },
  3463. ping: { left: 20, top: 220 },
  3464. kills: { left: 20, top: 280 },
  3465. };
  3466. this.defaultSizes = {
  3467. fps: { width: 100, height: 30 },
  3468. ping: { width: 100, height: 30 },
  3469. kills: { width: 100, height: 30 },
  3470. };
  3471. // Before all, load local storage
  3472. this.loadLocalStorage();
  3473. this.changeSurvevLogo();
  3474. this.nm = NotificationManager.getInstance();
  3475. this.discordRPC = new DiscordWebSocket(this, this.parseToken(this.discordToken));
  3476. this.updater = new UpdateChecker(this);
  3477. this.kill_leader = new KillLeaderTracker(this);
  3478. this.healWarning = new HealthWarning(this);
  3479. this.setAnimationFrameCallback();
  3480. this.loadBackgroundFromLocalStorage();
  3481. this.initDeathDetection();
  3482. this.discordRPC.connect();
  3483. if (this.isLegaySecondaryMenu) {
  3484. this.secondaryMenu = new KxsLegacyClientSecondaryMenu(this);
  3485. }
  3486. else {
  3487. this.secondaryMenu = new KxsClientSecondaryMenu(this);
  3488. }
  3489. this.discordTracker = new DiscordTracking(this, this.discordWebhookUrl);
  3490. if (this.isSpotifyPlayerEnabled) {
  3491. this.createSimpleSpotifyPlayer();
  3492. }
  3493. }
  3494. parseToken(token) {
  3495. if (token) {
  3496. return token.replace(/^(["'`])(.+)\1$/, '$2');
  3497. }
  3498. return null;
  3499. }
  3500. getPlayerName() {
  3501. let config = localStorage.getItem("surviv_config");
  3502. if (config) {
  3503. let configObject = JSON.parse(config);
  3504. return configObject.playerName;
  3505. }
  3506. }
  3507. changeSurvevLogo() {
  3508. var startRowHeader = document.querySelector("#start-row-header");
  3509. if (startRowHeader) {
  3510. startRowHeader.style.backgroundImage =
  3511. `url("${this.config.base_url}/assets/KysClient.gif")`;
  3512. }
  3513. }
  3514. updateLocalStorage() {
  3515. localStorage.setItem("userSettings", JSON.stringify({
  3516. isFpsVisible: this.isFpsVisible,
  3517. isPingVisible: this.isPingVisible,
  3518. isFpsUncapped: this.isFpsUncapped,
  3519. isKillsVisible: this.isKillsVisible,
  3520. discordWebhookUrl: this.discordWebhookUrl,
  3521. isDeathSoundEnabled: this.isDeathSoundEnabled,
  3522. isWinSoundEnabled: this.isWinSoundEnabled,
  3523. isHealthWarningEnabled: this.isHealthWarningEnabled,
  3524. isAutoUpdateEnabled: this.isAutoUpdateEnabled,
  3525. isWinningAnimationEnabled: this.isWinningAnimationEnabled,
  3526. discordToken: this.discordToken,
  3527. isKillLeaderTrackerEnabled: this.isKillLeaderTrackerEnabled,
  3528. isLegaySecondaryMenu: this.isLegaySecondaryMenu,
  3529. isKillFeedBlint: this.isKillFeedBlint,
  3530. all_friends: this.all_friends,
  3531. isSpotifyPlayerEnabled: this.isSpotifyPlayerEnabled
  3532. }));
  3533. }
  3534. ;
  3535. initDeathDetection() {
  3536. const config = {
  3537. childList: true,
  3538. subtree: true,
  3539. attributes: false,
  3540. characterData: false,
  3541. };
  3542. this.deathObserver = new MutationObserver((mutations) => {
  3543. for (const mutation of mutations) {
  3544. if (mutation.addedNodes.length) {
  3545. this.checkForDeathScreen(mutation.addedNodes);
  3546. }
  3547. }
  3548. });
  3549. this.deathObserver.observe(document.body, config);
  3550. }
  3551. checkForDeathScreen(nodes) {
  3552. let loseArray = [
  3553. "died",
  3554. "eliminated",
  3555. "was"
  3556. ];
  3557. let winArray = [
  3558. "Winner",
  3559. "Victory",
  3560. "dinner",
  3561. ];
  3562. nodes.forEach((node) => {
  3563. if (node instanceof HTMLElement) {
  3564. const deathTitle = node.querySelector(".ui-stats-header-title");
  3565. if (loseArray.some((word) => { var _a; return (_a = deathTitle === null || deathTitle === void 0 ? void 0 : deathTitle.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(word); })) {
  3566. this.handlePlayerDeath();
  3567. }
  3568. else if (winArray.some((word) => { var _a; return (_a = deathTitle === null || deathTitle === void 0 ? void 0 : deathTitle.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(word); })) {
  3569. this.handlePlayerWin();
  3570. }
  3571. }
  3572. });
  3573. }
  3574. handlePlayerDeath() {
  3575. return KxsClient_awaiter(this, void 0, void 0, function* () {
  3576. try {
  3577. if (this.isDeathSoundEnabled) {
  3578. const audio = new Audio(this.config.base_url + "/assets/dead.m4a");
  3579. audio.volume = 0.3;
  3580. audio.play().catch((err) => false);
  3581. }
  3582. }
  3583. catch (error) {
  3584. console.error("Reading error:", error);
  3585. }
  3586. const stats = this.getPlayerStats(false);
  3587. yield this.discordTracker.trackGameEnd({
  3588. username: stats.username,
  3589. kills: stats.kills,
  3590. damageDealt: stats.damageDealt,
  3591. damageTaken: stats.damageTaken,
  3592. duration: stats.duration,
  3593. position: stats.position,
  3594. isWin: false,
  3595. });
  3596. });
  3597. }
  3598. handlePlayerWin() {
  3599. return KxsClient_awaiter(this, void 0, void 0, function* () {
  3600. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
  3601. if (this.isWinningAnimationEnabled) {
  3602. this.felicitation();
  3603. }
  3604. const stats = this.getPlayerStats(true);
  3605. yield this.discordTracker.trackGameEnd({
  3606. username: stats.username,
  3607. kills: stats.kills,
  3608. damageDealt: stats.damageDealt,
  3609. damageTaken: stats.damageTaken,
  3610. duration: stats.duration,
  3611. position: stats.position,
  3612. isWin: true,
  3613. stuff: {
  3614. main_weapon: (_a = document.querySelector('#ui-weapon-id-1 .ui-weapon-name')) === null || _a === void 0 ? void 0 : _a.textContent,
  3615. secondary_weapon: (_b = document.querySelector('#ui-weapon-id-2 .ui-weapon-name')) === null || _b === void 0 ? void 0 : _b.textContent,
  3616. soda: (_c = document.querySelector("#ui-loot-soda .ui-loot-count")) === null || _c === void 0 ? void 0 : _c.textContent,
  3617. melees: (_d = document.querySelector('#ui-weapon-id-3 .ui-weapon-name')) === null || _d === void 0 ? void 0 : _d.textContent,
  3618. grenades: (_e = document.querySelector(`#ui-weapon-id-4 .ui-weapon-name`)) === null || _e === void 0 ? void 0 : _e.textContent,
  3619. medkit: (_f = document.querySelector("#ui-loot-healthkit .ui-loot-count")) === null || _f === void 0 ? void 0 : _f.textContent,
  3620. bandage: (_g = document.querySelector("#ui-loot-bandage .ui-loot-count")) === null || _g === void 0 ? void 0 : _g.textContent,
  3621. pills: (_h = document.querySelector("#ui-loot-painkiller .ui-loot-count")) === null || _h === void 0 ? void 0 : _h.textContent,
  3622. backpack: (_j = document.querySelector("#ui-armor-backpack .ui-armor-level")) === null || _j === void 0 ? void 0 : _j.textContent,
  3623. chest: (_k = document.querySelector("#ui-armor-chest .ui-armor-level")) === null || _k === void 0 ? void 0 : _k.textContent,
  3624. helmet: (_l = document.querySelector("#ui-armor-helmet .ui-armor-level")) === null || _l === void 0 ? void 0 : _l.textContent,
  3625. }
  3626. });
  3627. });
  3628. }
  3629. felicitation() {
  3630. const goldText = document.createElement("div");
  3631. goldText.textContent = "#1";
  3632. goldText.style.position = "fixed";
  3633. goldText.style.top = "50%";
  3634. goldText.style.left = "50%";
  3635. goldText.style.transform = "translate(-50%, -50%)";
  3636. goldText.style.fontSize = "80px";
  3637. goldText.style.color = "gold";
  3638. goldText.style.textShadow = "2px 2px 4px rgba(0,0,0,0.3)";
  3639. goldText.style.zIndex = "10000";
  3640. document.body.appendChild(goldText);
  3641. function createConfetti() {
  3642. const colors = [
  3643. "#ff0000",
  3644. "#00ff00",
  3645. "#0000ff",
  3646. "#ffff00",
  3647. "#ff00ff",
  3648. "#00ffff",
  3649. "gold",
  3650. ];
  3651. const confetti = document.createElement("div");
  3652. confetti.style.position = "fixed";
  3653. confetti.style.width = Math.random() * 10 + 5 + "px";
  3654. confetti.style.height = Math.random() * 10 + 5 + "px";
  3655. confetti.style.backgroundColor =
  3656. colors[Math.floor(Math.random() * colors.length)];
  3657. confetti.style.borderRadius = "50%";
  3658. confetti.style.zIndex = "9999";
  3659. confetti.style.left = Math.random() * 100 + "vw";
  3660. confetti.style.top = "-20px";
  3661. document.body.appendChild(confetti);
  3662. let posY = -20;
  3663. let posX = parseFloat(confetti.style.left);
  3664. let rotation = 0;
  3665. let speedY = Math.random() * 2 + 1;
  3666. let speedX = Math.random() * 2 - 1;
  3667. function fall() {
  3668. posY += speedY;
  3669. posX += speedX;
  3670. rotation += 5;
  3671. confetti.style.top = posY + "px";
  3672. confetti.style.left = posX + "vw";
  3673. confetti.style.transform = `rotate(${rotation}deg)`;
  3674. if (posY < window.innerHeight) {
  3675. requestAnimationFrame(fall);
  3676. }
  3677. else {
  3678. confetti.remove();
  3679. }
  3680. }
  3681. fall();
  3682. }
  3683. const confettiInterval = setInterval(() => {
  3684. for (let i = 0; i < 5; i++) {
  3685. createConfetti();
  3686. }
  3687. }, 100);
  3688. if (this.isWinSoundEnabled) {
  3689. const audio = new Audio(this.config.base_url + "/assets/win.m4a");
  3690. audio.play().catch((err) => console.error("Erreur lecture:", err));
  3691. }
  3692. setTimeout(() => {
  3693. clearInterval(confettiInterval);
  3694. goldText.style.transition = "opacity 1s";
  3695. goldText.style.opacity = "0";
  3696. setTimeout(() => goldText.remove(), 1000);
  3697. }, 5000);
  3698. }
  3699. cleanup() {
  3700. if (this.deathObserver) {
  3701. this.deathObserver.disconnect();
  3702. this.deathObserver = null;
  3703. }
  3704. }
  3705. getUsername() {
  3706. const configKey = "surviv_config";
  3707. const savedConfig = localStorage.getItem(configKey);
  3708. const config = JSON.parse(savedConfig);
  3709. if (config.playerName) {
  3710. return config.playerName;
  3711. }
  3712. else {
  3713. return "Player";
  3714. }
  3715. }
  3716. getPlayerStats(win) {
  3717. const statsInfo = win
  3718. ? document.querySelector(".ui-stats-info-player")
  3719. : document.querySelector(".ui-stats-info-player.ui-stats-info-status");
  3720. const rank = document.querySelector(".ui-stats-header-value");
  3721. if (!(statsInfo === null || statsInfo === void 0 ? void 0 : statsInfo.textContent) || !(rank === null || rank === void 0 ? void 0 : rank.textContent)) {
  3722. return {
  3723. username: this.getUsername(),
  3724. kills: 0,
  3725. damageDealt: 0,
  3726. damageTaken: 0,
  3727. duration: "0s",
  3728. position: "#unknown",
  3729. };
  3730. }
  3731. const parsedStats = StatsParser.parse(statsInfo.textContent, rank === null || rank === void 0 ? void 0 : rank.textContent);
  3732. parsedStats.username = this.getUsername();
  3733. return parsedStats;
  3734. }
  3735. setAnimationFrameCallback() {
  3736. this.animationFrameCallback = this.isFpsUncapped
  3737. ? (callback) => setTimeout(callback, 1)
  3738. : window.requestAnimationFrame.bind(window);
  3739. }
  3740. makeResizable(element, storageKey) {
  3741. let isResizing = false;
  3742. let startX, startY, startWidth, startHeight;
  3743. // Ajouter une zone de redimensionnement en bas à droite
  3744. const resizer = document.createElement("div");
  3745. Object.assign(resizer.style, {
  3746. width: "10px",
  3747. height: "10px",
  3748. backgroundColor: "white",
  3749. position: "absolute",
  3750. right: "0",
  3751. bottom: "0",
  3752. cursor: "nwse-resize",
  3753. zIndex: "10001",
  3754. });
  3755. element.appendChild(resizer);
  3756. resizer.addEventListener("mousedown", (event) => {
  3757. isResizing = true;
  3758. startX = event.clientX;
  3759. startY = event.clientY;
  3760. startWidth = element.offsetWidth;
  3761. startHeight = element.offsetHeight;
  3762. event.stopPropagation(); // Empêche l'activation du déplacement
  3763. });
  3764. window.addEventListener("mousemove", (event) => {
  3765. if (isResizing) {
  3766. const newWidth = startWidth + (event.clientX - startX);
  3767. const newHeight = startHeight + (event.clientY - startY);
  3768. element.style.width = `${newWidth}px`;
  3769. element.style.height = `${newHeight}px`;
  3770. // Sauvegarde de la taille
  3771. localStorage.setItem(storageKey, JSON.stringify({
  3772. width: newWidth,
  3773. height: newHeight,
  3774. }));
  3775. }
  3776. });
  3777. window.addEventListener("mouseup", () => {
  3778. isResizing = false;
  3779. });
  3780. const savedSize = localStorage.getItem(storageKey);
  3781. if (savedSize) {
  3782. const { width, height } = JSON.parse(savedSize);
  3783. element.style.width = `${width}px`;
  3784. element.style.height = `${height}px`;
  3785. }
  3786. else {
  3787. element.style.width = "150px"; // Taille par défaut
  3788. element.style.height = "50px";
  3789. }
  3790. }
  3791. makeDraggable(element, storageKey) {
  3792. const gridSystem = new GridSystem();
  3793. let isDragging = false;
  3794. let dragOffset = { x: 0, y: 0 };
  3795. element.addEventListener("mousedown", (event) => {
  3796. if (event.button === 0) {
  3797. // Left click only
  3798. isDragging = true;
  3799. dragOffset = {
  3800. x: event.clientX - element.offsetLeft,
  3801. y: event.clientY - element.offsetTop,
  3802. };
  3803. element.style.cursor = "grabbing";
  3804. }
  3805. });
  3806. window.addEventListener("mousemove", (event) => {
  3807. if (isDragging) {
  3808. const rawX = event.clientX - dragOffset.x;
  3809. const rawY = event.clientY - dragOffset.y;
  3810. // Get snapped coordinates from grid system
  3811. const snapped = gridSystem.snapToGrid(element, rawX, rawY);
  3812. // Prevent moving off screen
  3813. const maxX = window.innerWidth - element.offsetWidth;
  3814. const maxY = window.innerHeight - element.offsetHeight;
  3815. element.style.left = `${Math.max(0, Math.min(snapped.x, maxX))}px`;
  3816. element.style.top = `${Math.max(0, Math.min(snapped.y, maxY))}px`;
  3817. // Highlight nearest grid lines while dragging
  3818. gridSystem.highlightNearestGridLine(rawX, rawY);
  3819. // Save position
  3820. localStorage.setItem(storageKey, JSON.stringify({
  3821. x: parseInt(element.style.left),
  3822. y: parseInt(element.style.top),
  3823. }));
  3824. }
  3825. });
  3826. window.addEventListener("mouseup", () => {
  3827. if (isDragging) {
  3828. isDragging = false;
  3829. element.style.cursor = "move";
  3830. }
  3831. });
  3832. // Load saved position
  3833. const savedPosition = localStorage.getItem(storageKey);
  3834. if (savedPosition) {
  3835. const { x, y } = JSON.parse(savedPosition);
  3836. const snapped = gridSystem.snapToGrid(element, x, y);
  3837. element.style.left = `${snapped.x}px`;
  3838. element.style.top = `${snapped.y}px`;
  3839. }
  3840. }
  3841. getKills() {
  3842. const killElement = document.querySelector(".ui-player-kills.js-ui-player-kills");
  3843. if (killElement) {
  3844. const kills = parseInt(killElement.textContent || "", 10);
  3845. return isNaN(kills) ? 0 : kills;
  3846. }
  3847. return 0;
  3848. }
  3849. getRegionFromLocalStorage() {
  3850. let config = localStorage.getItem("surviv_config");
  3851. if (config) {
  3852. let configObject = JSON.parse(config);
  3853. return configObject.region;
  3854. }
  3855. return null;
  3856. }
  3857. getFpsUncappedFromLocalStorage() {
  3858. const savedConfig = localStorage.getItem("userSettings");
  3859. if (savedConfig) {
  3860. const configObject = JSON.parse(savedConfig);
  3861. return configObject.isFpsUncapped || false;
  3862. }
  3863. return false;
  3864. }
  3865. saveFpsUncappedToLocalStorage() {
  3866. let config = JSON.parse(localStorage.getItem("userSettings")) || {};
  3867. config.isFpsUncapped = this.isFpsUncapped;
  3868. localStorage.setItem("userSettings", JSON.stringify(config));
  3869. }
  3870. saveBackgroundToLocalStorage(image) {
  3871. if (typeof image === "string") {
  3872. localStorage.setItem("lastBackgroundUrl", image);
  3873. }
  3874. if (typeof image === "string") {
  3875. localStorage.setItem("lastBackgroundType", "url");
  3876. localStorage.setItem("lastBackgroundValue", image);
  3877. }
  3878. else {
  3879. localStorage.setItem("lastBackgroundType", "local");
  3880. const reader = new FileReader();
  3881. reader.onload = () => {
  3882. localStorage.setItem("lastBackgroundValue", reader.result);
  3883. };
  3884. reader.readAsDataURL(image);
  3885. }
  3886. }
  3887. loadBackgroundFromLocalStorage() {
  3888. const backgroundType = localStorage.getItem("lastBackgroundType");
  3889. const backgroundValue = localStorage.getItem("lastBackgroundValue");
  3890. const backgroundElement = document.getElementById("background");
  3891. if (backgroundElement && backgroundType && backgroundValue) {
  3892. if (backgroundType === "url") {
  3893. backgroundElement.style.backgroundImage = `url(${backgroundValue})`;
  3894. }
  3895. else if (backgroundType === "local") {
  3896. backgroundElement.style.backgroundImage = `url(${backgroundValue})`;
  3897. }
  3898. }
  3899. }
  3900. loadLocalStorage() {
  3901. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
  3902. const savedSettings = localStorage.getItem("userSettings")
  3903. ? JSON.parse(localStorage.getItem("userSettings"))
  3904. : null;
  3905. if (savedSettings) {
  3906. this.isFpsVisible = (_a = savedSettings.isFpsVisible) !== null && _a !== void 0 ? _a : this.isFpsVisible;
  3907. this.isPingVisible = (_b = savedSettings.isPingVisible) !== null && _b !== void 0 ? _b : this.isPingVisible;
  3908. this.isFpsUncapped = (_c = savedSettings.isFpsUncapped) !== null && _c !== void 0 ? _c : this.isFpsUncapped;
  3909. this.isKillsVisible = (_d = savedSettings.isKillsVisible) !== null && _d !== void 0 ? _d : this.isKillsVisible;
  3910. this.discordWebhookUrl = (_e = savedSettings.discordWebhookUrl) !== null && _e !== void 0 ? _e : this.discordWebhookUrl;
  3911. this.isHealthWarningEnabled = (_f = savedSettings.isHealthWarningEnabled) !== null && _f !== void 0 ? _f : this.isHealthWarningEnabled;
  3912. this.isAutoUpdateEnabled = (_g = savedSettings.isAutoUpdateEnabled) !== null && _g !== void 0 ? _g : this.isAutoUpdateEnabled;
  3913. this.isWinningAnimationEnabled = (_h = savedSettings.isWinningAnimationEnabled) !== null && _h !== void 0 ? _h : this.isWinningAnimationEnabled;
  3914. this.discordToken = (_j = savedSettings.discordToken) !== null && _j !== void 0 ? _j : this.discordToken;
  3915. this.isKillLeaderTrackerEnabled = (_k = savedSettings.isKillLeaderTrackerEnabled) !== null && _k !== void 0 ? _k : this.isKillLeaderTrackerEnabled;
  3916. this.isLegaySecondaryMenu = (_l = savedSettings.isLegaySecondaryMenu) !== null && _l !== void 0 ? _l : this.isLegaySecondaryMenu;
  3917. this.isKillFeedBlint = (_m = savedSettings.isKillFeedBlint) !== null && _m !== void 0 ? _m : this.isKillFeedBlint;
  3918. this.all_friends = (_o = savedSettings.all_friends) !== null && _o !== void 0 ? _o : this.all_friends;
  3919. this.isSpotifyPlayerEnabled = (_p = savedSettings.isSpotifyPlayerEnabled) !== null && _p !== void 0 ? _p : this.isSpotifyPlayerEnabled;
  3920. }
  3921. this.updateKillsVisibility();
  3922. this.updateFpsVisibility();
  3923. this.updatePingVisibility();
  3924. }
  3925. updateFpsVisibility() {
  3926. if (this.counters.fps) {
  3927. this.counters.fps.style.display = this.isFpsVisible ? "block" : "none";
  3928. this.counters.fps.style.backgroundColor = this.isFpsVisible
  3929. ? "rgba(0, 0, 0, 0.2)"
  3930. : "transparent";
  3931. }
  3932. }
  3933. updatePingVisibility() {
  3934. if (this.counters.ping) {
  3935. this.counters.ping.style.display = this.isPingVisible ? "block" : "none";
  3936. }
  3937. }
  3938. updateKillsVisibility() {
  3939. if (this.counters.kills) {
  3940. this.counters.kills.style.display = this.isKillsVisible
  3941. ? "block"
  3942. : "none";
  3943. this.counters.kills.style.backgroundColor = this.isKillsVisible
  3944. ? "rgba(0, 0, 0, 0.2)"
  3945. : "transparent";
  3946. }
  3947. }
  3948. toggleFpsUncap() {
  3949. this.isFpsUncapped = !this.isFpsUncapped;
  3950. this.setAnimationFrameCallback();
  3951. this.saveFpsUncappedToLocalStorage();
  3952. }
  3953. createSimpleSpotifyPlayer() {
  3954. // Main container
  3955. const container = document.createElement('div');
  3956. container.id = 'spotify-player-container';
  3957. Object.assign(container.style, {
  3958. position: 'fixed',
  3959. bottom: '20px',
  3960. right: '20px',
  3961. width: '320px',
  3962. backgroundColor: '#121212',
  3963. borderRadius: '12px',
  3964. boxShadow: '0 8px 24px rgba(0, 0, 0, 0.5)',
  3965. overflow: 'hidden',
  3966. zIndex: '10000',
  3967. fontFamily: 'Montserrat, Arial, sans-serif',
  3968. transition: 'transform 0.3s ease, opacity 0.3s ease',
  3969. transform: 'translateY(0)',
  3970. opacity: '1'
  3971. });
  3972. // Player header
  3973. const header = document.createElement('div');
  3974. Object.assign(header.style, {
  3975. display: 'flex',
  3976. alignItems: 'center',
  3977. justifyContent: 'space-between',
  3978. padding: '12px 16px',
  3979. backgroundColor: '#070707',
  3980. color: 'white',
  3981. borderBottom: '1px solid #282828',
  3982. position: 'relative' // For absolute positioning of the button
  3983. });
  3984. // Spotify logo
  3985. const logo = document.createElement('div');
  3986. logo.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path fill="#1DB954" d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"/></svg>`;
  3987. const title = document.createElement('span');
  3988. title.textContent = 'Spotify Player';
  3989. title.style.marginLeft = '8px';
  3990. title.style.fontWeight = 'bold';
  3991. const logoContainer = document.createElement('div');
  3992. logoContainer.style.display = 'flex';
  3993. logoContainer.style.alignItems = 'center';
  3994. logoContainer.appendChild(logo);
  3995. logoContainer.appendChild(title);
  3996. // Control buttons
  3997. const controls = document.createElement('div');
  3998. controls.style.display = 'flex';
  3999. controls.style.alignItems = 'center';
  4000. // Minimize button
  4001. const minimizeBtn = document.createElement('button');
  4002. Object.assign(minimizeBtn.style, {
  4003. background: 'none',
  4004. border: 'none',
  4005. color: '#aaa',
  4006. cursor: 'pointer',
  4007. fontSize: '18px',
  4008. padding: '0',
  4009. marginLeft: '10px',
  4010. width: '24px',
  4011. height: '24px',
  4012. display: 'flex',
  4013. alignItems: 'center',
  4014. justifyContent: 'center'
  4015. });
  4016. minimizeBtn.innerHTML = '−';
  4017. minimizeBtn.title = 'Minimize';
  4018. // Close button
  4019. const closeBtn = document.createElement('button');
  4020. Object.assign(closeBtn.style, {
  4021. background: 'none',
  4022. border: 'none',
  4023. color: '#aaa',
  4024. cursor: 'pointer',
  4025. fontSize: '18px',
  4026. padding: '0',
  4027. marginLeft: '10px',
  4028. width: '24px',
  4029. height: '24px',
  4030. display: 'flex',
  4031. alignItems: 'center',
  4032. justifyContent: 'center'
  4033. });
  4034. closeBtn.innerHTML = '×';
  4035. closeBtn.title = 'Close';
  4036. controls.appendChild(minimizeBtn);
  4037. controls.appendChild(closeBtn);
  4038. header.appendChild(logoContainer);
  4039. header.appendChild(controls);
  4040. // Album cover image
  4041. const albumArt = document.createElement('div');
  4042. Object.assign(albumArt.style, {
  4043. width: '50px',
  4044. height: '50px',
  4045. backgroundColor: '#333',
  4046. backgroundSize: 'cover',
  4047. backgroundPosition: 'center',
  4048. borderRadius: '4px',
  4049. flexShrink: '0'
  4050. });
  4051. albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67616d00001e02fe24b9ffeb3c3fdb4f9abbe9')`;
  4052. // Track information
  4053. const trackInfo = document.createElement('div');
  4054. Object.assign(trackInfo.style, {
  4055. flex: '1',
  4056. overflow: 'hidden'
  4057. });
  4058. // Player content
  4059. const content = document.createElement('div');
  4060. content.style.padding = '0';
  4061. // Spotify iframe
  4062. const iframe = document.createElement('iframe');
  4063. iframe.id = 'spotify-player-iframe';
  4064. iframe.src = 'https://open.spotify.com/embed/playlist/37i9dQZEVXcJZyENOWUFo7';
  4065. iframe.width = '100%';
  4066. iframe.height = '80px';
  4067. iframe.frameBorder = '0';
  4068. iframe.allow = 'encrypted-media';
  4069. iframe.style.border = 'none';
  4070. content.appendChild(iframe);
  4071. // Playlist change button integrated in the header
  4072. const changePlaylistContainer = document.createElement('div');
  4073. Object.assign(changePlaylistContainer.style, {
  4074. display: 'flex',
  4075. alignItems: 'center',
  4076. marginRight: '10px'
  4077. });
  4078. // Square button to enter a playlist ID
  4079. const changePlaylistBtn = document.createElement('button');
  4080. Object.assign(changePlaylistBtn.style, {
  4081. width: '24px',
  4082. height: '24px',
  4083. backgroundColor: '#1DB954',
  4084. color: 'white',
  4085. border: 'none',
  4086. borderRadius: '4px',
  4087. fontSize: '14px',
  4088. fontWeight: 'bold',
  4089. cursor: 'pointer',
  4090. display: 'flex',
  4091. alignItems: 'center',
  4092. justifyContent: 'center',
  4093. margin: '0 8px 0 0'
  4094. });
  4095. changePlaylistBtn.innerHTML = `
  4096. <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  4097. <path d="M12 5V19M5 12H19" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
  4098. </svg>
  4099. `;
  4100. changePlaylistBtn.addEventListener('click', () => {
  4101. const id = prompt('Enter the Spotify playlist ID:', '37i9dQZEVXcJZyENOWUFo7');
  4102. if (id) {
  4103. iframe.src = `https://open.spotify.com/embed/playlist/${id}`;
  4104. localStorage.setItem('kxsSpotifyPlaylist', id);
  4105. // Simulate an album cover based on the playlist ID
  4106. albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67706f00000002${id.substring(0, 16)}')`;
  4107. }
  4108. });
  4109. changePlaylistContainer.appendChild(changePlaylistBtn);
  4110. // Load saved playlist
  4111. const savedPlaylist = localStorage.getItem('kxsSpotifyPlaylist');
  4112. if (savedPlaylist) {
  4113. iframe.src = `https://open.spotify.com/embed/playlist/${savedPlaylist}`;
  4114. // Simuler une pochette d'album basée sur l'ID de la playlist
  4115. albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67706f00000002${savedPlaylist.substring(0, 16)}')`;
  4116. }
  4117. // Integrate the playlist change button into the controls
  4118. controls.insertBefore(changePlaylistContainer, minimizeBtn);
  4119. // Assemble the elements
  4120. container.appendChild(header);
  4121. container.appendChild(content);
  4122. // Add a title to the button for accessibility
  4123. changePlaylistBtn.title = "Change playlist";
  4124. // Add to document
  4125. document.body.appendChild(container);
  4126. // Player states
  4127. let isMinimized = false;
  4128. // Events
  4129. minimizeBtn.addEventListener('click', () => {
  4130. if (isMinimized) {
  4131. content.style.display = 'block';
  4132. changePlaylistContainer.style.display = 'block';
  4133. container.style.transform = 'translateY(0)';
  4134. minimizeBtn.innerHTML = '−';
  4135. }
  4136. else {
  4137. content.style.display = 'none';
  4138. changePlaylistContainer.style.display = 'none';
  4139. container.style.transform = 'translateY(0)';
  4140. minimizeBtn.innerHTML = '+';
  4141. }
  4142. isMinimized = !isMinimized;
  4143. });
  4144. closeBtn.addEventListener('click', () => {
  4145. container.style.transform = 'translateY(150%)';
  4146. container.style.opacity = '0';
  4147. setTimeout(() => {
  4148. container.style.display = 'none';
  4149. showButton.style.display = 'flex';
  4150. showButton.style.alignItems = 'center';
  4151. showButton.style.justifyContent = 'center';
  4152. }, 300);
  4153. });
  4154. // Make the player draggable
  4155. let isDragging = false;
  4156. let offsetX = 0;
  4157. let offsetY = 0;
  4158. header.addEventListener('mousedown', (e) => {
  4159. isDragging = true;
  4160. offsetX = e.clientX - container.getBoundingClientRect().left;
  4161. offsetY = e.clientY - container.getBoundingClientRect().top;
  4162. container.style.transition = 'none';
  4163. });
  4164. document.addEventListener('mousemove', (e) => {
  4165. if (isDragging) {
  4166. container.style.right = 'auto';
  4167. container.style.bottom = 'auto';
  4168. container.style.left = (e.clientX - offsetX) + 'px';
  4169. container.style.top = (e.clientY - offsetY) + 'px';
  4170. }
  4171. });
  4172. document.addEventListener('mouseup', () => {
  4173. isDragging = false;
  4174. container.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
  4175. });
  4176. // Button to show the player again
  4177. const showButton = document.createElement('button');
  4178. showButton.id = 'spotify-float-button';
  4179. Object.assign(showButton.style, {
  4180. position: 'fixed',
  4181. bottom: '20px',
  4182. right: '20px',
  4183. width: '50px',
  4184. height: '50px',
  4185. borderRadius: '50%',
  4186. backgroundColor: '#1DB954',
  4187. color: 'white',
  4188. border: 'none',
  4189. boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
  4190. cursor: 'pointer',
  4191. zIndex: '9999',
  4192. fontSize: '24px',
  4193. transition: 'transform 0.2s ease',
  4194. display: 'flex',
  4195. alignItems: 'center',
  4196. justifyContent: 'center'
  4197. });
  4198. showButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"/></svg>`;
  4199. document.body.appendChild(showButton);
  4200. showButton.addEventListener('mouseenter', () => {
  4201. showButton.style.transform = 'scale(1.1)';
  4202. });
  4203. showButton.addEventListener('mouseleave', () => {
  4204. showButton.style.transform = 'scale(1)';
  4205. });
  4206. showButton.addEventListener('click', () => {
  4207. container.style.display = 'block';
  4208. container.style.transform = 'translateY(0)';
  4209. container.style.opacity = '1';
  4210. showButton.style.display = 'none';
  4211. });
  4212. return container;
  4213. }
  4214. toggleSpotifyMenu() {
  4215. if (this.isSpotifyPlayerEnabled) {
  4216. this.createSimpleSpotifyPlayer();
  4217. }
  4218. else {
  4219. this.removeSimpleSpotifyPlayer();
  4220. }
  4221. }
  4222. removeSimpleSpotifyPlayer() {
  4223. // Supprimer le conteneur principal du lecteur
  4224. const container = document.getElementById('spotify-player-container');
  4225. if (container) {
  4226. container.remove();
  4227. }
  4228. // Supprimer aussi le bouton flottant grâce à son ID
  4229. const floatButton = document.getElementById('spotify-float-button');
  4230. if (floatButton) {
  4231. floatButton.remove();
  4232. }
  4233. }
  4234. }
  4235.  
  4236. ;// ./src/LoadingScreen.ts
  4237. /**
  4238. * LoadingScreen.ts
  4239. *
  4240. * Ce module fournit une animation de chargement avec un logo et un cercle de chargement rotatif
  4241. * qui s'affiche pendant le chargement des ressources du jeu.
  4242. */
  4243. class LoadingScreen {
  4244. /**
  4245. * Creates a new instance of the loading screen
  4246. * @param logoUrl URL of the Kxs logo to display
  4247. */
  4248. constructor(logoUrl) {
  4249. this.logoUrl = logoUrl;
  4250. this.container = document.createElement('div');
  4251. this.initializeStyles();
  4252. this.createContent();
  4253. }
  4254. /**
  4255. * Initializes CSS styles for the loading screen
  4256. */
  4257. initializeStyles() {
  4258. // Styles for the main container
  4259. Object.assign(this.container.style, {
  4260. position: 'fixed',
  4261. top: '0',
  4262. left: '0',
  4263. width: '100%',
  4264. height: '100%',
  4265. backgroundColor: 'rgba(0, 0, 0, 0.9)',
  4266. display: 'flex',
  4267. flexDirection: 'column',
  4268. justifyContent: 'center',
  4269. alignItems: 'center',
  4270. zIndex: '9999',
  4271. transition: 'opacity 0.5s ease-in-out',
  4272. animation: 'fadeIn 0.5s ease-in-out',
  4273. backdropFilter: 'blur(5px)'
  4274. });
  4275. }
  4276. /**
  4277. * Creates the loading screen content (logo and loading circle)
  4278. */
  4279. createContent() {
  4280. // Create container for the logo
  4281. const logoContainer = document.createElement('div');
  4282. Object.assign(logoContainer.style, {
  4283. width: '200px',
  4284. height: '200px',
  4285. marginBottom: '20px',
  4286. position: 'relative',
  4287. display: 'flex',
  4288. justifyContent: 'center',
  4289. alignItems: 'center'
  4290. });
  4291. // Create the logo element
  4292. const logo = document.createElement('img');
  4293. logo.src = this.logoUrl;
  4294. Object.assign(logo.style, {
  4295. width: '150px',
  4296. height: '150px',
  4297. objectFit: 'contain',
  4298. position: 'absolute',
  4299. zIndex: '2',
  4300. animation: 'pulse 2s ease-in-out infinite'
  4301. });
  4302. // Create the main loading circle
  4303. const loadingCircle = document.createElement('div');
  4304. Object.assign(loadingCircle.style, {
  4305. width: '180px',
  4306. height: '180px',
  4307. border: '4px solid transparent',
  4308. borderTopColor: '#3498db',
  4309. borderRadius: '50%',
  4310. animation: 'spin 1.5s linear infinite',
  4311. position: 'absolute',
  4312. zIndex: '1'
  4313. });
  4314. // Create a second loading circle (rotating in the opposite direction)
  4315. const loadingCircle2 = document.createElement('div');
  4316. Object.assign(loadingCircle2.style, {
  4317. width: '200px',
  4318. height: '200px',
  4319. border: '2px solid transparent',
  4320. borderLeftColor: '#e74c3c',
  4321. borderRightColor: '#e74c3c',
  4322. borderRadius: '50%',
  4323. animation: 'spin-reverse 3s linear infinite',
  4324. position: 'absolute',
  4325. zIndex: '0'
  4326. });
  4327. // Add animations
  4328. const styleSheet = document.createElement('style');
  4329. styleSheet.textContent = `
  4330. @keyframes spin {
  4331. 0% { transform: rotate(0deg); }
  4332. 100% { transform: rotate(360deg); }
  4333. }
  4334. @keyframes spin-reverse {
  4335. 0% { transform: rotate(0deg); }
  4336. 100% { transform: rotate(-360deg); }
  4337. }
  4338. @keyframes pulse {
  4339. 0% { transform: scale(1); }
  4340. 50% { transform: scale(1.05); }
  4341. 100% { transform: scale(1); }
  4342. }
  4343. @keyframes fadeIn {
  4344. 0% { opacity: 0; }
  4345. 100% { opacity: 1; }
  4346. }
  4347. `;
  4348. document.head.appendChild(styleSheet);
  4349. // Ajout d'un texte de chargement
  4350. const loadingText = document.createElement('div');
  4351. loadingText.textContent = 'Loading...';
  4352. Object.assign(loadingText.style, {
  4353. color: 'white',
  4354. fontFamily: 'Arial, sans-serif',
  4355. fontSize: '18px',
  4356. marginTop: '20px',
  4357. animation: 'pulse 1.5s ease-in-out infinite'
  4358. });
  4359. // Ajout d'un sous-texte
  4360. const subText = document.createElement('div');
  4361. subText.textContent = 'Initializing resources...';
  4362. Object.assign(subText.style, {
  4363. color: 'rgba(255, 255, 255, 0.7)',
  4364. fontFamily: 'Arial, sans-serif',
  4365. fontSize: '14px',
  4366. marginTop: '5px'
  4367. });
  4368. // Assemble the elements
  4369. logoContainer.appendChild(loadingCircle2);
  4370. logoContainer.appendChild(loadingCircle);
  4371. logoContainer.appendChild(logo);
  4372. this.container.appendChild(logoContainer);
  4373. this.container.appendChild(loadingText);
  4374. this.container.appendChild(subText);
  4375. }
  4376. /**
  4377. * Shows the loading screen
  4378. */
  4379. show() {
  4380. document.body.appendChild(this.container);
  4381. }
  4382. /**
  4383. * Hides the loading screen with a fade transition
  4384. */
  4385. hide() {
  4386. this.container.style.opacity = '0';
  4387. setTimeout(() => {
  4388. if (this.container.parentNode) {
  4389. document.body.removeChild(this.container);
  4390. }
  4391. }, 500); // Wait for the transition to finish before removing the element
  4392. }
  4393. }
  4394.  
  4395. ;// ./src/index.ts
  4396.  
  4397.  
  4398.  
  4399.  
  4400.  
  4401. const src_packageInfo = __webpack_require__(330);
  4402. const src_config = __webpack_require__(272);
  4403. const background_song = src_config.base_url + "/assets/Stranger_Things_Theme_Song_C418_REMIX.mp3";
  4404. const kxs_logo = src_config.base_url + "/assets/KysClientLogo.png";
  4405. const loadingScreen = new LoadingScreen(kxs_logo);
  4406. loadingScreen.show();
  4407. const backgroundElement = document.getElementById("background");
  4408. if (backgroundElement)
  4409. backgroundElement.style.backgroundImage = `url("${src_config.base_url}/assets/background.jpg")`;
  4410. const favicon = document.createElement('link');
  4411. favicon.rel = 'icon';
  4412. favicon.type = 'image/png';
  4413. favicon.href = kxs_logo;
  4414. document.head.appendChild(favicon);
  4415. document.title = "KxsClient";
  4416. intercept("audio/ambient/menu_music_01.mp3", background_song);
  4417. intercept('img/survev_logo_full.png', kxs_logo);
  4418. const uiStatsLogo = document.querySelector('#ui-stats-logo');
  4419. if (uiStatsLogo) {
  4420. uiStatsLogo.style.backgroundImage = `url('${kxs_logo}')`;
  4421. }
  4422. const newChangelogUrl = src_config.base_url;
  4423. const startBottomMiddle = document.getElementById("start-bottom-middle");
  4424. if (startBottomMiddle) {
  4425. const links = startBottomMiddle.getElementsByTagName("a");
  4426. for (let i = 0; i < links.length; i++) {
  4427. const link = links[i];
  4428. if (link.href.includes("changelogRec.html") || link.href.includes("changelog.html")) {
  4429. link.href = newChangelogUrl;
  4430. link.textContent = src_packageInfo.version;
  4431. }
  4432. if (i === 1) {
  4433. link.remove();
  4434. }
  4435. }
  4436. }
  4437. const kxsClient = new KxsClient();
  4438. const kxsClientHUD = new KxsClientHUD(kxsClient);
  4439. const mainMenu = new KxsMainClientMenu(kxsClient);
  4440. setInterval(() => {
  4441. loadingScreen.hide();
  4442. }, 1400);
  4443.  
  4444. /******/ })()
  4445. ;