Duality

A fusion of KxsClient and Surplus, adding many extra features and cheats.

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

  1. // ==UserScript==
  2. // @name Duality
  3. // @version 1.1
  4. // @description A fusion of KxsClient and Surplus, adding many extra features and cheats.
  5. // @author plazmascripts, mahdi, noam, Kisakay
  6. // @run-at document-start
  7. // @grant none
  8. // @namespace https://github.com/Kisakay/KxsClient
  9. // @author Kisakay
  10. // @license AGPL-3.0
  11. // @run-at document-end
  12. // @icon https://files.catbox.moe/onhbvw.png
  13. // @match *://survev.io/*
  14. // @match *://66.179.254.36/*
  15. // @match *://zurviv.io/*
  16. // @match *://resurviv.biz/*
  17. // @match *://leia-uwu.github.io/survev/*
  18. // @match *://survev.leia-is.gay/*
  19. // @match *://survivx.org
  20. // @match *://kxs.rip/*
  21. // @grant none
  22. // ==/UserScript==
  23. ;
  24. /******/ (() => { // webpackBootstrap
  25. /******/ var __webpack_modules__ = ({
  26.  
  27. /***/ 123:
  28. /***/ ((module) => {
  29.  
  30. const numeric = /^[0-9]+$/
  31. const compareIdentifiers = (a, b) => {
  32. const anum = numeric.test(a)
  33. const bnum = numeric.test(b)
  34.  
  35. if (anum && bnum) {
  36. a = +a
  37. b = +b
  38. }
  39.  
  40. return a === b ? 0
  41. : (anum && !bnum) ? -1
  42. : (bnum && !anum) ? 1
  43. : a < b ? -1
  44. : 1
  45. }
  46.  
  47. const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a)
  48.  
  49. module.exports = {
  50. compareIdentifiers,
  51. rcompareIdentifiers,
  52. }
  53.  
  54.  
  55. /***/ }),
  56.  
  57. /***/ 229:
  58. /***/ ((__unused_webpack_module, exports) => {
  59.  
  60. "use strict";
  61. var __webpack_unused_export__;
  62.  
  63. __webpack_unused_export__ = ({ value: true });
  64. exports.A = void 0;
  65. ;
  66. class SimplifiedSteganoDB {
  67. data;
  68. options;
  69. database;
  70. constructor(options) {
  71. this.database = options?.database || "stegano.db";
  72. this.data = {};
  73. this.fetchDataFromFile();
  74. }
  75. read() { return localStorage.getItem(this.database) || this.data; }
  76. write() { return localStorage.setItem(this.database, JSON.stringify(this.data)); }
  77. setNestedProperty = (object, key, value) => {
  78. const properties = key.split('.');
  79. let currentObject = object;
  80. for (let i = 0; i < properties.length - 1; i++) {
  81. const property = properties[i];
  82. if (typeof currentObject[property] !== 'object' || currentObject[property] === null) {
  83. currentObject[property] = {};
  84. }
  85. currentObject = currentObject[property];
  86. }
  87. currentObject[properties[properties.length - 1]] = value;
  88. };
  89. getNestedProperty = (object, key) => {
  90. const properties = key.split('.');
  91. let index = 0;
  92. for (; index < properties.length; ++index) {
  93. object = object && object[properties[index]];
  94. }
  95. return object;
  96. };
  97. fetchDataFromFile() {
  98. try {
  99. const content = this.read();
  100. this.data = JSON.parse(content);
  101. }
  102. catch (error) {
  103. this.data = {};
  104. }
  105. }
  106. updateNestedProperty(key, operation, value) {
  107. const [id, ...rest] = key.split('.');
  108. const nestedPath = rest.join('.');
  109. if (!this.data[id] && operation !== 'get') {
  110. this.data[id] = nestedPath ? {} : undefined;
  111. }
  112. if (this.data[id] === undefined && operation === 'get') {
  113. return undefined;
  114. }
  115. switch (operation) {
  116. case 'get':
  117. return nestedPath ? this.getNestedProperty(this.data[id], nestedPath) : this.data[id];
  118. case 'set':
  119. if (nestedPath) {
  120. if (typeof this.data[id] !== 'object' || this.data[id] === null) {
  121. this.data[id] = {};
  122. }
  123. this.setNestedProperty(this.data[id], nestedPath, value);
  124. }
  125. else {
  126. this.data[id] = value;
  127. }
  128. this.write();
  129. break;
  130. case 'add':
  131. if (!nestedPath) {
  132. this.data[id] = (typeof this.data[id] === 'number' ? this.data[id] : 0) + value;
  133. }
  134. else {
  135. if (typeof this.data[id] !== 'object' || this.data[id] === null) {
  136. this.data[id] = {};
  137. }
  138. const existingValue = this.getNestedProperty(this.data[id], nestedPath);
  139. if (typeof existingValue !== 'number' && existingValue !== undefined) {
  140. throw new TypeError('The existing value is not a number.');
  141. }
  142. this.setNestedProperty(this.data[id], nestedPath, (typeof existingValue === 'number' ? existingValue : 0) + value);
  143. }
  144. this.write();
  145. break;
  146. case 'sub':
  147. if (!nestedPath) {
  148. this.data[id] = (typeof this.data[id] === 'number' ? this.data[id] : 0) - value;
  149. }
  150. else {
  151. if (typeof this.data[id] !== 'object' || this.data[id] === null) {
  152. this.data[id] = {};
  153. }
  154. const existingValue = this.getNestedProperty(this.data[id], nestedPath);
  155. if (typeof existingValue !== 'number' && existingValue !== undefined && existingValue !== null) {
  156. throw new TypeError('The existing value is not a number.');
  157. }
  158. this.setNestedProperty(this.data[id], nestedPath, (typeof existingValue === 'number' ? existingValue : 0) - value);
  159. }
  160. this.write();
  161. break;
  162. case 'delete':
  163. if (nestedPath) {
  164. if (typeof this.data[id] !== 'object' || this.data[id] === null) {
  165. return;
  166. }
  167. const properties = nestedPath.split('.');
  168. let currentObject = this.data[id];
  169. for (let i = 0; i < properties.length - 1; i++) {
  170. const property = properties[i];
  171. if (!currentObject[property]) {
  172. return;
  173. }
  174. currentObject = currentObject[property];
  175. }
  176. delete currentObject[properties[properties.length - 1]];
  177. }
  178. else {
  179. delete this.data[id];
  180. }
  181. this.write();
  182. break;
  183. case 'pull':
  184. const existingArray = nestedPath ?
  185. this.getNestedProperty(this.data[id], nestedPath) :
  186. this.data[id];
  187. if (!Array.isArray(existingArray)) {
  188. throw new Error('The stored value is not an array');
  189. }
  190. const newArray = existingArray.filter((item) => item !== value);
  191. if (nestedPath) {
  192. this.setNestedProperty(this.data[id], nestedPath, newArray);
  193. }
  194. else {
  195. this.data[id] = newArray;
  196. }
  197. this.write();
  198. break;
  199. }
  200. }
  201. get(key) {
  202. return this.updateNestedProperty(key, 'get');
  203. }
  204. set(key, value) {
  205. if (key.includes(" ") || !key || key === "") {
  206. throw new SyntaxError("Key can't be null or contain a space.");
  207. }
  208. this.updateNestedProperty(key, 'set', value);
  209. }
  210. pull(key, value) {
  211. if (key.includes(" ") || !key || key === "") {
  212. throw new SyntaxError("Key can't be null or contain a space.");
  213. }
  214. this.updateNestedProperty(key, 'pull', value);
  215. }
  216. add(key, count) {
  217. if (key.includes(" ") || !key || key === "") {
  218. throw new SyntaxError("Key can't be null or contain a space.");
  219. }
  220. if (isNaN(count)) {
  221. throw new SyntaxError("The value is NaN.");
  222. }
  223. this.updateNestedProperty(key, 'add', count);
  224. }
  225. sub(key, count) {
  226. if (key.includes(" ") || !key || key === "") {
  227. throw new SyntaxError("Key can't be null or contain a space.");
  228. }
  229. if (isNaN(count)) {
  230. throw new SyntaxError("The value is NaN.");
  231. }
  232. this.updateNestedProperty(key, 'sub', count);
  233. }
  234. delete(key) {
  235. this.updateNestedProperty(key, 'delete');
  236. }
  237. cache(key, value, time) {
  238. if (key.includes(" ") || !key || key === "") {
  239. throw new SyntaxError("Key can't be null ou contain a space.");
  240. }
  241. if (!time || isNaN(time)) {
  242. throw new SyntaxError("The time needs to be a number. (ms)");
  243. }
  244. this.updateNestedProperty(key, 'set', value);
  245. setTimeout(() => {
  246. this.updateNestedProperty(key, 'delete');
  247. }, time);
  248. }
  249. push(key, element) {
  250. if (key.includes(" ") || !key || key === "") {
  251. throw new SyntaxError("Key can't be null or contain a space.");
  252. }
  253. const [id, ...rest] = key.split('.');
  254. const nestedPath = rest.join('.');
  255. if (!this.data[id]) {
  256. this.data[id] = nestedPath ? {} : [];
  257. }
  258. if (nestedPath) {
  259. const existingArray = this.getNestedProperty(this.data[id], nestedPath);
  260. if (!existingArray) {
  261. this.setNestedProperty(this.data[id], nestedPath, [element]);
  262. }
  263. else if (!Array.isArray(existingArray)) {
  264. throw new Error('The stored value is not an array');
  265. }
  266. else {
  267. existingArray.push(element);
  268. this.setNestedProperty(this.data[id], nestedPath, existingArray);
  269. }
  270. }
  271. else {
  272. if (!Array.isArray(this.data[id])) {
  273. this.data[id] = [];
  274. }
  275. this.data[id].push(element);
  276. }
  277. this.write();
  278. }
  279. has(key) {
  280. return Boolean(this.get(key));
  281. }
  282. deleteAll() {
  283. this.data = {};
  284. this.write();
  285. }
  286. all() {
  287. return this.data;
  288. }
  289. }
  290. exports.A = SimplifiedSteganoDB;
  291.  
  292.  
  293. /***/ }),
  294.  
  295. /***/ 272:
  296. /***/ ((module) => {
  297.  
  298. const debug = (
  299. typeof process === 'object' &&
  300. process.env &&
  301. process.env.NODE_DEBUG &&
  302. /\bsemver\b/i.test(process.env.NODE_DEBUG)
  303. ) ? (...args) => console.error('SEMVER', ...args)
  304. : () => {}
  305.  
  306. module.exports = debug
  307.  
  308.  
  309. /***/ }),
  310.  
  311. /***/ 560:
  312. /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
  313.  
  314. const SemVer = __webpack_require__(908)
  315. const compare = (a, b, loose) =>
  316. new SemVer(a, loose).compare(new SemVer(b, loose))
  317.  
  318. module.exports = compare
  319.  
  320.  
  321. /***/ }),
  322.  
  323. /***/ 580:
  324. /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
  325.  
  326. const compare = __webpack_require__(560)
  327. const gt = (a, b, loose) => compare(a, b, loose) > 0
  328. module.exports = gt
  329.  
  330.  
  331. /***/ }),
  332.  
  333. /***/ 587:
  334. /***/ ((module) => {
  335.  
  336. // parse out just the options we care about
  337. const looseOption = Object.freeze({ loose: true })
  338. const emptyOpts = Object.freeze({ })
  339. const parseOptions = options => {
  340. if (!options) {
  341. return emptyOpts
  342. }
  343.  
  344. if (typeof options !== 'object') {
  345. return looseOption
  346. }
  347.  
  348. return options
  349. }
  350. module.exports = parseOptions
  351.  
  352.  
  353. /***/ }),
  354.  
  355. /***/ 686:
  356. /***/ ((__unused_webpack_module, exports) => {
  357.  
  358. "use strict";
  359. var __webpack_unused_export__;
  360.  
  361. __webpack_unused_export__ = ({ value: true });
  362. exports.w = void 0;
  363. ;
  364. class SteganoDB {
  365. data;
  366. currentTable;
  367. options;
  368. database;
  369. constructor(options) {
  370. this.currentTable = options?.tableName || "json";
  371. this.database = options.database || "stegano.db";
  372. this.data = {
  373. [this.currentTable]: []
  374. };
  375. this.fetchDataFromFile();
  376. }
  377. read() { return localStorage.getItem(this.database) || this.data; }
  378. write() { return localStorage.setItem(this.database, JSON.stringify(this.data)); }
  379. setNestedProperty = (object, key, value) => {
  380. const properties = key.split('.');
  381. let currentObject = object;
  382. for (let i = 0; i < properties.length - 1; i++) {
  383. const property = properties[i];
  384. if (typeof currentObject[property] !== 'object' || currentObject[property] === null) {
  385. currentObject[property] = {};
  386. }
  387. currentObject = currentObject[property];
  388. }
  389. currentObject[properties[properties.length - 1]] = value;
  390. };
  391. getNestedProperty = (object, key) => {
  392. const properties = key.split('.');
  393. let index = 0;
  394. for (; index < properties.length; ++index) {
  395. object = object && object[properties[index]];
  396. }
  397. return object;
  398. };
  399. fetchDataFromFile() {
  400. try {
  401. const content = this.read();
  402. this.data = JSON.parse(content);
  403. }
  404. catch (error) {
  405. this.data = { [this.currentTable]: [] };
  406. }
  407. }
  408. updateNestedProperty(key, operation, value) {
  409. const [id, ...rest] = key.split('.');
  410. const nestedPath = rest.join('.');
  411. let currentValue = this.data[this.currentTable].find((entry) => entry.id === id);
  412. if (!currentValue && operation !== 'get') {
  413. currentValue = { id, value: {} };
  414. this.data[this.currentTable].push(currentValue);
  415. }
  416. if (!currentValue && operation === 'get') {
  417. return undefined;
  418. }
  419. switch (operation) {
  420. case 'get':
  421. return nestedPath ? this.getNestedProperty(currentValue.value, nestedPath) : currentValue.value;
  422. case 'set':
  423. if (nestedPath) {
  424. this.setNestedProperty(currentValue.value, nestedPath, value);
  425. }
  426. else {
  427. currentValue.value = value;
  428. }
  429. this.write();
  430. break;
  431. case 'add':
  432. if (!nestedPath) {
  433. currentValue.value = (typeof currentValue.value === 'number' ? currentValue.value : 0) + value;
  434. }
  435. else {
  436. const existingValue = this.getNestedProperty(currentValue.value, nestedPath);
  437. if (typeof existingValue !== 'number' && existingValue !== undefined) {
  438. throw new TypeError('The existing value is not a number.');
  439. }
  440. this.setNestedProperty(currentValue.value, nestedPath, (typeof existingValue === 'number' ? existingValue : 0) + value);
  441. }
  442. this.write();
  443. break;
  444. case 'sub':
  445. if (!nestedPath) {
  446. currentValue.value = (typeof currentValue.value === 'number' ? currentValue.value : 0) - value;
  447. }
  448. else {
  449. const existingValue = this.getNestedProperty(currentValue.value, nestedPath);
  450. if (typeof existingValue !== 'number' && existingValue !== undefined && existingValue !== null) {
  451. throw new TypeError('The existing value is not a number.');
  452. }
  453. this.setNestedProperty(currentValue.value, nestedPath, (typeof existingValue === 'number' ? existingValue : 0) - value);
  454. }
  455. this.write();
  456. break;
  457. case 'delete':
  458. if (nestedPath) {
  459. const properties = nestedPath.split('.');
  460. let currentObject = currentValue.value;
  461. for (let i = 0; i < properties.length - 1; i++) {
  462. const property = properties[i];
  463. if (!currentObject[property]) {
  464. return;
  465. }
  466. currentObject = currentObject[property];
  467. }
  468. delete currentObject[properties[properties.length - 1]];
  469. }
  470. else {
  471. const index = this.data[this.currentTable].findIndex((entry) => entry.id === id);
  472. if (index !== -1) {
  473. this.data[this.currentTable].splice(index, 1);
  474. }
  475. }
  476. this.write();
  477. break;
  478. case 'pull':
  479. const existingArray = nestedPath ? this.getNestedProperty(currentValue.value, nestedPath) : currentValue.value;
  480. if (!Array.isArray(existingArray)) {
  481. throw new Error('The stored value is not an array');
  482. }
  483. const newArray = existingArray.filter((item) => item !== value);
  484. if (nestedPath) {
  485. this.setNestedProperty(currentValue.value, nestedPath, newArray);
  486. }
  487. else {
  488. currentValue.value = newArray;
  489. }
  490. this.write();
  491. break;
  492. }
  493. }
  494. table(tableName) {
  495. if (tableName.includes(" ") || !tableName || tableName === "") {
  496. throw new SyntaxError("Key can't be null or contain a space.");
  497. }
  498. if (!this.data[tableName]) {
  499. this.data[tableName] = [];
  500. }
  501. return new SteganoDB(this.options);
  502. }
  503. get(key) {
  504. return this.updateNestedProperty(key, 'get');
  505. }
  506. set(key, value) {
  507. if (key.includes(" ") || !key || key === "") {
  508. throw new SyntaxError("Key can't be null or contain a space.");
  509. }
  510. this.updateNestedProperty(key, 'set', value);
  511. }
  512. pull(key, value) {
  513. if (key.includes(" ") || !key || key === "") {
  514. throw new SyntaxError("Key can't be null or contain a space.");
  515. }
  516. this.updateNestedProperty(key, 'pull', value);
  517. }
  518. add(key, count) {
  519. if (key.includes(" ") || !key || key === "") {
  520. throw new SyntaxError("Key can't be null or contain a space.");
  521. }
  522. if (isNaN(count)) {
  523. throw new SyntaxError("The value is NaN.");
  524. }
  525. this.updateNestedProperty(key, 'add', count);
  526. }
  527. sub(key, count) {
  528. if (key.includes(" ") || !key || key === "") {
  529. throw new SyntaxError("Key can't be null or contain a space.");
  530. }
  531. if (isNaN(count)) {
  532. throw new SyntaxError("The value is NaN.");
  533. }
  534. this.updateNestedProperty(key, 'sub', count);
  535. }
  536. delete(key) {
  537. this.updateNestedProperty(key, 'delete');
  538. }
  539. cache(key, value, time) {
  540. if (key.includes(" ") || !key || key === "") {
  541. throw new SyntaxError("Key can't be null ou contain a space.");
  542. }
  543. if (!time || isNaN(time)) {
  544. throw new SyntaxError("The time needs to be a number. (ms)");
  545. }
  546. this.updateNestedProperty(key, 'set', value);
  547. setTimeout(() => {
  548. this.updateNestedProperty(key, 'delete');
  549. }, time);
  550. }
  551. push(key, element) {
  552. if (key.includes(" ") || !key || key === "") {
  553. throw new SyntaxError("Key can't be null or contain a space.");
  554. }
  555. const [id, ...rest] = key.split('.');
  556. const nestedPath = rest.join('.');
  557. let currentValue = this.data[this.currentTable].find((entry) => entry.id === id);
  558. if (!currentValue) {
  559. currentValue = { id, value: nestedPath ? {} : [] };
  560. this.data[this.currentTable].push(currentValue);
  561. }
  562. if (nestedPath) {
  563. const existingArray = this.getNestedProperty(currentValue.value, nestedPath);
  564. if (!existingArray) {
  565. this.setNestedProperty(currentValue.value, nestedPath, [element]);
  566. }
  567. else if (!Array.isArray(existingArray)) {
  568. throw new Error('The stored value is not an array');
  569. }
  570. else {
  571. existingArray.push(element);
  572. this.setNestedProperty(currentValue.value, nestedPath, existingArray);
  573. }
  574. }
  575. else {
  576. if (!Array.isArray(currentValue.value)) {
  577. currentValue.value = [];
  578. }
  579. currentValue.value.push(element);
  580. }
  581. this.write();
  582. }
  583. has(key) {
  584. return Boolean(this.get(key));
  585. }
  586. deleteAll() {
  587. this.data[this.currentTable] = [];
  588. this.write();
  589. }
  590. all() {
  591. return this.data[this.currentTable];
  592. }
  593. }
  594. exports.w = SteganoDB;
  595.  
  596.  
  597. /***/ }),
  598.  
  599. /***/ 718:
  600. /***/ ((module, exports, __webpack_require__) => {
  601.  
  602. const {
  603. MAX_SAFE_COMPONENT_LENGTH,
  604. MAX_SAFE_BUILD_LENGTH,
  605. MAX_LENGTH,
  606. } = __webpack_require__(874)
  607. const debug = __webpack_require__(272)
  608. exports = module.exports = {}
  609.  
  610. // The actual regexps go on exports.re
  611. const re = exports.re = []
  612. const safeRe = exports.safeRe = []
  613. const src = exports.src = []
  614. const safeSrc = exports.safeSrc = []
  615. const t = exports.t = {}
  616. let R = 0
  617.  
  618. const LETTERDASHNUMBER = '[a-zA-Z0-9-]'
  619.  
  620. // Replace some greedy regex tokens to prevent regex dos issues. These regex are
  621. // used internally via the safeRe object since all inputs in this library get
  622. // normalized first to trim and collapse all extra whitespace. The original
  623. // regexes are exported for userland consumption and lower level usage. A
  624. // future breaking change could export the safer regex only with a note that
  625. // all input should have extra whitespace removed.
  626. const safeRegexReplacements = [
  627. ['\\s', 1],
  628. ['\\d', MAX_LENGTH],
  629. [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
  630. ]
  631.  
  632. const makeSafeRegex = (value) => {
  633. for (const [token, max] of safeRegexReplacements) {
  634. value = value
  635. .split(`${token}*`).join(`${token}{0,${max}}`)
  636. .split(`${token}+`).join(`${token}{1,${max}}`)
  637. }
  638. return value
  639. }
  640.  
  641. const createToken = (name, value, isGlobal) => {
  642. const safe = makeSafeRegex(value)
  643. const index = R++
  644. debug(name, index, value)
  645. t[name] = index
  646. src[index] = value
  647. safeSrc[index] = safe
  648. re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
  649. safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)
  650. }
  651.  
  652. // The following Regular Expressions can be used for tokenizing,
  653. // validating, and parsing SemVer version strings.
  654.  
  655. // ## Numeric Identifier
  656. // A single `0`, or a non-zero digit followed by zero or more digits.
  657.  
  658. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')
  659. createToken('NUMERICIDENTIFIERLOOSE', '\\d+')
  660.  
  661. // ## Non-numeric Identifier
  662. // Zero or more digits, followed by a letter or hyphen, and then zero or
  663. // more letters, digits, or hyphens.
  664.  
  665. createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`)
  666.  
  667. // ## Main Version
  668. // Three dot-separated numeric identifiers.
  669.  
  670. createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
  671. `(${src[t.NUMERICIDENTIFIER]})\\.` +
  672. `(${src[t.NUMERICIDENTIFIER]})`)
  673.  
  674. createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
  675. `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
  676. `(${src[t.NUMERICIDENTIFIERLOOSE]})`)
  677.  
  678. // ## Pre-release Version Identifier
  679. // A numeric identifier, or a non-numeric identifier.
  680.  
  681. createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]
  682. }|${src[t.NONNUMERICIDENTIFIER]})`)
  683.  
  684. createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]
  685. }|${src[t.NONNUMERICIDENTIFIER]})`)
  686.  
  687. // ## Pre-release Version
  688. // Hyphen, followed by one or more dot-separated pre-release version
  689. // identifiers.
  690.  
  691. createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]
  692. }(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`)
  693.  
  694. createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
  695. }(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`)
  696.  
  697. // ## Build Metadata Identifier
  698. // Any combination of digits, letters, or hyphens.
  699.  
  700. createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`)
  701.  
  702. // ## Build Metadata
  703. // Plus sign, followed by one or more period-separated build metadata
  704. // identifiers.
  705.  
  706. createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]
  707. }(?:\\.${src[t.BUILDIDENTIFIER]})*))`)
  708.  
  709. // ## Full Version String
  710. // A main version, followed optionally by a pre-release version and
  711. // build metadata.
  712.  
  713. // Note that the only major, minor, patch, and pre-release sections of
  714. // the version string are capturing groups. The build metadata is not a
  715. // capturing group, because it should not ever be used in version
  716. // comparison.
  717.  
  718. createToken('FULLPLAIN', `v?${src[t.MAINVERSION]
  719. }${src[t.PRERELEASE]}?${
  720. src[t.BUILD]}?`)
  721.  
  722. createToken('FULL', `^${src[t.FULLPLAIN]}$`)
  723.  
  724. // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
  725. // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
  726. // common in the npm registry.
  727. createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]
  728. }${src[t.PRERELEASELOOSE]}?${
  729. src[t.BUILD]}?`)
  730.  
  731. createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`)
  732.  
  733. createToken('GTLT', '((?:<|>)?=?)')
  734.  
  735. // Something like "2.*" or "1.2.x".
  736. // Note that "x.x" is a valid xRange identifer, meaning "any version"
  737. // Only the first item is strictly required.
  738. createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`)
  739. createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`)
  740.  
  741. createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +
  742. `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
  743. `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
  744. `(?:${src[t.PRERELEASE]})?${
  745. src[t.BUILD]}?` +
  746. `)?)?`)
  747.  
  748. createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +
  749. `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
  750. `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
  751. `(?:${src[t.PRERELEASELOOSE]})?${
  752. src[t.BUILD]}?` +
  753. `)?)?`)
  754.  
  755. createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`)
  756. createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`)
  757.  
  758. // Coercion.
  759. // Extract anything that could conceivably be a part of a valid semver
  760. createToken('COERCEPLAIN', `${'(^|[^\\d])' +
  761. '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +
  762. `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
  763. `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`)
  764. createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`)
  765. createToken('COERCEFULL', src[t.COERCEPLAIN] +
  766. `(?:${src[t.PRERELEASE]})?` +
  767. `(?:${src[t.BUILD]})?` +
  768. `(?:$|[^\\d])`)
  769. createToken('COERCERTL', src[t.COERCE], true)
  770. createToken('COERCERTLFULL', src[t.COERCEFULL], true)
  771.  
  772. // Tilde ranges.
  773. // Meaning is "reasonably at or greater than"
  774. createToken('LONETILDE', '(?:~>?)')
  775.  
  776. createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true)
  777. exports.tildeTrimReplace = '$1~'
  778.  
  779. createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`)
  780. createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`)
  781.  
  782. // Caret ranges.
  783. // Meaning is "at least and backwards compatible with"
  784. createToken('LONECARET', '(?:\\^)')
  785.  
  786. createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true)
  787. exports.caretTrimReplace = '$1^'
  788.  
  789. createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`)
  790. createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`)
  791.  
  792. // A simple gt/lt/eq thing, or just "" to indicate "any version"
  793. createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`)
  794. createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`)
  795.  
  796. // An expression to strip any whitespace between the gtlt and the thing
  797. // it modifies, so that `> 1.2.3` ==> `>1.2.3`
  798. createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]
  799. }\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true)
  800. exports.comparatorTrimReplace = '$1$2$3'
  801.  
  802. // Something like `1.2.3 - 1.2.4`
  803. // Note that these all use the loose form, because they'll be
  804. // checked against either the strict or loose comparator form
  805. // later.
  806. createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +
  807. `\\s+-\\s+` +
  808. `(${src[t.XRANGEPLAIN]})` +
  809. `\\s*$`)
  810.  
  811. createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
  812. `\\s+-\\s+` +
  813. `(${src[t.XRANGEPLAINLOOSE]})` +
  814. `\\s*$`)
  815.  
  816. // Star ranges basically just allow anything at all.
  817. createToken('STAR', '(<|>)?=?\\s*\\*')
  818. // >=0.0.0 is like a star
  819. createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$')
  820. createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$')
  821.  
  822.  
  823. /***/ }),
  824.  
  825. /***/ 746:
  826. /***/ (() => {
  827.  
  828. "use strict";
  829.  
  830. // --- HOOK GLOBAL WEBSOCKET POUR INTERCEPTION gameId & PTC monitoring ---
  831. (function () {
  832. const OriginalWebSocket = window.WebSocket;
  833. function HookedWebSocket(url, protocols) {
  834. const ws = protocols !== undefined
  835. ? new OriginalWebSocket(url, protocols)
  836. : new OriginalWebSocket(url);
  837. if (typeof url === "string" && url.includes("gameId=")) {
  838. const gameId = url.split("gameId=")[1];
  839. globalThis.kxsClient.kxsNetwork.sendGameInfoToWebSocket(gameId);
  840. }
  841. return ws;
  842. }
  843. // Copie le prototype
  844. HookedWebSocket.prototype = OriginalWebSocket.prototype;
  845. // Copie les propriétés statiques (CONNECTING, OPEN, etc.)
  846. Object.defineProperties(HookedWebSocket, {
  847. CONNECTING: { value: OriginalWebSocket.CONNECTING, writable: false },
  848. OPEN: { value: OriginalWebSocket.OPEN, writable: false },
  849. CLOSING: { value: OriginalWebSocket.CLOSING, writable: false },
  850. CLOSED: { value: OriginalWebSocket.CLOSED, writable: false },
  851. });
  852. // Remplace le constructeur global
  853. window.WebSocket = HookedWebSocket;
  854. })();
  855.  
  856.  
  857. /***/ }),
  858.  
  859. /***/ 874:
  860. /***/ ((module) => {
  861.  
  862. // Note: this is the semver.org version of the spec that it implements
  863. // Not necessarily the package version of this code.
  864. const SEMVER_SPEC_VERSION = '2.0.0'
  865.  
  866. const MAX_LENGTH = 256
  867. const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
  868. /* istanbul ignore next */ 9007199254740991
  869.  
  870. // Max safe segment length for coercion.
  871. const MAX_SAFE_COMPONENT_LENGTH = 16
  872.  
  873. // Max safe length for a build identifier. The max length minus 6 characters for
  874. // the shortest version with a build 0.0.0+BUILD.
  875. const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
  876.  
  877. const RELEASE_TYPES = [
  878. 'major',
  879. 'premajor',
  880. 'minor',
  881. 'preminor',
  882. 'patch',
  883. 'prepatch',
  884. 'prerelease',
  885. ]
  886.  
  887. module.exports = {
  888. MAX_LENGTH,
  889. MAX_SAFE_COMPONENT_LENGTH,
  890. MAX_SAFE_BUILD_LENGTH,
  891. MAX_SAFE_INTEGER,
  892. RELEASE_TYPES,
  893. SEMVER_SPEC_VERSION,
  894. FLAG_INCLUDE_PRERELEASE: 0b001,
  895. FLAG_LOOSE: 0b010,
  896. }
  897.  
  898.  
  899. /***/ }),
  900.  
  901. /***/ 908:
  902. /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
  903.  
  904. const debug = __webpack_require__(272)
  905. const { MAX_LENGTH, MAX_SAFE_INTEGER } = __webpack_require__(874)
  906. const { safeRe: re, safeSrc: src, t } = __webpack_require__(718)
  907.  
  908. const parseOptions = __webpack_require__(587)
  909. const { compareIdentifiers } = __webpack_require__(123)
  910. class SemVer {
  911. constructor (version, options) {
  912. options = parseOptions(options)
  913.  
  914. if (version instanceof SemVer) {
  915. if (version.loose === !!options.loose &&
  916. version.includePrerelease === !!options.includePrerelease) {
  917. return version
  918. } else {
  919. version = version.version
  920. }
  921. } else if (typeof version !== 'string') {
  922. throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`)
  923. }
  924.  
  925. if (version.length > MAX_LENGTH) {
  926. throw new TypeError(
  927. `version is longer than ${MAX_LENGTH} characters`
  928. )
  929. }
  930.  
  931. debug('SemVer', version, options)
  932. this.options = options
  933. this.loose = !!options.loose
  934. // this isn't actually relevant for versions, but keep it so that we
  935. // don't run into trouble passing this.options around.
  936. this.includePrerelease = !!options.includePrerelease
  937.  
  938. const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])
  939.  
  940. if (!m) {
  941. throw new TypeError(`Invalid Version: ${version}`)
  942. }
  943.  
  944. this.raw = version
  945.  
  946. // these are actually numbers
  947. this.major = +m[1]
  948. this.minor = +m[2]
  949. this.patch = +m[3]
  950.  
  951. if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
  952. throw new TypeError('Invalid major version')
  953. }
  954.  
  955. if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
  956. throw new TypeError('Invalid minor version')
  957. }
  958.  
  959. if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
  960. throw new TypeError('Invalid patch version')
  961. }
  962.  
  963. // numberify any prerelease numeric ids
  964. if (!m[4]) {
  965. this.prerelease = []
  966. } else {
  967. this.prerelease = m[4].split('.').map((id) => {
  968. if (/^[0-9]+$/.test(id)) {
  969. const num = +id
  970. if (num >= 0 && num < MAX_SAFE_INTEGER) {
  971. return num
  972. }
  973. }
  974. return id
  975. })
  976. }
  977.  
  978. this.build = m[5] ? m[5].split('.') : []
  979. this.format()
  980. }
  981.  
  982. format () {
  983. this.version = `${this.major}.${this.minor}.${this.patch}`
  984. if (this.prerelease.length) {
  985. this.version += `-${this.prerelease.join('.')}`
  986. }
  987. return this.version
  988. }
  989.  
  990. toString () {
  991. return this.version
  992. }
  993.  
  994. compare (other) {
  995. debug('SemVer.compare', this.version, this.options, other)
  996. if (!(other instanceof SemVer)) {
  997. if (typeof other === 'string' && other === this.version) {
  998. return 0
  999. }
  1000. other = new SemVer(other, this.options)
  1001. }
  1002.  
  1003. if (other.version === this.version) {
  1004. return 0
  1005. }
  1006.  
  1007. return this.compareMain(other) || this.comparePre(other)
  1008. }
  1009.  
  1010. compareMain (other) {
  1011. if (!(other instanceof SemVer)) {
  1012. other = new SemVer(other, this.options)
  1013. }
  1014.  
  1015. return (
  1016. compareIdentifiers(this.major, other.major) ||
  1017. compareIdentifiers(this.minor, other.minor) ||
  1018. compareIdentifiers(this.patch, other.patch)
  1019. )
  1020. }
  1021.  
  1022. comparePre (other) {
  1023. if (!(other instanceof SemVer)) {
  1024. other = new SemVer(other, this.options)
  1025. }
  1026.  
  1027. // NOT having a prerelease is > having one
  1028. if (this.prerelease.length && !other.prerelease.length) {
  1029. return -1
  1030. } else if (!this.prerelease.length && other.prerelease.length) {
  1031. return 1
  1032. } else if (!this.prerelease.length && !other.prerelease.length) {
  1033. return 0
  1034. }
  1035.  
  1036. let i = 0
  1037. do {
  1038. const a = this.prerelease[i]
  1039. const b = other.prerelease[i]
  1040. debug('prerelease compare', i, a, b)
  1041. if (a === undefined && b === undefined) {
  1042. return 0
  1043. } else if (b === undefined) {
  1044. return 1
  1045. } else if (a === undefined) {
  1046. return -1
  1047. } else if (a === b) {
  1048. continue
  1049. } else {
  1050. return compareIdentifiers(a, b)
  1051. }
  1052. } while (++i)
  1053. }
  1054.  
  1055. compareBuild (other) {
  1056. if (!(other instanceof SemVer)) {
  1057. other = new SemVer(other, this.options)
  1058. }
  1059.  
  1060. let i = 0
  1061. do {
  1062. const a = this.build[i]
  1063. const b = other.build[i]
  1064. debug('build compare', i, a, b)
  1065. if (a === undefined && b === undefined) {
  1066. return 0
  1067. } else if (b === undefined) {
  1068. return 1
  1069. } else if (a === undefined) {
  1070. return -1
  1071. } else if (a === b) {
  1072. continue
  1073. } else {
  1074. return compareIdentifiers(a, b)
  1075. }
  1076. } while (++i)
  1077. }
  1078.  
  1079. // preminor will bump the version up to the next minor release, and immediately
  1080. // down to pre-release. premajor and prepatch work the same way.
  1081. inc (release, identifier, identifierBase) {
  1082. if (release.startsWith('pre')) {
  1083. if (!identifier && identifierBase === false) {
  1084. throw new Error('invalid increment argument: identifier is empty')
  1085. }
  1086. // Avoid an invalid semver results
  1087. if (identifier) {
  1088. const r = new RegExp(`^${this.options.loose ? src[t.PRERELEASELOOSE] : src[t.PRERELEASE]}$`)
  1089. const match = `-${identifier}`.match(r)
  1090. if (!match || match[1] !== identifier) {
  1091. throw new Error(`invalid identifier: ${identifier}`)
  1092. }
  1093. }
  1094. }
  1095.  
  1096. switch (release) {
  1097. case 'premajor':
  1098. this.prerelease.length = 0
  1099. this.patch = 0
  1100. this.minor = 0
  1101. this.major++
  1102. this.inc('pre', identifier, identifierBase)
  1103. break
  1104. case 'preminor':
  1105. this.prerelease.length = 0
  1106. this.patch = 0
  1107. this.minor++
  1108. this.inc('pre', identifier, identifierBase)
  1109. break
  1110. case 'prepatch':
  1111. // If this is already a prerelease, it will bump to the next version
  1112. // drop any prereleases that might already exist, since they are not
  1113. // relevant at this point.
  1114. this.prerelease.length = 0
  1115. this.inc('patch', identifier, identifierBase)
  1116. this.inc('pre', identifier, identifierBase)
  1117. break
  1118. // If the input is a non-prerelease version, this acts the same as
  1119. // prepatch.
  1120. case 'prerelease':
  1121. if (this.prerelease.length === 0) {
  1122. this.inc('patch', identifier, identifierBase)
  1123. }
  1124. this.inc('pre', identifier, identifierBase)
  1125. break
  1126. case 'release':
  1127. if (this.prerelease.length === 0) {
  1128. throw new Error(`version ${this.raw} is not a prerelease`)
  1129. }
  1130. this.prerelease.length = 0
  1131. break
  1132.  
  1133. case 'major':
  1134. // If this is a pre-major version, bump up to the same major version.
  1135. // Otherwise increment major.
  1136. // 1.0.0-5 bumps to 1.0.0
  1137. // 1.1.0 bumps to 2.0.0
  1138. if (
  1139. this.minor !== 0 ||
  1140. this.patch !== 0 ||
  1141. this.prerelease.length === 0
  1142. ) {
  1143. this.major++
  1144. }
  1145. this.minor = 0
  1146. this.patch = 0
  1147. this.prerelease = []
  1148. break
  1149. case 'minor':
  1150. // If this is a pre-minor version, bump up to the same minor version.
  1151. // Otherwise increment minor.
  1152. // 1.2.0-5 bumps to 1.2.0
  1153. // 1.2.1 bumps to 1.3.0
  1154. if (this.patch !== 0 || this.prerelease.length === 0) {
  1155. this.minor++
  1156. }
  1157. this.patch = 0
  1158. this.prerelease = []
  1159. break
  1160. case 'patch':
  1161. // If this is not a pre-release version, it will increment the patch.
  1162. // If it is a pre-release it will bump up to the same patch version.
  1163. // 1.2.0-5 patches to 1.2.0
  1164. // 1.2.0 patches to 1.2.1
  1165. if (this.prerelease.length === 0) {
  1166. this.patch++
  1167. }
  1168. this.prerelease = []
  1169. break
  1170. // This probably shouldn't be used publicly.
  1171. // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
  1172. case 'pre': {
  1173. const base = Number(identifierBase) ? 1 : 0
  1174.  
  1175. if (this.prerelease.length === 0) {
  1176. this.prerelease = [base]
  1177. } else {
  1178. let i = this.prerelease.length
  1179. while (--i >= 0) {
  1180. if (typeof this.prerelease[i] === 'number') {
  1181. this.prerelease[i]++
  1182. i = -2
  1183. }
  1184. }
  1185. if (i === -1) {
  1186. // didn't increment anything
  1187. if (identifier === this.prerelease.join('.') && identifierBase === false) {
  1188. throw new Error('invalid increment argument: identifier already exists')
  1189. }
  1190. this.prerelease.push(base)
  1191. }
  1192. }
  1193. if (identifier) {
  1194. // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
  1195. // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
  1196. let prerelease = [identifier, base]
  1197. if (identifierBase === false) {
  1198. prerelease = [identifier]
  1199. }
  1200. if (compareIdentifiers(this.prerelease[0], identifier) === 0) {
  1201. if (isNaN(this.prerelease[1])) {
  1202. this.prerelease = prerelease
  1203. }
  1204. } else {
  1205. this.prerelease = prerelease
  1206. }
  1207. }
  1208. break
  1209. }
  1210. default:
  1211. throw new Error(`invalid increment argument: ${release}`)
  1212. }
  1213. this.raw = this.format()
  1214. if (this.build.length) {
  1215. this.raw += `+${this.build.join('.')}`
  1216. }
  1217. return this
  1218. }
  1219. }
  1220.  
  1221. module.exports = SemVer
  1222.  
  1223.  
  1224. /***/ })
  1225.  
  1226. /******/ });
  1227. /************************************************************************/
  1228. /******/ // The module cache
  1229. /******/ var __webpack_module_cache__ = {};
  1230. /******/
  1231. /******/ // The require function
  1232. /******/ function __webpack_require__(moduleId) {
  1233. /******/ // Check if module is in cache
  1234. /******/ var cachedModule = __webpack_module_cache__[moduleId];
  1235. /******/ if (cachedModule !== undefined) {
  1236. /******/ return cachedModule.exports;
  1237. /******/ }
  1238. /******/ // Create a new module (and put it into the cache)
  1239. /******/ var module = __webpack_module_cache__[moduleId] = {
  1240. /******/ // no module.id needed
  1241. /******/ // no module.loaded needed
  1242. /******/ exports: {}
  1243. /******/ };
  1244. /******/
  1245. /******/ // Execute the module function
  1246. /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  1247. /******/
  1248. /******/ // Return the exports of the module
  1249. /******/ return module.exports;
  1250. /******/ }
  1251. /******/
  1252. /************************************************************************/
  1253. /******/ /* webpack/runtime/compat get default export */
  1254. /******/ (() => {
  1255. /******/ // getDefaultExport function for compatibility with non-harmony modules
  1256. /******/ __webpack_require__.n = (module) => {
  1257. /******/ var getter = module && module.__esModule ?
  1258. /******/ () => (module['default']) :
  1259. /******/ () => (module);
  1260. /******/ __webpack_require__.d(getter, { a: getter });
  1261. /******/ return getter;
  1262. /******/ };
  1263. /******/ })();
  1264. /******/
  1265. /******/ /* webpack/runtime/define property getters */
  1266. /******/ (() => {
  1267. /******/ // define getter functions for harmony exports
  1268. /******/ __webpack_require__.d = (exports, definition) => {
  1269. /******/ for(var key in definition) {
  1270. /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
  1271. /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
  1272. /******/ }
  1273. /******/ }
  1274. /******/ };
  1275. /******/ })();
  1276. /******/
  1277. /******/ /* webpack/runtime/hasOwnProperty shorthand */
  1278. /******/ (() => {
  1279. /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
  1280. /******/ })();
  1281. /******/
  1282. /************************************************************************/
  1283. var __webpack_exports__ = {};
  1284. // This entry needs to be wrapped in an IIFE because it needs to be in strict mode.
  1285. (() => {
  1286. "use strict";
  1287.  
  1288. // EXTERNAL MODULE: ./src/UTILS/websocket-hook.ts
  1289. var websocket_hook = __webpack_require__(746);
  1290. // EXTERNAL MODULE: ../../GitLab/SteganoDB2/lib/simplified_browser.js
  1291. var simplified_browser = __webpack_require__(229);
  1292. ;// ./config.json
  1293. const config_namespaceObject = /*#__PURE__*/JSON.parse('{"base_url":"https://kxs.rip","api_url":"https://network.kxs.rip","fileName":"KxsClient.user.js","match":["*://survev.io/*","*://66.179.254.36/*","*://zurviv.io/*","*://resurviv.biz/*","*://leia-uwu.github.io/survev/*","*://survev.leia-is.gay/*","*://survivx.org","*://kxs.rip/*"],"grant":["none"]}');
  1294. ;// ./src/UTILS/vars.ts
  1295.  
  1296.  
  1297. const background_song = config_namespaceObject.base_url + "/assets/Stranger_Things_Theme_Song_C418_REMIX.mp3";
  1298. const kxs_logo = "https://files.catbox.moe/onhbvw.png";
  1299. const full_logo = "https://files.catbox.moe/1yu9ii.png";
  1300. const background_image = config_namespaceObject.base_url + "/assets/background.jpg";
  1301. const win_sound = config_namespaceObject.base_url + "/assets/win.m4a";
  1302. const death_sound = config_namespaceObject.base_url + "/assets/dead.m4a";
  1303. const survev_settings = new simplified_browser/* SimplifiedSteganoDB */.A({
  1304. database: "surviv_config",
  1305. });
  1306. const kxs_settings = new simplified_browser/* SimplifiedSteganoDB */.A({
  1307. database: "userSettings"
  1308. });
  1309.  
  1310. ;// ./src/MECHANIC/intercept.ts
  1311. function intercept(link, targetUrl) {
  1312. const open = XMLHttpRequest.prototype.open;
  1313. XMLHttpRequest.prototype.open = function (method, url) {
  1314. if (url.includes(link)) {
  1315. arguments[1] = targetUrl;
  1316. }
  1317. open.apply(this, arguments);
  1318. };
  1319. const originalFetch = window.fetch;
  1320. window.fetch = function (url, options) {
  1321. if (url.includes(link)) {
  1322. url = targetUrl;
  1323. }
  1324. return originalFetch.apply(this, arguments);
  1325. };
  1326. }
  1327.  
  1328.  
  1329. ;// ./src/HUD/MOD/HealthWarning.ts
  1330. class HealthWarning {
  1331. constructor(kxsClient) {
  1332. this.isDraggable = false;
  1333. this.isDragging = false;
  1334. this.dragOffset = { x: 0, y: 0 };
  1335. this.POSITION_KEY = 'lowHpWarning';
  1336. this.menuCheckInterval = null;
  1337. this.warningElement = null;
  1338. this.kxsClient = kxsClient;
  1339. this.createWarningElement();
  1340. this.setFixedPosition();
  1341. this.setupDragAndDrop();
  1342. this.startMenuCheckInterval();
  1343. }
  1344. createWarningElement() {
  1345. const warning = document.createElement("div");
  1346. const uiTopLeft = document.getElementById("ui-top-left");
  1347. warning.style.cssText = `
  1348. position: fixed;
  1349. background: rgba(0, 0, 0, 0.8);
  1350. border: 2px solid #ff0000;
  1351. border-radius: 5px;
  1352. padding: 10px 15px;
  1353. color: #ff0000;
  1354. font-family: Arial, sans-serif;
  1355. font-size: 14px;
  1356. z-index: 9999;
  1357. display: none;
  1358. backdrop-filter: blur(5px);
  1359. pointer-events: none;
  1360. transition: border-color 0.3s ease;
  1361. `;
  1362. const content = document.createElement("div");
  1363. content.style.cssText = `
  1364. display: flex;
  1365. align-items: center;
  1366. gap: 8px;
  1367. `;
  1368. const icon = document.createElement("div");
  1369. icon.innerHTML = `
  1370. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  1371. <circle cx="12" cy="12" r="10"></circle>
  1372. <line x1="12" y1="8" x2="12" y2="12"></line>
  1373. <line x1="12" y1="16" x2="12.01" y2="16"></line>
  1374. </svg>
  1375. `;
  1376. const text = document.createElement("span");
  1377. text.textContent = "LOW HP!";
  1378. if (uiTopLeft) {
  1379. content.appendChild(icon);
  1380. content.appendChild(text);
  1381. warning.appendChild(content);
  1382. uiTopLeft.appendChild(warning);
  1383. }
  1384. this.warningElement = warning;
  1385. this.addPulseAnimation();
  1386. }
  1387. setFixedPosition() {
  1388. if (!this.warningElement)
  1389. return;
  1390. // Récupérer la position depuis le localStorage ou les valeurs par défaut
  1391. const storageKey = `position_${this.POSITION_KEY}`;
  1392. const savedPosition = localStorage.getItem(storageKey);
  1393. let position;
  1394. if (savedPosition) {
  1395. try {
  1396. // Utiliser la position sauvegardée
  1397. const { x, y } = JSON.parse(savedPosition);
  1398. position = { left: x, top: y };
  1399. }
  1400. catch (error) {
  1401. // En cas d'erreur, utiliser la position par défaut
  1402. position = this.kxsClient.defaultPositions[this.POSITION_KEY];
  1403. this.kxsClient.logger.error('Erreur lors du chargement de la position LOW HP:', error);
  1404. }
  1405. }
  1406. else {
  1407. // Utiliser la position par défaut
  1408. position = this.kxsClient.defaultPositions[this.POSITION_KEY];
  1409. }
  1410. // Appliquer la position
  1411. if (position) {
  1412. this.warningElement.style.top = `${position.top}px`;
  1413. this.warningElement.style.left = `${position.left}px`;
  1414. }
  1415. }
  1416. addPulseAnimation() {
  1417. const keyframes = `
  1418. @keyframes pulse {
  1419. 0% { opacity: 1; }
  1420. 50% { opacity: 0.5; }
  1421. 100% { opacity: 1; }
  1422. }
  1423. `;
  1424. const style = document.createElement("style");
  1425. style.textContent = keyframes;
  1426. document.head.appendChild(style);
  1427. if (this.warningElement) {
  1428. this.warningElement.style.animation = "pulse 1.5s infinite";
  1429. }
  1430. }
  1431. show(health) {
  1432. if (!this.warningElement)
  1433. return;
  1434. this.warningElement.style.display = "block";
  1435. const span = this.warningElement.querySelector("span");
  1436. if (span) {
  1437. span.textContent = `LOW HP: ${health}%`;
  1438. }
  1439. }
  1440. hide() {
  1441. if (!this.warningElement)
  1442. return;
  1443. // Ne pas masquer si en mode placement
  1444. // if (this.isDraggable) return;
  1445. this.warningElement.style.display = "none";
  1446. }
  1447. update(health) {
  1448. // Si le mode placement est actif (isDraggable), on ne fait rien pour maintenir l'affichage
  1449. if (this.isDraggable) {
  1450. return;
  1451. }
  1452. // Sinon, comportement normal
  1453. if (health <= 30 && health > 0) {
  1454. this.show(health);
  1455. }
  1456. else {
  1457. this.hide();
  1458. }
  1459. }
  1460. setupDragAndDrop() {
  1461. // Nous n'avons plus besoin d'écouteurs pour RSHIFT car nous utilisons maintenant
  1462. // l'état du menu secondaire pour déterminer quand activer/désactiver le mode placement
  1463. // Écouteurs d'événements de souris pour le glisser-déposer
  1464. document.addEventListener('mousedown', this.handleMouseDown.bind(this));
  1465. document.addEventListener('mousemove', this.handleMouseMove.bind(this));
  1466. document.addEventListener('mouseup', this.handleMouseUp.bind(this));
  1467. }
  1468. enableDragging() {
  1469. if (!this.warningElement)
  1470. return;
  1471. this.isDraggable = true;
  1472. this.warningElement.style.pointerEvents = 'auto';
  1473. this.warningElement.style.cursor = 'move';
  1474. this.warningElement.style.borderColor = '#00ff00'; // Feedback visuel quand déplaçable
  1475. // Force l'affichage de l'avertissement LOW HP, peu importe la santé actuelle
  1476. this.warningElement.style.display = 'block';
  1477. const span = this.warningElement.querySelector("span");
  1478. if (span) {
  1479. span.textContent = 'LOW HP: Placement Mode';
  1480. }
  1481. }
  1482. disableDragging() {
  1483. if (!this.warningElement)
  1484. return;
  1485. this.isDraggable = false;
  1486. this.isDragging = false;
  1487. this.warningElement.style.pointerEvents = 'none';
  1488. this.warningElement.style.cursor = 'default';
  1489. this.warningElement.style.borderColor = '#ff0000'; // Retour à la couleur normale
  1490. // Remet le texte original si l'avertissement est visible
  1491. if (this.warningElement.style.display === 'block') {
  1492. const span = this.warningElement.querySelector("span");
  1493. if (span) {
  1494. span.textContent = 'LOW HP';
  1495. }
  1496. }
  1497. // Récupérer la santé actuelle à partir de l'élément UI de santé du jeu
  1498. const healthBars = document.querySelectorAll("#ui-health-container");
  1499. if (healthBars.length > 0) {
  1500. const bar = healthBars[0].querySelector("#ui-health-actual");
  1501. if (bar) {
  1502. const currentHealth = Math.round(parseFloat(bar.style.width));
  1503. // Forcer une mise à jour immédiate en fonction de la santé actuelle
  1504. this.update(currentHealth);
  1505. }
  1506. }
  1507. }
  1508. handleMouseDown(event) {
  1509. if (!this.isDraggable || !this.warningElement)
  1510. return;
  1511. // Check if click was on the warning element
  1512. if (this.warningElement.contains(event.target)) {
  1513. this.isDragging = true;
  1514. // Calculate offset from mouse position to element corner
  1515. const rect = this.warningElement.getBoundingClientRect();
  1516. this.dragOffset = {
  1517. x: event.clientX - rect.left,
  1518. y: event.clientY - rect.top
  1519. };
  1520. // Prevent text selection during drag
  1521. event.preventDefault();
  1522. }
  1523. }
  1524. handleMouseMove(event) {
  1525. if (!this.isDragging || !this.warningElement)
  1526. return;
  1527. // Calculate new position
  1528. const newX = event.clientX - this.dragOffset.x;
  1529. const newY = event.clientY - this.dragOffset.y;
  1530. // Update element position
  1531. this.warningElement.style.left = `${newX}px`;
  1532. this.warningElement.style.top = `${newY}px`;
  1533. }
  1534. handleMouseUp() {
  1535. if (this.isDragging && this.warningElement) {
  1536. this.isDragging = false;
  1537. // Récupérer les positions actuelles
  1538. const left = parseInt(this.warningElement.style.left);
  1539. const top = parseInt(this.warningElement.style.top);
  1540. // Sauvegarder la position
  1541. const storageKey = `position_${this.POSITION_KEY}`;
  1542. localStorage.setItem(storageKey, JSON.stringify({ x: left, y: top }));
  1543. }
  1544. }
  1545. startMenuCheckInterval() {
  1546. // Créer un intervalle qui vérifie régulièrement l'état du menu RSHIFT
  1547. this.menuCheckInterval = window.setInterval(() => {
  1548. var _a;
  1549. // Vérifier si le menu secondaire est ouvert
  1550. const isMenuOpen = ((_a = this.kxsClient.secondaryMenu) === null || _a === void 0 ? void 0 : _a.isOpen) || false;
  1551. // Si le menu est ouvert et que nous ne sommes pas en mode placement, activer le mode placement
  1552. if (isMenuOpen && this.kxsClient.isHealthWarningEnabled && !this.isDraggable) {
  1553. this.enableDragging();
  1554. }
  1555. // Si le menu est fermé et que nous sommes en mode placement, désactiver le mode placement
  1556. else if (!isMenuOpen && this.isDraggable) {
  1557. this.disableDragging();
  1558. }
  1559. }, 100); // Vérifier toutes les 100ms
  1560. }
  1561. }
  1562.  
  1563.  
  1564. ;// ./src/MECHANIC/KillLeaderTracking.ts
  1565. class KillLeaderTracker {
  1566. constructor(kxsClient) {
  1567. this.offsetX = 20;
  1568. this.offsetY = 20;
  1569. this.lastKnownKills = 0;
  1570. this.wasKillLeader = false;
  1571. this.MINIMUM_KILLS_FOR_LEADER = 3;
  1572. this.kxsClient = kxsClient;
  1573. this.warningElement = null;
  1574. this.encouragementElement = null;
  1575. this.killLeaderKillCount = 0;
  1576. this.wasKillLeader = false;
  1577. this.createEncouragementElement();
  1578. this.initMouseTracking();
  1579. }
  1580. createEncouragementElement() {
  1581. const encouragement = document.createElement("div");
  1582. encouragement.style.cssText = `
  1583. position: fixed;
  1584. background: rgba(0, 255, 0, 0.1);
  1585. border: 2px solid #00ff00;
  1586. border-radius: 5px;
  1587. padding: 10px 15px;
  1588. color: #00ff00;
  1589. font-family: Arial, sans-serif;
  1590. font-size: 14px;
  1591. z-index: 9999;
  1592. display: none;
  1593. backdrop-filter: blur(5px);
  1594. transition: all 0.3s ease;
  1595. pointer-events: none;
  1596. box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
  1597. `;
  1598. const content = document.createElement("div");
  1599. content.style.cssText = `
  1600. display: flex;
  1601. align-items: center;
  1602. gap: 8px;
  1603. `;
  1604. const icon = document.createElement("div");
  1605. icon.innerHTML = `
  1606. <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
  1607. <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"/>
  1608. </svg>
  1609. `;
  1610. const text = document.createElement("span");
  1611. text.textContent = "Nice Kill!";
  1612. content.appendChild(icon);
  1613. content.appendChild(text);
  1614. encouragement.appendChild(content);
  1615. document.body.appendChild(encouragement);
  1616. this.encouragementElement = encouragement;
  1617. this.addEncouragementAnimation();
  1618. }
  1619. initMouseTracking() {
  1620. document.addEventListener("mousemove", (e) => {
  1621. this.updateElementPosition(this.warningElement, e);
  1622. this.updateElementPosition(this.encouragementElement, e);
  1623. });
  1624. }
  1625. updateElementPosition(element, e) {
  1626. if (!element || element.style.display === "none")
  1627. return;
  1628. const x = e.clientX + this.offsetX;
  1629. const y = e.clientY + this.offsetY;
  1630. const rect = element.getBoundingClientRect();
  1631. const maxX = window.innerWidth - rect.width;
  1632. const maxY = window.innerHeight - rect.height;
  1633. const finalX = Math.min(Math.max(0, x), maxX);
  1634. const finalY = Math.min(Math.max(0, y), maxY);
  1635. element.style.transform = `translate(${finalX}px, ${finalY}px)`;
  1636. }
  1637. addEncouragementAnimation() {
  1638. const keyframes = `
  1639. @keyframes encouragementPulse {
  1640. 0% { transform: scale(1); opacity: 1; }
  1641. 50% { transform: scale(1.1); opacity: 0.8; }
  1642. 100% { transform: scale(1); opacity: 1; }
  1643. }
  1644. @keyframes fadeInOut {
  1645. 0% { opacity: 0; transform: translateY(20px); }
  1646. 10% { opacity: 1; transform: translateY(0); }
  1647. 90% { opacity: 1; transform: translateY(0); }
  1648. 100% { opacity: 0; transform: translateY(-20px); }
  1649. }
  1650. `;
  1651. const style = document.createElement("style");
  1652. style.textContent = keyframes;
  1653. document.head.appendChild(style);
  1654. if (this.encouragementElement) {
  1655. this.encouragementElement.style.animation = "fadeInOut 3s forwards";
  1656. }
  1657. }
  1658. showEncouragement(killsToLeader, isDethrone = false, noKillLeader = false) {
  1659. if (!this.encouragementElement)
  1660. return;
  1661. let message;
  1662. if (isDethrone && killsToLeader !== 0) {
  1663. message = "Oh no! You've been dethroned!";
  1664. this.encouragementElement.style.borderColor = "#ff0000";
  1665. this.encouragementElement.style.color = "#ff0000";
  1666. this.encouragementElement.style.background = "rgba(255, 0, 0, 0.1)";
  1667. }
  1668. else if (noKillLeader) {
  1669. const killsNeeded = this.MINIMUM_KILLS_FOR_LEADER - this.lastKnownKills;
  1670. message = `Nice Kill! Get ${killsNeeded} more kills to become the first Kill Leader!`;
  1671. }
  1672. else {
  1673. message =
  1674. killsToLeader <= 0
  1675. ? "You're the Kill Leader! 👑"
  1676. : `Nice Kill! ${killsToLeader} more to become Kill Leader!`;
  1677. }
  1678. const span = this.encouragementElement.querySelector("span");
  1679. if (span)
  1680. span.textContent = message;
  1681. this.encouragementElement.style.display = "block";
  1682. this.encouragementElement.style.animation = "fadeInOut 3s forwards";
  1683. setTimeout(() => {
  1684. if (this.encouragementElement) {
  1685. this.encouragementElement.style.display = "none";
  1686. // Reset colors
  1687. this.encouragementElement.style.borderColor = "#00ff00";
  1688. this.encouragementElement.style.color = "#00ff00";
  1689. this.encouragementElement.style.background = "rgba(0, 255, 0, 0.1)";
  1690. }
  1691. }, 7000);
  1692. }
  1693. isKillLeader() {
  1694. const killLeaderNameElement = document.querySelector("#ui-kill-leader-name");
  1695. return this.kxsClient.getPlayerName() === (killLeaderNameElement === null || killLeaderNameElement === void 0 ? void 0 : killLeaderNameElement.textContent);
  1696. }
  1697. update(myKills) {
  1698. if (!this.kxsClient.isKillLeaderTrackerEnabled)
  1699. return;
  1700. const killLeaderElement = document.querySelector("#ui-kill-leader-count");
  1701. this.killLeaderKillCount = parseInt((killLeaderElement === null || killLeaderElement === void 0 ? void 0 : killLeaderElement.textContent) || "0", 10);
  1702. if (myKills > this.lastKnownKills) {
  1703. if (this.killLeaderKillCount === 0) {
  1704. // Pas encore de kill leader, encourager le joueur à atteindre 3 kills
  1705. this.showEncouragement(0, false, true);
  1706. }
  1707. else if (this.killLeaderKillCount < this.MINIMUM_KILLS_FOR_LEADER) {
  1708. // Ne rien faire si le kill leader n'a pas atteint le minimum requis
  1709. return;
  1710. }
  1711. else if (this.isKillLeader()) {
  1712. this.showEncouragement(0);
  1713. this.wasKillLeader = true;
  1714. }
  1715. else {
  1716. const killsNeeded = this.killLeaderKillCount + 1 - myKills;
  1717. this.showEncouragement(killsNeeded);
  1718. }
  1719. }
  1720. else if (this.wasKillLeader && !this.isKillLeader()) {
  1721. // Détroné
  1722. this.showEncouragement(0, true);
  1723. this.wasKillLeader = false;
  1724. }
  1725. this.lastKnownKills = myKills;
  1726. }
  1727. }
  1728.  
  1729.  
  1730. ;// ./src/HUD/GridSystem.ts
  1731. class GridSystem {
  1732. constructor() {
  1733. this.gridSize = 20; // Size of each grid cell
  1734. this.snapThreshold = 15; // Distance in pixels to trigger snap
  1735. this.gridVisible = false;
  1736. this.magneticEdges = true;
  1737. this.counterElements = {};
  1738. this.gridContainer = this.createGridOverlay();
  1739. this.setupKeyBindings();
  1740. }
  1741. createGridOverlay() {
  1742. const container = document.createElement("div");
  1743. container.id = "grid-overlay";
  1744. Object.assign(container.style, {
  1745. position: "fixed",
  1746. top: "0",
  1747. left: "0",
  1748. width: "100%",
  1749. height: "100%",
  1750. pointerEvents: "none",
  1751. zIndex: "9999",
  1752. display: "none",
  1753. opacity: "0.2",
  1754. });
  1755. // Create vertical lines
  1756. for (let x = this.gridSize; x < window.innerWidth; x += this.gridSize) {
  1757. const vLine = document.createElement("div");
  1758. Object.assign(vLine.style, {
  1759. position: "absolute",
  1760. left: `${x}px`,
  1761. top: "0",
  1762. width: "1px",
  1763. height: "100%",
  1764. backgroundColor: "#4CAF50",
  1765. });
  1766. container.appendChild(vLine);
  1767. }
  1768. // Create horizontal lines
  1769. for (let y = this.gridSize; y < window.innerHeight; y += this.gridSize) {
  1770. const hLine = document.createElement("div");
  1771. Object.assign(hLine.style, {
  1772. position: "absolute",
  1773. left: "0",
  1774. top: `${y}px`,
  1775. width: "100%",
  1776. height: "1px",
  1777. backgroundColor: "#4CAF50",
  1778. });
  1779. container.appendChild(hLine);
  1780. }
  1781. document.body.appendChild(container);
  1782. return container;
  1783. }
  1784. setupKeyBindings() {
  1785. document.addEventListener("keydown", (e) => {
  1786. if (e.key === "g" && e.altKey) {
  1787. this.toggleGrid();
  1788. }
  1789. });
  1790. }
  1791. toggleGrid() {
  1792. this.gridVisible = !this.gridVisible;
  1793. this.gridContainer.style.display = this.gridVisible ? "block" : "none";
  1794. }
  1795. registerCounter(id, element) {
  1796. if (element) {
  1797. this.counterElements[id] = element;
  1798. }
  1799. else {
  1800. delete this.counterElements[id];
  1801. }
  1802. }
  1803. areElementsAdjacent(element1, element2) {
  1804. const rect1 = element1.getBoundingClientRect();
  1805. const rect2 = element2.getBoundingClientRect();
  1806. const tolerance = 5;
  1807. const isLeftAdjacent = Math.abs((rect1.left + rect1.width) - rect2.left) < tolerance;
  1808. const isRightAdjacent = Math.abs((rect2.left + rect2.width) - rect1.left) < tolerance;
  1809. const isTopAdjacent = Math.abs((rect1.top + rect1.height) - rect2.top) < tolerance;
  1810. const isBottomAdjacent = Math.abs((rect2.top + rect2.height) - rect1.top) < tolerance;
  1811. const overlapVertically = (rect1.top < rect2.bottom && rect1.bottom > rect2.top) ||
  1812. (rect2.top < rect1.bottom && rect2.bottom > rect1.top);
  1813. const overlapHorizontally = (rect1.left < rect2.right && rect1.right > rect2.left) ||
  1814. (rect2.left < rect1.right && rect2.right > rect1.left);
  1815. let position = "";
  1816. if (isLeftAdjacent && overlapVertically)
  1817. position = "left";
  1818. else if (isRightAdjacent && overlapVertically)
  1819. position = "right";
  1820. else if (isTopAdjacent && overlapHorizontally)
  1821. position = "top";
  1822. else if (isBottomAdjacent && overlapHorizontally)
  1823. position = "bottom";
  1824. return {
  1825. isAdjacent: (isLeftAdjacent || isRightAdjacent) && overlapVertically ||
  1826. (isTopAdjacent || isBottomAdjacent) && overlapHorizontally,
  1827. position
  1828. };
  1829. }
  1830. updateCounterCorners() {
  1831. const counterIds = Object.keys(this.counterElements);
  1832. counterIds.forEach(id => {
  1833. const container = this.counterElements[id];
  1834. const counter = container.querySelector('div');
  1835. if (counter) {
  1836. counter.style.borderRadius = '5px';
  1837. }
  1838. });
  1839. for (let i = 0; i < counterIds.length; i++) {
  1840. for (let j = i + 1; j < counterIds.length; j++) {
  1841. const container1 = this.counterElements[counterIds[i]];
  1842. const container2 = this.counterElements[counterIds[j]];
  1843. const counter1 = container1.querySelector('div');
  1844. const counter2 = container2.querySelector('div');
  1845. if (counter1 && counter2) {
  1846. const { isAdjacent, position } = this.areElementsAdjacent(container1, container2);
  1847. if (isAdjacent) {
  1848. switch (position) {
  1849. case "left":
  1850. counter1.style.borderTopRightRadius = '0';
  1851. counter1.style.borderBottomRightRadius = '0';
  1852. counter2.style.borderTopLeftRadius = '0';
  1853. counter2.style.borderBottomLeftRadius = '0';
  1854. break;
  1855. case "right":
  1856. counter1.style.borderTopLeftRadius = '0';
  1857. counter1.style.borderBottomLeftRadius = '0';
  1858. counter2.style.borderTopRightRadius = '0';
  1859. counter2.style.borderBottomRightRadius = '0';
  1860. break;
  1861. case "top":
  1862. counter1.style.borderBottomLeftRadius = '0';
  1863. counter1.style.borderBottomRightRadius = '0';
  1864. counter2.style.borderTopLeftRadius = '0';
  1865. counter2.style.borderTopRightRadius = '0';
  1866. break;
  1867. case "bottom":
  1868. counter1.style.borderTopLeftRadius = '0';
  1869. counter1.style.borderTopRightRadius = '0';
  1870. counter2.style.borderBottomLeftRadius = '0';
  1871. counter2.style.borderBottomRightRadius = '0';
  1872. break;
  1873. }
  1874. }
  1875. }
  1876. }
  1877. }
  1878. }
  1879. snapToGrid(element, x, y) {
  1880. const rect = element.getBoundingClientRect();
  1881. const elementWidth = rect.width;
  1882. const elementHeight = rect.height;
  1883. // Snap to grid
  1884. let snappedX = Math.round(x / this.gridSize) * this.gridSize;
  1885. let snappedY = Math.round(y / this.gridSize) * this.gridSize;
  1886. // Edge snapping
  1887. if (this.magneticEdges) {
  1888. const screenEdges = {
  1889. left: 0,
  1890. right: window.innerWidth - elementWidth,
  1891. center: (window.innerWidth - elementWidth) / 2,
  1892. top: 0,
  1893. bottom: window.innerHeight - elementHeight,
  1894. middle: (window.innerHeight - elementHeight) / 2,
  1895. };
  1896. // Snap to horizontal edges
  1897. if (Math.abs(x - screenEdges.left) < this.snapThreshold) {
  1898. snappedX = screenEdges.left;
  1899. }
  1900. else if (Math.abs(x - screenEdges.right) < this.snapThreshold) {
  1901. snappedX = screenEdges.right;
  1902. }
  1903. else if (Math.abs(x - screenEdges.center) < this.snapThreshold) {
  1904. snappedX = screenEdges.center;
  1905. }
  1906. // Snap to vertical edges
  1907. if (Math.abs(y - screenEdges.top) < this.snapThreshold) {
  1908. snappedY = screenEdges.top;
  1909. }
  1910. else if (Math.abs(y - screenEdges.bottom) < this.snapThreshold) {
  1911. snappedY = screenEdges.bottom;
  1912. }
  1913. else if (Math.abs(y - screenEdges.middle) < this.snapThreshold) {
  1914. snappedY = screenEdges.middle;
  1915. }
  1916. }
  1917. setTimeout(() => this.updateCounterCorners(), 10);
  1918. return { x: snappedX, y: snappedY };
  1919. }
  1920. highlightNearestGridLine(x, y) {
  1921. if (!this.gridVisible)
  1922. return;
  1923. // Remove existing highlights
  1924. const highlights = document.querySelectorAll(".grid-highlight");
  1925. highlights.forEach((h) => h.remove());
  1926. // Create highlight for nearest vertical line
  1927. const nearestX = Math.round(x / this.gridSize) * this.gridSize;
  1928. if (Math.abs(x - nearestX) < this.snapThreshold) {
  1929. const vHighlight = document.createElement("div");
  1930. Object.assign(vHighlight.style, {
  1931. position: "absolute",
  1932. left: `${nearestX}px`,
  1933. top: "0",
  1934. width: "2px",
  1935. height: "100%",
  1936. backgroundColor: "#FFD700",
  1937. zIndex: "10000",
  1938. pointerEvents: "none",
  1939. });
  1940. vHighlight.classList.add("grid-highlight");
  1941. this.gridContainer.appendChild(vHighlight);
  1942. }
  1943. // Create highlight for nearest horizontal line
  1944. const nearestY = Math.round(y / this.gridSize) * this.gridSize;
  1945. if (Math.abs(y - nearestY) < this.snapThreshold) {
  1946. const hHighlight = document.createElement("div");
  1947. Object.assign(hHighlight.style, {
  1948. position: "absolute",
  1949. left: "0",
  1950. top: `${nearestY}px`,
  1951. width: "100%",
  1952. height: "2px",
  1953. backgroundColor: "#FFD700",
  1954. zIndex: "10000",
  1955. pointerEvents: "none",
  1956. });
  1957. hHighlight.classList.add("grid-highlight");
  1958. this.gridContainer.appendChild(hHighlight);
  1959. }
  1960. }
  1961. }
  1962.  
  1963.  
  1964. ;// ./src/SERVER/DiscordTracking.ts
  1965. var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  1966. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  1967. return new (P || (P = Promise))(function (resolve, reject) {
  1968. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  1969. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  1970. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  1971. step((generator = generator.apply(thisArg, _arguments || [])).next());
  1972. });
  1973. };
  1974.  
  1975. const stuff_emojis = {
  1976. main_weapon: "🔫",
  1977. secondary_weapon: "🔫",
  1978. grenades: "💣",
  1979. melees: "🔪",
  1980. soda: "🥤",
  1981. medkit: "🩹",
  1982. bandage: "🩹",
  1983. pills: "💊",
  1984. backpack: "🎒",
  1985. chest: "📦",
  1986. helmet: "⛑️"
  1987. };
  1988. class WebhookValidator {
  1989. static isValidWebhookUrl(url = '') {
  1990. return url.startsWith("https://");
  1991. }
  1992. static isWebhookAlive(webhookUrl) {
  1993. return __awaiter(this, void 0, void 0, function* () {
  1994. try {
  1995. // First check if the URL format is valid
  1996. if (!this.isValidWebhookUrl(webhookUrl)) {
  1997. throw new Error("Invalid webhook URL format");
  1998. }
  1999. // Test the webhook with a GET request (Discord allows GET on webhooks)
  2000. const response = yield fetch(webhookUrl, {
  2001. method: "GET",
  2002. headers: {
  2003. "Content-Type": "application/json",
  2004. },
  2005. });
  2006. // Discord returns 200 for valid webhooks
  2007. return response.status === 200;
  2008. }
  2009. catch (error) {
  2010. return false;
  2011. }
  2012. });
  2013. }
  2014. static testWebhook(webhookUrl) {
  2015. return __awaiter(this, void 0, void 0, function* () {
  2016. try {
  2017. if (!webhookUrl) {
  2018. return {
  2019. isValid: false,
  2020. message: "Please enter a webhook URL",
  2021. };
  2022. }
  2023. if (!this.isValidWebhookUrl(webhookUrl)) {
  2024. return {
  2025. isValid: false,
  2026. message: "Invalid Discord webhook URL format",
  2027. };
  2028. }
  2029. const isAlive = yield this.isWebhookAlive(webhookUrl);
  2030. return {
  2031. isValid: isAlive,
  2032. message: isAlive
  2033. ? "Webhook is valid and working!"
  2034. : "Webhook is not responding or has been deleted",
  2035. };
  2036. }
  2037. catch (error) {
  2038. return {
  2039. isValid: false,
  2040. message: "Error testing webhook connection",
  2041. };
  2042. }
  2043. });
  2044. }
  2045. }
  2046. class DiscordTracking {
  2047. constructor(kxsClient, webhookUrl) {
  2048. this.kxsClient = kxsClient;
  2049. this.webhookUrl = webhookUrl;
  2050. }
  2051. setWebhookUrl(webhookUrl) {
  2052. this.webhookUrl = webhookUrl;
  2053. }
  2054. validateCurrentWebhook() {
  2055. return __awaiter(this, void 0, void 0, function* () {
  2056. return WebhookValidator.isWebhookAlive(this.webhookUrl);
  2057. });
  2058. }
  2059. sendWebhookMessage(message) {
  2060. return __awaiter(this, void 0, void 0, function* () {
  2061. if (!WebhookValidator.isValidWebhookUrl(this.webhookUrl)) {
  2062. return;
  2063. }
  2064. this.kxsClient.nm.showNotification("Sending Discord message...", "info", 2300);
  2065. try {
  2066. const response = yield fetch(this.webhookUrl, {
  2067. method: "POST",
  2068. headers: {
  2069. "Content-Type": "application/json",
  2070. },
  2071. body: JSON.stringify(message),
  2072. });
  2073. if (!response.ok) {
  2074. throw new Error(`Discord Webhook Error: ${response.status}`);
  2075. }
  2076. }
  2077. catch (error) {
  2078. this.kxsClient.logger.error("Error sending Discord message:", error);
  2079. }
  2080. });
  2081. }
  2082. getEmbedColor(isWin) {
  2083. return isWin ? 0x2ecc71 : 0xe74c3c; // Green for victory, red for defeat
  2084. }
  2085. trackGameEnd(result) {
  2086. return __awaiter(this, void 0, void 0, function* () {
  2087. const title = result.isWin
  2088. ? "🏆 VICTORY ROYALE!"
  2089. : `${result.position} - Game Over`;
  2090. const embed = {
  2091. title,
  2092. description: `${result.username}'s Match`,
  2093. color: this.getEmbedColor(result.isWin),
  2094. fields: [
  2095. {
  2096. name: "💀 Eliminations",
  2097. value: result.kills.toString(),
  2098. inline: true,
  2099. },
  2100. ],
  2101. };
  2102. if (result.duration) {
  2103. embed.fields.push({
  2104. name: "⏱️ Duration",
  2105. value: result.duration,
  2106. inline: true,
  2107. });
  2108. }
  2109. if (result.damageDealt) {
  2110. embed.fields.push({
  2111. name: "💥 Damage Dealt",
  2112. value: Math.round(result.damageDealt).toString(),
  2113. inline: true,
  2114. });
  2115. }
  2116. if (result.damageTaken) {
  2117. embed.fields.push({
  2118. name: "💢 Damage Taken",
  2119. value: Math.round(result.damageTaken).toString(),
  2120. inline: true,
  2121. });
  2122. }
  2123. if (result.username) {
  2124. embed.fields.push({
  2125. name: "📝 Username",
  2126. value: result.username,
  2127. inline: true,
  2128. });
  2129. }
  2130. if (result.stuff) {
  2131. for (const [key, value] of Object.entries(result.stuff)) {
  2132. if (value) {
  2133. embed.fields.push({
  2134. name: `${stuff_emojis[key]} ${key.replace("_", " ").toUpperCase()}`,
  2135. value,
  2136. inline: true,
  2137. });
  2138. }
  2139. }
  2140. }
  2141. const message = {
  2142. username: "DualityClient",
  2143. avatar_url: kxs_logo,
  2144. content: result.isWin ? "🎉 New Victory!" : "Match Ended",
  2145. embeds: [embed],
  2146. };
  2147. yield this.sendWebhookMessage(message);
  2148. });
  2149. }
  2150. }
  2151.  
  2152.  
  2153. ;// ./src/FUNC/StatsParser.ts
  2154. class StatsParser {
  2155. static cleanNumber(str) {
  2156. return parseInt(str.replace(/[^\d.-]/g, "")) || 0;
  2157. }
  2158. /**
  2159. * Extract the full duration string including the unit
  2160. */
  2161. static extractDuration(str) {
  2162. const match = str.match(/(\d+\s*[smh])/i);
  2163. return match ? match[1].trim() : "0s";
  2164. }
  2165. static parse(statsText, rankContent) {
  2166. let stats = {
  2167. username: "Player",
  2168. kills: 0,
  2169. damageDealt: 0,
  2170. damageTaken: 0,
  2171. duration: "",
  2172. position: "#unknown",
  2173. };
  2174. // Handle developer format
  2175. const devPattern = /Developer.*?Kills(\d+).*?Damage Dealt(\d+).*?Damage Taken(\d+).*?Survived(\d+\s*[smh])/i;
  2176. const devMatch = statsText.match(devPattern);
  2177. if (devMatch) {
  2178. return {
  2179. username: "Player",
  2180. kills: this.cleanNumber(devMatch[1]),
  2181. damageDealt: this.cleanNumber(devMatch[2]),
  2182. damageTaken: this.cleanNumber(devMatch[3]),
  2183. duration: devMatch[4].trim(), // Keep the full duration string with unit
  2184. position: rankContent.replace("##", "#"),
  2185. };
  2186. }
  2187. // Handle template format
  2188. const templatePattern = /%username%.*?Kills%kills_number%.*?Dealt%number_dealt%.*?Taken%damage_taken%.*?Survived%duration%/;
  2189. const templateMatch = statsText.match(templatePattern);
  2190. if (templateMatch) {
  2191. const parts = statsText.split(/Kills|Dealt|Taken|Survived/);
  2192. if (parts.length >= 5) {
  2193. return {
  2194. username: parts[0].trim(),
  2195. kills: this.cleanNumber(parts[1]),
  2196. damageDealt: this.cleanNumber(parts[2]),
  2197. damageTaken: this.cleanNumber(parts[3]),
  2198. duration: this.extractDuration(parts[4]), // Extract full duration with unit
  2199. position: rankContent.replace("##", "#"),
  2200. };
  2201. }
  2202. }
  2203. // Generic parsing as fallback
  2204. const usernameMatch = statsText.match(/^([^0-9]+)/);
  2205. if (usernameMatch) {
  2206. stats.username = usernameMatch[1].trim();
  2207. }
  2208. const killsMatch = statsText.match(/Kills[^0-9]*(\d+)/i);
  2209. if (killsMatch) {
  2210. stats.kills = this.cleanNumber(killsMatch[1]);
  2211. }
  2212. const dealtMatch = statsText.match(/Dealt[^0-9]*(\d+)/i);
  2213. if (dealtMatch) {
  2214. stats.damageDealt = this.cleanNumber(dealtMatch[1]);
  2215. }
  2216. const takenMatch = statsText.match(/Taken[^0-9]*(\d+)/i);
  2217. if (takenMatch) {
  2218. stats.damageTaken = this.cleanNumber(takenMatch[1]);
  2219. }
  2220. // Extract survival time with unit
  2221. const survivalMatch = statsText.match(/Survived[^0-9]*(\d+\s*[smh])/i);
  2222. if (survivalMatch) {
  2223. stats.duration = survivalMatch[1].trim();
  2224. }
  2225. stats.position = rankContent.replace("##", "#");
  2226. return stats;
  2227. }
  2228. }
  2229.  
  2230.  
  2231. // EXTERNAL MODULE: ./node_modules/semver/functions/gt.js
  2232. var gt = __webpack_require__(580);
  2233. var gt_default = /*#__PURE__*/__webpack_require__.n(gt);
  2234. ;// ./package.json
  2235. const package_namespaceObject = {"rE":"2.1.24"};
  2236. ;// ./src/FUNC/UpdateChecker.ts
  2237. var UpdateChecker_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  2238. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  2239. return new (P || (P = Promise))(function (resolve, reject) {
  2240. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  2241. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  2242. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  2243. step((generator = generator.apply(thisArg, _arguments || [])).next());
  2244. });
  2245. };
  2246.  
  2247.  
  2248.  
  2249. class UpdateChecker {
  2250. constructor(kxsClient) {
  2251. this.remoteScriptUrl = config_namespaceObject.api_url + "/getLatestVersion";
  2252. this.kxsClient = kxsClient;
  2253. if (this.kxsClient.isAutoUpdateEnabled) {
  2254. this.checkForUpdate();
  2255. }
  2256. }
  2257. copyScriptToClipboard() {
  2258. return UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2259. try {
  2260. const response = yield fetch(this.remoteScriptUrl, {
  2261. method: "GET",
  2262. headers: {
  2263. "cache-control": "no-cache, no-store, must-revalidate",
  2264. "pragma": "no-cache",
  2265. "expires": "0"
  2266. }
  2267. });
  2268. if (!response.ok) {
  2269. throw new Error("Error retrieving script: " + response.statusText);
  2270. }
  2271. const scriptContent = yield response.text();
  2272. yield navigator.clipboard.writeText(scriptContent);
  2273. this.kxsClient.nm.showNotification("Script copied to clipboard!", "success", 2300);
  2274. }
  2275. catch (error) {
  2276. throw new Error("Error copying script to clipboard: " + error);
  2277. }
  2278. });
  2279. }
  2280. getNewScriptVersion() {
  2281. return UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2282. try {
  2283. const response = yield fetch(this.remoteScriptUrl, {
  2284. method: "GET",
  2285. headers: {
  2286. "cache-control": "no-cache, no-store, must-revalidate",
  2287. "pragma": "no-cache",
  2288. "expires": "0"
  2289. }
  2290. });
  2291. if (!response.ok) {
  2292. throw new Error("Error retrieving remote script: " + response.statusText);
  2293. }
  2294. const scriptContent = yield response.text();
  2295. const versionMatch = scriptContent.match(/\/\/\s*@version\s+([\d.]+)/);
  2296. if (versionMatch && versionMatch[1]) {
  2297. return versionMatch[1];
  2298. }
  2299. else {
  2300. throw new Error("Script version was not found in the file.");
  2301. }
  2302. }
  2303. catch (error) {
  2304. throw new Error("Error retrieving remote script: " + error);
  2305. }
  2306. });
  2307. }
  2308. checkForUpdate() {
  2309. return UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2310. const localScriptVersion = yield this.getCurrentScriptVersion();
  2311. const hostedScriptVersion = yield this.getNewScriptVersion();
  2312. this.hostedScriptVersion = hostedScriptVersion;
  2313. // Vérifie si la version hébergée est supérieure à la version locale
  2314. if (gt_default()(hostedScriptVersion, localScriptVersion)) {
  2315. this.displayUpdateNotification();
  2316. }
  2317. else {
  2318. this.kxsClient.nm.showNotification("Client is up to date", "success", 2300);
  2319. }
  2320. });
  2321. }
  2322. displayUpdateNotification() {
  2323. const modal = document.createElement("div");
  2324. modal.style.position = "fixed";
  2325. modal.style.top = "50%";
  2326. modal.style.left = "50%";
  2327. modal.style.transform = "translate(-50%, -50%)";
  2328. modal.style.backgroundColor = "rgb(250, 250, 250)";
  2329. modal.style.borderRadius = "10px";
  2330. modal.style.padding = "20px";
  2331. modal.style.width = "500px";
  2332. modal.style.boxShadow = "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)";
  2333. modal.style.border = "1px solid rgb(229, 229, 229)";
  2334. modal.style.zIndex = "10000";
  2335. const header = document.createElement("div");
  2336. header.style.display = "flex";
  2337. header.style.alignItems = "center";
  2338. header.style.marginBottom = "15px";
  2339. const title = document.createElement("h3");
  2340. title.textContent = "Update Available";
  2341. title.style.margin = "0";
  2342. title.style.fontSize = "18px";
  2343. title.style.fontWeight = "600";
  2344. header.appendChild(title);
  2345. const closeButton = document.createElement("button");
  2346. closeButton.innerHTML = "×";
  2347. closeButton.style.marginLeft = "auto";
  2348. closeButton.style.border = "none";
  2349. closeButton.style.background = "none";
  2350. closeButton.style.fontSize = "24px";
  2351. closeButton.style.cursor = "pointer";
  2352. closeButton.style.padding = "0 5px";
  2353. closeButton.onclick = () => modal.remove();
  2354. header.appendChild(closeButton);
  2355. const content = document.createElement("div");
  2356. content.innerHTML = `<div style="margin-bottom: 20px;">
  2357. <p style="margin-bottom: 10px; font-weight: 500;">A new version of KxsClient is available!</p>
  2358. <p style="margin-bottom: 10px;">
  2359. Current version: <span style="font-weight: 500;">${this.getCurrentScriptVersion()}</span> |
  2360. New version: <span style="font-weight: 500; color: #4f46e5;">${this.hostedScriptVersion}</span>
  2361. </p>
  2362. <p style="margin-bottom: 15px;">To update, follow these steps:</p>
  2363. <ol style="margin-left: 20px; margin-bottom: 15px;">
  2364. <li style="margin-bottom: 8px;">Click "Copy Script" below</li>
  2365. <li style="margin-bottom: 8px;">Open your script manager (Tampermonkey, Violentmonkey, etc.)</li>
  2366. <li style="margin-bottom: 8px;">Overwrite the current script with the new one and paste the content</li>
  2367. <li style="margin-bottom: 8px;">Save the script (Ctrl+S or Cmd+S)</li>
  2368. <li>Reload the game page</li>
  2369. </ol>
  2370. </div>`;
  2371. content.style.color = "rgb(75, 85, 99)";
  2372. content.style.fontSize = "14px";
  2373. content.style.lineHeight = "1.5";
  2374. const updateButton = document.createElement("button");
  2375. updateButton.textContent = "Copy Script";
  2376. updateButton.style.backgroundColor = "rgb(79, 70, 229)";
  2377. updateButton.style.color = "white";
  2378. updateButton.style.padding = "10px 16px";
  2379. updateButton.style.borderRadius = "6px";
  2380. updateButton.style.border = "none";
  2381. updateButton.style.cursor = "pointer";
  2382. updateButton.style.width = "100%";
  2383. updateButton.style.fontWeight = "500";
  2384. updateButton.style.fontSize = "15px";
  2385. updateButton.style.transition = "background-color 0.2s ease";
  2386. updateButton.onmouseover = () => updateButton.style.backgroundColor = "rgb(67, 56, 202)";
  2387. updateButton.onmouseout = () => updateButton.style.backgroundColor = "rgb(79, 70, 229)";
  2388. updateButton.onclick = () => UpdateChecker_awaiter(this, void 0, void 0, function* () {
  2389. try {
  2390. yield this.copyScriptToClipboard();
  2391. updateButton.textContent = "Script copied!";
  2392. updateButton.style.backgroundColor = "rgb(16, 185, 129)";
  2393. setTimeout(() => {
  2394. if (updateButton.isConnected) {
  2395. updateButton.textContent = "Copy Script";
  2396. updateButton.style.backgroundColor = "rgb(79, 70, 229)";
  2397. }
  2398. }, 3000);
  2399. }
  2400. catch (error) {
  2401. this.kxsClient.nm.showNotification("Error: " + error.message, "error", 5000);
  2402. }
  2403. });
  2404. modal.appendChild(header);
  2405. modal.appendChild(content);
  2406. modal.appendChild(updateButton);
  2407. document.body.appendChild(modal);
  2408. }
  2409. getCurrentScriptVersion() {
  2410. return package_namespaceObject.rE;
  2411. }
  2412. }
  2413.  
  2414.  
  2415. ;// ./src/SERVER/DiscordRichPresence.ts
  2416.  
  2417. class DiscordWebSocket {
  2418. constructor(kxsClient, token) {
  2419. this.ws = null;
  2420. this.heartbeatInterval = 0;
  2421. this.sequence = null;
  2422. this.isAuthenticated = false;
  2423. this.kxsClient = kxsClient;
  2424. }
  2425. connect() {
  2426. if (this.kxsClient.discordToken === ""
  2427. || this.kxsClient.discordToken === null
  2428. || this.kxsClient.discordToken === undefined) {
  2429. return;
  2430. }
  2431. this.ws = new WebSocket('wss://gateway.discord.gg/?v=9&encoding=json');
  2432. this.ws.onopen = () => { };
  2433. this.ws.onmessage = (event) => {
  2434. const data = JSON.parse(event.data);
  2435. this.handleMessage(data);
  2436. };
  2437. this.ws.onerror = (error) => { };
  2438. this.ws.onclose = () => {
  2439. clearInterval(this.heartbeatInterval);
  2440. this.isAuthenticated = false;
  2441. };
  2442. }
  2443. identify() {
  2444. const payload = {
  2445. op: 2,
  2446. d: {
  2447. token: this.kxsClient.discordToken,
  2448. properties: {
  2449. $os: 'linux',
  2450. $browser: 'chrome',
  2451. $device: 'chrome'
  2452. },
  2453. presence: {
  2454. activities: [{
  2455. name: "DualityClient",
  2456. type: 0,
  2457. application_id: "1321193265533550602",
  2458. assets: {
  2459. large_image: "mp:app-icons/1321193265533550602/bccd2479ec56ed7d4e69fa2fdfb47197.png?size=512",
  2460. large_text: "DualityClient v" + package_namespaceObject.rE,
  2461. }
  2462. }],
  2463. status: 'online',
  2464. afk: false
  2465. }
  2466. }
  2467. };
  2468. this.send(payload);
  2469. }
  2470. handleMessage(data) {
  2471. switch (data.op) {
  2472. case 10: // Hello
  2473. const { heartbeat_interval } = data.d;
  2474. this.startHeartbeat(heartbeat_interval);
  2475. this.identify();
  2476. break;
  2477. case 11: // Heartbeat ACK
  2478. this.kxsClient.logger.log('[RichPresence] Heartbeat acknowledged');
  2479. break;
  2480. case 0: // Dispatch
  2481. this.sequence = data.s;
  2482. if (data.t === 'READY') {
  2483. this.isAuthenticated = true;
  2484. this.kxsClient.nm.showNotification('Started Discord RPC', 'success', 3000);
  2485. }
  2486. break;
  2487. }
  2488. }
  2489. startHeartbeat(interval) {
  2490. this.heartbeatInterval = setInterval(() => {
  2491. this.send({
  2492. op: 1,
  2493. d: this.sequence
  2494. });
  2495. }, interval);
  2496. }
  2497. send(data) {
  2498. var _a;
  2499. if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
  2500. this.ws.send(JSON.stringify(data));
  2501. }
  2502. }
  2503. disconnect() {
  2504. if (this.ws) {
  2505. clearInterval(this.heartbeatInterval);
  2506. this.ws.close();
  2507. }
  2508. }
  2509. }
  2510.  
  2511.  
  2512. ;// ./src/HUD/MOD/NotificationManager.ts
  2513. class NotificationManager {
  2514. constructor() {
  2515. this.notifications = [];
  2516. this.NOTIFICATION_HEIGHT = 65; // Height + margin
  2517. this.NOTIFICATION_MARGIN = 10;
  2518. this.addGlobalStyles();
  2519. }
  2520. static getInstance() {
  2521. if (!NotificationManager.instance) {
  2522. NotificationManager.instance = new NotificationManager();
  2523. }
  2524. return NotificationManager.instance;
  2525. }
  2526. addGlobalStyles() {
  2527. const styleSheet = document.createElement("style");
  2528. styleSheet.textContent = `
  2529. @keyframes slideIn {
  2530. 0% { transform: translateX(-120%); opacity: 0; }
  2531. 50% { transform: translateX(10px); opacity: 0.8; }
  2532. 100% { transform: translateX(0); opacity: 1; }
  2533. }
  2534.  
  2535. @keyframes slideOut {
  2536. 0% { transform: translateX(0); opacity: 1; }
  2537. 50% { transform: translateX(10px); opacity: 0.8; }
  2538. 100% { transform: translateX(-120%); opacity: 0; }
  2539. }
  2540.  
  2541. @keyframes slideLeft {
  2542. from { transform-origin: right; transform: scaleX(1); }
  2543. to { transform-origin: right; transform: scaleX(0); }
  2544. }
  2545.  
  2546. @keyframes bounce {
  2547. 0%, 100% { transform: scale(1); }
  2548. 50% { transform: scale(1.1); }
  2549. }
  2550. `;
  2551. document.head.appendChild(styleSheet);
  2552. }
  2553. updateNotificationPositions() {
  2554. this.notifications.forEach((notification, index) => {
  2555. const topPosition = 20 + (index * this.NOTIFICATION_HEIGHT);
  2556. notification.style.top = `${topPosition}px`;
  2557. });
  2558. }
  2559. removeNotification(notification) {
  2560. const index = this.notifications.indexOf(notification);
  2561. if (index > -1) {
  2562. this.notifications.splice(index, 1);
  2563. this.updateNotificationPositions();
  2564. }
  2565. }
  2566. getIconConfig(type) {
  2567. const configs = {
  2568. success: {
  2569. color: '#4CAF50',
  2570. svg: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
  2571. <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"/>
  2572. </svg>`
  2573. },
  2574. error: {
  2575. color: '#F44336',
  2576. svg: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
  2577. <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"/>
  2578. </svg>`
  2579. },
  2580. info: {
  2581. color: '#FFD700',
  2582. svg: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
  2583. <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"/>
  2584. </svg>`
  2585. }
  2586. };
  2587. return configs[type];
  2588. }
  2589. showNotification(message, type, duration = 5000) {
  2590. const notification = document.createElement("div");
  2591. // Base styles
  2592. Object.assign(notification.style, {
  2593. position: "fixed",
  2594. top: "20px",
  2595. left: "20px",
  2596. padding: "12px 20px",
  2597. backgroundColor: "#333333",
  2598. color: "white",
  2599. zIndex: "9999",
  2600. minWidth: "200px",
  2601. borderRadius: "4px",
  2602. display: "flex",
  2603. alignItems: "center",
  2604. gap: "10px",
  2605. transform: "translateX(-120%)",
  2606. opacity: "0",
  2607. boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)"
  2608. });
  2609. // Create icon
  2610. const icon = document.createElement("div");
  2611. Object.assign(icon.style, {
  2612. width: "20px",
  2613. height: "20px",
  2614. display: "flex",
  2615. alignItems: "center",
  2616. justifyContent: "center",
  2617. animation: "bounce 0.5s ease-in-out"
  2618. });
  2619. const iconConfig = this.getIconConfig(type);
  2620. icon.style.color = iconConfig.color;
  2621. icon.innerHTML = iconConfig.svg;
  2622. // Create message
  2623. const messageDiv = document.createElement("div");
  2624. messageDiv.textContent = message;
  2625. messageDiv.style.flex = "1";
  2626. // Create progress bar
  2627. const progressBar = document.createElement("div");
  2628. Object.assign(progressBar.style, {
  2629. height: "4px",
  2630. backgroundColor: "#e6f3ff",
  2631. width: "100%",
  2632. position: "absolute",
  2633. bottom: "0",
  2634. left: "0",
  2635. animation: `slideLeft ${duration}ms linear forwards`
  2636. });
  2637. // Assemble notification
  2638. notification.appendChild(icon);
  2639. notification.appendChild(messageDiv);
  2640. notification.appendChild(progressBar);
  2641. document.body.appendChild(notification);
  2642. // Add to stack and update positions
  2643. this.notifications.push(notification);
  2644. this.updateNotificationPositions();
  2645. // Entrance animation
  2646. requestAnimationFrame(() => {
  2647. notification.style.transition = "all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55)";
  2648. notification.style.animation = "slideIn 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards";
  2649. });
  2650. // Exit animation and cleanup
  2651. setTimeout(() => {
  2652. notification.style.animation = "slideOut 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards";
  2653. setTimeout(() => {
  2654. this.removeNotification(notification);
  2655. notification.remove();
  2656. }, 500);
  2657. }, duration);
  2658. }
  2659. }
  2660.  
  2661.  
  2662. ;// ./src/HUD/ClientSecondaryMenu.ts
  2663.  
  2664.  
  2665. const category = ["ALL", "HUD", "SERVER", "MECHANIC", "MISC"];
  2666. class KxsClientSecondaryMenu {
  2667. constructor(kxsClient) {
  2668. this.searchTerm = '';
  2669. // Fonction pour fermer un sous-menu
  2670. this.closeSubMenu = () => { };
  2671. this.shiftListener = (event) => {
  2672. if (event.key === "Shift" && event.location == 2) {
  2673. this.clearMenu();
  2674. this.toggleMenuVisibility();
  2675. // Ensure options are displayed after loading
  2676. this.filterOptions();
  2677. }
  2678. };
  2679. this.mouseMoveListener = (e) => {
  2680. if (this.isDragging) {
  2681. const x = e.clientX - this.dragOffset.x;
  2682. const y = e.clientY - this.dragOffset.y;
  2683. this.menu.style.transform = 'none';
  2684. this.menu.style.left = `${x}px`;
  2685. this.menu.style.top = `${y}px`;
  2686. }
  2687. };
  2688. this.mouseUpListener = () => {
  2689. this.isDragging = false;
  2690. this.menu.style.cursor = "grab";
  2691. };
  2692. this.kxsClient = kxsClient;
  2693. this.isClientMenuVisible = false;
  2694. this.isDragging = false;
  2695. this.dragOffset = { x: 0, y: 0 };
  2696. this.sections = [];
  2697. this.allOptions = [];
  2698. this.activeCategory = "ALL";
  2699. this.isOpen = false;
  2700. this.menu = document.createElement("div");
  2701. this.initMenu();
  2702. this.addShiftListener();
  2703. this.addDragListeners();
  2704. this.loadOption();
  2705. }
  2706. initMenu() {
  2707. this.menu.id = "kxsClientMenu";
  2708. this.applyMenuStyles();
  2709. this.createHeader();
  2710. this.createGridContainer();
  2711. document.body.appendChild(this.menu);
  2712. this.menu.style.display = "none";
  2713. // Empêcher la propagation des événements souris (clics et molette) vers la page web
  2714. // Utiliser la phase de bouillonnement (bubbling) au lieu de la phase de capture
  2715. // pour permettre aux éléments enfants de recevoir les événements d'abord
  2716. this.menu.addEventListener('click', (e) => {
  2717. e.stopPropagation();
  2718. });
  2719. this.menu.addEventListener('wheel', (e) => {
  2720. e.stopPropagation();
  2721. });
  2722. // Nous ne gérons pas mousedown et mouseup ici car ils sont gérés dans addDragListeners()
  2723. }
  2724. applyMenuStyles() {
  2725. // Styles par défaut (desktop/tablette)
  2726. const defaultStyles = {
  2727. backgroundColor: "rgba(17, 24, 39, 0.95)",
  2728. padding: "20px",
  2729. borderRadius: "12px",
  2730. boxShadow: "0 4px 20px rgba(0, 0, 0, 0.8)",
  2731. zIndex: "10001",
  2732. width: "800px",
  2733. fontFamily: "'Segoe UI', Arial, sans-serif",
  2734. color: "#fff",
  2735. maxHeight: "80vh",
  2736. overflowY: "auto",
  2737. overflowX: "hidden", // Prevent horizontal scrolling
  2738. position: "fixed",
  2739. top: "10%",
  2740. left: "50%",
  2741. transform: "translateX(-50%)",
  2742. display: "none",
  2743. boxSizing: "border-box", // Include padding in width calculation
  2744. };
  2745. // Styles réduits pour mobile
  2746. const mobileStyles = {
  2747. padding: "6px",
  2748. borderRadius: "7px",
  2749. width: "78vw",
  2750. maxWidth: "84vw",
  2751. fontSize: "10px",
  2752. maxHeight: "60vh",
  2753. top: "4%",
  2754. left: "50%",
  2755. };
  2756. Object.assign(this.menu.style, defaultStyles);
  2757. if (this.kxsClient.isMobile && this.kxsClient.isMobile()) {
  2758. Object.assign(this.menu.style, mobileStyles);
  2759. }
  2760. }
  2761. blockMousePropagation(element, preventDefault = true) {
  2762. ['click', 'mousedown', 'mouseup', 'dblclick', 'contextmenu', 'wheel'].forEach(eventType => {
  2763. element.addEventListener(eventType, (e) => {
  2764. e.stopPropagation();
  2765. if (preventDefault && (eventType === 'contextmenu' || eventType === 'wheel' || element.tagName !== 'INPUT')) {
  2766. e.preventDefault();
  2767. }
  2768. }, false);
  2769. });
  2770. }
  2771. createHeader() {
  2772. const header = document.createElement("div");
  2773. // Détection mobile pour styles réduits
  2774. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  2775. const logoSize = isMobile ? 20 : 30;
  2776. const titleFontSize = isMobile ? 12 : 20;
  2777. const headerGap = isMobile ? 4 : 10;
  2778. const headerMarginBottom = isMobile ? 8 : 20;
  2779. const closeBtnPadding = isMobile ? 2 : 6;
  2780. const closeBtnFontSize = isMobile ? 12 : 18;
  2781. header.style.marginBottom = `${headerMarginBottom}px`;
  2782. header.innerHTML = `
  2783. <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: ${isMobile ? 7 : 15}px; width: 100%; box-sizing: border-box;">
  2784. <div style="display: flex; align-items: center; gap: ${headerGap}px;">
  2785. <img src="${kxs_logo}"
  2786. alt="Logo" style="width: ${logoSize}px; height: ${logoSize}px;">
  2787. <span style="font-size: ${titleFontSize}px; font-weight: bold;">DUALITY CLIENT <span style="
  2788. font-size: ${isMobile ? 10 : 14}px;
  2789. font-weight: 700;
  2790. color: #3B82F6;
  2791. opacity: 0.95;
  2792. position: relative;
  2793. top: ${isMobile ? -1 : -2}px;
  2794. margin-left: ${isMobile ? 2 : 3}px;
  2795. letter-spacing: 0.5px;
  2796. ">v${package_namespaceObject.rE}</span></span>
  2797. </div>
  2798. <div style="display: flex; gap: ${headerGap}px;">
  2799. <button style="
  2800. padding: ${closeBtnPadding}px;
  2801. background: none;
  2802. border: none;
  2803. color: white;
  2804. cursor: pointer;
  2805. font-size: ${closeBtnFontSize}px;
  2806. ">×</button>
  2807. </div>
  2808. </div>
  2809. <div style="display: flex; flex-direction: column; gap: 10px; margin-bottom: 15px; width: 100%; box-sizing: border-box;">
  2810. <div style="display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 5px;">
  2811. ${category.map(cat => `
  2812. <button class="category-btn" data-category="${cat}" style="
  2813. padding: ${isMobile ? '2px 6px' : '6px 16px'};
  2814. background: ${this.activeCategory === cat ? '#3B82F6' : 'rgba(55, 65, 81, 0.8)'};
  2815. border: none;
  2816. border-radius: ${isMobile ? '3px' : '6px'};
  2817. color: white;
  2818. cursor: pointer;
  2819. font-size: ${isMobile ? '9px' : '14px'};
  2820. transition: background 0.2s;
  2821. ">${cat}</button>
  2822. `).join('')}
  2823. </div>
  2824. <div style="display: flex; width: 100%; box-sizing: border-box;">
  2825. <div style="position: relative; width: 100%; box-sizing: border-box;">
  2826. <input type="text" id="kxsSearchInput" placeholder="Search options..." style="
  2827. width: 100%;
  2828. padding: ${isMobile ? '3px 5px 3px 20px' : '8px 12px 8px 32px'};
  2829. background: rgba(55, 65, 81, 0.8);
  2830. border: none;
  2831. border-radius: ${isMobile ? '3px' : '6px'};
  2832. color: white;
  2833. font-size: ${isMobile ? '9px' : '14px'};
  2834. outline: none;
  2835. box-sizing: border-box;
  2836. ">
  2837. <div style="
  2838. position: absolute;
  2839. left: ${isMobile ? '4px' : '10px'};
  2840. top: 50%;
  2841. transform: translateY(-50%);
  2842. width: ${isMobile ? '9px' : '14px'};
  2843. height: ${isMobile ? '9px' : '14px'};
  2844. ">
  2845. <svg fill="#ffffff" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  2846. <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"/>
  2847. </svg>
  2848. </div>
  2849. </div>
  2850. </div>
  2851. </div>
  2852. `;
  2853. header.querySelectorAll('.category-btn').forEach(btn => {
  2854. this.blockMousePropagation(btn);
  2855. btn.addEventListener('click', (e) => {
  2856. const category = e.target.dataset.category;
  2857. if (category) {
  2858. this.setActiveCategory(category);
  2859. }
  2860. });
  2861. });
  2862. const closeButton = header.querySelector('button');
  2863. closeButton === null || closeButton === void 0 ? void 0 : closeButton.addEventListener('click', () => {
  2864. this.toggleMenuVisibility();
  2865. });
  2866. const searchInput = header.querySelector('#kxsSearchInput');
  2867. if (searchInput) {
  2868. this.blockMousePropagation(searchInput, false);
  2869. // Gestionnaire pour mettre à jour la recherche
  2870. searchInput.addEventListener('input', (e) => {
  2871. this.searchTerm = e.target.value.toLowerCase();
  2872. this.filterOptions();
  2873. });
  2874. // Prevent keys from being interpreted by the game
  2875. // We only block the propagation of keyboard events, except for special keys
  2876. ['keydown', 'keyup', 'keypress'].forEach(eventType => {
  2877. searchInput.addEventListener(eventType, (e) => {
  2878. const keyEvent = e;
  2879. // Don't block special keys (Escape, Shift)
  2880. if (keyEvent.key === 'Escape' || (keyEvent.key === 'Shift' && keyEvent.location === 2)) {
  2881. return; // Let the event propagate normally
  2882. }
  2883. // Block propagation for all other keys
  2884. e.stopPropagation();
  2885. });
  2886. });
  2887. // Éviter que la barre de recherche ne reprenne automatiquement le focus
  2888. // lorsque l'utilisateur interagit avec un autre champ de texte
  2889. searchInput.addEventListener('blur', (e) => {
  2890. // Ne pas reprendre le focus si l'utilisateur clique sur un autre input
  2891. const newFocusElement = e.relatedTarget;
  2892. if (newFocusElement && (newFocusElement.tagName === 'INPUT' || newFocusElement.tagName === 'TEXTAREA')) {
  2893. // L'utilisateur a cliqué sur un autre champ de texte, ne pas reprendre le focus
  2894. return;
  2895. }
  2896. // Pour les autres cas, seulement si aucun autre élément n'a le focus
  2897. setTimeout(() => {
  2898. const activeElement = document.activeElement;
  2899. if (this.isClientMenuVisible &&
  2900. activeElement &&
  2901. activeElement !== searchInput &&
  2902. activeElement.tagName !== 'INPUT' &&
  2903. activeElement.tagName !== 'TEXTAREA') {
  2904. searchInput.focus();
  2905. }
  2906. }, 100);
  2907. });
  2908. }
  2909. this.menu.appendChild(header);
  2910. }
  2911. clearMenu() {
  2912. const gridContainer = document.getElementById('kxsMenuGrid');
  2913. if (gridContainer) {
  2914. gridContainer.innerHTML = '';
  2915. }
  2916. // Reset search term when clearing menu
  2917. this.searchTerm = '';
  2918. const searchInput = document.getElementById('kxsSearchInput');
  2919. if (searchInput) {
  2920. searchInput.value = '';
  2921. }
  2922. }
  2923. loadOption() {
  2924. // Clear existing options to avoid duplicates
  2925. this.allOptions = [];
  2926. let HUD = this.addSection("HUD", 'HUD');
  2927. let MECHANIC = this.addSection("MECHANIC", 'MECHANIC');
  2928. let SERVER = this.addSection("SERVER", 'SERVER');
  2929. let MISC = this.addSection("MISC", 'MISC');
  2930. this.addOption(SERVER, {
  2931. label: "Kxs Network",
  2932. value: true,
  2933. category: "SERVER",
  2934. type: "sub",
  2935. 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>network</title> <path d="M27 21.75c-0.795 0.004-1.538 0.229-2.169 0.616l0.018-0.010-2.694-2.449c0.724-1.105 1.154-2.459 1.154-3.913 0-1.572-0.503-3.027-1.358-4.212l0.015 0.021 3.062-3.062c0.57 0.316 1.249 0.503 1.971 0.508h0.002c2.347 0 4.25-1.903 4.25-4.25s-1.903-4.25-4.25-4.25c-2.347 0-4.25 1.903-4.25 4.25v0c0.005 0.724 0.193 1.403 0.519 1.995l-0.011-0.022-3.062 3.062c-1.147-0.84-2.587-1.344-4.144-1.344-0.868 0-1.699 0.157-2.467 0.443l0.049-0.016-0.644-1.17c0.726-0.757 1.173-1.787 1.173-2.921 0-2.332-1.891-4.223-4.223-4.223s-4.223 1.891-4.223 4.223c0 2.332 1.891 4.223 4.223 4.223 0.306 0 0.605-0.033 0.893-0.095l-0.028 0.005 0.642 1.166c-1.685 1.315-2.758 3.345-2.758 5.627 0 0.605 0.076 1.193 0.218 1.754l-0.011-0.049-0.667 0.283c-0.78-0.904-1.927-1.474-3.207-1.474-2.334 0-4.226 1.892-4.226 4.226s1.892 4.226 4.226 4.226c2.334 0 4.226-1.892 4.226-4.226 0-0.008-0-0.017-0-0.025v0.001c-0.008-0.159-0.023-0.307-0.046-0.451l0.003 0.024 0.667-0.283c1.303 2.026 3.547 3.349 6.1 3.349 1.703 0 3.268-0.589 4.503-1.574l-0.015 0.011 2.702 2.455c-0.258 0.526-0.41 1.144-0.414 1.797v0.001c0 2.347 1.903 4.25 4.25 4.25s4.25-1.903 4.25-4.25c0-2.347-1.903-4.25-4.25-4.25v0zM8.19 5c0-0.966 0.784-1.75 1.75-1.75s1.75 0.784 1.75 1.75c0 0.966-0.784 1.75-1.75 1.75v0c-0.966-0.001-1.749-0.784-1.75-1.75v-0zM5 22.42c-0.966-0.001-1.748-0.783-1.748-1.749s0.783-1.749 1.749-1.749c0.966 0 1.748 0.782 1.749 1.748v0c-0.001 0.966-0.784 1.749-1.75 1.75h-0zM27 3.25c0.966 0 1.75 0.784 1.75 1.75s-0.784 1.75-1.75 1.75c-0.966 0-1.75-0.784-1.75-1.75v0c0.001-0.966 0.784-1.749 1.75-1.75h0zM11.19 16c0-0.001 0-0.002 0-0.003 0-2.655 2.152-4.807 4.807-4.807 1.328 0 2.53 0.539 3.4 1.409l0.001 0.001 0.001 0.001c0.87 0.87 1.407 2.072 1.407 3.399 0 2.656-2.153 4.808-4.808 4.808s-4.808-2.153-4.808-4.808c0-0 0-0 0-0v0zM27 27.75c-0.966 0-1.75-0.784-1.75-1.75s0.784-1.75 1.75-1.75c0.966 0 1.75 0.784 1.75 1.75v0c-0.001 0.966-0.784 1.749-1.75 1.75h-0z"></path> </g></svg>',
  2936. fields: [
  2937. {
  2938. label: "Spoof Nickname",
  2939. category: "SERVER",
  2940. 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"> <title>hacker-solid</title> <g id="Layer_2" data-name="Layer 2"> <g id="invisible_box" data-name="invisible box"> <rect width="48" height="48" fill="none"></rect> </g> <g id="Q3_icons" data-name="Q3 icons"> <g> <path d="M24,30a60.3,60.3,0,0,1-13-1.3L7,27.6V40.2a1.9,1.9,0,0,0,1.5,1.9l12,2.9a2.4,2.4,0,0,0,2.1-.8L24,42.5l1.4,1.7A2.1,2.1,0,0,0,27,45h.5l12-2.9A1.9,1.9,0,0,0,41,40.2V27.6l-4,1.1A60.3,60.3,0,0,1,24,30Zm-7,8c-2,0-4-1.9-4-3s2-1,4-1,4,.9,4,2S19,38,17,38Zm14,0c-2,0-4-.9-4-2s2-2,4-2,4-.1,4,1S33,38,31,38Z"></path> <path d="M39.4,16,37.3,6.2A4,4,0,0,0,33.4,3H29.1a3.9,3.9,0,0,0-3.4,1.9L24,7.8,22.3,4.9A3.9,3.9,0,0,0,18.9,3H14.6a4,4,0,0,0-3.9,3.2L8.6,16C4.5,17.3,2,19,2,21c0,3.9,9.8,7,22,7s22-3.1,22-7C46,19,43.5,17.3,39.4,16Z"></path> </g> </g> </g> </g></svg>',
  2941. type: "toggle",
  2942. value: this.kxsClient.kxsNetworkSettings.nickname_anonymized,
  2943. onChange: () => {
  2944. this.kxsClient.kxsNetworkSettings.nickname_anonymized = !this.kxsClient.kxsNetworkSettings.nickname_anonymized;
  2945. this.kxsClient.updateLocalStorage();
  2946. }
  2947. },
  2948. {
  2949. label: "Voice Chat",
  2950. value: this.kxsClient.isVoiceChatEnabled,
  2951. icon: '<svg fill="#000000" viewBox="0 0 32 32" id="icon" 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"> <defs> <style> .cls-1 { fill: none; } </style> </defs> <path d="M26,30H24V27H20a5.0055,5.0055,0,0,1-5-5V20.7207l-2.3162-.772a1,1,0,0,1-.5412-1.4631L15,13.7229V11a9.01,9.01,0,0,1,9-9h5V4H24a7.0078,7.0078,0,0,0-7,7v3a.9991.9991,0,0,1-.1426.5144l-2.3586,3.9312,1.8174.6057A1,1,0,0,1,17,20v2a3.0033,3.0033,0,0,0,3,3h5a1,1,0,0,1,1,1Z"></path> <rect x="19" y="12" width="4" height="2"></rect> <path d="M9.3325,25.2168a7.0007,7.0007,0,0,1,0-10.4341l1.334,1.49a5,5,0,0,0,0,7.4537Z"></path> <path d="M6.3994,28.8008a11.0019,11.0019,0,0,1,0-17.6006L7.6,12.8a9.0009,9.0009,0,0,0,0,14.4014Z"></path> <rect id="_Transparent_Rectangle_" data-name="<Transparent Rectangle>" class="cls-1" width="32" height="32"></rect> </g></svg>',
  2952. category: "SERVER",
  2953. type: "toggle",
  2954. onChange: () => {
  2955. this.kxsClient.isVoiceChatEnabled = !this.kxsClient.isVoiceChatEnabled;
  2956. this.kxsClient.updateLocalStorage();
  2957. this.kxsClient.voiceChat.toggleVoiceChat();
  2958. },
  2959. },
  2960. {
  2961. label: "Chat",
  2962. value: this.kxsClient.isKxsChatEnabled,
  2963. icon: '<svg fill="#000000" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" 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> <g> <path d="M232.727,238.545v186.182H281.6l-13.964,69.818l97.745-69.818H512V238.545H232.727z M477.091,389.818H365.382h-11.187 l-9.103,6.502l-25.912,18.508l5.003-25.01H281.6h-13.964V273.455h209.455V389.818z"></path> </g> </g> <g> <g> <path d="M279.273,17.455H0v186.182h65.164l97.745,69.818l-13.964-69.818h130.327V17.455z M244.364,168.727h-95.418h-42.582 l5.003,25.01L85.455,175.23l-9.104-6.502H65.164H34.909V52.364h209.455V168.727z"></path> </g> </g> <g> <g> <rect x="180.364" y="93.091" width="34.909" height="34.909"></rect> </g> </g> <g> <g> <rect x="122.182" y="93.091" width="34.909" height="34.909"></rect> </g> </g> <g> <g> <rect x="64" y="93.091" width="34.909" height="34.909"></rect> </g> </g> <g> <g> <rect x="413.091" y="314.182" width="34.909" height="34.909"></rect> </g> </g> <g> <g> <rect x="354.909" y="314.182" width="34.909" height="34.909"></rect> </g> </g> <g> <g> <rect x="296.727" y="314.182" width="34.909" height="34.909"></rect> </g> </g> </g></svg>',
  2964. category: "SERVER",
  2965. type: "toggle",
  2966. onChange: () => {
  2967. this.kxsClient.isKxsChatEnabled = !this.kxsClient.isKxsChatEnabled;
  2968. this.kxsClient.updateLocalStorage();
  2969. this.kxsClient.chat.toggleChat();
  2970. },
  2971. }
  2972. ],
  2973. });
  2974. this.addOption(MISC, {
  2975. label: "Game History",
  2976. value: true,
  2977. category: "MISC",
  2978. 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="M5.52786 16.7023C6.6602 18.2608 8.3169 19.3584 10.1936 19.7934C12.0703 20.2284 14.0409 19.9716 15.7434 19.0701C17.446 18.1687 18.766 16.6832 19.4611 14.8865C20.1562 13.0898 20.1796 11.1027 19.527 9.29011C18.8745 7.47756 17.5898 5.96135 15.909 5.02005C14.2282 4.07875 12.2641 3.77558 10.3777 4.16623C8.49129 4.55689 6.80919 5.61514 5.64045 7.14656C4.47171 8.67797 3.89482 10.5797 4.01579 12.5023M4.01579 12.5023L2.51579 11.0023M4.01579 12.5023L5.51579 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>',
  2979. type: "click",
  2980. onChange: () => {
  2981. this.kxsClient.historyManager.show();
  2982. }
  2983. });
  2984. this.addOption(MECHANIC, {
  2985. label: "Win sound",
  2986. value: true,
  2987. type: "sub",
  2988. 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>',
  2989. category: "MECHANIC",
  2990. fields: [
  2991. {
  2992. label: "Enable",
  2993. value: this.kxsClient.isWinSoundEnabled,
  2994. category: "MECHANIC",
  2995. 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="M3 11V13M6 10V14M9 11V13M12 9V15M15 6V18M18 10V14M21 11V13" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>',
  2996. type: "toggle",
  2997. onChange: () => {
  2998. this.kxsClient.isWinSoundEnabled = !this.kxsClient.isWinSoundEnabled;
  2999. this.kxsClient.updateLocalStorage();
  3000. },
  3001. },
  3002. {
  3003. label: "Sound URL",
  3004. value: this.kxsClient.soundLibrary.win_sound_url,
  3005. category: "MECHANIC",
  3006. 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="M3 11V13M6 10V14M9 11V13M12 9V15M15 6V18M18 10V14M21 11V13" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>',
  3007. type: "input",
  3008. placeholder: "URL of a sound",
  3009. onChange: (value) => {
  3010. this.kxsClient.soundLibrary.win_sound_url = value;
  3011. this.kxsClient.updateLocalStorage();
  3012. }
  3013. }
  3014. ]
  3015. });
  3016. this.addOption(MECHANIC, {
  3017. label: "Death sound",
  3018. value: true,
  3019. type: "sub",
  3020. 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>',
  3021. category: "MECHANIC",
  3022. fields: [
  3023. {
  3024. label: "Enable",
  3025. value: this.kxsClient.isDeathSoundEnabled,
  3026. category: "MECHANIC",
  3027. 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="M3 11V13M6 10V14M9 11V13M12 9V15M15 6V18M18 10V14M21 11V13" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>',
  3028. type: "toggle",
  3029. onChange: () => {
  3030. this.kxsClient.isDeathSoundEnabled = !this.kxsClient.isDeathSoundEnabled;
  3031. this.kxsClient.updateLocalStorage();
  3032. },
  3033. },
  3034. {
  3035. label: "Sound URL",
  3036. value: this.kxsClient.soundLibrary.death_sound_url,
  3037. category: "MECHANIC",
  3038. 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="M3 11V13M6 10V14M9 11V13M12 9V15M15 12V18M15 6V8M18 10V14M21 11V13" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>',
  3039. type: "input",
  3040. placeholder: "URL of a sound",
  3041. onChange: (value) => {
  3042. this.kxsClient.soundLibrary.death_sound_url = value;
  3043. this.kxsClient.updateLocalStorage();
  3044. }
  3045. }
  3046. ]
  3047. });
  3048. this.addOption(MECHANIC, {
  3049. label: "Background Music",
  3050. value: this.kxsClient.soundLibrary.background_sound_url,
  3051. type: "input",
  3052. icon: '<svg viewBox="0 0 16 16" 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="M15 1H4V9H3C1.34315 9 0 10.3431 0 12C0 13.6569 1.34315 15 3 15C4.65685 15 6 13.6569 6 12V5H13V9H12C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15C13.6569 15 15 13.6569 15 12V1Z" fill="#000000"></path> </g></svg>',
  3053. category: "MECHANIC",
  3054. placeholder: background_song,
  3055. onChange: (value) => {
  3056. this.kxsClient.soundLibrary.background_sound_url = value;
  3057. this.kxsClient.updateLocalStorage();
  3058. },
  3059. });
  3060. this.addOption(HUD, {
  3061. label: "Clean Main Menu",
  3062. value: this.kxsClient.isMainMenuCleaned,
  3063. category: "HUD",
  3064. icon: '<svg fill="#000000" viewBox="0 0 32 32" id="icon" 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"> <defs> <style> .cls-1 { fill: none; } </style> </defs> <title>clean</title> <rect x="20" y="18" width="6" height="2" transform="translate(46 38) rotate(-180)"></rect> <rect x="24" y="26" width="6" height="2" transform="translate(54 54) rotate(-180)"></rect> <rect x="22" y="22" width="6" height="2" transform="translate(50 46) rotate(-180)"></rect> <path d="M17.0029,20a4.8952,4.8952,0,0,0-2.4044-4.1729L22,3,20.2691,2,12.6933,15.126A5.6988,5.6988,0,0,0,7.45,16.6289C3.7064,20.24,3.9963,28.6821,4.01,29.04a1,1,0,0,0,1,.96H20.0012a1,1,0,0,0,.6-1.8C17.0615,25.5439,17.0029,20.0537,17.0029,20ZM11.93,16.9971A3.11,3.11,0,0,1,15.0041,20c0,.0381.0019.208.0168.4688L9.1215,17.8452A3.8,3.8,0,0,1,11.93,16.9971ZM15.4494,28A5.2,5.2,0,0,1,14,25H12a6.4993,6.4993,0,0,0,.9684,3H10.7451A16.6166,16.6166,0,0,1,10,24H8a17.3424,17.3424,0,0,0,.6652,4H6c.031-1.8364.29-5.8921,1.8027-8.5527l7.533,3.35A13.0253,13.0253,0,0,0,17.5968,28Z"></path> <rect id="_Transparent_Rectangle_" data-name="<Transparent Rectangle>" class="cls-1" width="32" height="32"></rect> </g></svg>',
  3065. type: "toggle",
  3066. onChange: (value) => {
  3067. this.kxsClient.isMainMenuCleaned = !this.kxsClient.isMainMenuCleaned;
  3068. this.kxsClient.MainMenuCleaning();
  3069. this.kxsClient.updateLocalStorage();
  3070. },
  3071. });
  3072. this.addOption(HUD, {
  3073. label: "Counters",
  3074. value: true,
  3075. category: "SERVER",
  3076. type: "sub",
  3077. icon: '<svg fill="#000000" viewBox="0 0 56 56" 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="M 13.1640 4.6562 L 43.3280 4.6562 C 43.1874 2.6875 42.0624 1.6328 39.9062 1.6328 L 16.5858 1.6328 C 14.4296 1.6328 13.3046 2.6875 13.1640 4.6562 Z M 8.1015 11.1484 L 47.9454 11.1484 C 47.5936 9.0156 46.5625 7.8438 44.2187 7.8438 L 11.8046 7.8438 C 9.4609 7.8438 8.4531 9.0156 8.1015 11.1484 Z M 10.2343 54.3672 L 45.7888 54.3672 C 50.6641 54.3672 53.1251 51.9297 53.1251 47.1016 L 53.1251 22.2109 C 53.1251 17.3828 50.6641 14.9453 45.7888 14.9453 L 10.2343 14.9453 C 5.3358 14.9453 2.8749 17.3594 2.8749 22.2109 L 2.8749 47.1016 C 2.8749 51.9297 5.3358 54.3672 10.2343 54.3672 Z"></path></g></svg>',
  3078. fields: [
  3079. {
  3080. label: "Show Kills",
  3081. value: this.kxsClient.isKillsVisible,
  3082. type: "toggle",
  3083. category: "HUD",
  3084. 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>',
  3085. onChange: (value) => {
  3086. this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible;
  3087. this.kxsClient.updateKillsVisibility();
  3088. this.kxsClient.updateLocalStorage();
  3089. },
  3090. },
  3091. {
  3092. label: "Show FPS",
  3093. value: this.kxsClient.isFpsVisible,
  3094. category: "HUD",
  3095. type: "toggle",
  3096. 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>',
  3097. onChange: (value) => {
  3098. this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible;
  3099. this.kxsClient.updateFpsVisibility();
  3100. this.kxsClient.updateLocalStorage();
  3101. },
  3102. },
  3103. {
  3104. label: "Show Ping",
  3105. value: this.kxsClient.isPingVisible,
  3106. category: "HUD",
  3107. 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>',
  3108. type: "toggle",
  3109. onChange: (value) => {
  3110. this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible;
  3111. this.kxsClient.updatePingVisibility();
  3112. this.kxsClient.updateLocalStorage();
  3113. },
  3114. }
  3115. ],
  3116. });
  3117. this.addOption(HUD, {
  3118. label: "Weapon Border",
  3119. value: this.kxsClient.isGunOverlayColored,
  3120. category: "HUD",
  3121. type: "toggle",
  3122. icon: '<svg fill="#000000" height="200px" width="200px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" 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> <g> <path d="M363.929,0l-12.346,12.346L340.036,0.799l-21.458,21.458l11.547,11.547L107.782,256.147L96.235,244.6l-21.458,21.458 l11.863,11.863c-17.171,21.661-18.478,51.842-3.925,74.805L399.683,35.755L363.929,0z"></path> </g> </g> <g> <g> <path d="M304.934,330.282c27.516-27.516,29.126-71.268,4.845-100.695l129.522-129.523l-30.506-30.506L115.402,362.954 l30.506,30.506l16.191-16.191L259.625,512l84.679-84.679l-57.279-79.13L304.934,330.282z M269.003,323.296l-18.666-25.788 l-3.561-4.919l5.696-5.696l15.814,15.814l21.458-21.458l-15.814-15.814l14.228-14.228c12.546,17.432,10.985,41.949-4.683,57.617 L269.003,323.296z"></path> </g> </g> </g></svg>',
  3123. onChange: (value) => {
  3124. this.kxsClient.isGunOverlayColored = !this.kxsClient.isGunOverlayColored;
  3125. this.kxsClient.updateLocalStorage();
  3126. this.kxsClient.hud.toggleWeaponBorderHandler();
  3127. },
  3128. });
  3129. this.addOption(HUD, {
  3130. label: "Chromatic Weapon Border",
  3131. value: this.kxsClient.isGunBorderChromatic,
  3132. category: "HUD",
  3133. type: "toggle",
  3134. icon: '<svg fill="#000000" height="200px" width="200px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" 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> <g> <path d="M256.005,13.477c-84.682,0-153.734,68.227-155.075,152.594c0.535-0.164,1.073-0.318,1.61-0.477 c0.341-0.101,0.681-0.204,1.022-0.303c1.366-0.398,2.733-0.781,4.107-1.146c0.166-0.043,0.331-0.084,0.497-0.127 c1.214-0.318,2.43-0.623,3.651-0.917c0.389-0.094,0.777-0.186,1.167-0.276c1.147-0.268,2.296-0.526,3.448-0.771 c0.253-0.055,0.506-0.112,0.76-0.166c1.377-0.288,2.757-0.557,4.139-0.813c0.347-0.064,0.695-0.124,1.044-0.186 c1.091-0.195,2.183-0.38,3.278-0.555c0.385-0.062,0.769-0.124,1.154-0.184c1.396-0.214,2.793-0.417,4.195-0.599 c0.112-0.014,0.225-0.026,0.337-0.041c1.298-0.167,2.599-0.316,3.902-0.454c0.404-0.042,0.808-0.084,1.213-0.124 c1.139-0.113,2.278-0.216,3.42-0.309c0.303-0.024,0.605-0.052,0.907-0.076c1.408-0.106,2.818-0.197,4.231-0.271 c0.333-0.018,0.668-0.03,1.001-0.045c1.128-0.054,2.258-0.097,3.389-0.13c0.402-0.012,0.803-0.024,1.205-0.033 c1.43-0.032,2.863-0.055,4.297-0.055c1.176,0,2.351,0.013,3.525,0.036c0.389,0.007,0.778,0.022,1.167,0.032 c0.787,0.02,1.574,0.041,2.361,0.072c0.46,0.018,0.921,0.042,1.382,0.064c0.715,0.033,1.429,0.067,2.144,0.108 c0.487,0.028,0.974,0.062,1.462,0.094c0.689,0.045,1.379,0.093,2.068,0.146c0.497,0.038,0.993,0.081,1.49,0.123 c0.68,0.059,1.36,0.119,2.039,0.186c0.497,0.047,0.993,0.098,1.49,0.149c0.684,0.072,1.368,0.148,2.051,0.228 c0.487,0.057,0.973,0.113,1.46,0.174c0.701,0.087,1.402,0.181,2.102,0.276c0.464,0.064,0.929,0.125,1.393,0.191 c0.74,0.106,1.479,0.221,2.218,0.336c0.423,0.066,0.846,0.128,1.269,0.198c0.85,0.138,1.698,0.287,2.546,0.437 c0.307,0.055,0.616,0.105,0.923,0.16c1.166,0.212,2.33,0.435,3.49,0.67c0.07,0.014,0.138,0.03,0.208,0.043 c1.082,0.22,2.161,0.449,3.239,0.688c0.352,0.078,0.704,0.163,1.055,0.242c0.793,0.181,1.588,0.362,2.379,0.554 c0.421,0.102,0.841,0.209,1.262,0.314c0.722,0.18,1.442,0.36,2.162,0.548c0.446,0.116,0.89,0.237,1.335,0.357 c0.693,0.187,1.387,0.377,2.078,0.57c0.453,0.127,0.906,0.258,1.359,0.39c0.683,0.198,1.367,0.4,2.048,0.606 c0.451,0.136,0.902,0.273,1.353,0.414c0.685,0.212,1.369,0.43,2.051,0.651c0.439,0.141,0.878,0.283,1.316,0.428 c0.703,0.232,1.402,0.471,2.101,0.712c0.414,0.142,0.828,0.283,1.24,0.427c0.747,0.262,1.491,0.534,2.235,0.805 c0.36,0.132,0.722,0.26,1.081,0.395c0.887,0.331,1.771,0.671,2.654,1.015c0.212,0.083,0.425,0.161,0.636,0.245 c1.108,0.437,2.212,0.884,3.313,1.342c0.125,0.052,0.249,0.107,0.374,0.16c0.958,0.401,1.913,0.81,2.865,1.226 c0.329,0.144,0.656,0.295,0.985,0.441c0.744,0.332,1.487,0.665,2.227,1.006c0.391,0.181,0.779,0.365,1.169,0.548 c0.673,0.316,1.345,0.634,2.015,0.959c0.417,0.202,0.832,0.408,1.248,0.613c0.64,0.316,1.278,0.634,1.914,0.957 c0.425,0.216,0.849,0.435,1.273,0.654c0.625,0.323,1.248,0.65,1.868,0.981c0.424,0.226,0.847,0.452,1.269,0.68 c0.621,0.336,1.239,0.678,1.856,1.021c0.413,0.23,0.826,0.459,1.237,0.692c0.63,0.357,1.257,0.721,1.882,1.086 c0.392,0.228,0.784,0.454,1.174,0.685c0.664,0.394,1.323,0.795,1.982,1.197c0.344,0.21,0.69,0.417,1.034,0.629 c0.803,0.498,1.601,1.003,2.396,1.513c0.118,0.076,0.237,0.148,0.355,0.224l0.297-0.218l0.297,0.218 c0.118-0.076,0.237-0.148,0.355-0.224c0.795-0.51,1.593-1.015,2.396-1.513c0.343-0.212,0.689-0.419,1.034-0.629 c0.659-0.402,1.318-0.803,1.982-1.197c0.39-0.231,0.782-0.457,1.174-0.685c0.626-0.365,1.253-0.729,1.882-1.086 c0.411-0.233,0.824-0.462,1.236-0.692c0.617-0.343,1.235-0.685,1.856-1.021c0.422-0.229,0.845-0.455,1.268-0.68 c0.622-0.331,1.246-0.658,1.871-0.982c0.422-0.218,0.845-0.436,1.269-0.652c0.637-0.323,1.276-0.642,1.917-0.959 c0.415-0.205,0.83-0.41,1.247-0.612c0.669-0.324,1.341-0.642,2.015-0.959c0.39-0.183,0.778-0.368,1.169-0.548 c0.74-0.341,1.483-0.674,2.227-1.006c0.328-0.146,0.655-0.296,0.985-0.441c0.951-0.418,1.907-0.826,2.865-1.226 c0.125-0.052,0.249-0.108,0.374-0.16c1.1-0.458,2.203-0.905,3.313-1.342c0.211-0.084,0.425-0.162,0.636-0.245 c0.882-0.344,1.766-0.685,2.654-1.015c0.359-0.134,0.721-0.262,1.081-0.395c0.744-0.273,1.488-0.543,2.235-0.805 c0.413-0.145,0.827-0.286,1.24-0.427c0.699-0.24,1.399-0.479,2.102-0.712c0.438-0.145,0.877-0.287,1.316-0.428 c0.683-0.221,1.367-0.438,2.051-0.651c0.45-0.139,0.901-0.277,1.353-0.414c0.681-0.206,1.364-0.407,2.048-0.606 c0.452-0.131,0.905-0.261,1.359-0.39c0.691-0.195,1.385-0.384,2.078-0.57c0.445-0.12,0.89-0.24,1.335-0.357 c0.72-0.189,1.44-0.369,2.162-0.548c0.421-0.105,0.841-0.212,1.262-0.314c0.791-0.191,1.585-0.373,2.379-0.554 c0.352-0.081,0.703-0.164,1.055-0.242c1.078-0.239,2.157-0.468,3.239-0.688c0.07-0.014,0.138-0.029,0.208-0.043 c1.162-0.234,2.325-0.457,3.49-0.67c0.307-0.057,0.615-0.106,0.922-0.161c0.848-0.149,1.697-0.298,2.548-0.437 c0.422-0.069,0.844-0.131,1.266-0.197c0.739-0.115,1.479-0.23,2.219-0.336c0.463-0.067,0.928-0.128,1.392-0.191 c0.701-0.095,1.401-0.189,2.103-0.276c0.487-0.061,0.973-0.117,1.461-0.174c0.682-0.08,1.366-0.155,2.05-0.228 c0.497-0.051,0.993-0.102,1.49-0.149c0.679-0.066,1.359-0.127,2.039-0.186c0.497-0.042,0.993-0.085,1.49-0.123 c0.688-0.054,1.378-0.101,2.068-0.146c0.487-0.032,0.974-0.066,1.462-0.094c0.715-0.042,1.429-0.076,2.144-0.108 c0.46-0.021,0.921-0.045,1.382-0.064c0.786-0.03,1.574-0.051,2.361-0.072c0.389-0.01,0.778-0.024,1.167-0.032 c1.175-0.023,2.35-0.036,3.525-0.036c1.435,0,2.867,0.022,4.297,0.055c0.402,0.009,0.803,0.021,1.205,0.033 c1.132,0.033,2.261,0.077,3.388,0.13c0.334,0.016,0.668,0.028,1.002,0.045c1.413,0.075,2.823,0.166,4.23,0.272 c0.303,0.023,0.605,0.051,0.907,0.076c1.142,0.093,2.282,0.195,3.42,0.309c0.405,0.04,0.808,0.081,1.213,0.124 c1.303,0.138,2.604,0.288,3.902,0.454c0.112,0.015,0.225,0.026,0.337,0.041c1.401,0.182,2.799,0.385,4.194,0.599 c0.386,0.06,0.77,0.122,1.156,0.184c1.094,0.175,2.186,0.36,3.277,0.555c0.348,0.063,0.696,0.122,1.044,0.186 c1.383,0.255,2.763,0.526,4.139,0.813c0.253,0.054,0.507,0.111,0.76,0.166c1.152,0.245,2.3,0.503,3.448,0.771 c0.39,0.091,0.778,0.183,1.167,0.276c1.218,0.294,2.435,0.598,3.648,0.915c0.167,0.043,0.334,0.084,0.501,0.128 c1.373,0.364,2.74,0.749,4.106,1.145c0.341,0.1,0.681,0.203,1.022,0.304c0.537,0.159,1.074,0.314,1.61,0.477 C409.734,81.704,340.686,13.477,256.005,13.477z"></path> </g> </g> <g> <g> <path d="M436.614,210.342c-0.136,0.588-0.291,1.172-0.433,1.758c-0.163,0.671-0.325,1.341-0.495,2.01 c-0.317,1.249-0.651,2.49-0.993,3.73c-0.161,0.585-0.317,1.174-0.484,1.757c-0.482,1.682-0.986,3.354-1.515,5.017 c-0.039,0.124-0.075,0.25-0.114,0.374c-0.571,1.784-1.175,3.555-1.8,5.318c-0.197,0.554-0.406,1.103-0.608,1.655 c-0.442,1.21-0.891,2.417-1.358,3.617c-0.253,0.652-0.515,1.3-0.775,1.948c-0.445,1.108-0.9,2.211-1.367,3.309 c-0.277,0.652-0.555,1.304-0.84,1.953c-0.497,1.133-1.008,2.258-1.527,3.379c-0.268,0.579-0.53,1.161-0.803,1.738 c-0.687,1.446-1.395,2.883-2.119,4.31c-0.118,0.233-0.229,0.469-0.348,0.701c-0.843,1.644-1.713,3.273-2.603,4.89 c-0.286,0.52-0.584,1.032-0.875,1.548c-0.624,1.107-1.254,2.211-1.9,3.306c-0.358,0.607-0.724,1.209-1.089,1.811 c-0.61,1.007-1.228,2.008-1.857,3.003c-0.383,0.607-0.767,1.211-1.158,1.813c-0.659,1.016-1.331,2.023-2.01,3.027 c-0.368,0.544-0.731,1.091-1.104,1.63c-0.867,1.254-1.753,2.495-2.652,3.727c-0.196,0.268-0.384,0.542-0.579,0.808 c-1.093,1.483-2.21,2.946-3.346,4.397c-0.357,0.455-0.725,0.902-1.086,1.354c-0.801,1.003-1.608,2.001-2.43,2.987 c-0.449,0.54-0.906,1.073-1.362,1.608c-0.763,0.895-1.534,1.785-2.314,2.666c-0.477,0.539-0.956,1.076-1.439,1.609 c-0.811,0.894-1.633,1.778-2.462,2.658c-0.455,0.482-0.905,0.968-1.367,1.446c-1.03,1.07-2.075,2.123-3.131,3.168 c-0.268,0.265-0.529,0.537-0.798,0.8c-1.322,1.293-2.666,2.565-4.027,3.819c-0.409,0.377-0.827,0.742-1.24,1.115 c-0.973,0.881-1.952,1.756-2.945,2.617c-0.526,0.456-1.059,0.905-1.591,1.356c-0.909,0.771-1.825,1.534-2.75,2.288 c-0.557,0.454-1.117,0.906-1.681,1.355c-0.951,0.757-1.912,1.502-2.879,2.241c-0.532,0.406-1.06,0.817-1.596,1.217 c-1.172,0.876-2.359,1.734-3.554,2.584c-0.337,0.239-0.667,0.487-1.005,0.724c-1.528,1.071-3.076,2.119-4.638,3.145 c-0.451,0.296-0.911,0.581-1.367,0.874c-1.131,0.729-2.268,1.45-3.417,2.156c-0.598,0.366-1.202,0.725-1.804,1.085 c-1.034,0.618-2.073,1.228-3.121,1.828c-0.635,0.363-1.272,0.724-1.913,1.08c-1.067,0.592-2.142,1.173-3.222,1.746 c-0.61,0.323-1.217,0.651-1.832,0.968c-0.13,0.067-0.258,0.138-0.389,0.205l0.04,0.361l-0.333,0.146 c0.008,0.161,0.01,0.322,0.018,0.483c0.03,0.645,0.049,1.289,0.073,1.935c0.046,1.27,0.083,2.539,0.103,3.808 c0.011,0.696,0.017,1.392,0.02,2.088c0.005,1.247-0.004,2.491-0.023,3.736c-0.01,0.67-0.018,1.34-0.036,2.01 c-0.037,1.388-0.095,2.772-0.164,4.156c-0.025,0.505-0.04,1.009-0.069,1.514c-0.108,1.877-0.242,3.752-0.407,5.621 c-0.033,0.382-0.079,0.761-0.115,1.143c-0.14,1.488-0.294,2.973-0.469,4.454c-0.078,0.656-0.168,1.31-0.252,1.965 c-0.157,1.214-0.323,2.427-0.505,3.638c-0.106,0.708-0.217,1.414-0.331,2.121c-0.191,1.18-0.395,2.356-0.608,3.531 c-0.124,0.685-0.247,1.372-0.379,2.056c-0.247,1.283-0.515,2.56-0.788,3.836c-0.119,0.553-0.229,1.109-0.353,1.661 c-0.405,1.801-0.833,3.595-1.29,5.382c-0.099,0.388-0.21,0.771-0.312,1.157c-0.371,1.412-0.754,2.821-1.158,4.224 c-0.189,0.654-0.389,1.304-0.584,1.956c-0.342,1.139-0.69,2.275-1.054,3.407c-0.225,0.699-0.455,1.397-0.687,2.093 c-0.366,1.097-0.745,2.19-1.132,3.28c-0.242,0.681-0.482,1.363-0.732,2.04c-0.431,1.172-0.88,2.337-1.334,3.498 c-0.223,0.571-0.438,1.145-0.667,1.713c-0.682,1.696-1.387,3.383-2.119,5.058c-0.155,0.356-0.323,0.707-0.48,1.062 c-0.596,1.339-1.201,2.673-1.828,3.998c-0.295,0.622-0.601,1.238-0.901,1.857c-0.515,1.059-1.036,2.114-1.571,3.162 c-0.338,0.662-0.681,1.321-1.028,1.979c-0.532,1.012-1.074,2.02-1.625,3.023c-0.353,0.643-0.706,1.286-1.066,1.925 c-0.605,1.071-1.225,2.132-1.851,3.192c-0.321,0.543-0.635,1.091-0.962,1.631c-0.948,1.567-1.915,3.122-2.908,4.661 c-0.177,0.274-0.363,0.542-0.542,0.815c-0.839,1.284-1.691,2.561-2.562,3.824c-0.38,0.55-0.77,1.093-1.156,1.64 c-0.694,0.985-1.395,1.965-2.109,2.936c-0.432,0.587-0.87,1.171-1.309,1.753c-0.703,0.934-1.417,1.861-2.139,2.782 c-0.443,0.566-0.886,1.131-1.336,1.693c-0.787,0.981-1.59,1.951-2.398,2.917c-0.395,0.472-0.783,0.949-1.184,1.417 c-1.207,1.413-2.433,2.813-3.684,4.192c-0.119,0.131-0.243,0.257-0.362,0.389c-1.146,1.255-2.309,2.494-3.491,3.718 c-0.434,0.45-0.879,0.891-1.318,1.337c-0.889,0.903-1.785,1.8-2.694,2.686c-0.503,0.49-1.01,0.974-1.519,1.458 c-0.433,0.412-0.856,0.833-1.293,1.24c23.044,12.82,49.005,19.586,75.238,19.589c0.002,0,0.006,0,0.008,0 c26.778,0,53.256-6.96,76.576-20.133c24-13.557,44.018-33.419,57.887-57.44C533.598,347.601,509.03,253.68,436.614,210.342z"></path> </g> </g> <g> <g> <path d="M357.097,188.296c-26.339,0-52.539,6.858-75.519,19.563c0.206,0.192,0.401,0.394,0.605,0.585 c1.334,1.256,2.652,2.526,3.946,3.82c0.146,0.146,0.298,0.289,0.443,0.435c1.417,1.426,2.803,2.881,4.172,4.352 c0.372,0.4,0.74,0.804,1.108,1.208c1.12,1.225,2.224,2.466,3.311,3.723c0.272,0.314,0.548,0.623,0.818,0.939 c1.302,1.527,2.579,3.077,3.831,4.647c0.299,0.374,0.589,0.754,0.885,1.13c1.013,1.291,2.011,2.596,2.991,3.915 c0.303,0.408,0.61,0.814,0.909,1.224c1.192,1.632,2.364,3.283,3.505,4.959c0.19,0.278,0.372,0.562,0.56,0.842 c0.973,1.444,1.926,2.906,2.861,4.382c0.296,0.466,0.59,0.933,0.882,1.402c1.085,1.746,2.154,3.505,3.187,5.294 c0.721,1.249,1.421,2.506,2.112,3.767c0.195,0.355,0.387,0.712,0.578,1.068c0.539,0.999,1.068,2.003,1.588,3.009 c0.154,0.3,0.313,0.599,0.465,0.899c0.645,1.265,1.275,2.536,1.888,3.812c0.133,0.278,0.262,0.556,0.394,0.835 c0.493,1.036,0.974,2.076,1.446,3.12c0.169,0.372,0.337,0.745,0.503,1.118c0.536,1.205,1.061,2.413,1.57,3.627 c0.042,0.101,0.087,0.201,0.129,0.302c0.547,1.31,1.073,2.627,1.589,3.949c0.141,0.36,0.279,0.723,0.417,1.085 c0.399,1.042,0.788,2.087,1.168,3.135c0.12,0.331,0.242,0.661,0.36,0.993c0.472,1.328,0.932,2.663,1.374,4.001 c0.082,0.246,0.157,0.495,0.238,0.741c0.364,1.119,0.717,2.243,1.06,3.369c0.118,0.389,0.235,0.777,0.351,1.166 c0.354,1.194,0.697,2.391,1.028,3.592c0.048,0.175,0.1,0.349,0.147,0.525c0.37,1.364,0.721,2.732,1.06,4.104 c0.091,0.367,0.178,0.736,0.265,1.104c0.127,0.529,0.259,1.056,0.382,1.587c37.925-22.771,64.617-60.932,72.753-104.53 C391.963,191.252,374.736,188.296,357.097,188.296z"></path> </g> </g> <g> <g> <path d="M229.161,477.688c-0.508-0.483-1.014-0.967-1.516-1.455c-0.909-0.886-1.806-1.785-2.696-2.688 c-0.439-0.446-0.883-0.886-1.317-1.336c-1.182-1.224-2.346-2.464-3.491-3.718c-0.119-0.13-0.243-0.257-0.362-0.389 c-1.252-1.379-2.477-2.779-3.684-4.192c-0.4-0.468-0.788-0.946-1.184-1.417c-0.808-0.966-1.611-1.936-2.398-2.917 c-0.45-0.561-0.893-1.126-1.336-1.693c-0.722-0.922-1.435-1.848-2.139-2.782c-0.439-0.582-0.877-1.166-1.309-1.753 c-0.714-0.971-1.414-1.952-2.109-2.936c-0.386-0.547-0.776-1.089-1.156-1.64c-0.871-1.264-1.723-2.54-2.562-3.824 c-0.179-0.273-0.364-0.541-0.542-0.815c-0.994-1.539-1.962-3.096-2.91-4.662c-0.326-0.538-0.638-1.084-0.958-1.625 c-0.627-1.061-1.249-2.124-1.854-3.197c-0.36-0.639-0.713-1.282-1.066-1.924c-0.551-1.003-1.093-2.011-1.625-3.023 c-0.346-0.658-0.689-1.317-1.028-1.979c-0.535-1.049-1.056-2.104-1.571-3.162c-0.301-0.619-0.608-1.235-0.901-1.857 c-0.627-1.325-1.232-2.659-1.828-3.998c-0.157-0.355-0.325-0.706-0.48-1.062c-0.732-1.674-1.435-3.362-2.119-5.058 c-0.229-0.568-0.443-1.142-0.667-1.713c-0.454-1.162-0.903-2.327-1.334-3.498c-0.249-0.678-0.491-1.36-0.732-2.04 c-0.387-1.09-0.765-2.183-1.132-3.28c-0.232-0.696-0.463-1.394-0.687-2.093c-0.363-1.133-0.713-2.271-1.055-3.412 c-0.195-0.649-0.395-1.297-0.582-1.949c-0.405-1.404-0.788-2.814-1.16-4.228c-0.101-0.386-0.212-0.768-0.311-1.155 c-0.457-1.786-0.885-3.58-1.29-5.382c-0.124-0.552-0.234-1.108-0.353-1.661c-0.275-1.276-0.541-2.553-0.788-3.836 c-0.132-0.684-0.254-1.37-0.378-2.056c-0.213-1.175-0.417-2.351-0.608-3.531c-0.114-0.706-0.225-1.412-0.331-2.121 c-0.182-1.21-0.347-2.423-0.505-3.638c-0.085-0.655-0.175-1.308-0.252-1.965c-0.176-1.481-0.329-2.966-0.469-4.454 c-0.036-0.382-0.082-0.761-0.115-1.143c-0.164-1.869-0.299-3.744-0.407-5.621c-0.029-0.504-0.044-1.008-0.069-1.512 c-0.069-1.385-0.126-2.771-0.164-4.16c-0.018-0.667-0.025-1.335-0.036-2.004c-0.019-1.246-0.028-2.492-0.023-3.741 c0.003-0.695,0.009-1.391,0.02-2.087c0.02-1.269,0.057-2.539,0.103-3.808c0.023-0.645,0.042-1.289,0.073-1.935 c0.007-0.162,0.01-0.322,0.018-0.484l-0.333-0.146l0.04-0.361c-0.13-0.067-0.258-0.138-0.389-0.205 c-0.615-0.317-1.222-0.645-1.833-0.968c-1.081-0.573-2.157-1.154-3.224-1.747c-0.638-0.355-1.274-0.714-1.907-1.076 c-1.051-0.601-2.092-1.213-3.129-1.833c-0.601-0.358-1.202-0.716-1.798-1.081c-1.15-0.706-2.286-1.427-3.418-2.157 c-0.454-0.293-0.915-0.577-1.367-0.874c-1.563-1.025-3.109-2.072-4.636-3.143c-0.343-0.24-0.678-0.492-1.02-0.735 c-1.189-0.845-2.37-1.7-3.537-2.571c-0.54-0.403-1.071-0.816-1.606-1.224c-0.964-0.737-1.922-1.479-2.871-2.234 c-0.565-0.45-1.126-0.904-1.687-1.361c-0.921-0.751-1.833-1.511-2.738-2.278c-0.536-0.454-1.072-0.906-1.602-1.366 c-0.984-0.854-1.955-1.722-2.921-2.595c-0.421-0.38-0.849-0.755-1.266-1.14c-1.358-1.251-2.698-2.519-4.017-3.809 c-0.281-0.275-0.552-0.558-0.832-0.835c-1.044-1.034-2.078-2.075-3.097-3.133c-0.466-0.484-0.924-0.978-1.385-1.468 c-0.821-0.871-1.637-1.747-2.441-2.634c-0.489-0.539-0.973-1.082-1.456-1.626c-0.774-0.875-1.54-1.757-2.297-2.647 c-0.461-0.541-0.923-1.08-1.377-1.625c-0.814-0.977-1.612-1.965-2.405-2.958c-0.368-0.461-0.744-0.917-1.108-1.382 c-1.133-1.446-2.248-2.906-3.337-4.385c-0.21-0.284-0.41-0.574-0.617-0.86c-0.885-1.215-1.758-2.439-2.614-3.674 c-0.379-0.548-0.747-1.103-1.12-1.654c-0.672-0.995-1.339-1.993-1.992-3.001c-0.395-0.609-0.783-1.22-1.171-1.834 c-0.624-0.986-1.236-1.978-1.841-2.978c-0.369-0.609-0.739-1.216-1.1-1.83c-0.642-1.088-1.269-2.186-1.888-3.286 c-0.295-0.523-0.596-1.04-0.884-1.565c-0.889-1.615-1.758-3.241-2.6-4.883c-0.124-0.241-0.239-0.487-0.361-0.729 c-0.719-1.417-1.421-2.842-2.103-4.279c-0.278-0.584-0.543-1.174-0.815-1.76c-0.516-1.115-1.024-2.234-1.518-3.36 c-0.286-0.651-0.564-1.304-0.843-1.959c-0.466-1.098-0.922-2.201-1.367-3.309c-0.26-0.647-0.521-1.294-0.773-1.944 c-0.467-1.201-0.917-2.409-1.36-3.622c-0.201-0.551-0.41-1.099-0.606-1.652c-0.625-1.763-1.228-3.535-1.8-5.319 c-0.039-0.123-0.074-0.247-0.113-0.369c-0.53-1.666-1.035-3.343-1.518-5.027c-0.166-0.578-0.32-1.162-0.48-1.742 c-0.344-1.246-0.679-2.493-0.998-3.748c-0.169-0.662-0.329-1.326-0.491-1.991c-0.143-0.59-0.299-1.178-0.436-1.77 C2.971,253.679-21.598,347.601,20.747,420.946c13.866,24.018,33.884,43.88,57.888,57.436 c23.322,13.172,49.804,20.136,76.584,20.137c0.002,0,0.005,0,0.007,0c26.227,0,52.183-6.767,75.23-19.589 C230.018,478.523,229.594,478.101,229.161,477.688z"></path> </g> </g> <g> <g> <path d="M154.899,188.295c-17.638,0-34.866,2.956-51.358,8.799c8.136,43.599,34.828,81.76,72.749,104.53 c0.126-0.53,0.258-1.058,0.385-1.586c0.089-0.368,0.175-0.737,0.266-1.104c0.338-1.372,0.689-2.74,1.06-4.104 c0.047-0.176,0.099-0.349,0.147-0.525c0.33-1.201,0.673-2.398,1.028-3.592c0.115-0.39,0.233-0.778,0.351-1.166 c0.342-1.126,0.695-2.25,1.06-3.369c0.081-0.247,0.157-0.495,0.238-0.741c0.442-1.338,0.9-2.672,1.374-4.001 c0.118-0.332,0.24-0.662,0.36-0.993c0.38-1.049,0.769-2.093,1.168-3.135c0.138-0.361,0.276-0.724,0.417-1.085 c0.516-1.321,1.042-2.637,1.589-3.949c0.042-0.101,0.087-0.201,0.129-0.302c0.509-1.214,1.034-2.421,1.57-3.627 c0.166-0.373,0.334-0.746,0.503-1.118c0.472-1.044,0.955-2.083,1.446-3.12c0.132-0.278,0.26-0.557,0.394-0.835 c0.614-1.277,1.243-2.547,1.888-3.812c0.152-0.301,0.311-0.6,0.465-0.899c0.52-1.006,1.049-2.01,1.588-3.009 c0.192-0.356,0.384-0.713,0.578-1.068c0.69-1.262,1.391-2.518,2.112-3.767c1.033-1.789,2.102-3.548,3.187-5.294 c0.292-0.469,0.586-0.936,0.882-1.402c0.936-1.476,1.888-2.937,2.861-4.382c0.188-0.28,0.37-0.564,0.56-0.842 c1.142-1.676,2.312-3.327,3.505-4.959c0.3-0.411,0.606-0.817,0.909-1.224c0.98-1.32,1.977-2.625,2.991-3.915 c0.296-0.376,0.587-0.756,0.885-1.13c1.254-1.571,2.529-3.12,3.831-4.647c0.27-0.316,0.546-0.625,0.818-0.939 c1.087-1.257,2.19-2.497,3.311-3.722c0.368-0.403,0.736-0.807,1.108-1.208c1.369-1.472,2.755-2.926,4.172-4.352 c0.146-0.146,0.297-0.289,0.443-0.435c1.294-1.294,2.612-2.565,3.945-3.82c0.205-0.192,0.4-0.394,0.605-0.585 C207.437,195.152,181.237,188.295,154.899,188.295z"></path> </g> </g> <g> <g> <path d="M308.604,346.356c-0.375,0.111-0.751,0.224-1.126,0.333c-1.352,0.392-2.707,0.77-4.067,1.129 c-0.183,0.048-0.365,0.092-0.548,0.14c-1.2,0.314-2.404,0.614-3.61,0.902c-0.394,0.095-0.788,0.188-1.183,0.279 c-1.15,0.268-2.302,0.524-3.458,0.769c-0.249,0.053-0.499,0.11-0.749,0.161c-1.385,0.288-2.774,0.558-4.166,0.814 c-0.336,0.063-0.674,0.119-1.011,0.18c-1.108,0.197-2.219,0.385-3.33,0.561c-0.376,0.06-0.752,0.121-1.128,0.178 c-1.406,0.215-2.815,0.418-4.227,0.601c-0.096,0.012-0.193,0.022-0.29,0.035c-1.319,0.169-2.642,0.319-3.967,0.458 c-0.398,0.042-0.796,0.082-1.195,0.121c-1.152,0.114-2.305,0.217-3.461,0.31c-0.298,0.024-0.594,0.051-0.891,0.074 c-1.416,0.106-2.834,0.197-4.255,0.272c-0.334,0.018-0.669,0.03-1.004,0.045c-1.131,0.054-2.266,0.097-3.4,0.13 c-0.407,0.012-0.813,0.023-1.219,0.033c-1.436,0.032-2.875,0.055-4.316,0.055c-1.441,0-2.88-0.022-4.316-0.055 c-0.407-0.009-0.814-0.021-1.219-0.033c-1.135-0.033-2.269-0.077-3.401-0.13c-0.335-0.016-0.67-0.028-1.004-0.045 c-1.421-0.075-2.84-0.165-4.255-0.272c-0.298-0.022-0.595-0.049-0.891-0.074c-1.156-0.093-2.309-0.196-3.461-0.31 c-0.399-0.039-0.796-0.079-1.194-0.121c-1.325-0.139-2.649-0.291-3.968-0.458c-0.096-0.012-0.193-0.022-0.29-0.035 c-1.413-0.183-2.821-0.386-4.227-0.601c-0.376-0.058-0.752-0.118-1.128-0.178c-1.112-0.177-2.223-0.364-3.33-0.561 c-0.337-0.061-0.674-0.117-1.011-0.18c-1.392-0.255-2.781-0.526-4.166-0.814c-0.25-0.052-0.499-0.108-0.749-0.161 c-1.156-0.245-2.307-0.502-3.458-0.769c-0.395-0.092-0.788-0.185-1.183-0.279c-1.206-0.289-2.41-0.588-3.61-0.902 c-0.183-0.047-0.365-0.092-0.548-0.14c-1.36-0.359-2.715-0.738-4.067-1.129c-0.376-0.109-0.751-0.222-1.126-0.333 c-0.513-0.151-1.028-0.298-1.538-0.455c0.756,44.222,20.455,86.416,54.141,115.261c33.685-28.845,53.385-71.039,54.143-115.261 C309.631,346.057,309.117,346.204,308.604,346.356z"></path> </g> </g> <g> <g> <path d="M289.37,265.857c-8.877-15.374-20.073-28.874-33.371-40.251c-13.298,11.376-24.494,24.875-33.371,40.251 c-8.876,15.374-14.969,31.819-18.173,49.024c16.502,5.827,33.79,8.773,51.544,8.773c17.754,0,35.045-2.945,51.546-8.773 C304.339,297.676,298.247,281.232,289.37,265.857z"></path> </g> </g> </g></svg>',
  3135. onChange: (value) => {
  3136. this.kxsClient.isGunBorderChromatic = !this.kxsClient.isGunBorderChromatic;
  3137. this.kxsClient.updateLocalStorage();
  3138. this.kxsClient.hud.toggleChromaticWeaponBorder();
  3139. },
  3140. });
  3141. this.addOption(HUD, {
  3142. label: "Focus Mode",
  3143. value: (() => {
  3144. const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
  3145. return `Press ${isMac ? 'Command+F (⌘+F)' : 'Ctrl+F'} to toggle Focus Mode.\nWhen enabled, the HUD will dim and notifications will appear.`;
  3146. })(),
  3147. category: "HUD",
  3148. type: "info",
  3149. icon: '<svg version="1.1" id="Uploaded to svgrepo.com" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32" xml:space="preserve" 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"> <path d="M30.146,28.561l-1.586,1.586c-0.292,0.292-0.676,0.438-1.061,0.438s-0.768-0.146-1.061-0.438 l-4.293-4.293l-2.232,2.232c-0.391,0.391-0.902,0.586-1.414,0.586s-1.024-0.195-1.414-0.586l-0.172-0.172 c-0.781-0.781-0.781-2.047,0-2.828l8.172-8.172c0.391-0.391,0.902-0.586,1.414-0.586s1.024,0.195,1.414,0.586l0.172,0.172 c0.781,0.781,0.781,2.047,0,2.828l-2.232,2.232l4.293,4.293C30.731,27.024,30.731,27.976,30.146,28.561z M22.341,18.244 l-4.097,4.097L3.479,13.656C2.567,13.12,2,12.128,2,11.07V3c0-0.551,0.449-1,1-1h8.07c1.058,0,2.049,0.567,2.586,1.479 L22.341,18.244z M19.354,19.354c0.195-0.195,0.195-0.512,0-0.707l-15.5-15.5c-0.195-0.195-0.512-0.195-0.707,0s-0.195,0.512,0,0.707 l15.5,15.5C18.744,19.451,18.872,19.5,19,19.5S19.256,19.451,19.354,19.354z" fill="#000000"></path> </g></svg>',
  3150. onChange: () => {
  3151. }
  3152. });
  3153. this.addOption(HUD, {
  3154. label: "Health Bar Indicator",
  3155. value: this.kxsClient.isHealBarIndicatorEnabled,
  3156. type: "toggle",
  3157. 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.0001 8.59997C13.3334 7.01474 15 5.42847 17 5.42847C19.6667 5.42847 22 7.52847 22 11.2855C22 13.7143 20.2683 16.4912 18.1789 18.9912C16.5956 20.8955 14.7402 22.5713 13.2302 22.5713H10.7698C9.25981 22.5713 7.40446 20.8955 5.82112 18.9912C3.73174 16.4912 2 13.7143 2 11.2855C2 7.52847 4.33333 5.42847 7 5.42847C9 5.42847 10.6667 7.01474 12.0001 8.59997Z" fill="#000000"></path> </g></svg>',
  3158. category: "HUD",
  3159. onChange: () => {
  3160. this.kxsClient.isHealBarIndicatorEnabled = !this.kxsClient.isHealBarIndicatorEnabled;
  3161. this.kxsClient.updateLocalStorage();
  3162. },
  3163. });
  3164. this.addOption(HUD, {
  3165. label: "Message Open/Close RSHIFT Menu",
  3166. value: this.kxsClient.isNotifyingForToggleMenu,
  3167. type: "toggle",
  3168. icon: '<svg viewBox="0 0 24 24" 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"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g></svg>',
  3169. category: "HUD",
  3170. onChange: (value) => {
  3171. this.kxsClient.isNotifyingForToggleMenu = !this.kxsClient.isNotifyingForToggleMenu;
  3172. this.kxsClient.updateLocalStorage();
  3173. },
  3174. });
  3175. this.addOption(SERVER, {
  3176. label: "Webhook URL",
  3177. value: this.kxsClient.discordWebhookUrl || "",
  3178. 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>',
  3179. category: "SERVER",
  3180. type: "input",
  3181. placeholder: "discord webhook url",
  3182. onChange: (value) => {
  3183. value = value.toString().trim();
  3184. this.kxsClient.discordWebhookUrl = value;
  3185. this.kxsClient.discordTracker.setWebhookUrl(value);
  3186. this.kxsClient.updateLocalStorage();
  3187. },
  3188. });
  3189. this.addOption(MECHANIC, {
  3190. label: "Custom Crosshair",
  3191. value: this.kxsClient.customCrosshair || "",
  3192. type: "input",
  3193. category: "MECHANIC",
  3194. 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>crosshair</title> <path d="M30 14.75h-2.824c-0.608-5.219-4.707-9.318-9.874-9.921l-0.053-0.005v-2.824c0-0.69-0.56-1.25-1.25-1.25s-1.25 0.56-1.25 1.25v0 2.824c-5.219 0.608-9.318 4.707-9.921 9.874l-0.005 0.053h-2.824c-0.69 0-1.25 0.56-1.25 1.25s0.56 1.25 1.25 1.25v0h2.824c0.608 5.219 4.707 9.318 9.874 9.921l0.053 0.005v2.824c0 0.69 0.56 1.25 1.25 1.25s1.25-0.56 1.25-1.25v0-2.824c5.219-0.608 9.318-4.707 9.921-9.874l0.005-0.053h2.824c0.69 0 1.25-0.56 1.25-1.25s-0.56-1.25-1.25-1.25v0zM17.25 24.624v-2.624c0-0.69-0.56-1.25-1.25-1.25s-1.25 0.56-1.25 1.25v0 2.624c-3.821-0.57-6.803-3.553-7.368-7.326l-0.006-0.048h2.624c0.69 0 1.25-0.56 1.25-1.25s-0.56-1.25-1.25-1.25v0h-2.624c0.57-3.821 3.553-6.804 7.326-7.368l0.048-0.006v2.624c0 0.69 0.56 1.25 1.25 1.25s1.25-0.56 1.25-1.25v0-2.624c3.821 0.57 6.803 3.553 7.368 7.326l0.006 0.048h-2.624c-0.69 0-1.25 0.56-1.25 1.25s0.56 1.25 1.25 1.25v0h2.624c-0.571 3.821-3.553 6.803-7.326 7.368l-0.048 0.006z"></path> </g></svg>',
  3195. placeholder: "URL of png,gif,svg",
  3196. onChange: (value) => {
  3197. this.kxsClient.customCrosshair = value;
  3198. this.kxsClient.updateLocalStorage();
  3199. this.kxsClient.hud.loadCustomCrosshair();
  3200. },
  3201. });
  3202. this.addOption(MECHANIC, {
  3203. label: "Heal Warning",
  3204. value: this.kxsClient.isHealthWarningEnabled,
  3205. type: "toggle",
  3206. category: "MECHANIC",
  3207. 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>',
  3208. onChange: (value) => {
  3209. var _a, _b;
  3210. this.kxsClient.isHealthWarningEnabled = !this.kxsClient.isHealthWarningEnabled;
  3211. if (this.kxsClient.isHealthWarningEnabled) {
  3212. // Always enter placement mode when enabling from RSHIFT menu
  3213. (_a = this.kxsClient.healWarning) === null || _a === void 0 ? void 0 : _a.enableDragging();
  3214. }
  3215. else {
  3216. (_b = this.kxsClient.healWarning) === null || _b === void 0 ? void 0 : _b.hide();
  3217. }
  3218. this.kxsClient.updateLocalStorage();
  3219. },
  3220. });
  3221. this.addOption(SERVER, {
  3222. label: "Update Checker",
  3223. value: this.kxsClient.isAutoUpdateEnabled,
  3224. type: "toggle",
  3225. 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>',
  3226. category: "SERVER",
  3227. onChange: (value) => {
  3228. this.kxsClient.isAutoUpdateEnabled = !this.kxsClient.isAutoUpdateEnabled;
  3229. this.kxsClient.updateLocalStorage();
  3230. },
  3231. });
  3232. this.addOption(MECHANIC, {
  3233. label: `Uncap FPS`,
  3234. value: this.kxsClient.isFpsUncapped,
  3235. type: "toggle",
  3236. 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>',
  3237. category: 'MECHANIC',
  3238. onChange: () => {
  3239. this.kxsClient.isFpsUncapped = !this.kxsClient.isFpsUncapped;
  3240. this.kxsClient.setAnimationFrameCallback();
  3241. this.kxsClient.updateLocalStorage();
  3242. },
  3243. });
  3244. this.addOption(HUD, {
  3245. label: `Winning Animation`,
  3246. value: this.kxsClient.isWinningAnimationEnabled,
  3247. 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>',
  3248. category: "HUD",
  3249. type: "toggle",
  3250. onChange: () => {
  3251. this.kxsClient.isWinningAnimationEnabled = !this.kxsClient.isWinningAnimationEnabled;
  3252. this.kxsClient.updateLocalStorage();
  3253. },
  3254. });
  3255. this.addOption(HUD, {
  3256. label: `KxsClient Logo`,
  3257. value: this.kxsClient.isKxsClientLogoEnable,
  3258. icon: '<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52" enable-background="new 0 0 52 52" 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="M20,37.5c0-0.8-0.7-1.5-1.5-1.5h-15C2.7,36,2,36.7,2,37.5v11C2,49.3,2.7,50,3.5,50h15c0.8,0,1.5-0.7,1.5-1.5 V37.5z"></path> <path d="M8.1,22H3.2c-1,0-1.5,0.9-0.9,1.4l8,8.3c0.4,0.3,1,0.3,1.4,0l8-8.3c0.6-0.6,0.1-1.4-0.9-1.4h-4.7 c0-5,4.9-10,9.9-10V6C15,6,8.1,13,8.1,22z"></path> <path d="M41.8,20.3c-0.4-0.3-1-0.3-1.4,0l-8,8.3c-0.6,0.6-0.1,1.4,0.9,1.4h4.8c0,6-4.1,10-10.1,10v6 c9,0,16.1-7,16.1-16H49c1,0,1.5-0.9,0.9-1.4L41.8,20.3z"></path> <path d="M50,3.5C50,2.7,49.3,2,48.5,2h-15C32.7,2,32,2.7,32,3.5v11c0,0.8,0.7,1.5,1.5,1.5h15c0.8,0,1.5-0.7,1.5-1.5 V3.5z"></path> </g></svg>',
  3259. category: "HUD",
  3260. type: "toggle",
  3261. onChange: () => {
  3262. this.kxsClient.isKxsClientLogoEnable = !this.kxsClient.isKxsClientLogoEnable;
  3263. this.kxsClient.updateLocalStorage();
  3264. },
  3265. });
  3266. this.addOption(HUD, {
  3267. label: `Spotify Player`,
  3268. value: this.kxsClient.isSpotifyPlayerEnabled,
  3269. 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>',
  3270. category: "HUD",
  3271. type: "toggle",
  3272. onChange: () => {
  3273. this.kxsClient.isSpotifyPlayerEnabled = !this.kxsClient.isSpotifyPlayerEnabled;
  3274. this.kxsClient.updateLocalStorage();
  3275. this.kxsClient.toggleSpotifyMenu();
  3276. },
  3277. });
  3278. this.addOption(HUD, {
  3279. label: "Brightness",
  3280. value: this.kxsClient.brightness,
  3281. icon: '<svg fill="#000000" viewBox="-5.5 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>light</title> <path d="M11.875 6v2.469c0 0.844-0.375 1.25-1.156 1.25s-1.156-0.406-1.156-1.25v-2.469c0-0.813 0.375-1.219 1.156-1.219s1.156 0.406 1.156 1.219zM14.219 9.25l1.438-2.031c0.469-0.625 1.063-0.75 1.656-0.313s0.656 1 0.188 1.688l-1.438 2c-0.469 0.688-1.031 0.75-1.656 0.313-0.594-0.438-0.656-0.969-0.188-1.656zM5.781 7.25l1.469 2c0.469 0.688 0.406 1.219-0.219 1.656-0.594 0.469-1.156 0.375-1.625-0.313l-1.469-2c-0.469-0.688-0.406-1.219 0.219-1.656 0.594-0.469 1.156-0.375 1.625 0.313zM10.719 11.125c2.688 0 4.875 2.188 4.875 4.875 0 2.656-2.188 4.813-4.875 4.813s-4.875-2.156-4.875-4.813c0-2.688 2.188-4.875 4.875-4.875zM1.594 11.813l2.375 0.75c0.781 0.25 1.063 0.719 0.813 1.469-0.219 0.75-0.75 0.969-1.563 0.719l-2.313-0.75c-0.781-0.25-1.063-0.75-0.844-1.5 0.25-0.719 0.75-0.938 1.531-0.688zM17.5 12.563l2.344-0.75c0.813-0.25 1.313-0.031 1.531 0.688 0.25 0.75-0.031 1.25-0.844 1.469l-2.313 0.781c-0.781 0.25-1.281 0.031-1.531-0.719-0.219-0.75 0.031-1.219 0.813-1.469zM10.719 18.688c1.5 0 2.719-1.219 2.719-2.688 0-1.5-1.219-2.719-2.719-2.719s-2.688 1.219-2.688 2.719c0 1.469 1.188 2.688 2.688 2.688zM0.906 17.969l2.344-0.75c0.781-0.25 1.313-0.063 1.531 0.688 0.25 0.75-0.031 1.219-0.813 1.469l-2.375 0.781c-0.781 0.25-1.281 0.031-1.531-0.719-0.219-0.75 0.063-1.219 0.844-1.469zM18.219 17.219l2.344 0.75c0.781 0.25 1.063 0.719 0.813 1.469-0.219 0.75-0.719 0.969-1.531 0.719l-2.344-0.781c-0.813-0.25-1.031-0.719-0.813-1.469 0.25-0.75 0.75-0.938 1.531-0.688zM3.938 23.344l1.469-1.969c0.469-0.688 1.031-0.781 1.625-0.313 0.625 0.438 0.688 0.969 0.219 1.656l-1.469 1.969c-0.469 0.688-1.031 0.813-1.656 0.375-0.594-0.438-0.656-1.031-0.188-1.719zM16.063 21.375l1.438 1.969c0.469 0.688 0.406 1.281-0.188 1.719s-1.188 0.281-1.656-0.344l-1.438-2c-0.469-0.688-0.406-1.219 0.188-1.656 0.625-0.438 1.188-0.375 1.656 0.313zM11.875 23.469v2.469c0 0.844-0.375 1.25-1.156 1.25s-1.156-0.406-1.156-1.25v-2.469c0-0.844 0.375-1.25 1.156-1.25s1.156 0.406 1.156 1.25z"></path> </g></svg>',
  3282. category: "HUD",
  3283. type: "slider",
  3284. min: 20,
  3285. max: 100,
  3286. step: 1,
  3287. onChange: (value) => {
  3288. this.kxsClient.applyBrightness(value);
  3289. },
  3290. });
  3291. this.addOption(HUD, {
  3292. label: "Kill Feed Chroma",
  3293. value: this.kxsClient.isKillFeedBlint,
  3294. 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>`,
  3295. category: "HUD",
  3296. type: "toggle",
  3297. onChange: () => {
  3298. this.kxsClient.isKillFeedBlint = !this.kxsClient.isKillFeedBlint;
  3299. this.kxsClient.updateLocalStorage();
  3300. this.kxsClient.hud.toggleKillFeed();
  3301. },
  3302. });
  3303. this.addOption(SERVER, {
  3304. label: `Rich Presence (Account token required)`,
  3305. value: this.kxsClient.discordToken || "",
  3306. 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>',
  3307. category: "SERVER",
  3308. type: "input",
  3309. placeholder: "Your discord account token",
  3310. onChange: (value) => {
  3311. value = value.toString().trim();
  3312. this.kxsClient.discordToken = this.kxsClient.parseToken(value);
  3313. this.kxsClient.discordRPC.disconnect();
  3314. this.kxsClient.discordRPC.connect();
  3315. this.kxsClient.updateLocalStorage();
  3316. },
  3317. });
  3318. this.addOption(MECHANIC, {
  3319. label: `Kill Leader Tracking`,
  3320. 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>',
  3321. category: "MECHANIC",
  3322. value: this.kxsClient.isKillLeaderTrackerEnabled,
  3323. type: "toggle",
  3324. onChange: (value) => {
  3325. this.kxsClient.isKillLeaderTrackerEnabled = !this.kxsClient.isKillLeaderTrackerEnabled;
  3326. this.kxsClient.updateLocalStorage();
  3327. },
  3328. });
  3329. this.addOption(MECHANIC, {
  3330. label: `Friends Detector (separe with ',')`,
  3331. 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>',
  3332. category: "MECHANIC",
  3333. value: this.kxsClient.all_friends,
  3334. type: "input",
  3335. placeholder: "kisakay,iletal...",
  3336. onChange: (value) => {
  3337. this.kxsClient.all_friends = value;
  3338. this.kxsClient.updateLocalStorage();
  3339. },
  3340. });
  3341. this.addOption(HUD, {
  3342. label: `Change Background`,
  3343. icon: '<svg 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 179.006 179.006" xml:space="preserve" 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"> <g> <g> <polygon style="fill:#010002;" points="20.884,116.354 11.934,116.354 11.934,32.818 137.238,32.818 137.238,41.768 149.172,41.768 149.172,20.884 0,20.884 0,128.288 20.884,128.288 "></polygon> <path style="fill:#010002;" d="M29.834,50.718v107.404h149.172V50.718H29.834z M123.58,136.856c-0.024,0-0.048,0-0.072,0 c-0.012,0-1.187,0-2.81,0c-3.795,0-10.078,0-10.114,0c-19.625,0-39.25,0-58.875,0v-3.473c0.907-0.859,2.005-1.551,3.168-2.166 c1.981-1.062,3.938-2.148,5.967-3.115c1.957-0.937,3.998-1.742,6.003-2.59c1.886-0.8,3.801-1.545,5.674-2.363 c0.328-0.137,0.638-0.489,0.776-0.811c0.424-1.05,0.782-2.124,1.116-3.216c0.245-0.823,0.412-1.635,1.468-1.862 c0.263-0.048,0.597-0.513,0.627-0.817c0.209-1.581,0.37-3.168,0.489-4.744c0.024-0.346-0.149-0.776-0.382-1.038 c-1.384-1.557-2.142-3.353-2.47-5.406c-0.161-1.038-0.74-1.993-1.038-3.013c-0.394-1.366-0.728-2.745-1.038-4.129 c-0.119-0.501-0.048-1.038-0.125-1.551c-0.125-0.746-0.107-1.319,0.806-1.611c0.233-0.084,0.442-0.668,0.453-1.032 c0.048-2.214,0.012-4.433,0.024-6.641c0.012-1.36,0-2.727,0.107-4.087c0.185-2.596,1.718-4.421,3.622-5.997 c2.787-2.303,6.128-3.377,9.565-4.189c1.808-0.424,3.64-0.68,5.478-0.979c0.489-0.078,0.996-0.006,1.498-0.006 c0.095,0.125,0.161,0.251,0.251,0.37c-0.376,0.28-0.811,0.513-1.134,0.847c-0.746,0.746-0.674,1.265,0.125,1.945 c1.647,1.396,3.318,2.804,4.911,4.254c1.42,1.271,1.969,2.942,1.981,4.815c0,3.222,0,6.45,0,9.672c0,0.65-0.048,1.313,0.776,1.605 c0.167,0.066,0.352,0.424,0.34,0.632c-0.131,1.641-0.322,3.294-0.489,4.941c-0.006,0.066-0.018,0.131-0.054,0.185 c-1.486,2.166-1.677,4.827-2.733,7.148c-0.048,0.09-0.078,0.191-0.125,0.257c-1.969,2.315-1.36,5.102-1.396,7.769 c0,0.269,0.197,0.686,0.406,0.782c0.806,0.358,1.002,1.044,1.223,1.772c0.352,1.14,0.692,2.303,1.181,3.389 c0.179,0.394,0.716,0.746,1.17,0.907c0.943,0.364,1.886,0.74,2.834,1.11c2.363-1.002,5.734-2.434,6.385-2.727 c0.919-0.418,1.611-1.349,2.44-1.993c0.37-0.28,0.817-0.537,1.259-0.615c1.504-0.239,2.16-0.77,2.518-2.255 c0.465-1.945,0.806-3.89,0.388-5.913c-0.167-0.877-0.489-1.45-1.366-1.784c-1.778-0.698-3.532-1.474-5.293-2.22 c-1.319-0.555-1.396-1.02-0.919-2.387c1.516-4.296,2.631-8.658,3.007-13.258c0.28-3.443,0.048-6.981,1.307-10.305 c0.871-2.339,2.339-4.505,4.696-5.203c1.796-0.531,3.359-1.742,5.269-1.999c0.358-0.018,0.674-0.072,1.026-0.054 c0.042,0.006,0.078,0.012,0.113,0.012c4.529,0.286,9.923,3.019,11.2,8.043c0.066,0.257,0.101,0.525,0.143,0.788h0.125 c0.698,2.852,0.621,5.818,0.859,8.712c0.37,4.594,1.504,8.962,3.019,13.264c0.477,1.366,0.394,1.832-0.919,2.381 c-1.76,0.746-3.514,1.522-5.299,2.22c-0.871,0.34-1.181,0.895-1.36,1.784c-0.406,2.029-0.084,3.968,0.388,5.913 c0.346,1.48,1.014,2.011,2.512,2.25c0.442,0.078,0.883,0.334,1.259,0.615c0.829,0.644,1.516,1.569,2.44,1.993 c3.234,1.468,6.51,2.888,9.839,4.117c5.114,1.88,8.509,5.478,9.326,11.045C145.944,136.856,134.768,136.856,123.58,136.856z"></path> </g> </g> </g></svg>',
  3344. category: "HUD",
  3345. value: true,
  3346. type: "click",
  3347. onChange: () => {
  3348. const backgroundElement = document.getElementById("background");
  3349. if (!backgroundElement) {
  3350. alert("Element with id 'background' not found.");
  3351. return;
  3352. }
  3353. const choice = prompt("Enter '0' to default Kxs background, '1' to provide a URL or '2' to upload a local image:");
  3354. if (choice === "0") {
  3355. localStorage.removeItem("lastBackgroundUrl");
  3356. localStorage.removeItem("lastBackgroundFile");
  3357. localStorage.removeItem("lastBackgroundType");
  3358. localStorage.removeItem("lastBackgroundValue");
  3359. backgroundElement.style.backgroundImage = `url(${background_image})`;
  3360. }
  3361. else if (choice === "1") {
  3362. const newBackgroundUrl = prompt("Enter the URL of the new background image:");
  3363. if (newBackgroundUrl) {
  3364. backgroundElement.style.backgroundImage = `url(${newBackgroundUrl})`;
  3365. this.kxsClient.saveBackgroundToLocalStorage(newBackgroundUrl);
  3366. alert("Background updated successfully!");
  3367. }
  3368. }
  3369. else if (choice === "2") {
  3370. const fileInput = document.createElement("input");
  3371. fileInput.type = "file";
  3372. fileInput.accept = "image/*";
  3373. fileInput.onchange = (event) => {
  3374. var _a, _b;
  3375. const file = (_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0];
  3376. if (file) {
  3377. const reader = new FileReader();
  3378. reader.onload = () => {
  3379. backgroundElement.style.backgroundImage = `url(${reader.result})`;
  3380. this.kxsClient.saveBackgroundToLocalStorage(file);
  3381. alert("Background updated successfully!");
  3382. };
  3383. reader.readAsDataURL(file);
  3384. }
  3385. };
  3386. fileInput.click();
  3387. }
  3388. },
  3389. });
  3390. }
  3391. createOptionCard(option, container) {
  3392. const optionCard = document.createElement("div");
  3393. Object.assign(optionCard.style, {
  3394. background: "rgba(31, 41, 55, 0.8)",
  3395. borderRadius: "10px",
  3396. padding: "16px",
  3397. display: "flex",
  3398. flexDirection: "column",
  3399. alignItems: "center",
  3400. gap: "12px",
  3401. minHeight: "150px",
  3402. });
  3403. const iconContainer = document.createElement("div");
  3404. Object.assign(iconContainer.style, {
  3405. width: "48px",
  3406. height: "48px",
  3407. borderRadius: "50%",
  3408. display: "flex",
  3409. alignItems: "center",
  3410. justifyContent: "center",
  3411. marginBottom: "8px"
  3412. });
  3413. iconContainer.innerHTML = option.icon || '';
  3414. const title = document.createElement("div");
  3415. title.textContent = option.label;
  3416. title.style.fontSize = "16px";
  3417. title.style.textAlign = "center";
  3418. let control = null;
  3419. switch (option.type) {
  3420. case "info":
  3421. control = this.createInfoElement(option);
  3422. break;
  3423. case "input":
  3424. control = this.createInputElement(option);
  3425. break;
  3426. case "toggle":
  3427. control = this.createToggleButton(option);
  3428. break;
  3429. case "sub":
  3430. control = this.createSubButton(option);
  3431. break;
  3432. case "slider":
  3433. control = this.createSliderElement(option);
  3434. break;
  3435. case "click":
  3436. control = this.createClickButton(option);
  3437. }
  3438. optionCard.appendChild(iconContainer);
  3439. optionCard.appendChild(title);
  3440. optionCard.appendChild(control);
  3441. container.appendChild(optionCard);
  3442. }
  3443. setActiveCategory(category) {
  3444. this.activeCategory = category;
  3445. this.filterOptions();
  3446. // Update button styles
  3447. this.menu.querySelectorAll('.category-btn').forEach(btn => {
  3448. const btnCategory = btn.dataset.category;
  3449. btn.style.background =
  3450. btnCategory === category ? '#3B82F6' : 'rgba(55, 65, 81, 0.8)';
  3451. });
  3452. }
  3453. filterOptions() {
  3454. const gridContainer = document.getElementById('kxsMenuGrid');
  3455. if (gridContainer) {
  3456. // Clear existing content
  3457. gridContainer.innerHTML = '';
  3458. // Get unique options based on category and search term
  3459. const displayedOptions = new Set();
  3460. this.sections.forEach(section => {
  3461. if (this.activeCategory === 'ALL' || section.category === this.activeCategory) {
  3462. section.options.forEach(option => {
  3463. // Create a unique key for each option
  3464. const optionKey = `${option.label}-${option.category}`;
  3465. // Check if option matches search term
  3466. const matchesSearch = this.searchTerm === '' ||
  3467. option.label.toLowerCase().includes(this.searchTerm) ||
  3468. option.category.toLowerCase().includes(this.searchTerm);
  3469. if (!displayedOptions.has(optionKey) && matchesSearch) {
  3470. displayedOptions.add(optionKey);
  3471. this.createOptionCard(option, gridContainer);
  3472. }
  3473. });
  3474. }
  3475. });
  3476. // Show a message if no options match the search
  3477. if (displayedOptions.size === 0 && this.searchTerm !== '') {
  3478. const noResultsMsg = document.createElement('div');
  3479. noResultsMsg.textContent = `No results found for "${this.searchTerm}"`;
  3480. noResultsMsg.style.gridColumn = '1 / -1';
  3481. noResultsMsg.style.textAlign = 'center';
  3482. noResultsMsg.style.padding = '20px';
  3483. noResultsMsg.style.color = '#9CA3AF';
  3484. gridContainer.appendChild(noResultsMsg);
  3485. }
  3486. }
  3487. }
  3488. createGridContainer() {
  3489. const gridContainer = document.createElement("div");
  3490. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  3491. Object.assign(gridContainer.style, {
  3492. display: "grid",
  3493. gridTemplateColumns: isMobile ? "repeat(4, 1fr)" : "repeat(3, 1fr)",
  3494. gap: isMobile ? "5px" : "16px",
  3495. padding: isMobile ? "2px" : "16px",
  3496. gridAutoRows: isMobile ? "minmax(38px, auto)" : "minmax(150px, auto)",
  3497. overflowY: "auto",
  3498. overflowX: "hidden", // Prevent horizontal scrolling
  3499. maxHeight: isMobile ? "28vh" : "calc(3 * 150px + 2 * 16px)",
  3500. width: "100%",
  3501. boxSizing: "border-box" // Include padding in width calculation
  3502. });
  3503. gridContainer.id = "kxsMenuGrid";
  3504. this.menu.appendChild(gridContainer);
  3505. }
  3506. addOption(section, option) {
  3507. section.options.push(option);
  3508. // Store all options for searching
  3509. this.allOptions.push(option);
  3510. }
  3511. addSection(title, category = "ALL") {
  3512. const section = {
  3513. title,
  3514. options: [],
  3515. category
  3516. };
  3517. const sectionElement = document.createElement("div");
  3518. sectionElement.className = "menu-section";
  3519. sectionElement.style.display = this.activeCategory === "ALL" || this.activeCategory === category ? "block" : "none";
  3520. section.element = sectionElement;
  3521. this.sections.push(section);
  3522. this.menu.appendChild(sectionElement);
  3523. return section;
  3524. }
  3525. createToggleButton(option) {
  3526. const btn = document.createElement("button");
  3527. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  3528. Object.assign(btn.style, {
  3529. width: "100%",
  3530. padding: isMobile ? "2px 0px" : "8px",
  3531. height: isMobile ? "24px" : "auto",
  3532. background: option.value ? "#059669" : "#DC2626",
  3533. border: "none",
  3534. borderRadius: isMobile ? "3px" : "6px",
  3535. color: "white",
  3536. cursor: "pointer",
  3537. transition: "background 0.2s",
  3538. fontSize: isMobile ? "9px" : "14px",
  3539. fontWeight: "bold",
  3540. minHeight: isMobile ? "20px" : "unset",
  3541. letterSpacing: isMobile ? "0.5px" : "1px"
  3542. });
  3543. btn.textContent = option.value ? "ENABLED" : "DISABLED";
  3544. btn.addEventListener("click", () => {
  3545. var _a;
  3546. const newValue = !option.value;
  3547. option.value = newValue;
  3548. btn.textContent = newValue ? "ENABLED" : "DISABLED";
  3549. btn.style.background = newValue ? "#059669" : "#DC2626";
  3550. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, newValue);
  3551. });
  3552. this.blockMousePropagation(btn);
  3553. return btn;
  3554. }
  3555. createClickButton(option) {
  3556. const btn = document.createElement("button");
  3557. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  3558. Object.assign(btn.style, {
  3559. width: "100%",
  3560. padding: isMobile ? "2px 0px" : "8px",
  3561. height: isMobile ? "24px" : "auto",
  3562. background: "#3B82F6",
  3563. border: "none",
  3564. borderRadius: "6px",
  3565. color: "white",
  3566. cursor: "pointer",
  3567. transition: "background 0.2s",
  3568. fontSize: "14px",
  3569. fontWeight: "bold"
  3570. });
  3571. btn.textContent = option.label;
  3572. btn.addEventListener("click", () => {
  3573. var _a;
  3574. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, true);
  3575. });
  3576. this.blockMousePropagation(btn);
  3577. return btn;
  3578. }
  3579. addShiftListener() {
  3580. // Gestionnaire pour la touche Shift (ouverture du menu)
  3581. window.addEventListener("keydown", (event) => {
  3582. if (event.key === "Shift" && event.location == 2) {
  3583. this.clearMenu();
  3584. this.toggleMenuVisibility();
  3585. // Ensure options are displayed after loading
  3586. this.filterOptions();
  3587. }
  3588. });
  3589. // Gestionnaire séparé pour la touche Échap avec capture en phase de capture
  3590. // pour intercepter l'événement avant qu'il n'atteigne le jeu
  3591. document.addEventListener("keydown", (event) => {
  3592. if (event.key === "Escape" && this.isClientMenuVisible) {
  3593. // Fermer le menu si la touche Échap est pressée et que le menu est visible
  3594. this.toggleMenuVisibility();
  3595. // Empêcher la propagation ET l'action par défaut
  3596. event.stopPropagation();
  3597. event.preventDefault();
  3598. // Arrêter la propagation de l'événement
  3599. return false;
  3600. }
  3601. }, true); // true = phase de capture
  3602. }
  3603. createInputElement(option) {
  3604. const input = document.createElement("input");
  3605. input.type = "text";
  3606. input.value = String(option.value);
  3607. if (option.placeholder) {
  3608. input.placeholder = option.placeholder;
  3609. }
  3610. Object.assign(input.style, {
  3611. width: "100%",
  3612. padding: "8px",
  3613. background: "rgba(55, 65, 81, 0.8)",
  3614. border: "none",
  3615. borderRadius: "6px",
  3616. color: "#FFAE00",
  3617. fontSize: "14px"
  3618. });
  3619. input.addEventListener("change", () => {
  3620. var _a;
  3621. option.value = input.value;
  3622. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, input.value);
  3623. });
  3624. // Empêcher la propagation des touches de texte vers la page web
  3625. // mais permettre l'interaction avec l'input
  3626. input.addEventListener("keydown", (e) => {
  3627. // Ne pas arrêter la propagation des touches de navigation (flèches, tab, etc.)
  3628. // qui sont nécessaires pour naviguer dans le champ de texte
  3629. e.stopPropagation();
  3630. });
  3631. input.addEventListener("keyup", (e) => {
  3632. e.stopPropagation();
  3633. });
  3634. input.addEventListener("keypress", (e) => {
  3635. e.stopPropagation();
  3636. });
  3637. this.blockMousePropagation(input);
  3638. return input;
  3639. }
  3640. createSliderElement(option) {
  3641. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  3642. const container = document.createElement("div");
  3643. Object.assign(container.style, {
  3644. display: "flex",
  3645. flexDirection: "column",
  3646. width: "100%",
  3647. marginTop: isMobile ? "3px" : "5px",
  3648. });
  3649. // Conteneur pour le slider et la valeur
  3650. const sliderContainer = document.createElement("div");
  3651. Object.assign(sliderContainer.style, {
  3652. display: "flex",
  3653. alignItems: "center",
  3654. width: "100%",
  3655. gap: isMobile ? "8px" : "12px",
  3656. });
  3657. // Wrapper du slider pour gérer la couleur de fond
  3658. const sliderWrapper = document.createElement("div");
  3659. Object.assign(sliderWrapper.style, {
  3660. position: "relative",
  3661. width: "100%",
  3662. height: isMobile ? "10px" : "12px",
  3663. background: "rgba(30, 35, 45, 0.5)",
  3664. borderRadius: "6px",
  3665. overflow: "hidden",
  3666. border: "1px solid rgba(59, 130, 246, 0.25)",
  3667. });
  3668. // Barre de progression bleue
  3669. const progressBar = document.createElement("div");
  3670. Object.assign(progressBar.style, {
  3671. position: "absolute",
  3672. left: "0",
  3673. top: "0",
  3674. height: "100%",
  3675. width: `${((Number(option.value) - (option.min || 0)) / ((option.max || 100) - (option.min || 0))) * 100}%`,
  3676. background: "#3B82F6", // Couleur bleue pleine
  3677. borderRadius: "6px 0 0 6px", // Arrondi uniquement à gauche
  3678. transition: "width 0.1s ease-out",
  3679. });
  3680. // Créer le slider
  3681. const slider = document.createElement("input");
  3682. slider.type = "range";
  3683. slider.min = String(option.min || 0);
  3684. slider.max = String(option.max || 100);
  3685. slider.step = String(option.step || 1);
  3686. slider.value = String(option.value);
  3687. slider.className = "kxs-minimal-slider";
  3688. Object.assign(slider.style, {
  3689. position: "absolute",
  3690. left: "0",
  3691. top: "0",
  3692. width: "100%",
  3693. height: "100%",
  3694. margin: "0",
  3695. appearance: "none",
  3696. background: "transparent", // Transparent pour voir la barre de progression
  3697. cursor: "pointer",
  3698. outline: "none",
  3699. border: "none",
  3700. zIndex: "2", // Au-dessus de la barre de progression
  3701. });
  3702. // Valeur actuelle avec style simple
  3703. const valueDisplay = document.createElement("div");
  3704. valueDisplay.textContent = String(option.value);
  3705. Object.assign(valueDisplay.style, {
  3706. minWidth: isMobile ? "28px" : "36px",
  3707. textAlign: "center",
  3708. color: "#ffffff",
  3709. fontSize: isMobile ? "11px" : "13px",
  3710. fontFamily: "'Segoe UI', Arial, sans-serif",
  3711. background: "rgba(59, 130, 246, 0.1)",
  3712. padding: isMobile ? "2px 4px" : "3px 6px",
  3713. borderRadius: "4px",
  3714. border: "1px solid rgba(59, 130, 246, 0.3)",
  3715. transition: "all 0.15s ease-out",
  3716. });
  3717. // Styles personnalisés pour le curseur du slider uniquement
  3718. const sliderStyles = `
  3719. /* Style du curseur */
  3720. .kxs-minimal-slider::-webkit-slider-thumb {
  3721. appearance: none;
  3722. width: ${isMobile ? "14px" : "16px"};
  3723. height: ${isMobile ? "14px" : "16px"};
  3724. border-radius: 50%;
  3725. background: linear-gradient(135deg, #4f8bf9, #3B82F6);
  3726. cursor: pointer;
  3727. border: none;
  3728. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  3729. transition: background 0.2s, transform 0.1s;
  3730. z-index: 3;
  3731. }
  3732.  
  3733. .kxs-minimal-slider::-moz-range-thumb {
  3734. width: ${isMobile ? "14px" : "16px"};
  3735. height: ${isMobile ? "14px" : "16px"};
  3736. border-radius: 50%;
  3737. background: linear-gradient(135deg, #4f8bf9, #3B82F6);
  3738. cursor: pointer;
  3739. border: none;
  3740. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  3741. transition: background 0.2s, transform 0.1s;
  3742. z-index: 3;
  3743. }
  3744.  
  3745. /* Masquer la piste par défaut */
  3746. .kxs-minimal-slider::-webkit-slider-runnable-track {
  3747. background: transparent;
  3748. height: 100%;
  3749. }
  3750.  
  3751. .kxs-minimal-slider::-moz-range-track {
  3752. background: transparent;
  3753. height: 100%;
  3754. }
  3755.  
  3756. /* Effets au survol */
  3757. .kxs-minimal-slider:hover::-webkit-slider-thumb {
  3758. background: linear-gradient(135deg, #5a93fa, #4289f7);
  3759. transform: scale(1.05);
  3760. }
  3761.  
  3762. .kxs-minimal-slider:hover::-moz-range-thumb {
  3763. background: linear-gradient(135deg, #5a93fa, #4289f7);
  3764. transform: scale(1.05);
  3765. }
  3766. `;
  3767. // Ajouter les styles personnalisés
  3768. const style = document.createElement("style");
  3769. style.textContent = sliderStyles;
  3770. document.head.appendChild(style);
  3771. // Ajouter les gestionnaires d'événements
  3772. slider.addEventListener("input", () => {
  3773. var _a;
  3774. // Mettre à jour la valeur affichée
  3775. valueDisplay.textContent = slider.value;
  3776. // Mettre à jour la largeur de la barre de progression
  3777. const percentage = ((Number(slider.value) - (option.min || 0)) / ((option.max || 100) - (option.min || 0))) * 100;
  3778. progressBar.style.width = `${percentage}%`;
  3779. // Mettre à jour la valeur de l'option
  3780. option.value = Number(slider.value);
  3781. (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, Number(slider.value));
  3782. // Effet visuel sur la valeur
  3783. valueDisplay.style.background = "rgba(59, 130, 246, 0.25)";
  3784. setTimeout(() => {
  3785. valueDisplay.style.background = "rgba(59, 130, 246, 0.1)";
  3786. }, 150);
  3787. });
  3788. // Empêcher les événements de se propager vers le jeu
  3789. slider.addEventListener("mousedown", (e) => e.stopPropagation());
  3790. slider.addEventListener("mouseup", (e) => e.stopPropagation());
  3791. this.blockMousePropagation(slider, false);
  3792. // Assembler tous les éléments
  3793. sliderWrapper.appendChild(progressBar);
  3794. sliderWrapper.appendChild(slider);
  3795. sliderContainer.appendChild(sliderWrapper);
  3796. sliderContainer.appendChild(valueDisplay);
  3797. container.appendChild(sliderContainer);
  3798. return container;
  3799. }
  3800. createInfoElement(option) {
  3801. const info = document.createElement("div");
  3802. info.textContent = String(option.value);
  3803. Object.assign(info.style, {
  3804. color: "#b0b0b0",
  3805. fontSize: "12px",
  3806. fontStyle: "italic",
  3807. marginTop: "2px",
  3808. marginLeft: "6px",
  3809. marginBottom: "2px",
  3810. flex: "1 1 100%",
  3811. whiteSpace: "pre-line"
  3812. });
  3813. this.blockMousePropagation(info);
  3814. return info;
  3815. }
  3816. // Crée un bouton pour ouvrir un sous-menu de configuration de mode
  3817. createSubButton(option) {
  3818. const btn = document.createElement("button");
  3819. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  3820. // Styles pour le bouton de sous-menu (gris par défaut)
  3821. Object.assign(btn.style, {
  3822. width: "100%",
  3823. padding: isMobile ? "2px 0px" : "8px",
  3824. height: isMobile ? "24px" : "auto",
  3825. background: "#6B7280", // Couleur grise par défaut
  3826. border: "none",
  3827. borderRadius: isMobile ? "3px" : "6px",
  3828. color: "white",
  3829. cursor: "pointer",
  3830. transition: "background 0.2s",
  3831. fontSize: isMobile ? "9px" : "14px",
  3832. fontWeight: "bold",
  3833. minHeight: isMobile ? "20px" : "unset",
  3834. letterSpacing: isMobile ? "0.5px" : "1px"
  3835. });
  3836. btn.textContent = "CONFIGURE";
  3837. // Variables pour le sous-menu
  3838. let subMenuContainer = null;
  3839. // Sauvegarde des éléments originaux à masquer/afficher
  3840. let originalElements = [];
  3841. let isSubMenuOpen = false;
  3842. // Gestionnaire d'événement pour ouvrir le sous-menu
  3843. btn.addEventListener("click", () => {
  3844. // Si aucun champ n'est défini, ne rien faire
  3845. if (!option.fields || option.fields.length === 0) {
  3846. if (option.onChange) {
  3847. option.onChange(option.value);
  3848. }
  3849. return;
  3850. }
  3851. // Si le sous-menu est déjà ouvert, le fermer
  3852. if (isSubMenuOpen) {
  3853. this.closeSubMenu();
  3854. return;
  3855. }
  3856. // Trouver tous les éléments principaux à masquer
  3857. originalElements = [];
  3858. const allSections = document.querySelectorAll('.menu-section');
  3859. allSections.forEach(section => {
  3860. originalElements.push(section);
  3861. section.style.display = 'none';
  3862. });
  3863. // Masquer aussi le conteneur de la grille
  3864. const grid = document.getElementById('kxsMenuGrid');
  3865. if (grid) {
  3866. originalElements.push(grid);
  3867. grid.style.display = 'none';
  3868. }
  3869. // Créer le conteneur du sous-menu
  3870. subMenuContainer = document.createElement("div");
  3871. subMenuContainer.id = "kxs-submenu";
  3872. subMenuContainer.className = "kxs-submenu-container";
  3873. Object.assign(subMenuContainer.style, {
  3874. width: "100%",
  3875. padding: "10px 0",
  3876. boxSizing: "border-box",
  3877. overflowY: "auto",
  3878. background: "rgba(17, 24, 39, 0.95)"
  3879. });
  3880. this.blockMousePropagation(subMenuContainer);
  3881. // Créer l'en-tête du sous-menu
  3882. const subMenuHeader = document.createElement("div");
  3883. Object.assign(subMenuHeader.style, {
  3884. display: "flex",
  3885. justifyContent: "space-between",
  3886. alignItems: "center",
  3887. marginBottom: isMobile ? "10px" : "15px",
  3888. paddingBottom: isMobile ? "5px" : "10px",
  3889. borderBottom: "1px solid rgba(255, 255, 255, 0.1)",
  3890. paddingLeft: isMobile ? "10px" : "15px",
  3891. paddingRight: isMobile ? "10px" : "15px",
  3892. width: "100%",
  3893. boxSizing: "border-box"
  3894. });
  3895. this.blockMousePropagation(subMenuHeader);
  3896. // Bouton de retour
  3897. const backBtn = document.createElement("button");
  3898. backBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  3899. <path d="M15 19L8 12L15 5" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
  3900. </svg> Back`;
  3901. Object.assign(backBtn.style, {
  3902. background: "none",
  3903. border: "none",
  3904. color: "#fff",
  3905. cursor: "pointer",
  3906. display: "flex",
  3907. alignItems: "center",
  3908. gap: "6px",
  3909. padding: "5px",
  3910. fontSize: isMobile ? "12px" : "14px"
  3911. });
  3912. this.blockMousePropagation(backBtn);
  3913. // Titre du sous-menu
  3914. const subMenuTitle = document.createElement("h3");
  3915. subMenuTitle.textContent = option.label;
  3916. Object.assign(subMenuTitle.style, {
  3917. margin: "0",
  3918. color: "#fff",
  3919. fontSize: isMobile ? "16px" : "20px",
  3920. fontWeight: "bold",
  3921. textAlign: "center",
  3922. flex: "1"
  3923. });
  3924. this.blockMousePropagation(subMenuTitle);
  3925. // Ajouter l'événement au bouton retour pour fermer le sous-menu
  3926. backBtn.addEventListener("click", () => {
  3927. this.closeSubMenu();
  3928. });
  3929. // Assembler l'en-tête
  3930. subMenuHeader.appendChild(backBtn);
  3931. subMenuHeader.appendChild(subMenuTitle);
  3932. subMenuHeader.appendChild(document.createElement("div")); // Espace vide pour l'équilibre
  3933. subMenuContainer.appendChild(subMenuHeader);
  3934. // Créer la grille pour les options
  3935. const optionsGrid = document.createElement("div");
  3936. Object.assign(optionsGrid.style, {
  3937. display: "grid",
  3938. gridTemplateColumns: isMobile ? "repeat(2, 1fr)" : "repeat(3, 1fr)",
  3939. gap: isMobile ? "8px" : "16px",
  3940. padding: isMobile ? "4px" : "16px",
  3941. gridAutoRows: isMobile ? "minmax(100px, auto)" : "minmax(150px, auto)",
  3942. width: "100%",
  3943. boxSizing: "border-box"
  3944. });
  3945. this.blockMousePropagation(optionsGrid);
  3946. // Créer les cartes pour chaque option
  3947. option.fields.forEach(mod => {
  3948. this.createModCard(mod, optionsGrid);
  3949. });
  3950. subMenuContainer.appendChild(optionsGrid);
  3951. // Ajouter le sous-menu au menu principal
  3952. this.menu.appendChild(subMenuContainer);
  3953. isSubMenuOpen = true;
  3954. // Définir la méthode pour fermer le sous-menu
  3955. this.closeSubMenu = () => {
  3956. // Supprimer le sous-menu
  3957. if (subMenuContainer && subMenuContainer.parentElement) {
  3958. subMenuContainer.parentElement.removeChild(subMenuContainer);
  3959. }
  3960. // Réafficher tous les éléments originaux
  3961. originalElements.forEach(el => {
  3962. if (el.id === 'kxsMenuGrid') {
  3963. el.style.display = 'grid';
  3964. }
  3965. else {
  3966. el.style.display = 'block';
  3967. }
  3968. });
  3969. // Réinitialiser les états
  3970. this.filterOptions(); // S'assurer que les options sont correctement filtrées
  3971. subMenuContainer = null;
  3972. isSubMenuOpen = false;
  3973. };
  3974. // Appeler le callback si défini
  3975. if (option.onChange) {
  3976. option.onChange(option.value);
  3977. }
  3978. });
  3979. this.blockMousePropagation(btn);
  3980. return btn;
  3981. }
  3982. // Crée une carte pour un mod dans le sous-menu
  3983. createModCard(mod, container) {
  3984. const modCard = document.createElement("div");
  3985. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  3986. Object.assign(modCard.style, {
  3987. background: "rgba(31, 41, 55, 0.8)",
  3988. borderRadius: "10px",
  3989. padding: isMobile ? "10px" : "16px",
  3990. display: "flex",
  3991. flexDirection: "column",
  3992. alignItems: "center",
  3993. gap: isMobile ? "8px" : "12px",
  3994. minHeight: isMobile ? "100px" : "150px",
  3995. });
  3996. // Icône
  3997. const iconContainer = document.createElement("div");
  3998. Object.assign(iconContainer.style, {
  3999. width: isMobile ? "32px" : "48px",
  4000. height: isMobile ? "32px" : "48px",
  4001. borderRadius: "50%",
  4002. display: "flex",
  4003. alignItems: "center",
  4004. justifyContent: "center",
  4005. marginBottom: isMobile ? "4px" : "8px"
  4006. });
  4007. iconContainer.innerHTML = mod.icon || '';
  4008. // Titre
  4009. const title = document.createElement("div");
  4010. title.textContent = mod.label;
  4011. title.style.fontSize = isMobile ? "14px" : "16px";
  4012. title.style.textAlign = "center";
  4013. // Contrôle selon le type
  4014. let control = null;
  4015. switch (mod.type) {
  4016. case "info":
  4017. control = this.createInfoElement(mod);
  4018. break;
  4019. case "input":
  4020. control = this.createInputElement(mod);
  4021. break;
  4022. case "toggle":
  4023. control = this.createToggleButton(mod);
  4024. break;
  4025. case "slider":
  4026. control = this.createSliderElement(mod);
  4027. break;
  4028. case "click":
  4029. control = this.createClickButton(mod);
  4030. }
  4031. modCard.appendChild(iconContainer);
  4032. modCard.appendChild(title);
  4033. if (control) {
  4034. modCard.appendChild(control);
  4035. }
  4036. container.appendChild(modCard);
  4037. this.blockMousePropagation(modCard);
  4038. }
  4039. addDragListeners() {
  4040. this.menu.addEventListener('mousedown', (e) => {
  4041. // Ne pas arrêter la propagation si l'événement vient d'un élément interactif
  4042. if (e.target instanceof HTMLElement &&
  4043. e.target.matches("input, select, button, svg, path")) {
  4044. // Laisser l'événement se propager aux éléments interactifs
  4045. return;
  4046. }
  4047. // Empêcher la propagation de l'événement mousedown vers la page web
  4048. e.stopPropagation();
  4049. // Activer le drag & drop seulement si on clique sur une zone non interactive
  4050. if (e.target instanceof HTMLElement &&
  4051. !e.target.matches("input, select, button, svg, path")) {
  4052. this.isDragging = true;
  4053. const rect = this.menu.getBoundingClientRect();
  4054. this.dragOffset = {
  4055. x: e.clientX - rect.left,
  4056. y: e.clientY - rect.top
  4057. };
  4058. this.menu.style.cursor = "grabbing";
  4059. }
  4060. });
  4061. document.addEventListener('mousemove', (e) => {
  4062. if (this.isDragging) {
  4063. const x = e.clientX - this.dragOffset.x;
  4064. const y = e.clientY - this.dragOffset.y;
  4065. this.menu.style.transform = 'none';
  4066. this.menu.style.left = `${x}px`;
  4067. this.menu.style.top = `${y}px`;
  4068. }
  4069. });
  4070. document.addEventListener('mouseup', (e) => {
  4071. // Arrêter le drag & drop
  4072. const wasDragging = this.isDragging;
  4073. this.isDragging = false;
  4074. this.menu.style.cursor = "grab";
  4075. // Empêcher la propagation de l'événement mouseup vers la page web
  4076. // seulement si l'événement vient du menu et n'est pas un élément interactif
  4077. if (this.menu.contains(e.target)) {
  4078. if (wasDragging || !(e.target instanceof HTMLElement && e.target.matches("input, select, button, svg, path"))) {
  4079. e.stopPropagation();
  4080. }
  4081. }
  4082. });
  4083. }
  4084. toggleMenuVisibility() {
  4085. this.isClientMenuVisible = !this.isClientMenuVisible;
  4086. // Mettre à jour la propriété publique en même temps
  4087. this.isOpen = this.isClientMenuVisible;
  4088. if (this.kxsClient.isNotifyingForToggleMenu) {
  4089. this.kxsClient.nm.showNotification(this.isClientMenuVisible ? "Opening menu..." : "Closing menu...", "info", 1100);
  4090. }
  4091. this.menu.style.display = this.isClientMenuVisible ? "block" : "none";
  4092. // If opening the menu, make sure to display options
  4093. if (this.isClientMenuVisible) {
  4094. this.filterOptions();
  4095. }
  4096. }
  4097. destroy() {
  4098. // Remove global event listeners
  4099. window.removeEventListener("keydown", this.shiftListener);
  4100. document.removeEventListener('mousemove', this.mouseMoveListener);
  4101. document.removeEventListener('mouseup', this.mouseUpListener);
  4102. // Supprimer tous les écouteurs d'événements keydown du document
  4103. // Nous ne pouvons pas supprimer directement l'écouteur anonyme, mais ce n'est pas grave
  4104. // car la vérification isClientMenuVisible empêchera toute action une fois le menu détruit
  4105. // Remove all event listeners from menu elements
  4106. const removeAllListeners = (element) => {
  4107. var _a;
  4108. const clone = element.cloneNode(true);
  4109. (_a = element.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(clone, element);
  4110. };
  4111. // Clean up all buttons and inputs in the menu
  4112. this.menu.querySelectorAll('button, input').forEach(element => {
  4113. removeAllListeners(element);
  4114. });
  4115. // Remove the menu from DOM
  4116. this.menu.remove();
  4117. // Clear all sections
  4118. this.sections.forEach(section => {
  4119. if (section.element) {
  4120. removeAllListeners(section.element);
  4121. section.element.remove();
  4122. delete section.element;
  4123. }
  4124. section.options = [];
  4125. });
  4126. this.sections = [];
  4127. // Reset all class properties
  4128. this.isClientMenuVisible = false;
  4129. this.isDragging = false;
  4130. this.dragOffset = { x: 0, y: 0 };
  4131. this.activeCategory = "ALL";
  4132. // Clear references
  4133. this.menu = null;
  4134. this.kxsClient = null;
  4135. }
  4136. getMenuVisibility() {
  4137. return this.isClientMenuVisible;
  4138. }
  4139. }
  4140.  
  4141.  
  4142. ;// ./src/SERVER/Ping.ts
  4143. class PingTest {
  4144. constructor() {
  4145. this.ping = 0;
  4146. this.ws = null;
  4147. this.sendTime = 0;
  4148. this.retryCount = 0;
  4149. this.isConnecting = false;
  4150. this.isWebSocket = true;
  4151. this.url = "";
  4152. this.region = "";
  4153. this.hasPing = false;
  4154. this.reconnectTimer = null;
  4155. this.keepAliveTimer = null;
  4156. this.connectionCheckTimer = null;
  4157. this.ptcDataBuf = new ArrayBuffer(1);
  4158. this.waitForServerSelectElements();
  4159. this.startKeepAlive();
  4160. }
  4161. startKeepAlive() {
  4162. // Annuler l'ancien timer si existant
  4163. if (this.keepAliveTimer) {
  4164. clearInterval(this.keepAliveTimer);
  4165. }
  4166. this.keepAliveTimer = setInterval(() => {
  4167. var _a, _b, _c;
  4168. if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
  4169. this.ws.send(this.ptcDataBuf);
  4170. }
  4171. else if (((_b = this.ws) === null || _b === void 0 ? void 0 : _b.readyState) === WebSocket.CLOSED || ((_c = this.ws) === null || _c === void 0 ? void 0 : _c.readyState) === WebSocket.CLOSING) {
  4172. // Redémarrer la connexion si elle est fermée
  4173. this.restart();
  4174. }
  4175. }, 5000); // envoie toutes les 5s
  4176. }
  4177. waitForServerSelectElements() {
  4178. const checkInterval = setInterval(() => {
  4179. const teamSelect = document.getElementById("team-server-select");
  4180. const mainSelect = document.getElementById("server-select-main");
  4181. const selectedValue = (teamSelect === null || teamSelect === void 0 ? void 0 : teamSelect.value) || (mainSelect === null || mainSelect === void 0 ? void 0 : mainSelect.value);
  4182. if ((teamSelect || mainSelect) && selectedValue) {
  4183. clearInterval(checkInterval);
  4184. this.setServerFromDOM();
  4185. this.attachRegionChangeListener();
  4186. }
  4187. }, 100); // Vérifie toutes les 100ms
  4188. }
  4189. setServerFromDOM() {
  4190. const selectedServer = this.detectSelectedServer();
  4191. if (!selectedServer)
  4192. return;
  4193. const { region, url } = selectedServer;
  4194. this.region = region;
  4195. this.url = `wss://${url}/ptc`;
  4196. this.start();
  4197. }
  4198. detectSelectedServer() {
  4199. const currentUrl = window.location.href;
  4200. const isSpecialUrl = /\/#\w+/.test(currentUrl);
  4201. const teamSelectElement = document.getElementById("team-server-select");
  4202. const mainSelectElement = document.getElementById("server-select-main");
  4203. const region = isSpecialUrl && teamSelectElement
  4204. ? teamSelectElement.value
  4205. : (mainSelectElement === null || mainSelectElement === void 0 ? void 0 : mainSelectElement.value) || "NA";
  4206. const servers = [
  4207. { region: "NA", url: "usr.mathsiscoolfun.com:8001" },
  4208. { region: "EU", url: "eur.mathsiscoolfun.com:8001" },
  4209. { region: "Asia", url: "asr.mathsiscoolfun.com:8001" },
  4210. { region: "SA", url: "sa.mathsiscoolfun.com:8001" },
  4211. ];
  4212. const selectedServer = servers.find((s) => s.region.toUpperCase() === region.toUpperCase());
  4213. if (!selectedServer)
  4214. return undefined;
  4215. return selectedServer;
  4216. }
  4217. attachRegionChangeListener() {
  4218. const teamSelectElement = document.getElementById("team-server-select");
  4219. const mainSelectElement = document.getElementById("server-select-main");
  4220. const onChange = () => {
  4221. const selectedServer = this.detectSelectedServer();
  4222. if (!selectedServer)
  4223. return;
  4224. const { region } = selectedServer;
  4225. if (region !== this.region) {
  4226. this.restart();
  4227. }
  4228. };
  4229. teamSelectElement === null || teamSelectElement === void 0 ? void 0 : teamSelectElement.addEventListener("change", onChange);
  4230. mainSelectElement === null || mainSelectElement === void 0 ? void 0 : mainSelectElement.addEventListener("change", onChange);
  4231. }
  4232. start() {
  4233. if (this.isConnecting)
  4234. return;
  4235. this.isConnecting = true;
  4236. this.startWebSocketPing();
  4237. // Vérifier régulièrement l'état de la connexion
  4238. this.startConnectionCheck();
  4239. }
  4240. startConnectionCheck() {
  4241. // Annuler l'ancien timer si existant
  4242. if (this.connectionCheckTimer) {
  4243. clearInterval(this.connectionCheckTimer);
  4244. }
  4245. // Vérifier l'état de la connexion toutes les 10 secondes
  4246. this.connectionCheckTimer = setInterval(() => {
  4247. // Si on n'a pas de ping valide ou que la connexion est fermée, on tente de reconnecter
  4248. if (!this.hasPing || !this.ws || this.ws.readyState !== WebSocket.OPEN) {
  4249. this.restart();
  4250. }
  4251. }, 10000);
  4252. }
  4253. startWebSocketPing() {
  4254. if (this.ws || !this.url)
  4255. return;
  4256. const ws = new WebSocket(this.url);
  4257. ws.binaryType = "arraybuffer";
  4258. ws.onopen = () => {
  4259. this.ws = ws;
  4260. this.retryCount = 0;
  4261. this.isConnecting = false;
  4262. this.sendPing();
  4263. setTimeout(() => {
  4264. var _a;
  4265. if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) !== WebSocket.OPEN) {
  4266. this.restart();
  4267. }
  4268. }, 3000); // 3s pour sécuriser
  4269. };
  4270. ws.onmessage = () => {
  4271. this.hasPing = true;
  4272. const elapsed = (Date.now() - this.sendTime) / 1e3;
  4273. this.ping = Math.round(elapsed * 1000);
  4274. setTimeout(() => this.sendPing(), 1000);
  4275. };
  4276. ws.onerror = (error) => {
  4277. this.ping = 0;
  4278. this.hasPing = false;
  4279. this.retryCount++;
  4280. // Tentative immédiate mais avec backoff exponentiel
  4281. const retryDelay = Math.min(1000 * Math.pow(2, this.retryCount - 1), 10000);
  4282. // Annuler tout timer de reconnexion existant
  4283. if (this.reconnectTimer) {
  4284. clearTimeout(this.reconnectTimer);
  4285. }
  4286. this.reconnectTimer = setTimeout(() => {
  4287. this.ws = null; // S'assurer que l'ancienne connexion est effacée
  4288. this.startWebSocketPing();
  4289. }, retryDelay);
  4290. };
  4291. ws.onclose = (event) => {
  4292. this.hasPing = false;
  4293. this.ws = null;
  4294. this.isConnecting = false;
  4295. // Tentative de reconnexion après une fermeture
  4296. if (this.reconnectTimer) {
  4297. clearTimeout(this.reconnectTimer);
  4298. }
  4299. this.reconnectTimer = setTimeout(() => {
  4300. this.start();
  4301. }, 2000); // Attendre 2 secondes avant de reconnecter
  4302. };
  4303. }
  4304. sendPing() {
  4305. var _a, _b;
  4306. if (this.ws && this.ws.readyState === WebSocket.OPEN) {
  4307. this.sendTime = Date.now();
  4308. this.ws.send(this.ptcDataBuf);
  4309. }
  4310. else if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.CLOSED || ((_b = this.ws) === null || _b === void 0 ? void 0 : _b.readyState) === WebSocket.CLOSING) {
  4311. // Si la WebSocket est fermée au moment d'envoyer le ping, on tente de reconnecter
  4312. this.restart();
  4313. }
  4314. }
  4315. stop() {
  4316. // Annuler tous les timers
  4317. if (this.reconnectTimer) {
  4318. clearTimeout(this.reconnectTimer);
  4319. this.reconnectTimer = null;
  4320. }
  4321. if (this.keepAliveTimer) {
  4322. clearInterval(this.keepAliveTimer);
  4323. this.keepAliveTimer = null;
  4324. }
  4325. if (this.connectionCheckTimer) {
  4326. clearInterval(this.connectionCheckTimer);
  4327. this.connectionCheckTimer = null;
  4328. }
  4329. if (this.ws) {
  4330. this.ws.onclose = null;
  4331. this.ws.onerror = null;
  4332. this.ws.onmessage = null;
  4333. this.ws.onopen = null;
  4334. this.ws.close();
  4335. this.ws = null;
  4336. }
  4337. this.isConnecting = false;
  4338. this.retryCount = 0;
  4339. this.hasPing = false;
  4340. }
  4341. restart() {
  4342. this.stop();
  4343. setTimeout(() => {
  4344. this.setServerFromDOM();
  4345. }, 500); // Petit délai pour éviter les problèmes de rebond
  4346. }
  4347. /**
  4348. * Retourne le ping actuel. Ne touche jamais à la websocket ici !
  4349. * Si le ping n'est pas dispo, retourne -1 (jamais null).
  4350. * La reconnexion doit être gérée ailleurs (timer, event, etc).
  4351. */
  4352. getPingResult() {
  4353. if (this.ws && this.ws.readyState === WebSocket.OPEN && this.hasPing) {
  4354. return {
  4355. region: this.region,
  4356. ping: this.ping,
  4357. };
  4358. }
  4359. else {
  4360. // Si on détecte un problème ici, planifier une reconnexion
  4361. if (!this.reconnectTimer && (!this.ws || this.ws.readyState !== WebSocket.CONNECTING)) {
  4362. this.reconnectTimer = setTimeout(() => this.restart(), 1000);
  4363. }
  4364. return {
  4365. region: this.region,
  4366. ping: -1, // -1 indique que le ping n'est pas dispo, mais jamais null
  4367. };
  4368. }
  4369. }
  4370. }
  4371.  
  4372.  
  4373. ;// ./src/HUD/ClientHUD.ts
  4374.  
  4375. class KxsClientHUD {
  4376. constructor(kxsClient) {
  4377. this.healthAnimations = [];
  4378. this.lastHealthValue = 100;
  4379. this.hudOpacityObservers = [];
  4380. this.weaponBorderObservers = [];
  4381. this.ctrlFocusTimer = null;
  4382. this.killFeedObserver = null;
  4383. this.kxsClient = kxsClient;
  4384. this.frameCount = 0;
  4385. this.fps = 0;
  4386. this.kills = 0;
  4387. this.isMenuVisible = true;
  4388. this.pingManager = new PingTest();
  4389. this.allDivToHide = [
  4390. '#ui-medical-interactive > div',
  4391. '#ui-ammo-interactive > div',
  4392. '#ui-weapon-container .ui-weapon-switch',
  4393. '#ui-killfeed',
  4394. '#ui-killfeed-contents',
  4395. '.killfeed-div',
  4396. '.killfeed-text',
  4397. '#ui-kill-leader-container',
  4398. '#ui-kill-leader-wrapper',
  4399. '#ui-kill-leader-name',
  4400. '#ui-kill-leader-icon',
  4401. '#ui-kill-leader-count',
  4402. '#ui-leaderboard-wrapper',
  4403. '#ui-leaderboard',
  4404. '#ui-leaderboard-alive',
  4405. '#ui-leaderboard-alive-faction',
  4406. '.ui-leaderboard-header',
  4407. '#ui-kill-counter-wrapper',
  4408. '#ui-kill-counter',
  4409. '.ui-player-kills',
  4410. '.ui-kill-counter-header',
  4411. '#ui-bottom-center-right',
  4412. '#ui-armor-helmet',
  4413. '#ui-armor-chest',
  4414. '#ui-armor-backpack',
  4415. '.ui-armor-counter',
  4416. '.ui-armor-counter-inner',
  4417. '.ui-armor-level',
  4418. '.ui-armor-image',
  4419. '.ui-loot-image',
  4420. ];
  4421. if (this.kxsClient.isPingVisible) {
  4422. this.initCounter("ping", "Ping", "45ms");
  4423. }
  4424. if (this.kxsClient.isFpsVisible) {
  4425. this.initCounter("fps", "FPS", "60");
  4426. }
  4427. if (this.kxsClient.isKillsVisible) {
  4428. this.initCounter("kills", "Kills", "0");
  4429. }
  4430. if (this.kxsClient.isGunOverlayColored) {
  4431. this.toggleWeaponBorderHandler();
  4432. }
  4433. this.updateCountersDraggableState();
  4434. this.startUpdateLoop();
  4435. this.escapeMenu();
  4436. this.initFriendDetector();
  4437. if (this.kxsClient.isKillFeedBlint) {
  4438. if (document.readyState === 'loading') {
  4439. document.addEventListener('DOMContentLoaded', this.initKillFeed);
  4440. }
  4441. else {
  4442. this.initKillFeed();
  4443. }
  4444. }
  4445. if (this.kxsClient.customCrosshair !== null) {
  4446. this.loadCustomCrosshair();
  4447. }
  4448. this.setupCtrlFocusModeListener();
  4449. window.addEventListener('load', () => {
  4450. this.updateCounterCorners();
  4451. });
  4452. window.addEventListener('resize', () => {
  4453. this.updateCounterCorners();
  4454. });
  4455. }
  4456. setupCtrlFocusModeListener() {
  4457. // Déterminer la plateforme une seule fois à l'initialisation
  4458. const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
  4459. // Utiliser un flag pour suivre l'état des touches
  4460. let modifierKeyPressed = false;
  4461. document.addEventListener('keydown', (e) => {
  4462. // Détecter si la touche modificatrice est pressée (Command sur macOS, Ctrl sur Windows/Linux)
  4463. if ((isMac && e.key === 'Meta') || (!isMac && e.key === 'Control')) {
  4464. modifierKeyPressed = true;
  4465. }
  4466. // Activer le mode focus seulement si F est pressé pendant que la touche modificatrice est déjà enfoncée
  4467. if (modifierKeyPressed && e.code === 'KeyF') {
  4468. e.preventDefault(); // Empêcher le comportement par défaut (recherche)
  4469. this.kxsClient.isFocusModeEnabled = !this.kxsClient.isFocusModeEnabled;
  4470. this.kxsClient.hud.toggleFocusMode();
  4471. this.kxsClient.nm.showNotification("Focus mode toggled", "info", 1200);
  4472. }
  4473. });
  4474. // Réinitialiser le flag quand la touche modificatrice est relâchée
  4475. document.addEventListener('keyup', (e) => {
  4476. if ((isMac && e.key === 'Meta') || (!isMac && e.key === 'Control')) {
  4477. modifierKeyPressed = false;
  4478. }
  4479. });
  4480. }
  4481. initFriendDetector() {
  4482. // Initialize friends list
  4483. let all_friends = this.kxsClient.all_friends.split(',') || [];
  4484. if (all_friends.length >= 1) {
  4485. // Create a cache for detected friends
  4486. // Structure will be: { "friendName": timestamp }
  4487. const friendsCache = {};
  4488. // Cache duration in milliseconds (4 minutes = 240000 ms)
  4489. const cacheDuration = 4 * 60 * 1000;
  4490. // Select the element containing kill feeds
  4491. const killfeedContents = document.querySelector('#ui-killfeed-contents');
  4492. if (killfeedContents) {
  4493. // Keep track of last seen content for each div
  4494. const lastSeenContent = {
  4495. "ui-killfeed-0": "",
  4496. "ui-killfeed-1": "",
  4497. "ui-killfeed-2": "",
  4498. "ui-killfeed-3": "",
  4499. "ui-killfeed-4": "",
  4500. "ui-killfeed-5": ""
  4501. };
  4502. // Function to check if a friend is in the text with cache management
  4503. const checkForFriends = (text, divId) => {
  4504. // If the text is identical to the last seen, ignore
  4505. // @ts-ignore
  4506. if (text === lastSeenContent[divId])
  4507. return;
  4508. // Update the last seen content
  4509. // @ts-ignore
  4510. lastSeenContent[divId] = text;
  4511. // Ignore empty messages
  4512. if (!text.trim())
  4513. return;
  4514. // Current timestamp
  4515. const currentTime = Date.now();
  4516. // Check if a friend is mentioned
  4517. for (let friend of all_friends) {
  4518. if (friend !== "" && text.includes(friend)) {
  4519. // Check if the friend is in the cache and if the cache is still valid
  4520. // @ts-ignore
  4521. const lastSeen = friendsCache[friend];
  4522. if (!lastSeen || (currentTime - lastSeen > cacheDuration)) {
  4523. // Update the cache
  4524. // @ts-ignore
  4525. friendsCache[friend] = currentTime;
  4526. // Display notification
  4527. this.kxsClient.nm.showNotification(`[FriendDetector] ${friend} is in this game`, "info", 2300);
  4528. }
  4529. break;
  4530. }
  4531. }
  4532. };
  4533. // Function to check all kill feeds
  4534. const checkAllKillfeeds = () => {
  4535. all_friends = this.kxsClient.all_friends.split(',') || [];
  4536. for (let i = 0; i <= 5; i++) {
  4537. const divId = `ui-killfeed-${i}`;
  4538. const killDiv = document.getElementById(divId);
  4539. if (killDiv) {
  4540. const textElement = killDiv.querySelector('.killfeed-text');
  4541. if (textElement && textElement.textContent) {
  4542. checkForFriends(textElement.textContent, divId);
  4543. }
  4544. }
  4545. }
  4546. };
  4547. // Observe style or text changes in the entire container
  4548. const observer = new MutationObserver(() => {
  4549. checkAllKillfeeds();
  4550. });
  4551. // Start observing with a configuration that detects all changes
  4552. observer.observe(killfeedContents, {
  4553. childList: true, // Observe changes to child elements
  4554. subtree: true, // Observe the entire tree
  4555. characterData: true, // Observe text changes
  4556. attributes: true // Observe attribute changes (like style/opacity)
  4557. });
  4558. // Check current content immediately
  4559. checkAllKillfeeds();
  4560. }
  4561. else {
  4562. this.kxsClient.logger.error("Killfeed-contents element not found");
  4563. }
  4564. }
  4565. }
  4566. initKillFeed() {
  4567. this.applyCustomStyles();
  4568. this.setupObserver();
  4569. }
  4570. toggleKillFeed() {
  4571. if (this.kxsClient.isKillFeedBlint) {
  4572. this.initKillFeed(); // <-- injecte le CSS custom et observer
  4573. }
  4574. else {
  4575. this.resetKillFeed(); // <-- supprime styles et contenu
  4576. }
  4577. }
  4578. /**
  4579. * Réinitialise le Kill Feed à l'état par défaut (vide)
  4580. */
  4581. /**
  4582. * Supprime tous les styles custom KillFeed injectés par applyCustomStyles
  4583. */
  4584. resetKillFeedStyles() {
  4585. // Supprime tous les <style> contenant .killfeed-div ou .killfeed-text
  4586. const styles = Array.from(document.head.querySelectorAll('style'));
  4587. styles.forEach(style => {
  4588. if (style.textContent &&
  4589. (style.textContent.includes('.killfeed-div') || style.textContent.includes('.killfeed-text'))) {
  4590. style.remove();
  4591. }
  4592. });
  4593. }
  4594. observeHudOpacity(opacity) {
  4595. // Nettoie d'abord les observers existants
  4596. this.hudOpacityObservers.forEach(obs => obs.disconnect());
  4597. this.hudOpacityObservers = [];
  4598. this.allDivToHide.forEach(sel => {
  4599. const elements = document.querySelectorAll(sel);
  4600. elements.forEach(el => {
  4601. el.style.opacity = String(opacity);
  4602. // Applique aussi l'opacité à tous les descendants
  4603. const descendants = el.querySelectorAll('*');
  4604. descendants.forEach(child => {
  4605. child.style.opacity = String(opacity);
  4606. });
  4607. // Observer pour le parent
  4608. const observer = new MutationObserver(mutations => {
  4609. mutations.forEach(mutation => {
  4610. if (mutation.type === "attributes" &&
  4611. mutation.attributeName === "style") {
  4612. const currentOpacity = el.style.opacity;
  4613. if (currentOpacity !== String(opacity)) {
  4614. el.style.opacity = String(opacity);
  4615. }
  4616. // Vérifie aussi les enfants
  4617. const descendants = el.querySelectorAll('*');
  4618. descendants.forEach(child => {
  4619. if (child.style.opacity !== String(opacity)) {
  4620. child.style.opacity = String(opacity);
  4621. }
  4622. });
  4623. }
  4624. });
  4625. });
  4626. observer.observe(el, { attributes: true, attributeFilter: ["style"] });
  4627. this.hudOpacityObservers.push(observer);
  4628. // Observer pour chaque enfant (optionnel mais robuste)
  4629. descendants.forEach(child => {
  4630. const childObserver = new MutationObserver(mutations => {
  4631. mutations.forEach(mutation => {
  4632. if (mutation.type === "attributes" &&
  4633. mutation.attributeName === "style") {
  4634. if (child.style.opacity !== String(opacity)) {
  4635. child.style.opacity = String(opacity);
  4636. }
  4637. }
  4638. });
  4639. });
  4640. childObserver.observe(child, { attributes: true, attributeFilter: ["style"] });
  4641. this.hudOpacityObservers.push(childObserver);
  4642. });
  4643. });
  4644. });
  4645. }
  4646. toggleFocusMode() {
  4647. if (this.kxsClient.isFocusModeEnabled) {
  4648. this.observeHudOpacity(0.05);
  4649. }
  4650. else {
  4651. // 1. Stoppe tous les observers
  4652. this.hudOpacityObservers.forEach(obs => obs.disconnect());
  4653. this.hudOpacityObservers = [];
  4654. this.allDivToHide.forEach(sel => {
  4655. const elements = document.querySelectorAll(sel);
  4656. elements.forEach(el => {
  4657. el.style.removeProperty('opacity');
  4658. // Supprime aussi sur tous les enfants
  4659. const descendants = el.querySelectorAll('*');
  4660. descendants.forEach(child => {
  4661. child.style.removeProperty('opacity');
  4662. });
  4663. });
  4664. });
  4665. }
  4666. }
  4667. resetKillFeed() {
  4668. // Supprime les styles custom KillFeed
  4669. this.resetKillFeedStyles();
  4670. // Sélectionne le container du killfeed
  4671. const killfeedContents = document.getElementById('ui-killfeed-contents');
  4672. if (killfeedContents) {
  4673. // Vide tous les killfeed-div et killfeed-text
  4674. killfeedContents.querySelectorAll('.killfeed-div').forEach(div => {
  4675. const text = div.querySelector('.killfeed-text');
  4676. if (text)
  4677. text.textContent = '';
  4678. div.style.opacity = '0';
  4679. });
  4680. }
  4681. }
  4682. loadCustomCrosshair() {
  4683. const url = this.kxsClient.customCrosshair;
  4684. // Supprime l'ancienne règle si elle existe
  4685. const styleId = 'kxs-custom-cursor-style';
  4686. const oldStyle = document.getElementById(styleId);
  4687. if (oldStyle)
  4688. oldStyle.remove();
  4689. // Débranche l'ancien observer s'il existe
  4690. if (this.customCursorObserver) {
  4691. this.customCursorObserver.disconnect();
  4692. this.customCursorObserver = undefined;
  4693. }
  4694. // Réinitialise le curseur si pas d'URL
  4695. if (!url) {
  4696. // Supprime l'image animée si présente
  4697. if (this.animatedCursorImg) {
  4698. this.animatedCursorImg.remove();
  4699. this.animatedCursorImg = undefined;
  4700. }
  4701. // Supprime le style CSS qui cache le curseur natif
  4702. const hideCursorStyle = document.getElementById('kxs-hide-cursor-style');
  4703. if (hideCursorStyle)
  4704. hideCursorStyle.remove();
  4705. // Supprime le style CSS du curseur personnalisé
  4706. const customCursorStyle = document.getElementById('kxs-custom-cursor-style');
  4707. if (customCursorStyle)
  4708. customCursorStyle.remove();
  4709. // Retire l'eventListener mousemove si défini
  4710. if (this._mousemoveHandler) {
  4711. document.removeEventListener('mousemove', this._mousemoveHandler);
  4712. this._mousemoveHandler = undefined;
  4713. }
  4714. document.body.style.cursor = '';
  4715. return;
  4716. }
  4717. // Curseur animé JS : gestion d'un GIF
  4718. const isGif = url.split('?')[0].toLowerCase().endsWith('.gif');
  4719. // Nettoyage si on repasse sur un non-GIF
  4720. if (this.animatedCursorImg) {
  4721. this.animatedCursorImg.remove();
  4722. this.animatedCursorImg = undefined;
  4723. }
  4724. if (this._mousemoveHandler) {
  4725. document.removeEventListener('mousemove', this._mousemoveHandler);
  4726. this._mousemoveHandler = undefined;
  4727. }
  4728. if (isGif) {
  4729. // Ajoute une règle CSS globale pour cacher le curseur natif partout
  4730. let hideCursorStyle = document.getElementById('kxs-hide-cursor-style');
  4731. if (!hideCursorStyle) {
  4732. hideCursorStyle = document.createElement('style');
  4733. hideCursorStyle.id = 'kxs-hide-cursor-style';
  4734. hideCursorStyle.innerHTML = `
  4735. * { cursor: none !important; }
  4736. *:hover { cursor: none !important; }
  4737. *:active { cursor: none !important; }
  4738. *:focus { cursor: none !important; }
  4739. input, textarea { cursor: none !important; }
  4740. a, button, [role="button"], [onclick] { cursor: none !important; }
  4741. [draggable="true"] { cursor: none !important; }
  4742. [style*="cursor: pointer"] { cursor: none !important; }
  4743. [style*="cursor: text"] { cursor: none !important; }
  4744. [style*="cursor: move"] { cursor: none !important; }
  4745. [style*="cursor: crosshair"] { cursor: none !important; }
  4746. [style*="cursor: ew-resize"] { cursor: none !important; }
  4747. [style*="cursor: ns-resize"] { cursor: none !important; }
  4748. `;
  4749. document.head.appendChild(hideCursorStyle);
  4750. }
  4751. const animatedImg = document.createElement('img');
  4752. animatedImg.src = url;
  4753. animatedImg.style.position = 'fixed';
  4754. animatedImg.style.pointerEvents = 'none';
  4755. animatedImg.style.zIndex = '99999';
  4756. animatedImg.style.width = '38px';
  4757. animatedImg.style.height = '38px';
  4758. animatedImg.style.left = '0px';
  4759. animatedImg.style.top = '0px';
  4760. this.animatedCursorImg = animatedImg;
  4761. document.body.appendChild(animatedImg);
  4762. this._mousemoveHandler = (e) => {
  4763. if (this.animatedCursorImg) {
  4764. this.animatedCursorImg.style.left = `${e.clientX}px`;
  4765. this.animatedCursorImg.style.top = `${e.clientY}px`;
  4766. }
  4767. };
  4768. document.addEventListener('mousemove', this._mousemoveHandler);
  4769. return;
  4770. }
  4771. // Nettoie la règle cursor:none si on repasse sur un curseur natif
  4772. const hideCursorStyle = document.getElementById('kxs-hide-cursor-style');
  4773. if (hideCursorStyle)
  4774. hideCursorStyle.remove();
  4775. // Sinon, méthode classique : précharge l'image, puis applique le curseur natif
  4776. const img = new window.Image();
  4777. img.onload = () => {
  4778. const style = document.createElement('style');
  4779. style.id = styleId;
  4780. style.innerHTML = `
  4781. * { cursor: url('${url}'), auto !important; }
  4782. *:hover { cursor: url('${url}'), pointer !important; }
  4783. *:active { cursor: url('${url}'), pointer !important; }
  4784. *:focus { cursor: url('${url}'), text !important; }
  4785. input, textarea { cursor: url('${url}'), text !important; }
  4786. a, button, [role="button"], [onclick] { cursor: url('${url}'), pointer !important; }
  4787. [draggable="true"] { cursor: url('${url}'), move !important; }
  4788. [style*="cursor: pointer"] { cursor: url('${url}'), pointer !important; }
  4789. [style*="cursor: text"] { cursor: url('${url}'), text !important; }
  4790. [style*="cursor: move"] { cursor: url('${url}'), move !important; }
  4791. [style*="cursor: crosshair"] { cursor: url('${url}'), crosshair !important; }
  4792. [style*="cursor: ew-resize"] { cursor: url('${url}'), ew-resize !important; }
  4793. [style*="cursor: ns-resize"] { cursor: url('${url}'), ns-resize !important; }
  4794. `;
  4795. document.head.appendChild(style);
  4796. };
  4797. img.onerror = () => {
  4798. document.body.style.cursor = '';
  4799. this.kxsClient.logger.warn('Impossible de charger le curseur personnalisé:', url);
  4800. };
  4801. img.src = url;
  4802. // --- MutationObserver pour forcer le curseur même si le jeu le réécrit ---
  4803. this.customCursorObserver = new MutationObserver((mutations) => {
  4804. for (const mutation of mutations) {
  4805. if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
  4806. const node = mutation.target;
  4807. if (node.style && node.style.cursor && !node.style.cursor.includes(url)) {
  4808. node.style.cursor = `url('${url}'), auto`;
  4809. }
  4810. }
  4811. }
  4812. });
  4813. // Observe tous les changements de style sur tout le body et sur #game-touch-area
  4814. const gameTouchArea = document.getElementById('game-touch-area');
  4815. if (gameTouchArea) {
  4816. this.customCursorObserver.observe(gameTouchArea, {
  4817. attributes: true,
  4818. attributeFilter: ['style'],
  4819. subtree: true
  4820. });
  4821. }
  4822. this.customCursorObserver.observe(document.body, {
  4823. attributes: true,
  4824. attributeFilter: ['style'],
  4825. subtree: true
  4826. });
  4827. }
  4828. escapeMenu() {
  4829. const customStylesMobile = `
  4830. .ui-game-menu-desktop {
  4831. background: linear-gradient(135deg, rgba(25, 25, 35, 0.95) 0%, rgba(15, 15, 25, 0.98) 100%) !important;
  4832. border: 1px solid rgba(255, 255, 255, 0.1) !important;
  4833. border-radius: 4px !important;
  4834. box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15) !important;
  4835. padding: 2px 2px !important;
  4836. max-width: 45vw !important;
  4837. width: 45vw !important;
  4838. max-height: 28vh !important;
  4839. min-width: unset !important;
  4840. min-height: unset !important;
  4841. font-size: 9px !important;
  4842. margin: 0 auto !important;
  4843. box-sizing: border-box !important;
  4844. overflow-y: auto !important;
  4845. }
  4846. .ui-game-menu-desktop button, .ui-game-menu-desktop .btn, .ui-game-menu-desktop input, .ui-game-menu-desktop select {
  4847. font-size: 9px !important;
  4848. padding: 2px 3px !important;
  4849. margin: 1px 0 !important;
  4850. border-radius: 3px !important;
  4851. }
  4852. .ui-game-menu-desktop .kxs-header, .ui-game-menu-desktop h1, .ui-game-menu-desktop h2, .ui-game-menu-desktop h3, .ui-game-menu-desktop label, .ui-game-menu-desktop span {
  4853. font-size: 9px !important;
  4854. }
  4855. .ui-game-menu-desktop img, .ui-game-menu-desktop svg {
  4856. width: 10px !important;
  4857. height: 10px !important;
  4858. }
  4859. .ui-game-menu-desktop .mode-btn {
  4860. min-height: 12px !important;
  4861. font-size: 8px !important;
  4862. padding: 2px 3px !important;
  4863. }
  4864. /* Style pour les boutons de mode de jeu qui ont une image de fond */
  4865. .btn-mode-cobalt,
  4866. [style*="background: url("] {
  4867. background-repeat: no-repeat !important;
  4868. background-position: right center !important;
  4869. background-size: auto 70% !important;
  4870. position: relative !important;
  4871. padding-right: 8px !important;
  4872. }
  4873. #btn-start-mode-0 {
  4874. background-repeat: initial !important;
  4875. background-position: initial !important;
  4876. background-size: initial !important;
  4877. padding-right: initial !important;
  4878. }
  4879. `;
  4880. const customStylesDesktop = `
  4881. .ui-game-menu-desktop {
  4882. background: linear-gradient(135deg, rgba(25, 25, 35, 0.95) 0%, rgba(15, 15, 25, 0.98) 100%) !important;
  4883. border: 1px solid rgba(255, 255, 255, 0.1) !important;
  4884. border-radius: 12px !important;
  4885. box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important;
  4886. padding: 20px !important;
  4887. backdrop-filter: blur(10px) !important;
  4888. max-width: 350px !important;
  4889. /* max-height: 80vh !important; */ /* Optional: Limit the maximum height */
  4890. margin: auto !important;
  4891. box-sizing: border-box !important;
  4892. overflow-y: auto !important; /* Allow vertical scrolling if necessary */
  4893. }
  4894.  
  4895. /* Style pour les boutons de mode de jeu qui ont une image de fond */
  4896. .btn-mode-cobalt,
  4897. [style*="background: url("] {
  4898. background-repeat: no-repeat !important;
  4899. background-position: right center !important;
  4900. background-size: auto 80% !important;
  4901. position: relative !important;
  4902. padding-right: 40px !important;
  4903. }
  4904.  
  4905. /* Ne pas appliquer ce style aux boutons standards comme Play Solo */
  4906. #btn-start-mode-0 {
  4907. background-repeat: initial !important;
  4908. background-position: initial !important;
  4909. background-size: initial !important;
  4910. padding-right: initial !important;
  4911. }
  4912.  
  4913. .ui-game-menu-desktop::-webkit-scrollbar {
  4914. width: 8px !important;
  4915. }
  4916. .ui-game-menu-desktop::-webkit-scrollbar-track {
  4917. background: rgba(25, 25, 35, 0.5) !important;
  4918. border-radius: 10px !important;
  4919. }
  4920. .ui-game-menu-desktop::-webkit-scrollbar-thumb {
  4921. background-color: #4287f5 !important;
  4922. border-radius: 10px !important;
  4923. border: 2px solid rgba(25, 25, 35, 0.5) !important;
  4924. }
  4925. .ui-game-menu-desktop::-webkit-scrollbar-thumb:hover {
  4926. background-color: #5a9eff !important;
  4927. }
  4928.  
  4929. .ui-game-menu-desktop {
  4930. scrollbar-width: thin !important;
  4931. scrollbar-color: #4287f5 rgba(25, 25, 35, 0.5) !important;
  4932. }
  4933.  
  4934. .kxs-header {
  4935. display: flex;
  4936. align-items: center;
  4937. justify-content: flex-start;
  4938. margin-bottom: 20px;
  4939. padding: 10px;
  4940. border-bottom: 2px solid rgba(255, 255, 255, 0.1);
  4941. }
  4942.  
  4943. .kxs-logo {
  4944. width: 30px;
  4945. height: 30px;
  4946. margin-right: 10px;
  4947. border-radius: 6px;
  4948. }
  4949.  
  4950. .kxs-title {
  4951. font-size: 20px;
  4952. font-weight: 700;
  4953. color: #ffffff;
  4954. text-transform: uppercase;
  4955. text-shadow: 0 0 10px rgba(66, 135, 245, 0.5);
  4956. font-family: 'Arial', sans-serif;
  4957. letter-spacing: 2px;
  4958. }
  4959.  
  4960. .kxs-title span {
  4961. color: #4287f5;
  4962. }
  4963.  
  4964.  
  4965. .btn-game-menu {
  4966. background: linear-gradient(135deg, rgba(66, 135, 245, 0.1) 0%, rgba(66, 135, 245, 0.2) 100%) !important;
  4967. border: 1px solid rgba(66, 135, 245, 0.3) !important;
  4968. border-radius: 8px !important;
  4969. color: #ffffff !important;
  4970. transition: all 0.3s ease !important;
  4971. margin: 5px 0 !important;
  4972. padding: 12px !important;
  4973. font-weight: 600 !important;
  4974. width: 100% !important;
  4975. text-align: center !important;
  4976. display: block !important;
  4977. box-sizing: border-box !important;
  4978. line-height: 15px !important;
  4979. }
  4980.  
  4981. .btn-game-menu:hover {
  4982. background: linear-gradient(135deg, rgba(66, 135, 245, 0.2) 0%, rgba(66, 135, 245, 0.3) 100%) !important;
  4983. transform: translateY(-2px) !important;
  4984. box-shadow: 0 4px 12px rgba(66, 135, 245, 0.2) !important;
  4985. }
  4986.  
  4987. .slider-container {
  4988. background: rgba(66, 135, 245, 0.1) !important;
  4989. border-radius: 8px !important;
  4990. padding: 10px 15px !important;
  4991. margin: 10px 0 !important;
  4992. width: 100% !important;
  4993. box-sizing: border-box !important;
  4994. }
  4995.  
  4996. .slider-text {
  4997. color: #ffffff !important;
  4998. font-size: 14px !important;
  4999. margin-bottom: 8px !important;
  5000. text-align: center !important;
  5001. }
  5002.  
  5003. .slider {
  5004. -webkit-appearance: none !important;
  5005. width: 100% !important;
  5006. height: 6px !important;
  5007. border-radius: 3px !important;
  5008. background: rgba(66, 135, 245, 0.3) !important;
  5009. outline: none !important;
  5010. margin: 10px 0 !important;
  5011. }
  5012.  
  5013. .slider::-webkit-slider-thumb {
  5014. -webkit-appearance: none !important;
  5015. width: 16px !important;
  5016. height: 16px !important;
  5017. border-radius: 50% !important;
  5018. background: #4287f5 !important;
  5019. cursor: pointer !important;
  5020. transition: all 0.3s ease !important;
  5021. }
  5022.  
  5023. .slider::-webkit-slider-thumb:hover {
  5024. transform: scale(1.2) !important;
  5025. box-shadow: 0 0 10px rgba(66, 135, 245, 0.5) !important;
  5026. }
  5027.  
  5028. .btns-game-double-row {
  5029. display: flex !important;
  5030. justify-content: center !important;
  5031. gap: 10px !important;
  5032. margin-bottom: 10px !important;
  5033. width: 100% !important;
  5034. }
  5035.  
  5036. .btn-game-container {
  5037. flex: 1 !important;
  5038. }
  5039.  
  5040. #btn-touch-styles,
  5041. #btn-game-aim-line {
  5042. display: none !important;
  5043. pointer-events: none !important;
  5044. visibility: hidden !important;
  5045. }
  5046. `;
  5047. const addCustomStyles = () => {
  5048. const styleElement = document.createElement('style');
  5049. styleElement.textContent = this.kxsClient.isMobile() ? customStylesMobile : customStylesDesktop;
  5050. document.head.appendChild(styleElement);
  5051. };
  5052. const addKxsHeader = () => {
  5053. const menuContainer = document.querySelector('#ui-game-menu');
  5054. if (!menuContainer)
  5055. return;
  5056. const header = document.createElement('div');
  5057. header.className = 'kxs-header';
  5058. const title = document.createElement('span');
  5059. title.className = 'kxs-title';
  5060. title.innerHTML = '<span>Kxs</span> CLIENT';
  5061. header.appendChild(title);
  5062. menuContainer.insertBefore(header, menuContainer.firstChild);
  5063. };
  5064. const disableUnwantedButtons = () => {
  5065. const touchStyles = document.getElementById('btn-touch-styles');
  5066. const aimLine = document.getElementById('btn-game-aim-line');
  5067. if (touchStyles) {
  5068. touchStyles.style.display = 'none';
  5069. touchStyles.style.pointerEvents = 'none';
  5070. touchStyles.style.visibility = 'hidden';
  5071. }
  5072. if (aimLine) {
  5073. aimLine.style.display = 'none';
  5074. aimLine.style.pointerEvents = 'none';
  5075. aimLine.style.visibility = 'hidden';
  5076. }
  5077. };
  5078. if (document.querySelector('#ui-game-menu')) {
  5079. addCustomStyles();
  5080. addKxsHeader();
  5081. if (!this.kxsClient.isMobile()) {
  5082. disableUnwantedButtons();
  5083. }
  5084. // Désactiver uniquement le slider Music Volume
  5085. const sliders = document.querySelectorAll('.slider-container.ui-slider-container');
  5086. sliders.forEach(slider => {
  5087. const label = slider.querySelector('p.slider-text[data-l10n="index-music-volume"]');
  5088. if (label) {
  5089. slider.style.display = 'none';
  5090. }
  5091. });
  5092. // Ajout du bouton Toggle Right Shift Menu
  5093. const menuContainer = document.querySelector('#ui-game-menu');
  5094. if (menuContainer) {
  5095. const toggleRightShiftBtn = document.createElement('button');
  5096. toggleRightShiftBtn.textContent = 'Toggle Right Shift Menu';
  5097. toggleRightShiftBtn.className = 'btn-game-menu';
  5098. toggleRightShiftBtn.style.marginTop = '10px';
  5099. toggleRightShiftBtn.onclick = () => {
  5100. if (this.kxsClient.secondaryMenu && typeof this.kxsClient.secondaryMenu.toggleMenuVisibility === 'function') {
  5101. this.kxsClient.secondaryMenu.toggleMenuVisibility();
  5102. }
  5103. };
  5104. menuContainer.appendChild(toggleRightShiftBtn);
  5105. }
  5106. }
  5107. }
  5108. handleMessage(element) {
  5109. if (element instanceof HTMLElement && element.classList.contains('killfeed-div')) {
  5110. const killfeedText = element.querySelector('.killfeed-text');
  5111. if (killfeedText instanceof HTMLElement) {
  5112. if (killfeedText.textContent && killfeedText.textContent.trim() !== '') {
  5113. if (!killfeedText.hasAttribute('data-glint')) {
  5114. killfeedText.setAttribute('data-glint', 'true');
  5115. element.style.opacity = '1';
  5116. setTimeout(() => {
  5117. element.style.opacity = '0';
  5118. }, 5000);
  5119. }
  5120. }
  5121. else {
  5122. element.style.opacity = '0';
  5123. }
  5124. }
  5125. }
  5126. }
  5127. setupObserver() {
  5128. const killfeedContents = document.getElementById('ui-killfeed-contents');
  5129. if (killfeedContents) {
  5130. // Détruit l'ancien observer s'il existe
  5131. if (this.killFeedObserver) {
  5132. this.killFeedObserver.disconnect();
  5133. }
  5134. this.killFeedObserver = new MutationObserver((mutations) => {
  5135. mutations.forEach((mutation) => {
  5136. if (mutation.target instanceof HTMLElement &&
  5137. mutation.target.classList.contains('killfeed-text')) {
  5138. const parentDiv = mutation.target.closest('.killfeed-div');
  5139. if (parentDiv) {
  5140. this.handleMessage(parentDiv);
  5141. }
  5142. }
  5143. mutation.addedNodes.forEach((node) => {
  5144. if (node instanceof HTMLElement) {
  5145. this.handleMessage(node);
  5146. }
  5147. });
  5148. });
  5149. });
  5150. this.killFeedObserver.observe(killfeedContents, {
  5151. childList: true,
  5152. subtree: true,
  5153. characterData: true,
  5154. attributes: true,
  5155. attributeFilter: ['style', 'class']
  5156. });
  5157. killfeedContents.querySelectorAll('.killfeed-div').forEach(this.handleMessage);
  5158. }
  5159. }
  5160. /**
  5161. * Détruit l'observer du killfeed s'il existe
  5162. */
  5163. disableKillFeedObserver() {
  5164. if (this.killFeedObserver) {
  5165. this.killFeedObserver.disconnect();
  5166. this.killFeedObserver = null;
  5167. }
  5168. }
  5169. applyCustomStyles() {
  5170. const customStyles = document.createElement('style');
  5171. if (this.kxsClient.isKillFeedBlint) {
  5172. customStyles.innerHTML = `
  5173. @import url('https://fonts.googleapis.com/css2?family=Oxanium:wght@600&display=swap');
  5174.  
  5175. .killfeed-div {
  5176. position: absolute !important;
  5177. padding: 5px 10px !important;
  5178. background: rgba(0, 0, 0, 0.7) !important;
  5179. border-radius: 5px !important;
  5180. transition: opacity 0.5s ease-out !important;
  5181. }
  5182.  
  5183. .killfeed-text {
  5184. font-family: 'Oxanium', sans-serif !important;
  5185. font-weight: bold !important;
  5186. font-size: 16px !important;
  5187. text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5) !important;
  5188. background: linear-gradient(90deg,
  5189. rgb(255, 0, 0),
  5190. rgb(255, 127, 0),
  5191. rgb(255, 255, 0),
  5192. rgb(0, 255, 0),
  5193. rgb(0, 0, 255),
  5194. rgb(75, 0, 130),
  5195. rgb(148, 0, 211),
  5196. rgb(255, 0, 0));
  5197. background-size: 200%;
  5198. -webkit-background-clip: text;
  5199. -webkit-text-fill-color: transparent;
  5200. animation: glint 3s linear infinite;
  5201. }
  5202.  
  5203. @keyframes glint {
  5204. 0% {
  5205. background-position: 200% 0;
  5206. }
  5207. 100% {
  5208. background-position: -200% 0;
  5209. }
  5210. }
  5211.  
  5212. .killfeed-div .killfeed-text:empty {
  5213. display: none !important;
  5214. }
  5215. `;
  5216. }
  5217. else {
  5218. customStyles.innerHTML = `
  5219. .killfeed-div {
  5220. position: absolute;
  5221. padding: 5px 10px;
  5222. background: rgba(0, 0, 0, 0.7);
  5223. border-radius: 5px;
  5224. transition: opacity 0.5s ease-out;
  5225. }
  5226.  
  5227. .killfeed-text {
  5228. font-family: inherit;
  5229. font-weight: normal;
  5230. font-size: inherit;
  5231. color: inherit;
  5232. text-shadow: none;
  5233. background: none;
  5234. }
  5235.  
  5236. .killfeed-div .killfeed-text:empty {
  5237. display: none;
  5238. }
  5239. `;
  5240. }
  5241. document.head.appendChild(customStyles);
  5242. }
  5243. handleResize() {
  5244. const viewportWidth = window.innerWidth;
  5245. const viewportHeight = window.innerHeight;
  5246. for (const name of ['fps', 'kills', 'ping']) {
  5247. const counterContainer = document.getElementById(`${name}CounterContainer`);
  5248. if (!counterContainer)
  5249. continue;
  5250. const counter = this.kxsClient.counters[name];
  5251. if (!counter)
  5252. continue;
  5253. const rect = counterContainer.getBoundingClientRect();
  5254. const savedPosition = this.getSavedPosition(name);
  5255. let newPosition = this.calculateSafePosition(savedPosition, rect.width, rect.height, viewportWidth, viewportHeight);
  5256. this.applyPosition(counterContainer, newPosition);
  5257. this.savePosition(name, newPosition);
  5258. }
  5259. }
  5260. calculateSafePosition(currentPosition, elementWidth, elementHeight, viewportWidth, viewportHeight) {
  5261. let { left, top } = currentPosition;
  5262. if (left + elementWidth > viewportWidth) {
  5263. left = viewportWidth - elementWidth;
  5264. }
  5265. if (left < 0) {
  5266. left = 0;
  5267. }
  5268. if (top + elementHeight > viewportHeight) {
  5269. top = viewportHeight - elementHeight;
  5270. }
  5271. if (top < 0) {
  5272. top = 0;
  5273. }
  5274. return { left, top };
  5275. }
  5276. getSavedPosition(name) {
  5277. const savedPosition = localStorage.getItem(`${name}CounterPosition`);
  5278. if (savedPosition) {
  5279. try {
  5280. return JSON.parse(savedPosition);
  5281. }
  5282. catch (_a) {
  5283. return this.kxsClient.defaultPositions[name];
  5284. }
  5285. }
  5286. return this.kxsClient.defaultPositions[name];
  5287. }
  5288. applyPosition(element, position) {
  5289. element.style.left = `${position.left}px`;
  5290. element.style.top = `${position.top}px`;
  5291. }
  5292. savePosition(name, position) {
  5293. localStorage.setItem(`${name}CounterPosition`, JSON.stringify(position));
  5294. }
  5295. startUpdateLoop() {
  5296. var _a;
  5297. const now = performance.now();
  5298. const delta = now - this.kxsClient.lastFrameTime;
  5299. this.frameCount++;
  5300. if (delta >= 1000) {
  5301. this.fps = Math.round((this.frameCount * 1000) / delta);
  5302. this.frameCount = 0;
  5303. this.kxsClient.lastFrameTime = now;
  5304. this.kills = this.kxsClient.getKills();
  5305. // Vérifie et crée les compteurs s'ils n'existent pas encore mais sont activés
  5306. if (this.kxsClient.isFpsVisible && !this.kxsClient.counters.fps) {
  5307. this.initCounter("fps", "FPS", "60");
  5308. }
  5309. if (this.kxsClient.isKillsVisible && !this.kxsClient.counters.kills) {
  5310. this.initCounter("kills", "Kills", "0");
  5311. }
  5312. if (this.kxsClient.isPingVisible && !this.kxsClient.counters.ping) {
  5313. this.initCounter("ping", "Ping", "45ms");
  5314. }
  5315. // Met à jour les valeurs des compteurs visibles
  5316. if (this.kxsClient.isFpsVisible && this.kxsClient.counters.fps) {
  5317. this.kxsClient.counters.fps.textContent = `FPS: ${this.fps}`;
  5318. }
  5319. if (this.kxsClient.isKillsVisible && this.kxsClient.counters.kills) {
  5320. this.kxsClient.counters.kills.textContent = `Kills: ${this.kills}`;
  5321. }
  5322. if (this.kxsClient.isPingVisible &&
  5323. this.kxsClient.counters.ping &&
  5324. this.pingManager) {
  5325. const result = this.pingManager.getPingResult();
  5326. this.kxsClient.counters.ping.textContent = `PING: ${result.ping} ms`;
  5327. }
  5328. }
  5329. if (this.kxsClient.animationFrameCallback) {
  5330. this.kxsClient.animationFrameCallback(() => this.startUpdateLoop());
  5331. }
  5332. this.updateUiElements();
  5333. this.updateBoostBars();
  5334. this.updateHealthBars();
  5335. (_a = this.kxsClient.kill_leader) === null || _a === void 0 ? void 0 : _a.update(this.kills);
  5336. }
  5337. initCounter(name, label, initialText) {
  5338. // Vérifier si le compteur existe déjà et le supprimer si c'est le cas
  5339. this.removeCounter(name);
  5340. const counter = document.createElement("div");
  5341. counter.id = `${name}Counter`;
  5342. const counterContainer = document.createElement("div");
  5343. counterContainer.id = `${name}CounterContainer`;
  5344. Object.assign(counterContainer.style, {
  5345. position: "absolute",
  5346. left: `${this.kxsClient.defaultPositions[name].left}px`,
  5347. top: `${this.kxsClient.defaultPositions[name].top}px`,
  5348. zIndex: "10000",
  5349. });
  5350. Object.assign(counter.style, {
  5351. color: "white",
  5352. backgroundColor: "rgba(0, 0, 0, 0.2)",
  5353. borderRadius: "5px",
  5354. fontFamily: "Arial, sans-serif",
  5355. padding: "5px 10px",
  5356. pointerEvents: "none",
  5357. cursor: "default",
  5358. width: `${this.kxsClient.defaultSizes[name].width}px`,
  5359. height: `${this.kxsClient.defaultSizes[name].height}px`,
  5360. display: "flex",
  5361. alignItems: "center",
  5362. justifyContent: "center",
  5363. textAlign: "center",
  5364. resize: "both",
  5365. overflow: "hidden",
  5366. });
  5367. counter.textContent = `${label}: ${initialText}`;
  5368. counterContainer.appendChild(counter);
  5369. const uiTopLeft = document.getElementById("ui-top-left");
  5370. if (uiTopLeft) {
  5371. uiTopLeft.appendChild(counterContainer);
  5372. }
  5373. const adjustFontSize = () => {
  5374. const { width, height } = counter.getBoundingClientRect();
  5375. const size = Math.min(width, height) * 0.4;
  5376. counter.style.fontSize = `${size}px`;
  5377. };
  5378. new ResizeObserver(adjustFontSize).observe(counter);
  5379. counter.addEventListener("mousedown", (event) => {
  5380. if (event.button === 1) {
  5381. this.resetCounter(name, label, initialText);
  5382. event.preventDefault();
  5383. }
  5384. });
  5385. this.kxsClient.makeDraggable(counterContainer, `${name}CounterPosition`);
  5386. this.kxsClient.counters[name] = counter;
  5387. this.kxsClient.gridSystem.registerCounter(name, counterContainer);
  5388. const savedPosition = localStorage.getItem(`${name}CounterPosition`);
  5389. if (savedPosition) {
  5390. const { x, y } = JSON.parse(savedPosition);
  5391. counterContainer.style.left = `${x}px`;
  5392. counterContainer.style.top = `${y}px`;
  5393. }
  5394. this.updateCounterCorners();
  5395. }
  5396. /**
  5397. * Supprime un compteur du DOM et de la référence dans kxsClient.counters
  5398. * @param name Nom du compteur à supprimer (fps, kills, ping)
  5399. */
  5400. removeCounter(name) {
  5401. // Supprime l'élément du DOM s'il existe
  5402. const counterContainer = document.getElementById(`${name}CounterContainer`);
  5403. if (counterContainer) {
  5404. counterContainer.remove();
  5405. }
  5406. // Supprime la référence dans kxsClient.counters
  5407. if (this.kxsClient.counters[name]) {
  5408. // Utilise delete pour supprimer la propriété au lieu de l'affecter à null
  5409. delete this.kxsClient.counters[name];
  5410. }
  5411. this.kxsClient.gridSystem.registerCounter(name, null);
  5412. this.kxsClient.gridSystem.updateCounterCorners();
  5413. }
  5414. /**
  5415. * Gère l'affichage ou le masquage d'un compteur en fonction de son état
  5416. * @param name Nom du compteur (fps, kills, ping)
  5417. * @param visible État de visibilité souhaité
  5418. * @param label Libellé du compteur
  5419. * @param initialText Texte initial à afficher
  5420. */
  5421. toggleCounter(name, visible, label, initialText) {
  5422. if (visible) {
  5423. // Si le compteur doit être visible mais n'existe pas, on le crée
  5424. if (!this.kxsClient.counters[name]) {
  5425. this.initCounter(name, label, initialText);
  5426. }
  5427. }
  5428. else {
  5429. // Si le compteur ne doit pas être visible mais existe, on le supprime
  5430. this.removeCounter(name);
  5431. }
  5432. }
  5433. resetCounter(name, label, initialText) {
  5434. const counter = this.kxsClient.counters[name];
  5435. const container = document.getElementById(`${name}CounterContainer`);
  5436. if (!counter || !container)
  5437. return;
  5438. // Reset only this counter's position and size
  5439. Object.assign(container.style, {
  5440. left: `${this.kxsClient.defaultPositions[name].left}px`,
  5441. top: `${this.kxsClient.defaultPositions[name].top}px`,
  5442. });
  5443. Object.assign(counter.style, {
  5444. width: `${this.kxsClient.defaultSizes[name].width}px`,
  5445. height: `${this.kxsClient.defaultSizes[name].height}px`,
  5446. fontSize: "18px",
  5447. borderRadius: "5px",
  5448. });
  5449. counter.textContent = `${label}: ${initialText}`;
  5450. // Clear the saved position for this counter only
  5451. localStorage.removeItem(`${name}CounterPosition`);
  5452. setTimeout(() => {
  5453. this.kxsClient.gridSystem.updateCounterCorners();
  5454. }, 50);
  5455. }
  5456. updateBoostBars() {
  5457. const boostCounter = document.querySelector("#ui-boost-counter");
  5458. if (boostCounter) {
  5459. // Si les indicateurs sont désactivés, on supprime les éléments personnalisés
  5460. if (!this.kxsClient.isHealBarIndicatorEnabled) {
  5461. this.cleanBoostDisplay(boostCounter);
  5462. return;
  5463. }
  5464. const boostBars = boostCounter.querySelectorAll(".ui-boost-base .ui-bar-inner");
  5465. let totalBoost = 0;
  5466. const weights = [25, 25, 40, 10];
  5467. boostBars.forEach((bar, index) => {
  5468. const width = parseFloat(bar.style.width);
  5469. if (!isNaN(width)) {
  5470. totalBoost += width * (weights[index] / 100);
  5471. }
  5472. });
  5473. const averageBoost = Math.round(totalBoost);
  5474. let boostDisplay = boostCounter.querySelector(".boost-display");
  5475. if (!boostDisplay) {
  5476. boostDisplay = document.createElement("div");
  5477. boostDisplay.classList.add("boost-display");
  5478. Object.assign(boostDisplay.style, {
  5479. position: "absolute",
  5480. bottom: "75px",
  5481. right: "335px",
  5482. color: "#FF901A",
  5483. backgroundColor: "rgba(0, 0, 0, 0.4)",
  5484. padding: "5px 10px",
  5485. borderRadius: "5px",
  5486. fontFamily: "Arial, sans-serif",
  5487. fontSize: "14px",
  5488. zIndex: "10",
  5489. textAlign: "center",
  5490. });
  5491. boostCounter.appendChild(boostDisplay);
  5492. }
  5493. boostDisplay.textContent = `AD: ${averageBoost}%`;
  5494. }
  5495. }
  5496. toggleWeaponBorderHandler() {
  5497. // Get all weapon containers
  5498. const weaponContainers = Array.from(document.getElementsByClassName("ui-weapon-switch"));
  5499. // Get all weapon names
  5500. const weaponNames = Array.from(document.getElementsByClassName("ui-weapon-name"));
  5501. // Clear any existing observers
  5502. this.clearWeaponBorderObservers();
  5503. if (this.kxsClient.isGunOverlayColored) {
  5504. // Apply initial border colors
  5505. weaponContainers.forEach((container) => {
  5506. if (container.id === "ui-weapon-id-4") {
  5507. container.style.border = "3px solid #2f4032";
  5508. }
  5509. else {
  5510. container.style.border = "3px solid #FFFFFF";
  5511. }
  5512. });
  5513. const WEAPON_COLORS = {
  5514. ORANGE: '#FFAE00',
  5515. BLUE: '#007FFF',
  5516. GREEN: '#0f690d',
  5517. RED: '#FF0000',
  5518. BLACK: '#000000',
  5519. OLIVE: '#808000',
  5520. ORANGE_RED: '#FF4500',
  5521. PURPLE: '#800080',
  5522. TEAL: '#008080',
  5523. BROWN: '#A52A2A',
  5524. PINK: '#FFC0CB',
  5525. DEFAULT: '#FFFFFF'
  5526. };
  5527. const WEAPON_COLOR_MAPPING = {
  5528. ORANGE: ['CZ-3A1', 'G18C', 'M9', 'M93R', 'MAC-10', 'MP5', 'P30L', 'DUAL P30L', 'UMP9', 'VECTOR', 'VSS', 'FLAMETHROWER'],
  5529. 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'],
  5530. GREEN: ['FAMAS', 'M416', 'M249', 'QBB-97', 'MK 12 SPR', 'M4A1-S', 'SCOUT ELITE', 'L86A2'],
  5531. RED: ['M870', 'MP220', 'SAIGA-12', 'SPAS-12', 'USAS-12', 'SUPER 90', 'LASR GUN', 'M1100'],
  5532. BLACK: ['DEAGLE 50', 'RAINBOW BLASTER'],
  5533. OLIVE: ['AWM-S', 'MK 20 SSR'],
  5534. ORANGE_RED: ['FLARE GUN'],
  5535. PURPLE: ['MODEL 94', 'PEACEMAKER', 'VECTOR (.45 ACP)', 'M1911', 'M1A1', 'MK45G'],
  5536. TEAL: ['M79'],
  5537. BROWN: ['POTATO CANNON', 'SPUD GUN'],
  5538. PINK: ['HEART CANNON'],
  5539. DEFAULT: []
  5540. };
  5541. // Set up observers for dynamic color changes
  5542. weaponNames.forEach((weaponNameElement) => {
  5543. const weaponContainer = weaponNameElement.closest(".ui-weapon-switch");
  5544. const observer = new MutationObserver(() => {
  5545. var _a, _b, _c;
  5546. const weaponName = ((_b = (_a = weaponNameElement.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || '';
  5547. let colorKey = 'DEFAULT';
  5548. // Do a hack for "VECTOR" gun (because can be 2 weapons: yellow or purple)
  5549. if (weaponName === "VECTOR") {
  5550. // Get the weapon container and image element
  5551. const weaponContainer = weaponNameElement.closest(".ui-weapon-switch");
  5552. const weaponImage = weaponContainer === null || weaponContainer === void 0 ? void 0 : weaponContainer.querySelector(".ui-weapon-image");
  5553. if (weaponImage && weaponImage.src) {
  5554. // Check the image source to determine which Vector it is
  5555. if (weaponImage.src.includes("-acp") || weaponImage.src.includes("45")) {
  5556. colorKey = 'PURPLE';
  5557. }
  5558. else {
  5559. colorKey = 'ORANGE';
  5560. }
  5561. }
  5562. else {
  5563. // Default to orange if we can't determine the type
  5564. colorKey = 'ORANGE';
  5565. }
  5566. }
  5567. else {
  5568. colorKey = (((_c = Object.entries(WEAPON_COLOR_MAPPING)
  5569. .find(([_, weapons]) => weapons.includes(weaponName))) === null || _c === void 0 ? void 0 : _c[0]) || 'DEFAULT');
  5570. }
  5571. if (weaponContainer && weaponContainer.id !== "ui-weapon-id-4") {
  5572. weaponContainer.style.border = `3px solid ${WEAPON_COLORS[colorKey]}`;
  5573. }
  5574. });
  5575. observer.observe(weaponNameElement, { childList: true, characterData: true, subtree: true });
  5576. // Store the observer for later cleanup
  5577. this.weaponBorderObservers = this.weaponBorderObservers || [];
  5578. this.weaponBorderObservers.push(observer);
  5579. });
  5580. }
  5581. else {
  5582. // If the feature is disabled, reset all weapon borders to default
  5583. weaponContainers.forEach((container) => {
  5584. // Reset to game's default border style
  5585. container.style.border = "";
  5586. });
  5587. }
  5588. }
  5589. // Helper method to clear weapon border observers
  5590. clearWeaponBorderObservers() {
  5591. if (this.weaponBorderObservers && this.weaponBorderObservers.length > 0) {
  5592. this.weaponBorderObservers.forEach(observer => {
  5593. observer.disconnect();
  5594. });
  5595. this.weaponBorderObservers = [];
  5596. }
  5597. }
  5598. toggleChromaticWeaponBorder() {
  5599. const borderClass = 'kxs-chromatic-border';
  5600. const styleId = 'kxs-chromatic-border-style';
  5601. const weaponIds = [1, 2, 3, 4];
  5602. if (this.kxsClient.isGunBorderChromatic) {
  5603. // Inject CSS if not already present
  5604. if (!document.getElementById(styleId)) {
  5605. const style = document.createElement('style');
  5606. style.id = styleId;
  5607. style.innerHTML = `
  5608. @keyframes kxs-rainbow {
  5609. 0% { border-image: linear-gradient(120deg, #ff004c, #fffa00, #00ff90, #004cff, #ff004c) 1; }
  5610. 100% { border-image: linear-gradient(480deg, #ff004c, #fffa00, #00ff90, #004cff, #ff004c) 1; }
  5611. }
  5612. @keyframes kxs-glint {
  5613. 0% { box-shadow: 0 0 8px 2px #fff2; }
  5614. 50% { box-shadow: 0 0 24px 6px #fff8; }
  5615. 100% { box-shadow: 0 0 8px 2px #fff2; }
  5616. }
  5617. @keyframes kxs-bg-rainbow {
  5618. 0% { background-position: 0% 50%; }
  5619. 50% { background-position: 100% 50%; }
  5620. 100% { background-position: 0% 50%; }
  5621. }
  5622. .kxs-chromatic-border {
  5623. border: 3px solid transparent !important;
  5624. border-image: linear-gradient(120deg, #ff004c, #fffa00, #00ff90, #004cff, #ff004c) 1;
  5625. animation: kxs-rainbow 3s linear infinite, kxs-glint 2s ease-in-out infinite, kxs-bg-rainbow 8s linear infinite;
  5626. border-radius: 8px !important;
  5627. background: linear-gradient(270deg, #ff004c, #fffa00, #00ff90, #004cff, #ff004c);
  5628. background-size: 1200% 1200%;
  5629. background-position: 0% 50%;
  5630. background-clip: padding-box;
  5631. -webkit-background-clip: padding-box;
  5632. filter: brightness(1.15) saturate(1.4);
  5633. transition: background 0.5s;
  5634. }
  5635. `;
  5636. document.head.appendChild(style);
  5637. }
  5638. weaponIds.forEach(id => {
  5639. const el = document.getElementById(`ui-weapon-id-${id}`);
  5640. if (el) {
  5641. el.classList.add(borderClass);
  5642. }
  5643. });
  5644. }
  5645. else {
  5646. // Remove chromatic border and style
  5647. weaponIds.forEach(id => {
  5648. const el = document.getElementById(`ui-weapon-id-${id}`);
  5649. if (el) {
  5650. el.classList.remove(borderClass);
  5651. el.style.border = '';
  5652. }
  5653. });
  5654. const style = document.getElementById(styleId);
  5655. if (style)
  5656. style.remove();
  5657. // Reapply regular colored borders if that feature is enabled
  5658. if (this.kxsClient.isGunOverlayColored) {
  5659. this.toggleWeaponBorderHandler();
  5660. }
  5661. }
  5662. }
  5663. updateUiElements() {
  5664. // Réapplique l'effet chromatique si activé (corrige le bug d'affichage après un changement de page ou entrée en game)
  5665. if (this.kxsClient.isGunBorderChromatic) {
  5666. this.toggleChromaticWeaponBorder();
  5667. }
  5668. const currentUrl = window.location.href;
  5669. const isSpecialUrl = /\/#\w+/.test(currentUrl);
  5670. const playerOptions = document.getElementById("player-options");
  5671. const teamMenuContents = document.getElementById("team-menu-contents");
  5672. const startMenuContainer = document.querySelector("#start-menu .play-button-container");
  5673. // Update counters draggable state based on LSHIFT menu visibility
  5674. this.updateCountersDraggableState();
  5675. if (!playerOptions)
  5676. return;
  5677. if (isSpecialUrl &&
  5678. teamMenuContents &&
  5679. playerOptions.parentNode !== teamMenuContents) {
  5680. teamMenuContents.appendChild(playerOptions);
  5681. }
  5682. else if (!isSpecialUrl &&
  5683. startMenuContainer &&
  5684. playerOptions.parentNode !== startMenuContainer) {
  5685. const firstChild = startMenuContainer.firstChild;
  5686. startMenuContainer.insertBefore(playerOptions, firstChild);
  5687. }
  5688. const teamMenu = document.getElementById("team-menu");
  5689. if (teamMenu) {
  5690. teamMenu.style.height = "355px";
  5691. }
  5692. const menuBlocks = document.querySelectorAll(".menu-block");
  5693. menuBlocks.forEach((block) => {
  5694. block.style.maxHeight = "355px";
  5695. });
  5696. //scalable?
  5697. }
  5698. // Nettoie l'affichage boost personnalisé
  5699. cleanBoostDisplay(boostCounter) {
  5700. const boostDisplay = boostCounter.querySelector(".boost-display");
  5701. if (boostDisplay) {
  5702. boostDisplay.remove();
  5703. }
  5704. }
  5705. // Nettoie l'affichage santé personnalisé
  5706. cleanHealthDisplay(container) {
  5707. const percentageText = container.querySelector(".health-text");
  5708. if (percentageText) {
  5709. percentageText.remove();
  5710. }
  5711. const healthChangeElements = container.querySelectorAll(".health-change");
  5712. healthChangeElements.forEach(el => el.remove());
  5713. }
  5714. updateHealthBars() {
  5715. const healthBars = document.querySelectorAll("#ui-health-container");
  5716. healthBars.forEach((container) => {
  5717. var _a, _b;
  5718. // Si les indicateurs sont désactivés, on supprime les éléments personnalisés
  5719. if (!this.kxsClient.isHealBarIndicatorEnabled) {
  5720. this.cleanHealthDisplay(container);
  5721. return;
  5722. }
  5723. const bar = container.querySelector("#ui-health-actual");
  5724. if (bar) {
  5725. const currentHealth = Math.round(parseFloat(bar.style.width));
  5726. let percentageText = container.querySelector(".health-text");
  5727. // Create or update percentage text
  5728. if (!percentageText) {
  5729. percentageText = document.createElement("span");
  5730. percentageText.classList.add("health-text");
  5731. Object.assign(percentageText.style, {
  5732. width: "100%",
  5733. textAlign: "center",
  5734. marginTop: "5px",
  5735. color: "#333",
  5736. fontSize: "20px",
  5737. fontWeight: "bold",
  5738. position: "absolute",
  5739. zIndex: "10",
  5740. });
  5741. container.appendChild(percentageText);
  5742. }
  5743. // Check for health change
  5744. if (currentHealth !== this.lastHealthValue) {
  5745. const healthChange = currentHealth - this.lastHealthValue;
  5746. if (healthChange !== 0) {
  5747. this.showHealthChangeAnimation(container, healthChange);
  5748. }
  5749. this.lastHealthValue = currentHealth;
  5750. }
  5751. if (this.kxsClient.isHealthWarningEnabled) {
  5752. (_a = this.kxsClient.healWarning) === null || _a === void 0 ? void 0 : _a.update(currentHealth);
  5753. }
  5754. else {
  5755. (_b = this.kxsClient.healWarning) === null || _b === void 0 ? void 0 : _b.hide();
  5756. }
  5757. percentageText.textContent = `${currentHealth}%`;
  5758. // Update animations
  5759. this.updateHealthAnimations();
  5760. }
  5761. });
  5762. }
  5763. showHealthChangeAnimation(container, change) {
  5764. const healthContainer = container;
  5765. if (!healthContainer || !this.kxsClient.isHealBarIndicatorEnabled)
  5766. return;
  5767. // Create animation element
  5768. const animationElement = document.createElement("div");
  5769. animationElement.classList.add("health-change");
  5770. const isPositive = change > 0;
  5771. Object.assign(animationElement.style, {
  5772. position: "absolute",
  5773. color: isPositive ? "#2ecc71" : "#e74c3c",
  5774. fontSize: "24px",
  5775. fontWeight: "bold",
  5776. fontFamily: "Arial, sans-serif",
  5777. textShadow: "2px 2px 4px rgba(0,0,0,0.3)",
  5778. pointerEvents: "none",
  5779. zIndex: "100",
  5780. opacity: "1",
  5781. top: "50%",
  5782. right: "-80px", // Position à droite de la barre de vie
  5783. transform: "translateY(-50%)", // Centre verticalement
  5784. whiteSpace: "nowrap", // Empêche le retour à la ligne
  5785. });
  5786. // Check if change is a valid number before displaying it
  5787. if (!isNaN(change)) {
  5788. animationElement.textContent = `${isPositive ? "+" : ""}${change} HP`;
  5789. }
  5790. else {
  5791. // Skip showing animation if change is NaN
  5792. return;
  5793. }
  5794. container.appendChild(animationElement);
  5795. this.healthAnimations.push({
  5796. element: animationElement,
  5797. startTime: performance.now(),
  5798. duration: 1500, // Animation duration in milliseconds
  5799. value: change,
  5800. });
  5801. }
  5802. updateCounterCorners() {
  5803. if (document.readyState === 'loading') {
  5804. document.addEventListener('DOMContentLoaded', () => {
  5805. this.kxsClient.gridSystem.updateCounterCorners();
  5806. });
  5807. }
  5808. else {
  5809. setTimeout(() => {
  5810. this.kxsClient.gridSystem.updateCounterCorners();
  5811. }, 100);
  5812. }
  5813. }
  5814. updateCountersDraggableState() {
  5815. var _a;
  5816. const countersVisibility = {
  5817. fps: this.kxsClient.isFpsVisible,
  5818. ping: this.kxsClient.isPingVisible,
  5819. kills: this.kxsClient.isKillsVisible,
  5820. };
  5821. Object.entries(countersVisibility).forEach(([name, visible]) => {
  5822. const label = name.charAt(0).toUpperCase() + name.slice(1);
  5823. const initialText = name === "fps" ? "60" : name === "ping" ? "45ms" : "0";
  5824. this.toggleCounter(name, visible, label, initialText);
  5825. });
  5826. const isMenuOpen = ((_a = this.kxsClient.secondaryMenu) === null || _a === void 0 ? void 0 : _a.getMenuVisibility()) || false;
  5827. const counterNames = ['fps', 'kills', 'ping'];
  5828. counterNames.forEach(name => {
  5829. const counter = document.getElementById(`${name}Counter`);
  5830. if (counter) {
  5831. // Mise à jour des propriétés de draggabilité
  5832. counter.style.pointerEvents = isMenuOpen ? 'auto' : 'none';
  5833. counter.style.cursor = isMenuOpen ? 'move' : 'default';
  5834. // Mise à jour de la possibilité de redimensionnement
  5835. counter.style.resize = isMenuOpen ? 'both' : 'none';
  5836. }
  5837. });
  5838. this.updateCounterCorners();
  5839. }
  5840. updateHealthAnimations() {
  5841. const currentTime = performance.now();
  5842. this.healthAnimations = this.healthAnimations.filter(animation => {
  5843. const elapsed = currentTime - animation.startTime;
  5844. const progress = Math.min(elapsed / animation.duration, 1);
  5845. if (progress < 1) {
  5846. // Update animation position and opacity
  5847. // Maintenant l'animation se déplace horizontalement vers la droite
  5848. const translateX = progress * 20; // Déplacement horizontal
  5849. Object.assign(animation.element.style, {
  5850. transform: `translateY(-50%) translateX(${translateX}px)`,
  5851. opacity: String(1 - progress),
  5852. });
  5853. return true;
  5854. }
  5855. else {
  5856. // Remove completed animation
  5857. animation.element.remove();
  5858. return false;
  5859. }
  5860. });
  5861. }
  5862. }
  5863.  
  5864.  
  5865. ;// ./src/FUNC/Logger.ts
  5866. class Logger {
  5867. getHeader(method) {
  5868. return "[" + "KxsClient" + " - " + method + "]";
  5869. }
  5870. 展示(...args) {
  5871. console.log(...args);
  5872. }
  5873. ;
  5874. log(...args) {
  5875. // Convert args to string and join them
  5876. const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');
  5877. this.展示(this.getHeader("LOG"), message);
  5878. }
  5879. warn(...args) {
  5880. const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');
  5881. this.展示(this.getHeader("WARN"), message);
  5882. }
  5883. error(...args) {
  5884. const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');
  5885. this.展示(this.getHeader("ERROR"), message);
  5886. }
  5887. }
  5888.  
  5889.  
  5890. // EXTERNAL MODULE: ../../GitLab/SteganoDB2/lib/browser.js
  5891. var browser = __webpack_require__(686);
  5892. ;// ./src/HUD/HistoryManager.ts
  5893. var HistoryManager_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  5894. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  5895. return new (P || (P = Promise))(function (resolve, reject) {
  5896. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  5897. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  5898. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  5899. step((generator = generator.apply(thisArg, _arguments || [])).next());
  5900. });
  5901. };
  5902. class GameHistoryMenu {
  5903. constructor(kxsClient) {
  5904. this.kxsClient = kxsClient;
  5905. this.container = document.createElement('div');
  5906. this.closeBtn = document.createElement('button');
  5907. }
  5908. initContainer() {
  5909. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  5910. // Position the menu in the center of the screen
  5911. Object.assign(this.container.style, {
  5912. position: 'fixed',
  5913. top: '50%',
  5914. left: '50%',
  5915. transform: 'translate(-50%, -50%)',
  5916. width: isMobile ? '78vw' : '700px',
  5917. maxWidth: isMobile ? '84vw' : '90vw',
  5918. maxHeight: isMobile ? '60vh' : '80vh',
  5919. overflowY: 'auto',
  5920. overflowX: 'hidden',
  5921. backgroundColor: 'rgba(17, 24, 39, 0.95)',
  5922. color: '#fff',
  5923. borderRadius: isMobile ? '7px' : '12px',
  5924. border: '1px solid rgba(60, 80, 120, 0.3)',
  5925. boxShadow: '0 4px 20px rgba(0,0,0,0.8)',
  5926. zIndex: '10001',
  5927. padding: isMobile ? '6px' : '20px',
  5928. boxSizing: 'border-box',
  5929. fontFamily: "'Segoe UI', Arial, sans-serif",
  5930. });
  5931. }
  5932. addCloseButton() {
  5933. this.closeBtn.textContent = '✖';
  5934. Object.assign(this.closeBtn.style, {
  5935. position: 'absolute',
  5936. top: '10px',
  5937. right: '15px',
  5938. background: 'transparent',
  5939. color: '#fff',
  5940. border: 'none',
  5941. fontSize: '22px',
  5942. cursor: 'pointer',
  5943. transition: 'transform 0.2s ease, color 0.2s ease',
  5944. zIndex: '10',
  5945. });
  5946. this.closeBtn.onclick = (e) => {
  5947. e.stopPropagation();
  5948. this.hide();
  5949. };
  5950. this.closeBtn.onmouseenter = () => {
  5951. this.closeBtn.style.color = '#3B82F6';
  5952. this.closeBtn.style.transform = 'scale(1.1)';
  5953. };
  5954. this.closeBtn.onmouseleave = () => {
  5955. this.closeBtn.style.color = '#fff';
  5956. this.closeBtn.style.transform = 'scale(1)';
  5957. };
  5958. this.container.appendChild(this.closeBtn);
  5959. }
  5960. addHeader() {
  5961. // Create a title area at the top of the menu (header)
  5962. const header = document.createElement('div');
  5963. header.style.position = 'absolute';
  5964. header.style.top = '0';
  5965. header.style.left = '0';
  5966. header.style.right = '0';
  5967. header.style.height = '40px';
  5968. // No border at the bottom
  5969. // Add a centered title
  5970. const title = document.createElement('div');
  5971. title.textContent = 'Game History';
  5972. title.style.position = 'absolute';
  5973. title.style.left = '50%';
  5974. title.style.top = '50%';
  5975. title.style.transform = 'translate(-50%, -50%)';
  5976. title.style.fontWeight = 'bold';
  5977. title.style.fontSize = '14px';
  5978. title.style.color = '#fff';
  5979. header.appendChild(title);
  5980. this.container.insertBefore(header, this.container.firstChild);
  5981. }
  5982. renderContent() {
  5983. return HistoryManager_awaiter(this, void 0, void 0, function* () {
  5984. // Header
  5985. const header = document.createElement('div');
  5986. header.textContent = 'Game History';
  5987. const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
  5988. Object.assign(header.style, {
  5989. fontWeight: 'bold',
  5990. fontSize: isMobile ? '1.1em' : '1.3em',
  5991. letterSpacing: '1px',
  5992. margin: isMobile ? '10px 0 12px 0' : '10px 0 20px 0',
  5993. textAlign: 'center',
  5994. color: '#3B82F6',
  5995. });
  5996. this.container.appendChild(header);
  5997. // Liste de l'historique
  5998. let historyList = document.createElement('div');
  5999. Object.assign(historyList.style, {
  6000. display: 'flex',
  6001. flexDirection: 'column',
  6002. gap: isMobile ? '6px' : '8px',
  6003. padding: isMobile ? '0 8px' : '0 15px',
  6004. width: '100%',
  6005. });
  6006. // Récupération de l'historique via SteganoDB
  6007. let result = this.kxsClient.db.all();
  6008. let entries = [];
  6009. // Traitement de la structure JSON gameplay_history
  6010. if (result && typeof result === 'object') {
  6011. // Vérifier si c'est la structure gameplay_history
  6012. if ('gameplay_history' in result && Array.isArray(result.gameplay_history)) {
  6013. entries = result.gameplay_history;
  6014. // Tri par date décroissante
  6015. entries.sort((a, b) => {
  6016. if (a.id && b.id)
  6017. return b.id.localeCompare(a.id);
  6018. return 0;
  6019. });
  6020. }
  6021. else {
  6022. // Fallback pour l'ancienne structure
  6023. entries = Array.isArray(result) ? result : [];
  6024. }
  6025. }
  6026. // Affichage ligne par ligne
  6027. if (!entries || entries.length === 0) {
  6028. const empty = document.createElement('div');
  6029. empty.textContent = 'No games recorded.';
  6030. empty.style.textAlign = 'center';
  6031. empty.style.color = '#aaa';
  6032. historyList.appendChild(empty);
  6033. }
  6034. else {
  6035. let i = 1;
  6036. for (const entry of entries) {
  6037. let key, value;
  6038. // Structure gameplay_history
  6039. if (typeof entry === 'object' && entry.id && entry.value) {
  6040. key = entry.id;
  6041. value = entry.value;
  6042. // Traitement des games dans value
  6043. if (typeof value === 'object') {
  6044. for (const gameId in value) {
  6045. const gameStats = value[gameId];
  6046. this.createGameHistoryLine(historyList, gameStats, key, i, isMobile);
  6047. i++;
  6048. }
  6049. continue; // Passer à l'entrée suivante après avoir traité tous les jeux
  6050. }
  6051. }
  6052. // Ancienne structure
  6053. else if (Array.isArray(entry) && entry.length === 2) {
  6054. key = entry[0];
  6055. value = entry[1];
  6056. }
  6057. else if (typeof entry === 'object' && entry.key && entry.value) {
  6058. key = entry.key;
  6059. value = entry.value;
  6060. }
  6061. else {
  6062. continue;
  6063. }
  6064. // Pour l'ancienne structure, créer une ligne
  6065. this.createGameHistoryLine(historyList, value, key, i, isMobile);
  6066. i++;
  6067. }
  6068. }
  6069. this.container.appendChild(historyList);
  6070. });
  6071. }
  6072. createGameHistoryLine(historyList, stats, dateKey, index, isMobile) {
  6073. const line = document.createElement('div');
  6074. Object.assign(line.style, {
  6075. background: index % 2 ? 'rgba(31, 41, 55, 0.7)' : 'rgba(17, 24, 39, 0.8)',
  6076. borderRadius: isMobile ? '5px' : '8px',
  6077. padding: isMobile ? '6px 8px' : '10px 15px',
  6078. fontFamily: "'Segoe UI', Arial, sans-serif",
  6079. fontSize: isMobile ? '0.8em' : '0.95em',
  6080. display: 'flex',
  6081. flexDirection: isMobile ? 'column' : 'row',
  6082. alignItems: isMobile ? 'flex-start' : 'center',
  6083. gap: isMobile ? '4px' : '12px',
  6084. transition: 'background 0.2s, transform 0.1s',
  6085. cursor: 'pointer',
  6086. border: '1px solid transparent',
  6087. });
  6088. // Effet hover
  6089. line.onmouseenter = () => {
  6090. line.style.background = 'rgba(59, 130, 246, 0.3)';
  6091. line.style.borderColor = 'rgba(59, 130, 246, 0.5)';
  6092. line.style.transform = 'translateY(-1px)';
  6093. };
  6094. line.onmouseleave = () => {
  6095. line.style.background = index % 2 ? 'rgba(31, 41, 55, 0.7)' : 'rgba(17, 24, 39, 0.8)';
  6096. line.style.borderColor = 'transparent';
  6097. line.style.transform = 'translateY(0)';
  6098. };
  6099. // Date formatée
  6100. const dateStr = dateKey ? new Date(dateKey).toLocaleString() : '';
  6101. const dateEl = document.createElement('div');
  6102. dateEl.textContent = dateStr;
  6103. Object.assign(dateEl.style, {
  6104. color: '#93c5fd',
  6105. fontWeight: 'bold',
  6106. whiteSpace: 'nowrap',
  6107. marginRight: isMobile ? '0' : '10px',
  6108. });
  6109. // Stats du jeu
  6110. const statsContainer = document.createElement('div');
  6111. Object.assign(statsContainer.style, {
  6112. display: 'flex',
  6113. flexDirection: isMobile ? 'column' : 'row',
  6114. flexWrap: 'wrap',
  6115. gap: isMobile ? '3px 8px' : '0 15px',
  6116. flex: '1',
  6117. });
  6118. // Création des éléments de stats
  6119. const createStatElement = (label, value, color = '#fff') => {
  6120. const statEl = document.createElement('div');
  6121. statEl.style.display = 'inline-flex';
  6122. statEl.style.alignItems = 'center';
  6123. statEl.style.marginRight = isMobile ? '5px' : '12px';
  6124. const labelEl = document.createElement('span');
  6125. labelEl.textContent = `${label}: `;
  6126. labelEl.style.color = '#9ca3af';
  6127. const valueEl = document.createElement('span');
  6128. valueEl.textContent = value !== undefined && value !== null ? String(value) : '-';
  6129. valueEl.style.color = color;
  6130. valueEl.style.fontWeight = 'bold';
  6131. statEl.appendChild(labelEl);
  6132. statEl.appendChild(valueEl);
  6133. return statEl;
  6134. };
  6135. // Ajout des stats avec couleurs
  6136. if (typeof stats === 'object') {
  6137. const isWin = stats.isWin === true;
  6138. statsContainer.appendChild(createStatElement('Player', stats.username, '#fff'));
  6139. statsContainer.appendChild(createStatElement('Kills', stats.kills, '#ef4444'));
  6140. statsContainer.appendChild(createStatElement('DMG', stats.damageDealt, '#f59e0b'));
  6141. statsContainer.appendChild(createStatElement('Taken', stats.damageTaken, '#a855f7'));
  6142. statsContainer.appendChild(createStatElement('Duration', stats.duration, '#fff'));
  6143. // Position avec couleur selon le rang
  6144. let posColor = '#fff';
  6145. if (stats.position) {
  6146. const pos = parseInt(stats.position.replace('#', ''));
  6147. if (pos <= 10)
  6148. posColor = '#fbbf24'; // Or
  6149. else if (pos <= 25)
  6150. posColor = '#94a3b8'; // Argent
  6151. else if (pos <= 50)
  6152. posColor = '#b45309'; // Bronze
  6153. }
  6154. statsContainer.appendChild(createStatElement('Pos', stats.position, posColor));
  6155. // Indicateur de victoire
  6156. if (isWin) {
  6157. const winEl = document.createElement('div');
  6158. winEl.textContent = '🏆 WIN';
  6159. winEl.style.color = '#fbbf24';
  6160. winEl.style.fontWeight = 'bold';
  6161. winEl.style.marginLeft = 'auto';
  6162. statsContainer.appendChild(winEl);
  6163. }
  6164. }
  6165. else {
  6166. // Fallback si stats n'est pas un objet
  6167. const fallbackEl = document.createElement('div');
  6168. fallbackEl.textContent = typeof stats === 'string' ? stats : JSON.stringify(stats);
  6169. statsContainer.appendChild(fallbackEl);
  6170. }
  6171. line.appendChild(dateEl);
  6172. line.appendChild(statsContainer);
  6173. historyList.appendChild(line);
  6174. }
  6175. show() {
  6176. // Recréer le conteneur pour un contenu frais
  6177. this.container = document.createElement('div');
  6178. this.closeBtn = document.createElement('button');
  6179. // Réinitialiser le conteneur
  6180. this.initContainer();
  6181. // Ajouter le bouton de fermeture
  6182. this.addCloseButton();
  6183. // Ajouter l'en-tête avec le titre
  6184. this.addHeader();
  6185. // Charger et afficher l'historique des jeux actualisé
  6186. this.renderContent();
  6187. // Close RSHIFT menu if it's open
  6188. if (this.kxsClient.secondaryMenu && typeof this.kxsClient.secondaryMenu.getMenuVisibility === 'function') {
  6189. if (this.kxsClient.secondaryMenu.getMenuVisibility()) {
  6190. this.kxsClient.secondaryMenu.toggleMenuVisibility();
  6191. }
  6192. }
  6193. // Prevent mouse event propagation
  6194. this.container.addEventListener('click', (e) => e.stopPropagation());
  6195. this.container.addEventListener('wheel', (e) => e.stopPropagation());
  6196. this.container.addEventListener('mousedown', (e) => e.stopPropagation());
  6197. this.container.addEventListener('mouseup', (e) => e.stopPropagation());
  6198. this.container.addEventListener('contextmenu', (e) => e.stopPropagation());
  6199. this.container.addEventListener('dblclick', (e) => e.stopPropagation());
  6200. // Center the menu on screen
  6201. this.container.style.top = '50%';
  6202. this.container.style.left = '50%';
  6203. this.container.style.transform = 'translate(-50%, -50%)';
  6204. // Add fade-in animation
  6205. this.container.style.opacity = '0';
  6206. this.container.style.transition = 'opacity 0.2s ease-in-out';
  6207. setTimeout(() => {
  6208. this.container.style.opacity = '1';
  6209. }, 10);
  6210. document.body.appendChild(this.container);
  6211. }
  6212. hide() {
  6213. // Clean up listeners before removing the menu
  6214. if (this.container) {
  6215. // Supprimer tous les gestionnaires d'événements
  6216. const allElements = this.container.querySelectorAll('*');
  6217. allElements.forEach(element => {
  6218. const el = element;
  6219. el.replaceWith(el.cloneNode(true));
  6220. });
  6221. // Remove the container
  6222. this.container.remove();
  6223. }
  6224. // Reset document cursor in case it was changed
  6225. document.body.style.cursor = '';
  6226. }
  6227. // Méthode alias pour compatibilité avec le code existant
  6228. close() {
  6229. this.hide();
  6230. }
  6231. }
  6232.  
  6233.  
  6234. ;// ./src/NETWORK/KxsNetwork.ts
  6235.  
  6236. class KxsNetwork {
  6237. sendGlobalChatMessage(text) {
  6238. if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
  6239. return;
  6240. const payload = {
  6241. op: 7,
  6242. d: {
  6243. user: this.getUsername(),
  6244. text
  6245. }
  6246. };
  6247. this.send(payload);
  6248. }
  6249. constructor(kxsClient) {
  6250. this.currentGamePlayers = [];
  6251. this.ws = null;
  6252. this.heartbeatInterval = 0;
  6253. this.isAuthenticated = false;
  6254. this.HOST = config_namespaceObject.api_url;
  6255. this.reconnectAttempts = 0;
  6256. this.maxReconnectAttempts = 3;
  6257. this.reconnectTimeout = 0;
  6258. this.reconnectDelay = 15000; // Initial reconnect delay of 1 second
  6259. this.kxsUsers = 0;
  6260. this.privateUsername = this.generateRandomUsername();
  6261. this.kxs_users = [];
  6262. this.kxsClient = kxsClient;
  6263. }
  6264. connect() {
  6265. if (this.ws && this.ws.readyState === WebSocket.OPEN) {
  6266. this.kxsClient.logger.log('[KxsNetwork] WebSocket already connected');
  6267. return;
  6268. }
  6269. this.ws = new WebSocket(this.getWebSocketURL());
  6270. this.ws.onopen = () => {
  6271. this.kxsClient.logger.log('[KxsNetwork] WebSocket connection established');
  6272. // Reset reconnect attempts on successful connection
  6273. this.reconnectAttempts = 0;
  6274. this.reconnectDelay = 1000;
  6275. };
  6276. this.ws.onmessage = (event) => {
  6277. const data = JSON.parse(event.data);
  6278. this.handleMessage(data);
  6279. };
  6280. this.ws.onerror = (error) => {
  6281. this.kxsClient.nm.showNotification('WebSocket error: ' + error.type, 'error', 900);
  6282. };
  6283. this.ws.onclose = () => {
  6284. this.kxsClient.nm.showNotification('Disconnected from KxsNetwork', 'info', 1100);
  6285. clearInterval(this.heartbeatInterval);
  6286. this.isAuthenticated = false;
  6287. // Try to reconnect
  6288. this.attemptReconnect();
  6289. };
  6290. }
  6291. attemptReconnect() {
  6292. if (this.reconnectAttempts < this.maxReconnectAttempts) {
  6293. this.reconnectAttempts++;
  6294. // Use exponential backoff for reconnection attempts
  6295. const delay = this.reconnectDelay * Math.pow(1.5, this.reconnectAttempts - 1);
  6296. this.kxsClient.logger.log(`[KxsNetwork] Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts}) in ${delay}ms`);
  6297. // Clear any existing timeout
  6298. if (this.reconnectTimeout) {
  6299. clearTimeout(this.reconnectTimeout);
  6300. }
  6301. // Set timeout for reconnection
  6302. this.reconnectTimeout = setTimeout(() => {
  6303. this.connect();
  6304. }, delay);
  6305. }
  6306. else {
  6307. this.kxsClient.logger.log('[KxsNetwork] Maximum reconnection attempts reached');
  6308. this.kxsClient.nm.showNotification('Failed to reconnect after multiple attempts', 'error', 2000);
  6309. }
  6310. }
  6311. generateRandomUsername() {
  6312. let char = 'abcdefghijklmnopqrstuvwxyz0123456789';
  6313. let username = '';
  6314. for (let i = 0; i < 6; i++) {
  6315. username += char[Math.floor(Math.random() * char.length)];
  6316. }
  6317. return "kxs_" + username;
  6318. }
  6319. getUsername() {
  6320. return this.kxsClient.kxsNetworkSettings.nickname_anonymized ? this.privateUsername : JSON.parse(localStorage.getItem("surviv_config") || "{}").playerName;
  6321. }
  6322. identify() {
  6323. const payload = {
  6324. op: 2,
  6325. d: {
  6326. username: this.getUsername(),
  6327. isVoiceChat: this.kxsClient.isVoiceChatEnabled
  6328. }
  6329. };
  6330. this.send(payload);
  6331. }
  6332. handleMessage(_data) {
  6333. const { op, d } = _data;
  6334. switch (op) {
  6335. case 1: //Heart
  6336. {
  6337. if (d === null || d === void 0 ? void 0 : d.count)
  6338. this.kxsUsers = d.count;
  6339. if (d === null || d === void 0 ? void 0 : d.players)
  6340. this.kxs_users = d.players;
  6341. }
  6342. break;
  6343. case 3: // Kxs user join game
  6344. {
  6345. if (d && Array.isArray(d.players)) {
  6346. const myName = this.getUsername();
  6347. const previousPlayers = this.currentGamePlayers;
  6348. const currentPlayers = d.players.filter((name) => name !== myName);
  6349. // Détecter les nouveaux joueurs (hors soi-même)
  6350. const newPlayers = currentPlayers.filter((name) => !previousPlayers.includes(name));
  6351. for (const newPlayer of newPlayers) {
  6352. if (this.kxsClient.isKxsChatEnabled) {
  6353. this.kxsClient.chat.addSystemMessage(`${newPlayer} joined the game as a Kxs player`);
  6354. }
  6355. else {
  6356. this.kxsClient.nm.showNotification(`🎉 ${newPlayer} is a Kxs player!`, 'info', 3500);
  6357. }
  6358. }
  6359. this.currentGamePlayers = currentPlayers;
  6360. }
  6361. }
  6362. break;
  6363. case 7: // Global chat message
  6364. {
  6365. if (d && d.user && d.text) {
  6366. this.kxsClient.chat.addChatMessage(d.user, d.text);
  6367. }
  6368. }
  6369. break;
  6370. case 10: // Hello
  6371. {
  6372. const { heartbeat_interval } = d;
  6373. this.startHeartbeat(heartbeat_interval);
  6374. this.identify();
  6375. }
  6376. break;
  6377. case 2: // Dispatch
  6378. {
  6379. if (d === null || d === void 0 ? void 0 : d.uuid) {
  6380. this.isAuthenticated = true;
  6381. }
  6382. }
  6383. break;
  6384. case 98: // VOICE CHAT UPDATE
  6385. {
  6386. if (d && !d.isVoiceChat && d.user) {
  6387. this.kxsClient.voiceChat.removeUserFromVoice(d.user);
  6388. }
  6389. }
  6390. break;
  6391. }
  6392. }
  6393. startHeartbeat(interval) {
  6394. // Clear existing interval if it exists
  6395. if (this.heartbeatInterval) {
  6396. clearInterval(this.heartbeatInterval);
  6397. }
  6398. this.heartbeatInterval = setInterval(() => {
  6399. this.send({
  6400. op: 1,
  6401. d: {}
  6402. });
  6403. }, interval);
  6404. }
  6405. send(data) {
  6406. var _a;
  6407. if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
  6408. this.ws.send(JSON.stringify(data));
  6409. }
  6410. }
  6411. disconnect() {
  6412. if (this.ws) {
  6413. // Clear all timers
  6414. clearInterval(this.heartbeatInterval);
  6415. clearTimeout(this.reconnectTimeout);
  6416. // Reset reconnection state
  6417. this.reconnectAttempts = 0;
  6418. // Close the connection
  6419. this.ws.close();
  6420. }
  6421. }
  6422. reconnect() {
  6423. this.disconnect();
  6424. this.connect();
  6425. }
  6426. sendGameInfoToWebSocket(gameId) {
  6427. if (!this.isAuthenticated || !this.ws || this.ws.readyState !== WebSocket.OPEN) {
  6428. return;
  6429. }
  6430. try {
  6431. const payload = {
  6432. op: 3, // Custom operation code for game info
  6433. d: {
  6434. type: 'find_game_response',
  6435. gameId,
  6436. user: this.getUsername()
  6437. }
  6438. };
  6439. this.send(payload);
  6440. }
  6441. catch (error) {
  6442. }
  6443. }
  6444. getWebSocketURL() {
  6445. let isSecured = this.HOST.startsWith("https://");
  6446. let protocols = isSecured ? "wss://" : "ws://";
  6447. return protocols + this.HOST.split("/")[2];
  6448. }
  6449. getHTTPURL() {
  6450. return this.HOST;
  6451. }
  6452. getOnlineCount() {
  6453. return this.kxsUsers;
  6454. }
  6455. getKxsUsers() {
  6456. return this.kxs_users;
  6457. }
  6458. gameEnded() {
  6459. var _a;
  6460. (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({ op: 4, d: {} }));
  6461. }
  6462. }
  6463.  
  6464.  
  6465. ;// ./src/UTILS/KxsChat.ts
  6466. class KxsChat {
  6467. constructor(kxsClient) {
  6468. this.chatInput = null;
  6469. this.chatBox = null;
  6470. this.messagesContainer = null;
  6471. this.chatMessages = [];
  6472. this.chatOpen = false;
  6473. this.handleKeyDown = (e) => {
  6474. if (e.key === 'Enter' && !this.chatOpen && document.activeElement !== this.chatInput) {
  6475. e.preventDefault();
  6476. this.openChatInput();
  6477. }
  6478. else if (e.key === 'Escape' && this.chatOpen) {
  6479. this.closeChatInput();
  6480. }
  6481. };
  6482. this.kxsClient = kxsClient;
  6483. this.initGlobalChat();
  6484. // Initialize chat visibility based on the current setting
  6485. if (this.chatBox && !this.kxsClient.isKxsChatEnabled) {
  6486. this.chatBox.style.display = 'none';
  6487. window.removeEventListener('keydown', this.handleKeyDown);
  6488. }
  6489. }
  6490. initGlobalChat() {
  6491. const area = document.getElementById('game-touch-area');
  6492. if (!area)
  6493. return;
  6494. // Chat box
  6495. const chatBox = document.createElement('div');
  6496. chatBox.id = 'kxs-chat-box';
  6497. // Messages container
  6498. const messagesContainer = document.createElement('div');
  6499. messagesContainer.id = 'kxs-chat-messages';
  6500. messagesContainer.style.display = 'flex';
  6501. messagesContainer.style.flexDirection = 'column';
  6502. messagesContainer.style.gap = '3px';
  6503. chatBox.appendChild(messagesContainer);
  6504. this.messagesContainer = messagesContainer;
  6505. chatBox.style.position = 'absolute';
  6506. chatBox.style.left = '50%';
  6507. chatBox.style.bottom = '38px';
  6508. chatBox.style.transform = 'translateX(-50%)';
  6509. chatBox.style.minWidth = '260px';
  6510. chatBox.style.maxWidth = '480px';
  6511. chatBox.style.background = 'rgba(30,30,40,0.80)';
  6512. chatBox.style.color = '#fff';
  6513. chatBox.style.borderRadius = '10px';
  6514. chatBox.style.padding = '7px 14px 4px 14px';
  6515. chatBox.style.fontSize = '15px';
  6516. chatBox.style.fontFamily = 'inherit';
  6517. chatBox.style.zIndex = '1002';
  6518. chatBox.style.pointerEvents = 'auto';
  6519. chatBox.style.cursor = 'move'; // Indique que c'est déplaçable
  6520. chatBox.style.display = 'flex';
  6521. chatBox.style.flexDirection = 'column';
  6522. chatBox.style.gap = '3px';
  6523. chatBox.style.opacity = '0.5';
  6524. // Charger la position sauvegardée dès l'initialisation
  6525. const savedPosition = localStorage.getItem('kxs-chat-box-position');
  6526. if (savedPosition) {
  6527. try {
  6528. const { x, y } = JSON.parse(savedPosition);
  6529. chatBox.style.left = `${x}px`;
  6530. chatBox.style.top = `${y}px`;
  6531. chatBox.style.position = 'absolute';
  6532. }
  6533. catch (e) { }
  6534. }
  6535. area.appendChild(chatBox);
  6536. this.chatBox = chatBox;
  6537. // Rendre la chatbox draggable UNIQUEMENT si le menu secondaire est ouvert
  6538. const updateChatDraggable = () => {
  6539. const isMenuOpen = this.kxsClient.secondaryMenu.getMenuVisibility();
  6540. if (isMenuOpen) {
  6541. chatBox.style.pointerEvents = 'auto';
  6542. chatBox.style.cursor = 'move';
  6543. this.kxsClient.makeDraggable(chatBox, 'kxs-chat-box-position');
  6544. }
  6545. else {
  6546. chatBox.style.pointerEvents = 'none';
  6547. chatBox.style.cursor = 'default';
  6548. }
  6549. };
  6550. // Initial state
  6551. updateChatDraggable();
  6552. // Observe menu changes
  6553. const observer = new MutationObserver(updateChatDraggable);
  6554. if (this.kxsClient.secondaryMenu && this.kxsClient.secondaryMenu.menu) {
  6555. observer.observe(this.kxsClient.secondaryMenu.menu, { attributes: true, attributeFilter: ['style', 'class'] });
  6556. }
  6557. // Optionnel : timer pour fallback (si le menu est modifié autrement)
  6558. setInterval(updateChatDraggable, 500);
  6559. // Input
  6560. const input = document.createElement('input');
  6561. input.type = 'text';
  6562. input.placeholder = 'Press Enter to write...';
  6563. input.id = 'kxs-chat-input';
  6564. input.style.width = '100%';
  6565. input.style.boxSizing = 'border-box';
  6566. input.style.padding = '8px 12px';
  6567. input.style.borderRadius = '8px';
  6568. input.style.border = 'none';
  6569. input.style.background = 'rgba(40,40,50,0.95)';
  6570. input.style.color = '#fff';
  6571. input.style.fontSize = '15px';
  6572. input.style.fontFamily = 'inherit';
  6573. input.style.zIndex = '1003';
  6574. input.style.outline = 'none';
  6575. input.style.display = this.chatOpen ? 'block' : 'none';
  6576. input.style.opacity = '0.5';
  6577. input.style.marginTop = 'auto'; // Pour coller l'input en bas
  6578. chatBox.appendChild(input); // Ajoute l'input dans la chatBox
  6579. this.chatInput = input;
  6580. // Ajuste le style de chatBox pour le layout
  6581. chatBox.style.display = 'flex';
  6582. chatBox.style.flexDirection = 'column';
  6583. chatBox.style.gap = '3px';
  6584. chatBox.style.justifyContent = 'flex-end'; // S'assure que l'input est en bas
  6585. // Focus automatique sur l'input quand on clique dessus ou sur la chatBox
  6586. input.addEventListener('focus', () => {
  6587. // Rien de spécial, mais peut servir à customiser plus tard
  6588. });
  6589. chatBox.addEventListener('mousedown', (e) => {
  6590. // Focus l'input si clic sur la chatBox (hors drag)
  6591. if (e.target === chatBox) {
  6592. input.focus();
  6593. }
  6594. });
  6595. input.addEventListener('mousedown', () => {
  6596. input.focus();
  6597. });
  6598. ['keydown', 'keypress', 'keyup'].forEach(eventType => {
  6599. input.addEventListener(eventType, (e) => {
  6600. const ke = e;
  6601. if (eventType === 'keydown') {
  6602. if (ke.key === 'Enter') {
  6603. const txt = input.value.trim();
  6604. if (txt) {
  6605. this.kxsClient.kxsNetwork.sendGlobalChatMessage(txt);
  6606. input.value = '';
  6607. this.closeChatInput();
  6608. }
  6609. else {
  6610. // Ne ferme pas l'input si rien n'a été écrit
  6611. input.value = '';
  6612. }
  6613. }
  6614. else if (ke.key === 'Escape') {
  6615. this.closeChatInput();
  6616. }
  6617. }
  6618. e.stopImmediatePropagation();
  6619. e.stopPropagation();
  6620. }, true);
  6621. });
  6622. // Gestion clavier
  6623. window.addEventListener('keydown', this.handleKeyDown);
  6624. }
  6625. openChatInput() {
  6626. if (!this.chatInput)
  6627. return;
  6628. this.chatInput.placeholder = 'Press Enter to write...';
  6629. this.chatInput.value = '';
  6630. this.chatInput.style.display = 'block';
  6631. this.chatInput.focus();
  6632. this.chatOpen = true;
  6633. }
  6634. closeChatInput() {
  6635. if (!this.chatInput)
  6636. return;
  6637. this.chatInput.style.display = 'none';
  6638. this.chatInput.blur();
  6639. this.chatOpen = false;
  6640. }
  6641. addChatMessage(user, text) {
  6642. if (!this.chatBox || !this.kxsClient.isKxsChatEnabled)
  6643. return;
  6644. this.chatMessages.push({ user, text, isSystem: false });
  6645. if (this.chatMessages.length > 5)
  6646. this.chatMessages.shift();
  6647. this.renderMessages();
  6648. }
  6649. /**
  6650. * Ajoute un message système dans le chat
  6651. * @param text Texte du message système
  6652. */
  6653. addSystemMessage(text) {
  6654. if (!this.chatBox || !this.kxsClient.isKxsChatEnabled)
  6655. return;
  6656. // Ajouter le message système avec un marqueur spécifique isSystem = true
  6657. this.chatMessages.push({ user: "", text, isSystem: true });
  6658. if (this.chatMessages.length > 5)
  6659. this.chatMessages.shift();
  6660. this.renderMessages();
  6661. }
  6662. /**
  6663. * Rend les messages du chat avec leur style approprié
  6664. */
  6665. renderMessages() {
  6666. if (!this.messagesContainer)
  6667. return;
  6668. this.messagesContainer.innerHTML = this.chatMessages.map(m => {
  6669. if (m.isSystem) {
  6670. return `<span style='color:#3B82F6; font-style:italic;'>${m.text}</span>`;
  6671. }
  6672. else {
  6673. return `<span><b style='color:#3fae2a;'>${m.user}</b>: ${m.text}</span>`;
  6674. }
  6675. }).join('');
  6676. }
  6677. toggleChat() {
  6678. if (this.chatBox) {
  6679. this.chatBox.style.display = this.kxsClient.isKxsChatEnabled ? 'flex' : 'none';
  6680. }
  6681. if (this.kxsClient.isKxsChatEnabled) {
  6682. window.addEventListener('keydown', this.handleKeyDown);
  6683. }
  6684. else {
  6685. this.closeChatInput();
  6686. window.removeEventListener('keydown', this.handleKeyDown);
  6687. }
  6688. const message = this.kxsClient.isKxsChatEnabled ? 'Chat enabled' : 'Chat disabled';
  6689. const type = this.kxsClient.isKxsChatEnabled ? 'success' : 'info';
  6690. this.kxsClient.nm.showNotification(message, type, 600);
  6691. }
  6692. }
  6693.  
  6694.  
  6695. ;// ./src/UTILS/KxsVoiceChat.ts
  6696. var KxsVoiceChat_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  6697. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  6698. return new (P || (P = Promise))(function (resolve, reject) {
  6699. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6700. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  6701. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  6702. step((generator = generator.apply(thisArg, _arguments || [])).next());
  6703. });
  6704. };
  6705. class KxsVoiceChat {
  6706. constructor(kxsClient, kxsNetwork) {
  6707. this.audioCtx = null;
  6708. this.micStream = null;
  6709. this.micSource = null;
  6710. this.processor = null;
  6711. // Overlay elements
  6712. this.overlayContainer = null;
  6713. this.activeUsers = new Map();
  6714. this.mutedUsers = new Set();
  6715. this.activityCheckInterval = null;
  6716. this.isOverlayVisible = true;
  6717. this.isLocalMuted = false;
  6718. this.localMuteButton = null;
  6719. // Constants
  6720. this.ACTIVITY_THRESHOLD = 0.01;
  6721. this.INACTIVITY_TIMEOUT = 2000;
  6722. this.REMOVAL_TIMEOUT = 30000;
  6723. this.ACTIVITY_CHECK_INTERVAL = 500;
  6724. this.kxsClient = kxsClient;
  6725. this.kxsNetwork = kxsNetwork;
  6726. this.createOverlayContainer();
  6727. }
  6728. /**
  6729. * Remove a user from voice chat (e.g., when muted)
  6730. */
  6731. removeUserFromVoice(username) {
  6732. if (this.activeUsers.has(username)) {
  6733. this.activeUsers.delete(username);
  6734. this.updateOverlayUI();
  6735. }
  6736. }
  6737. startVoiceChat() {
  6738. return KxsVoiceChat_awaiter(this, void 0, void 0, function* () {
  6739. if (!this.kxsClient.isVoiceChatEnabled)
  6740. return;
  6741. this.cleanup();
  6742. this.showOverlay();
  6743. try {
  6744. this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  6745. this.micStream = yield navigator.mediaDevices.getUserMedia({
  6746. audio: {
  6747. sampleRate: 48000,
  6748. channelCount: 1,
  6749. echoCancellation: true,
  6750. noiseSuppression: true,
  6751. autoGainControl: true
  6752. }
  6753. });
  6754. this.micSource = this.audioCtx.createMediaStreamSource(this.micStream);
  6755. this.processor = this.audioCtx.createScriptProcessor(2048, 1, 1);
  6756. this.micSource.connect(this.processor);
  6757. this.processor.connect(this.audioCtx.destination);
  6758. // Set up audio processing
  6759. this.setupAudioProcessing();
  6760. this.setupWebSocketListeners();
  6761. }
  6762. catch (error) {
  6763. alert("Unable to initialize voice chat: " + error.message);
  6764. this.cleanup();
  6765. }
  6766. });
  6767. }
  6768. setupAudioProcessing() {
  6769. if (!this.processor)
  6770. return;
  6771. this.processor.onaudioprocess = (e) => {
  6772. if (!this.kxsNetwork.ws || this.kxsNetwork.ws.readyState !== WebSocket.OPEN)
  6773. return;
  6774. // Ne pas envoyer les données audio si l'utilisateur local est muté
  6775. if (this.isLocalMuted)
  6776. return;
  6777. const input = e.inputBuffer.getChannelData(0);
  6778. const int16 = new Int16Array(input.length);
  6779. for (let i = 0; i < input.length; i++) {
  6780. int16[i] = Math.max(-32768, Math.min(32767, input[i] * 32767));
  6781. }
  6782. this.kxsNetwork.ws.send(JSON.stringify({ op: 99, d: Array.from(int16) }));
  6783. };
  6784. }
  6785. setupWebSocketListeners() {
  6786. if (!this.kxsNetwork.ws)
  6787. return;
  6788. this.kxsNetwork.ws.addEventListener('message', this.handleAudioMessage.bind(this));
  6789. }
  6790. handleAudioMessage(msg) {
  6791. let parsed;
  6792. try {
  6793. parsed = typeof msg.data === 'string' ? JSON.parse(msg.data) : msg.data;
  6794. }
  6795. catch (_a) {
  6796. return;
  6797. }
  6798. if (!parsed || parsed.op !== 99 || !parsed.d || !parsed.u)
  6799. return;
  6800. try {
  6801. // Skip if user is muted
  6802. if (this.mutedUsers.has(parsed.u))
  6803. return;
  6804. const int16Data = new Int16Array(parsed.d);
  6805. const floatData = new Float32Array(int16Data.length);
  6806. // Calculate audio level for visualization
  6807. let audioLevel = 0;
  6808. for (let i = 0; i < int16Data.length; i++) {
  6809. floatData[i] = int16Data[i] / 32767;
  6810. audioLevel += floatData[i] * floatData[i];
  6811. }
  6812. audioLevel = Math.sqrt(audioLevel / int16Data.length);
  6813. // Update user activity in the overlay
  6814. this.updateUserActivity(parsed.u, audioLevel);
  6815. // Play the audio
  6816. this.playAudio(floatData);
  6817. }
  6818. catch (error) {
  6819. console.error("Audio processing error:", error);
  6820. }
  6821. }
  6822. playAudio(floatData) {
  6823. if (!this.audioCtx)
  6824. return;
  6825. const buffer = this.audioCtx.createBuffer(1, floatData.length, this.audioCtx.sampleRate);
  6826. buffer.getChannelData(0).set(floatData);
  6827. const source = this.audioCtx.createBufferSource();
  6828. source.buffer = buffer;
  6829. source.connect(this.audioCtx.destination);
  6830. source.start();
  6831. }
  6832. stopVoiceChat() {
  6833. this.cleanup();
  6834. this.hideOverlay();
  6835. }
  6836. cleanup() {
  6837. if (this.processor) {
  6838. this.processor.disconnect();
  6839. this.processor = null;
  6840. }
  6841. if (this.micSource) {
  6842. this.micSource.disconnect();
  6843. this.micSource = null;
  6844. }
  6845. if (this.micStream) {
  6846. this.micStream.getTracks().forEach(track => track.stop());
  6847. this.micStream = null;
  6848. }
  6849. if (this.audioCtx) {
  6850. this.audioCtx.close();
  6851. this.audioCtx = null;
  6852. }
  6853. if (this.activityCheckInterval) {
  6854. window.clearInterval(this.activityCheckInterval);
  6855. this.activityCheckInterval = null;
  6856. }
  6857. }
  6858. toggleVoiceChat() {
  6859. var _a, _b;
  6860. if (this.kxsClient.isVoiceChatEnabled) {
  6861. (_a = this.kxsNetwork.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({
  6862. op: 98,
  6863. d: { isVoiceChat: true }
  6864. }));
  6865. this.startVoiceChat();
  6866. }
  6867. else {
  6868. this.stopVoiceChat();
  6869. (_b = this.kxsNetwork.ws) === null || _b === void 0 ? void 0 : _b.send(JSON.stringify({
  6870. op: 98,
  6871. d: { isVoiceChat: false }
  6872. }));
  6873. }
  6874. }
  6875. createOverlayContainer() {
  6876. if (this.overlayContainer)
  6877. return;
  6878. this.overlayContainer = document.createElement('div');
  6879. this.overlayContainer.id = 'kxs-voice-chat-overlay';
  6880. Object.assign(this.overlayContainer.style, {
  6881. position: 'absolute',
  6882. top: '10px',
  6883. right: '10px',
  6884. width: '200px',
  6885. backgroundColor: 'rgba(0, 0, 0, 0.7)',
  6886. color: 'white',
  6887. padding: '10px',
  6888. borderRadius: '5px',
  6889. zIndex: '1000',
  6890. fontFamily: 'Arial, sans-serif',
  6891. fontSize: '14px',
  6892. display: 'none',
  6893. cursor: 'move'
  6894. });
  6895. // Charger la position sauvegardée si elle existe
  6896. const savedPosition = localStorage.getItem('kxs-voice-chat-position');
  6897. if (savedPosition) {
  6898. try {
  6899. const { x, y } = JSON.parse(savedPosition);
  6900. this.overlayContainer.style.left = `${x}px`;
  6901. this.overlayContainer.style.top = `${y}px`;
  6902. this.overlayContainer.style.right = 'auto';
  6903. }
  6904. catch (e) { }
  6905. }
  6906. // Add title and controls container (for title and mute button)
  6907. const controlsContainer = document.createElement('div');
  6908. Object.assign(controlsContainer.style, {
  6909. display: 'flex',
  6910. justifyContent: 'space-between',
  6911. alignItems: 'center',
  6912. marginBottom: '5px',
  6913. borderBottom: '1px solid rgba(255, 255, 255, 0.3)',
  6914. paddingBottom: '5px'
  6915. });
  6916. // Title
  6917. const title = document.createElement('div');
  6918. title.textContent = 'Voice Chat';
  6919. Object.assign(title.style, {
  6920. fontWeight: 'bold'
  6921. });
  6922. // Fonction pour mettre à jour l'état draggable selon la visibilité du menu RSHIFT
  6923. const updateVoiceChatDraggable = () => {
  6924. const isMenuOpen = this.kxsClient.secondaryMenu.getMenuVisibility();
  6925. if (isMenuOpen) {
  6926. this.overlayContainer.style.pointerEvents = 'auto';
  6927. this.overlayContainer.style.cursor = 'move';
  6928. this.kxsClient.makeDraggable(this.overlayContainer, 'kxs-voice-chat-position');
  6929. }
  6930. else {
  6931. this.overlayContainer.style.pointerEvents = 'none';
  6932. this.overlayContainer.style.cursor = 'default';
  6933. }
  6934. };
  6935. // Initial state
  6936. updateVoiceChatDraggable();
  6937. // Observer les changements du menu
  6938. const observer = new MutationObserver(updateVoiceChatDraggable);
  6939. if (this.kxsClient.secondaryMenu && this.kxsClient.secondaryMenu.menu) {
  6940. observer.observe(this.kxsClient.secondaryMenu.menu, { attributes: true, attributeFilter: ['style', 'class'] });
  6941. }
  6942. // Fallback timer pour s'assurer de l'état correct
  6943. setInterval(updateVoiceChatDraggable, 500);
  6944. // Create local mute button
  6945. this.localMuteButton = this.createLocalMuteButton();
  6946. // Add elements to controls container
  6947. controlsContainer.appendChild(title);
  6948. controlsContainer.appendChild(this.localMuteButton);
  6949. this.overlayContainer.appendChild(controlsContainer);
  6950. // Container for users
  6951. const usersContainer = document.createElement('div');
  6952. usersContainer.id = 'kxs-voice-chat-users';
  6953. this.overlayContainer.appendChild(usersContainer);
  6954. document.body.appendChild(this.overlayContainer);
  6955. this.startActivityCheck();
  6956. }
  6957. showOverlay() {
  6958. if (!this.overlayContainer)
  6959. return;
  6960. this.overlayContainer.style.display = 'block';
  6961. this.isOverlayVisible = true;
  6962. if (!this.activityCheckInterval) {
  6963. this.startActivityCheck();
  6964. }
  6965. }
  6966. hideOverlay() {
  6967. if (!this.overlayContainer)
  6968. return;
  6969. this.overlayContainer.style.display = 'none';
  6970. this.isOverlayVisible = false;
  6971. }
  6972. toggleOverlay() {
  6973. if (this.isOverlayVisible) {
  6974. this.hideOverlay();
  6975. }
  6976. else {
  6977. this.showOverlay();
  6978. }
  6979. return this.isOverlayVisible;
  6980. }
  6981. updateUserActivity(username, audioLevel) {
  6982. const now = Date.now();
  6983. const isActive = audioLevel > this.ACTIVITY_THRESHOLD;
  6984. let user = this.activeUsers.get(username);
  6985. if (!user) {
  6986. user = {
  6987. username,
  6988. isActive,
  6989. lastActivity: now,
  6990. audioLevel,
  6991. isMuted: this.mutedUsers.has(username)
  6992. };
  6993. this.activeUsers.set(username, user);
  6994. }
  6995. else {
  6996. user.isActive = isActive;
  6997. user.lastActivity = now;
  6998. user.audioLevel = audioLevel;
  6999. user.isMuted = this.mutedUsers.has(username);
  7000. }
  7001. this.updateOverlayUI();
  7002. }
  7003. startActivityCheck() {
  7004. this.activityCheckInterval = window.setInterval(() => {
  7005. const now = Date.now();
  7006. let updated = false;
  7007. this.activeUsers.forEach((user, username) => {
  7008. // Set inactive if no activity for the specified timeout
  7009. if (now - user.lastActivity > this.INACTIVITY_TIMEOUT && user.isActive) {
  7010. user.isActive = false;
  7011. updated = true;
  7012. }
  7013. // Remove users inactive for longer period
  7014. if (now - user.lastActivity > this.REMOVAL_TIMEOUT) {
  7015. this.activeUsers.delete(username);
  7016. updated = true;
  7017. }
  7018. });
  7019. if (updated) {
  7020. this.updateOverlayUI();
  7021. }
  7022. }, this.ACTIVITY_CHECK_INTERVAL);
  7023. }
  7024. updateOverlayUI() {
  7025. if (!this.overlayContainer || !this.isOverlayVisible)
  7026. return;
  7027. const usersContainer = document.getElementById('kxs-voice-chat-users');
  7028. if (!usersContainer)
  7029. return;
  7030. // Clear existing users
  7031. usersContainer.innerHTML = '';
  7032. // Add users or show "no users" message
  7033. if (this.activeUsers.size === 0) {
  7034. this.renderNoUsersMessage(usersContainer);
  7035. }
  7036. else {
  7037. this.activeUsers.forEach(user => {
  7038. this.renderUserElement(usersContainer, user);
  7039. });
  7040. }
  7041. }
  7042. renderNoUsersMessage(container) {
  7043. const noUsers = document.createElement('div');
  7044. noUsers.textContent = 'No active users';
  7045. Object.assign(noUsers.style, {
  7046. color: 'rgba(255, 255, 255, 0.6)',
  7047. fontStyle: 'italic',
  7048. textAlign: 'center',
  7049. padding: '5px'
  7050. });
  7051. container.appendChild(noUsers);
  7052. }
  7053. renderUserElement(container, user) {
  7054. const userElement = document.createElement('div');
  7055. userElement.className = 'kxs-voice-chat-user';
  7056. Object.assign(userElement.style, {
  7057. display: 'flex',
  7058. alignItems: 'center',
  7059. margin: '3px 0',
  7060. padding: '3px',
  7061. borderRadius: '3px',
  7062. backgroundColor: 'rgba(255, 255, 255, 0.1)'
  7063. });
  7064. // Status indicator
  7065. const indicator = this.createStatusIndicator(user);
  7066. // Username label
  7067. const usernameLabel = document.createElement('span');
  7068. usernameLabel.textContent = user.username;
  7069. Object.assign(usernameLabel.style, {
  7070. flexGrow: '1',
  7071. whiteSpace: 'nowrap',
  7072. overflow: 'hidden',
  7073. textOverflow: 'ellipsis'
  7074. });
  7075. // Mute button - FIX: Create this element properly
  7076. const muteButton = this.createMuteButton(user);
  7077. // Add elements to container
  7078. userElement.appendChild(indicator);
  7079. userElement.appendChild(usernameLabel);
  7080. userElement.appendChild(muteButton);
  7081. container.appendChild(userElement);
  7082. }
  7083. createStatusIndicator(user) {
  7084. const indicator = document.createElement('div');
  7085. Object.assign(indicator.style, {
  7086. width: '14px',
  7087. height: '14px',
  7088. borderRadius: '50%',
  7089. marginRight: '8px',
  7090. cursor: 'pointer'
  7091. });
  7092. indicator.title = user.isMuted ? 'Unmute' : 'Mute';
  7093. if (user.isActive) {
  7094. const scale = 1 + Math.min(user.audioLevel * 3, 1);
  7095. Object.assign(indicator.style, {
  7096. backgroundColor: '#2ecc71',
  7097. transform: `scale(${scale})`,
  7098. boxShadow: '0 0 5px #2ecc71',
  7099. transition: 'transform 0.1s ease-in-out'
  7100. });
  7101. }
  7102. else {
  7103. indicator.style.backgroundColor = '#7f8c8d';
  7104. }
  7105. return indicator;
  7106. }
  7107. createMuteButton(user) {
  7108. const muteButton = document.createElement('button');
  7109. muteButton.type = 'button'; // Important: specify type to prevent form submission behavior
  7110. muteButton.textContent = user.isMuted ? 'UNMUTE' : 'MUTE';
  7111. Object.assign(muteButton.style, {
  7112. backgroundColor: user.isMuted ? '#e74c3c' : '#7f8c8d',
  7113. color: 'white',
  7114. border: 'none',
  7115. borderRadius: '3px',
  7116. padding: '2px 5px',
  7117. marginLeft: '5px',
  7118. cursor: 'pointer',
  7119. fontSize: '11px',
  7120. fontWeight: 'bold',
  7121. minWidth: '40px'
  7122. });
  7123. muteButton.addEventListener('mouseover', () => {
  7124. muteButton.style.opacity = '0.8';
  7125. });
  7126. muteButton.addEventListener('mouseout', () => {
  7127. muteButton.style.opacity = '1';
  7128. });
  7129. const handleMuteToggle = (e) => {
  7130. e.stopImmediatePropagation();
  7131. e.stopPropagation();
  7132. e.preventDefault();
  7133. const newMutedState = !user.isMuted;
  7134. user.isMuted = newMutedState;
  7135. if (newMutedState) {
  7136. this.mutedUsers.add(user.username);
  7137. }
  7138. else {
  7139. this.mutedUsers.delete(user.username);
  7140. }
  7141. this.sendMuteState(user.username, newMutedState);
  7142. this.updateOverlayUI();
  7143. return false;
  7144. };
  7145. ['click', 'mousedown', 'pointerdown'].forEach(eventType => {
  7146. muteButton.addEventListener(eventType, handleMuteToggle, true);
  7147. });
  7148. muteButton.onclick = (e) => {
  7149. e.stopImmediatePropagation();
  7150. e.stopPropagation();
  7151. e.preventDefault();
  7152. const newMutedState = !user.isMuted;
  7153. user.isMuted = newMutedState;
  7154. if (newMutedState) {
  7155. this.mutedUsers.add(user.username);
  7156. }
  7157. else {
  7158. this.mutedUsers.delete(user.username);
  7159. }
  7160. this.sendMuteState(user.username, newMutedState);
  7161. this.updateOverlayUI();
  7162. return false;
  7163. };
  7164. return muteButton;
  7165. }
  7166. sendMuteState(username, isMuted) {
  7167. if (!this.kxsNetwork.ws || this.kxsNetwork.ws.readyState !== WebSocket.OPEN) {
  7168. return;
  7169. }
  7170. this.kxsNetwork.ws.send(JSON.stringify({
  7171. op: 100,
  7172. d: {
  7173. user: username,
  7174. isMuted: isMuted
  7175. }
  7176. }));
  7177. }
  7178. createLocalMuteButton() {
  7179. const muteButton = document.createElement('button');
  7180. muteButton.type = 'button';
  7181. muteButton.textContent = this.isLocalMuted ? 'UNMUTE' : 'MUTE';
  7182. muteButton.id = 'kxs-voice-chat-local-mute';
  7183. Object.assign(muteButton.style, {
  7184. backgroundColor: this.isLocalMuted ? '#e74c3c' : '#3498db',
  7185. color: 'white',
  7186. border: 'none',
  7187. borderRadius: '3px',
  7188. padding: '2px 5px',
  7189. cursor: 'pointer',
  7190. fontSize: '11px',
  7191. fontWeight: 'bold',
  7192. minWidth: '55px'
  7193. });
  7194. muteButton.addEventListener('mouseover', () => {
  7195. muteButton.style.opacity = '0.8';
  7196. });
  7197. muteButton.addEventListener('mouseout', () => {
  7198. muteButton.style.opacity = '1';
  7199. });
  7200. // Utiliser un gestionnaire d'événement unique plus simple avec une vérification pour éviter les multiples déclenchements
  7201. muteButton.onclick = (e) => {
  7202. // Arrêter complètement la propagation de l'événement
  7203. e.stopImmediatePropagation();
  7204. e.stopPropagation();
  7205. e.preventDefault();
  7206. // Basculer l'état de mute
  7207. this.toggleLocalMute();
  7208. return false;
  7209. };
  7210. return muteButton;
  7211. }
  7212. toggleLocalMute() {
  7213. // Inverser l'état
  7214. this.isLocalMuted = !this.isLocalMuted;
  7215. // Mettre à jour l'apparence du bouton si présent
  7216. if (this.localMuteButton) {
  7217. // Définir clairement le texte et la couleur du bouton en fonction de l'état
  7218. this.localMuteButton.textContent = this.isLocalMuted ? 'UNMUTE' : 'MUTE';
  7219. this.localMuteButton.style.backgroundColor = this.isLocalMuted ? '#e74c3c' : '#3498db';
  7220. }
  7221. // Type de notification en fonction de si nous sommes sur error, info ou success
  7222. const notificationType = this.isLocalMuted ? 'error' : 'success';
  7223. // Notification de changement d'état
  7224. const message = this.isLocalMuted ? 'You are muted' : 'You are unmuted';
  7225. this.kxsClient.nm.showNotification(message, notificationType, 2000);
  7226. }
  7227. }
  7228.  
  7229.  
  7230. ;// ./src/KxsClient.ts
  7231. var KxsClient_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
  7232. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  7233. return new (P || (P = Promise))(function (resolve, reject) {
  7234. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  7235. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7236. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  7237. step((generator = generator.apply(thisArg, _arguments || [])).next());
  7238. });
  7239. };
  7240.  
  7241.  
  7242.  
  7243.  
  7244.  
  7245.  
  7246.  
  7247.  
  7248.  
  7249.  
  7250.  
  7251.  
  7252.  
  7253.  
  7254.  
  7255.  
  7256.  
  7257.  
  7258. class KxsClient {
  7259. constructor() {
  7260. this.onlineMenuElement = null;
  7261. this.onlineMenuInterval = null;
  7262. this.deathObserver = null;
  7263. this.adBlockObserver = null;
  7264. globalThis.kxsClient = this;
  7265. this.logger = new Logger();
  7266. this.config = config_namespaceObject;
  7267. this.menu = document.createElement("div");
  7268. this.lastFrameTime = performance.now();
  7269. this.isFpsUncapped = false;
  7270. this.isFpsVisible = true;
  7271. this.isPingVisible = true;
  7272. this.isKillsVisible = true;
  7273. this.isDeathSoundEnabled = true;
  7274. this.isWinSoundEnabled = true;
  7275. this.isHealthWarningEnabled = true;
  7276. this.isAutoUpdateEnabled = true;
  7277. this.isWinningAnimationEnabled = true;
  7278. this.isKillLeaderTrackerEnabled = true;
  7279. this.isKillFeedBlint = false;
  7280. this.isSpotifyPlayerEnabled = false;
  7281. this.discordToken = null;
  7282. this.counters = {};
  7283. this.all_friends = '';
  7284. this.isMainMenuCleaned = false;
  7285. this.isNotifyingForToggleMenu = true;
  7286. this.isGunOverlayColored = true;
  7287. this.customCrosshair = null;
  7288. this.isGunBorderChromatic = false;
  7289. this.isKxsChatEnabled = true;
  7290. this.isVoiceChatEnabled = false;
  7291. this.isFocusModeEnabled = false;
  7292. this.isHealBarIndicatorEnabled = true;
  7293. this.brightness = 50;
  7294. this.isKxsClientLogoEnable = true;
  7295. this.defaultPositions = {
  7296. fps: { left: 20, top: 160 },
  7297. ping: { left: 20, top: 220 },
  7298. kills: { left: 20, top: 280 },
  7299. lowHpWarning: { left: 285, top: 742 },
  7300. };
  7301. this.defaultSizes = {
  7302. fps: { width: 100, height: 30 },
  7303. ping: { width: 100, height: 30 },
  7304. kills: { width: 100, height: 30 },
  7305. };
  7306. this.kxsNetworkSettings = {
  7307. nickname_anonymized: false,
  7308. };
  7309. this.soundLibrary = {
  7310. win_sound_url: win_sound,
  7311. death_sound_url: death_sound,
  7312. background_sound_url: background_song,
  7313. };
  7314. this.gridSystem = new GridSystem();
  7315. this.db = new browser/* SteganoDB */.w({ database: "KxsClient", tableName: "gameplay_history" });
  7316. // Before all, load local storage
  7317. this.loadLocalStorage();
  7318. this.updateLocalStorage();
  7319. this.changeSurvevLogo();
  7320. this.nm = NotificationManager.getInstance();
  7321. this.discordRPC = new DiscordWebSocket(this, this.parseToken(this.discordToken));
  7322. this.updater = new UpdateChecker(this);
  7323. this.kill_leader = new KillLeaderTracker(this);
  7324. this.healWarning = new HealthWarning(this);
  7325. this.historyManager = new GameHistoryMenu(this);
  7326. this.kxsNetwork = new KxsNetwork(this);
  7327. this.setAnimationFrameCallback();
  7328. this.loadBackgroundFromLocalStorage();
  7329. this.initDeathDetection();
  7330. this.discordRPC.connect();
  7331. this.hud = new KxsClientHUD(this);
  7332. this.secondaryMenu = new KxsClientSecondaryMenu(this);
  7333. this.discordTracker = new DiscordTracking(this, this.discordWebhookUrl);
  7334. this.chat = new KxsChat(this);
  7335. this.voiceChat = new KxsVoiceChat(this, this.kxsNetwork);
  7336. if (this.isSpotifyPlayerEnabled) {
  7337. this.createSimpleSpotifyPlayer();
  7338. }
  7339. this.MainMenuCleaning();
  7340. this.kxsNetwork.connect();
  7341. this.createOnlineMenu();
  7342. this.voiceChat.startVoiceChat();
  7343. }
  7344. parseToken(token) {
  7345. if (token) {
  7346. return token.replace(/^(["'`])(.+)\1$/, '$2');
  7347. }
  7348. return null;
  7349. }
  7350. getPlayerName() {
  7351. let config = localStorage.getItem("surviv_config");
  7352. if (config) {
  7353. let configObject = JSON.parse(config);
  7354. return configObject.playerName;
  7355. }
  7356. }
  7357. changeSurvevLogo() {
  7358. var startRowHeader = document.querySelector("#start-row-header");
  7359. if (startRowHeader) {
  7360. startRowHeader.style.backgroundImage =
  7361. `url("${full_logo}")`;
  7362. }
  7363. }
  7364. createOnlineMenu() {
  7365. const overlay = document.getElementById('start-overlay');
  7366. if (!overlay)
  7367. return;
  7368. const menu = document.createElement('div');
  7369. menu.id = 'kxs-online-menu';
  7370. menu.style.position = 'absolute';
  7371. menu.style.top = '18px';
  7372. menu.style.left = '18px';
  7373. menu.style.background = 'rgba(30,30,40,0.92)';
  7374. menu.style.color = '#fff';
  7375. menu.style.padding = '8px 18px';
  7376. menu.style.borderRadius = '12px';
  7377. menu.style.boxShadow = '0 2px 8px rgba(0,0,0,0.18)';
  7378. menu.style.fontSize = '15px';
  7379. menu.style.zIndex = '999';
  7380. menu.style.userSelect = 'none';
  7381. menu.style.pointerEvents = 'auto';
  7382. menu.style.fontFamily = 'inherit';
  7383. menu.style.display = 'flex';
  7384. menu.style.alignItems = 'center';
  7385. menu.style.cursor = 'pointer';
  7386. menu.innerHTML = `
  7387. <span id="kxs-online-dot" style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#3fae2a;margin-right:10px;box-shadow:0 0 8px #3fae2a;animation:kxs-pulse 1s infinite alternate;"></span>
  7388. <b></b> <span id="kxs-online-count">...</span>
  7389. `;
  7390. const userListMenu = document.createElement('div');
  7391. userListMenu.id = 'kxs-online-users-menu';
  7392. userListMenu.style.position = 'absolute';
  7393. userListMenu.style.top = '100%';
  7394. userListMenu.style.left = '0';
  7395. userListMenu.style.marginTop = '8px';
  7396. userListMenu.style.background = 'rgba(30,30,40,0.95)';
  7397. userListMenu.style.color = '#fff';
  7398. userListMenu.style.padding = '10px';
  7399. userListMenu.style.borderRadius = '8px';
  7400. userListMenu.style.boxShadow = '0 4px 12px rgba(0,0,0,0.25)';
  7401. userListMenu.style.fontSize = '14px';
  7402. userListMenu.style.zIndex = '1000';
  7403. userListMenu.style.minWidth = '180px';
  7404. userListMenu.style.maxHeight = '300px';
  7405. userListMenu.style.overflowY = 'auto';
  7406. userListMenu.style.display = 'none';
  7407. userListMenu.style.flexDirection = 'column';
  7408. userListMenu.style.gap = '6px';
  7409. userListMenu.innerHTML = '<div style="text-align:center;padding:5px;">Chargement...</div>';
  7410. menu.appendChild(userListMenu);
  7411. if (!document.getElementById('kxs-online-style')) {
  7412. const style = document.createElement('style');
  7413. style.id = 'kxs-online-style';
  7414. style.innerHTML = `
  7415. @keyframes kxs-pulse {
  7416. 0% { box-shadow:0 0 8px #3fae2a; opacity: 1; }
  7417. 100% { box-shadow:0 0 16px #3fae2a; opacity: 0.6; }
  7418. }
  7419. `;
  7420. document.head.appendChild(style);
  7421. }
  7422. menu.addEventListener('click', (e) => {
  7423. e.stopPropagation();
  7424. if (userListMenu) {
  7425. const isVisible = userListMenu.style.display === 'flex';
  7426. userListMenu.style.display = isVisible ? 'none' : 'flex';
  7427. if (userListMenu.style.display === 'flex') {
  7428. setTimeout(() => {
  7429. const closeMenuOnClickOutside = (event) => {
  7430. if (!menu.contains(event.target) && !userListMenu.contains(event.target)) {
  7431. userListMenu.style.display = 'none';
  7432. document.removeEventListener('click', closeMenuOnClickOutside);
  7433. }
  7434. };
  7435. document.addEventListener('click', closeMenuOnClickOutside);
  7436. }, 0);
  7437. }
  7438. }
  7439. });
  7440. userListMenu.addEventListener('click', (e) => {
  7441. e.stopPropagation();
  7442. });
  7443. overlay.appendChild(menu);
  7444. this.onlineMenuElement = menu;
  7445. this.updateOnlineMenu();
  7446. this.onlineMenuInterval = window.setInterval(() => this.updateOnlineMenu(), 2000);
  7447. }
  7448. updateOnlineMenu() {
  7449. return KxsClient_awaiter(this, void 0, void 0, function* () {
  7450. if (!this.onlineMenuElement)
  7451. return;
  7452. const countEl = this.onlineMenuElement.querySelector('#kxs-online-count');
  7453. const dot = this.onlineMenuElement.querySelector('#kxs-online-dot');
  7454. const userListMenu = this.onlineMenuElement.querySelector('#kxs-online-users-menu');
  7455. try {
  7456. const res = this.kxsNetwork.getOnlineCount();
  7457. const count = typeof res === 'number' ? res : '?';
  7458. if (countEl)
  7459. countEl.textContent = `${count} Kxs users`;
  7460. if (dot) {
  7461. dot.style.background = '#3fae2a';
  7462. dot.style.boxShadow = '0 0 8px #3fae2a';
  7463. dot.style.animation = 'kxs-pulse 1s infinite alternate';
  7464. }
  7465. if (userListMenu) {
  7466. const users = this.kxsNetwork.getKxsUsers();
  7467. if (users && Array.isArray(users) && users.length > 0) {
  7468. let userListHTML = '';
  7469. userListHTML += '<div style="text-align:center;font-weight:bold;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,0.2);margin-bottom:8px;">Online users</div>';
  7470. users.forEach(user => {
  7471. userListHTML += `<div style="padding:4px 8px;border-radius:4px;background:rgba(255,255,255,0.05);">
  7472. <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#3fae2a;margin-right:8px;"></span>
  7473. ${user}
  7474. </div>`;
  7475. });
  7476. userListMenu.innerHTML = userListHTML;
  7477. }
  7478. else {
  7479. userListMenu.innerHTML = '<div style="text-align:center;padding:5px;">No users online</div>';
  7480. }
  7481. }
  7482. }
  7483. catch (e) {
  7484. if (countEl)
  7485. countEl.textContent = 'API offline';
  7486. if (dot) {
  7487. dot.style.background = '#888';
  7488. dot.style.boxShadow = 'none';
  7489. dot.style.animation = '';
  7490. }
  7491. if (userListMenu) {
  7492. userListMenu.innerHTML = '<div style="text-align:center;padding:5px;">API offline</div>';
  7493. }
  7494. }
  7495. });
  7496. }
  7497. detectDeviceType() {
  7498. const ua = navigator.userAgent;
  7499. if (/Mobi|Android/i.test(ua)) {
  7500. if (/Tablet|iPad/i.test(ua)) {
  7501. return "tablet";
  7502. }
  7503. return "mobile";
  7504. }
  7505. if (/iPad|Tablet/i.test(ua) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) {
  7506. return "tablet";
  7507. }
  7508. return "desktop";
  7509. }
  7510. isMobile() {
  7511. return this.detectDeviceType() !== "desktop";
  7512. }
  7513. updateLocalStorage() {
  7514. localStorage.setItem("userSettings", JSON.stringify({
  7515. isFpsVisible: this.isFpsVisible,
  7516. isPingVisible: this.isPingVisible,
  7517. isFpsUncapped: this.isFpsUncapped,
  7518. isKillsVisible: this.isKillsVisible,
  7519. discordWebhookUrl: this.discordWebhookUrl,
  7520. isDeathSoundEnabled: this.isDeathSoundEnabled,
  7521. isWinSoundEnabled: this.isWinSoundEnabled,
  7522. isHealthWarningEnabled: this.isHealthWarningEnabled,
  7523. isAutoUpdateEnabled: this.isAutoUpdateEnabled,
  7524. isWinningAnimationEnabled: this.isWinningAnimationEnabled,
  7525. discordToken: this.discordToken,
  7526. isKillLeaderTrackerEnabled: this.isKillLeaderTrackerEnabled,
  7527. isKillFeedBlint: this.isKillFeedBlint,
  7528. all_friends: this.all_friends,
  7529. isSpotifyPlayerEnabled: this.isSpotifyPlayerEnabled,
  7530. isMainMenuCleaned: this.isMainMenuCleaned,
  7531. isNotifyingForToggleMenu: this.isNotifyingForToggleMenu,
  7532. soundLibrary: this.soundLibrary,
  7533. customCrosshair: this.customCrosshair,
  7534. isGunOverlayColored: this.isGunOverlayColored,
  7535. isGunBorderChromatic: this.isGunBorderChromatic,
  7536. isVoiceChatEnabled: this.isVoiceChatEnabled,
  7537. isKxsChatEnabled: this.isKxsChatEnabled,
  7538. kxsNetworkSettings: this.kxsNetworkSettings,
  7539. isHealBarIndicatorEnabled: this.isHealBarIndicatorEnabled,
  7540. brightness: this.brightness,
  7541. isKxsClientLogoEnable: this.isKxsClientLogoEnable
  7542. }));
  7543. }
  7544. ;
  7545. applyBrightness(value) {
  7546. this.brightness = value;
  7547. const brightnessValue = value / 50; // 0 à 2, avec 1 étant la luminosité normale
  7548. document.documentElement.style.filter = `brightness(${brightnessValue})`;
  7549. this.updateLocalStorage();
  7550. }
  7551. initDeathDetection() {
  7552. const config = {
  7553. childList: true,
  7554. subtree: true,
  7555. attributes: false,
  7556. characterData: false,
  7557. };
  7558. this.deathObserver = new MutationObserver((mutations) => {
  7559. for (const mutation of mutations) {
  7560. if (mutation.addedNodes.length) {
  7561. this.checkForDeathScreen(mutation.addedNodes);
  7562. }
  7563. }
  7564. });
  7565. this.deathObserver.observe(document.body, config);
  7566. }
  7567. checkForDeathScreen(nodes) {
  7568. let loseArray = [
  7569. "died",
  7570. "eliminated",
  7571. "was"
  7572. ];
  7573. let winArray = [
  7574. "Winner",
  7575. "Victory",
  7576. "dinner",
  7577. ];
  7578. nodes.forEach((node) => {
  7579. var _a;
  7580. if (node instanceof HTMLElement) {
  7581. const deathTitle = node.querySelector(".ui-stats-header-title");
  7582. const deathTitle_2 = node.querySelector(".ui-stats-title");
  7583. 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); })) {
  7584. this.kxsNetwork.gameEnded();
  7585. this.handlePlayerDeath();
  7586. }
  7587. 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); })) {
  7588. this.kxsNetwork.gameEnded();
  7589. this.handlePlayerWin();
  7590. }
  7591. else if ((_a = deathTitle_2 === null || deathTitle_2 === void 0 ? void 0 : deathTitle_2.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes("result")) {
  7592. this.kxsNetwork.gameEnded();
  7593. this.handlePlayerDeath();
  7594. }
  7595. }
  7596. });
  7597. }
  7598. handlePlayerDeath() {
  7599. return KxsClient_awaiter(this, void 0, void 0, function* () {
  7600. try {
  7601. if (this.isDeathSoundEnabled) {
  7602. const audio = new Audio(this.soundLibrary.death_sound_url);
  7603. audio.volume = 0.3;
  7604. audio.play().catch((err) => false);
  7605. }
  7606. }
  7607. catch (error) {
  7608. this.logger.error("Reading error:", error);
  7609. }
  7610. const stats = this.getPlayerStats(false);
  7611. const body = {
  7612. username: stats.username,
  7613. kills: stats.kills,
  7614. damageDealt: stats.damageDealt,
  7615. damageTaken: stats.damageTaken,
  7616. duration: stats.duration,
  7617. position: stats.position,
  7618. isWin: false,
  7619. };
  7620. yield this.discordTracker.trackGameEnd(body);
  7621. this.db.set(new Date().toISOString(), body);
  7622. });
  7623. }
  7624. handlePlayerWin() {
  7625. return KxsClient_awaiter(this, void 0, void 0, function* () {
  7626. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
  7627. if (this.isWinningAnimationEnabled) {
  7628. this.felicitation();
  7629. }
  7630. const stats = this.getPlayerStats(true);
  7631. const body = {
  7632. username: stats.username,
  7633. kills: stats.kills,
  7634. damageDealt: stats.damageDealt,
  7635. damageTaken: stats.damageTaken,
  7636. duration: stats.duration,
  7637. position: stats.position,
  7638. isWin: true,
  7639. stuff: {
  7640. main_weapon: (_a = document.querySelector('#ui-weapon-id-1 .ui-weapon-name')) === null || _a === void 0 ? void 0 : _a.textContent,
  7641. secondary_weapon: (_b = document.querySelector('#ui-weapon-id-2 .ui-weapon-name')) === null || _b === void 0 ? void 0 : _b.textContent,
  7642. soda: (_c = document.querySelector("#ui-loot-soda .ui-loot-count")) === null || _c === void 0 ? void 0 : _c.textContent,
  7643. melees: (_d = document.querySelector('#ui-weapon-id-3 .ui-weapon-name')) === null || _d === void 0 ? void 0 : _d.textContent,
  7644. grenades: (_e = document.querySelector(`#ui-weapon-id-4 .ui-weapon-name`)) === null || _e === void 0 ? void 0 : _e.textContent,
  7645. medkit: (_f = document.querySelector("#ui-loot-healthkit .ui-loot-count")) === null || _f === void 0 ? void 0 : _f.textContent,
  7646. bandage: (_g = document.querySelector("#ui-loot-bandage .ui-loot-count")) === null || _g === void 0 ? void 0 : _g.textContent,
  7647. pills: (_h = document.querySelector("#ui-loot-painkiller .ui-loot-count")) === null || _h === void 0 ? void 0 : _h.textContent,
  7648. backpack: (_j = document.querySelector("#ui-armor-backpack .ui-armor-level")) === null || _j === void 0 ? void 0 : _j.textContent,
  7649. chest: (_k = document.querySelector("#ui-armor-chest .ui-armor-level")) === null || _k === void 0 ? void 0 : _k.textContent,
  7650. helmet: (_l = document.querySelector("#ui-armor-helmet .ui-armor-level")) === null || _l === void 0 ? void 0 : _l.textContent,
  7651. }
  7652. };
  7653. yield this.discordTracker.trackGameEnd(body);
  7654. this.db.set(new Date().toISOString(), body);
  7655. });
  7656. }
  7657. felicitation() {
  7658. const goldText = document.createElement("div");
  7659. goldText.textContent = "#1";
  7660. goldText.style.position = "fixed";
  7661. goldText.style.top = "50%";
  7662. goldText.style.left = "50%";
  7663. goldText.style.transform = "translate(-50%, -50%)";
  7664. goldText.style.fontSize = "80px";
  7665. goldText.style.color = "gold";
  7666. goldText.style.textShadow = "2px 2px 4px rgba(0,0,0,0.3)";
  7667. goldText.style.zIndex = "10000";
  7668. document.body.appendChild(goldText);
  7669. function createConfetti() {
  7670. const colors = [
  7671. "#ff0000",
  7672. "#00ff00",
  7673. "#0000ff",
  7674. "#ffff00",
  7675. "#ff00ff",
  7676. "#00ffff",
  7677. "gold",
  7678. ];
  7679. const confetti = document.createElement("div");
  7680. confetti.style.position = "fixed";
  7681. confetti.style.width = Math.random() * 10 + 5 + "px";
  7682. confetti.style.height = Math.random() * 10 + 5 + "px";
  7683. confetti.style.backgroundColor =
  7684. colors[Math.floor(Math.random() * colors.length)];
  7685. confetti.style.borderRadius = "50%";
  7686. confetti.style.zIndex = "9999";
  7687. confetti.style.left = Math.random() * 100 + "vw";
  7688. confetti.style.top = "-20px";
  7689. document.body.appendChild(confetti);
  7690. let posY = -20;
  7691. let posX = parseFloat(confetti.style.left);
  7692. let rotation = 0;
  7693. let speedY = Math.random() * 2 + 1;
  7694. let speedX = Math.random() * 2 - 1;
  7695. function fall() {
  7696. posY += speedY;
  7697. posX += speedX;
  7698. rotation += 5;
  7699. confetti.style.top = posY + "px";
  7700. confetti.style.left = posX + "vw";
  7701. confetti.style.transform = `rotate(${rotation}deg)`;
  7702. if (posY < window.innerHeight) {
  7703. requestAnimationFrame(fall);
  7704. }
  7705. else {
  7706. confetti.remove();
  7707. }
  7708. }
  7709. fall();
  7710. }
  7711. const confettiInterval = setInterval(() => {
  7712. for (let i = 0; i < 5; i++) {
  7713. createConfetti();
  7714. }
  7715. }, 100);
  7716. if (this.isWinSoundEnabled) {
  7717. const audio = new Audio(this.soundLibrary.win_sound_url);
  7718. audio.play().catch((err) => this.logger.error("Erreur lecture:", err));
  7719. }
  7720. setTimeout(() => {
  7721. clearInterval(confettiInterval);
  7722. goldText.style.transition = "opacity 1s";
  7723. goldText.style.opacity = "0";
  7724. setTimeout(() => goldText.remove(), 1000);
  7725. }, 5000);
  7726. }
  7727. cleanup() {
  7728. if (this.deathObserver) {
  7729. this.deathObserver.disconnect();
  7730. this.deathObserver = null;
  7731. }
  7732. }
  7733. getUsername() {
  7734. const configKey = "surviv_config";
  7735. const savedConfig = localStorage.getItem(configKey);
  7736. const config = JSON.parse(savedConfig);
  7737. if (config.playerName) {
  7738. return config.playerName;
  7739. }
  7740. else {
  7741. return "Player";
  7742. }
  7743. }
  7744. getPlayerStats(win) {
  7745. const statsInfo = win
  7746. ? document.querySelector(".ui-stats-info-player")
  7747. : document.querySelector(".ui-stats-info-player.ui-stats-info-status");
  7748. const rank = document.querySelector(".ui-stats-header-value");
  7749. if (!(statsInfo === null || statsInfo === void 0 ? void 0 : statsInfo.textContent) || !(rank === null || rank === void 0 ? void 0 : rank.textContent)) {
  7750. return {
  7751. username: this.getUsername(),
  7752. kills: 0,
  7753. damageDealt: 0,
  7754. damageTaken: 0,
  7755. duration: "0s",
  7756. position: "#unknown",
  7757. };
  7758. }
  7759. const parsedStats = StatsParser.parse(statsInfo.textContent, rank === null || rank === void 0 ? void 0 : rank.textContent);
  7760. parsedStats.username = this.getUsername();
  7761. return parsedStats;
  7762. }
  7763. setAnimationFrameCallback() {
  7764. this.animationFrameCallback = this.isFpsUncapped
  7765. ? (callback) => setTimeout(callback, 1)
  7766. : window.requestAnimationFrame.bind(window);
  7767. }
  7768. makeResizable(element, storageKey) {
  7769. let isResizing = false;
  7770. let startX, startY, startWidth, startHeight;
  7771. // Add a resize area in the bottom right
  7772. const resizer = document.createElement("div");
  7773. Object.assign(resizer.style, {
  7774. width: "10px",
  7775. height: "10px",
  7776. backgroundColor: "white",
  7777. position: "absolute",
  7778. right: "0",
  7779. bottom: "0",
  7780. cursor: "nwse-resize",
  7781. zIndex: "10001",
  7782. });
  7783. element.appendChild(resizer);
  7784. resizer.addEventListener("mousedown", (event) => {
  7785. isResizing = true;
  7786. startX = event.clientX;
  7787. startY = event.clientY;
  7788. startWidth = element.offsetWidth;
  7789. startHeight = element.offsetHeight;
  7790. event.stopPropagation(); // Empêche l'activation du déplacement
  7791. });
  7792. window.addEventListener("mousemove", (event) => {
  7793. if (isResizing) {
  7794. const newWidth = startWidth + (event.clientX - startX);
  7795. const newHeight = startHeight + (event.clientY - startY);
  7796. element.style.width = `${newWidth}px`;
  7797. element.style.height = `${newHeight}px`;
  7798. // Sauvegarde de la taille
  7799. localStorage.setItem(storageKey, JSON.stringify({
  7800. width: newWidth,
  7801. height: newHeight,
  7802. }));
  7803. }
  7804. });
  7805. window.addEventListener("mouseup", () => {
  7806. isResizing = false;
  7807. });
  7808. const savedSize = localStorage.getItem(storageKey);
  7809. if (savedSize) {
  7810. const { width, height } = JSON.parse(savedSize);
  7811. element.style.width = `${width}px`;
  7812. element.style.height = `${height}px`;
  7813. }
  7814. else {
  7815. element.style.width = "150px"; // Taille par défaut
  7816. element.style.height = "50px";
  7817. }
  7818. }
  7819. makeDraggable(element, storageKey) {
  7820. let isDragging = false;
  7821. let dragOffset = { x: 0, y: 0 };
  7822. element.addEventListener("mousedown", (event) => {
  7823. if (event.button === 0) {
  7824. // Left click only
  7825. isDragging = true;
  7826. this.gridSystem.toggleGrid(); // Afficher la grille quand on commence à déplacer
  7827. dragOffset = {
  7828. x: event.clientX - element.offsetLeft,
  7829. y: event.clientY - element.offsetTop,
  7830. };
  7831. element.style.cursor = "grabbing";
  7832. }
  7833. });
  7834. window.addEventListener("mousemove", (event) => {
  7835. if (isDragging) {
  7836. const rawX = event.clientX - dragOffset.x;
  7837. const rawY = event.clientY - dragOffset.y;
  7838. // Get snapped coordinates from grid system
  7839. const snapped = this.gridSystem.snapToGrid(element, rawX, rawY);
  7840. // Prevent moving off screen
  7841. const maxX = window.innerWidth - element.offsetWidth;
  7842. const maxY = window.innerHeight - element.offsetHeight;
  7843. element.style.left = `${Math.max(0, Math.min(snapped.x, maxX))}px`;
  7844. element.style.top = `${Math.max(0, Math.min(snapped.y, maxY))}px`;
  7845. // Highlight nearest grid lines while dragging
  7846. this.gridSystem.highlightNearestGridLine(rawX, rawY);
  7847. // Save position
  7848. localStorage.setItem(storageKey, JSON.stringify({
  7849. x: parseInt(element.style.left),
  7850. y: parseInt(element.style.top),
  7851. }));
  7852. }
  7853. });
  7854. window.addEventListener("mouseup", () => {
  7855. if (isDragging) {
  7856. isDragging = false;
  7857. this.gridSystem.toggleGrid(); // Masquer la grille quand on arrête de déplacer
  7858. element.style.cursor = "move";
  7859. }
  7860. });
  7861. // Load saved position
  7862. const savedPosition = localStorage.getItem(storageKey);
  7863. if (savedPosition) {
  7864. const { x, y } = JSON.parse(savedPosition);
  7865. const snapped = this.gridSystem.snapToGrid(element, x, y);
  7866. element.style.left = `${snapped.x}px`;
  7867. element.style.top = `${snapped.y}px`;
  7868. }
  7869. setTimeout(() => {
  7870. this.gridSystem.updateCounterCorners();
  7871. }, 100);
  7872. }
  7873. getKills() {
  7874. const killElement = document.querySelector(".ui-player-kills.js-ui-player-kills");
  7875. if (killElement) {
  7876. const kills = parseInt(killElement.textContent || "", 10);
  7877. return isNaN(kills) ? 0 : kills;
  7878. }
  7879. return 0;
  7880. }
  7881. getRegionFromLocalStorage() {
  7882. let config = localStorage.getItem("surviv_config");
  7883. if (config) {
  7884. let configObject = JSON.parse(config);
  7885. return configObject.region;
  7886. }
  7887. return null;
  7888. }
  7889. saveBackgroundToLocalStorage(image) {
  7890. if (typeof image === "string") {
  7891. localStorage.setItem("lastBackgroundUrl", image);
  7892. }
  7893. if (typeof image === "string") {
  7894. localStorage.setItem("lastBackgroundType", "url");
  7895. localStorage.setItem("lastBackgroundValue", image);
  7896. }
  7897. else {
  7898. localStorage.setItem("lastBackgroundType", "local");
  7899. const reader = new FileReader();
  7900. reader.onload = () => {
  7901. localStorage.setItem("lastBackgroundValue", reader.result);
  7902. };
  7903. reader.readAsDataURL(image);
  7904. }
  7905. }
  7906. loadBackgroundFromLocalStorage() {
  7907. const backgroundType = localStorage.getItem("lastBackgroundType");
  7908. const backgroundValue = localStorage.getItem("lastBackgroundValue");
  7909. const backgroundElement = document.getElementById("background");
  7910. if (backgroundElement && backgroundType && backgroundValue) {
  7911. if (backgroundType === "url") {
  7912. backgroundElement.style.backgroundImage = `url(${backgroundValue})`;
  7913. }
  7914. else if (backgroundType === "local") {
  7915. backgroundElement.style.backgroundImage = `url(${backgroundValue})`;
  7916. }
  7917. }
  7918. }
  7919. loadLocalStorage() {
  7920. var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
  7921. const savedSettings = localStorage.getItem("userSettings")
  7922. ? JSON.parse(localStorage.getItem("userSettings"))
  7923. : null;
  7924. if (savedSettings) {
  7925. this.isFpsVisible = (_a = savedSettings.isFpsVisible) !== null && _a !== void 0 ? _a : this.isFpsVisible;
  7926. this.isPingVisible = (_b = savedSettings.isPingVisible) !== null && _b !== void 0 ? _b : this.isPingVisible;
  7927. this.isFpsUncapped = (_c = savedSettings.isFpsUncapped) !== null && _c !== void 0 ? _c : this.isFpsUncapped;
  7928. this.isKillsVisible = (_d = savedSettings.isKillsVisible) !== null && _d !== void 0 ? _d : this.isKillsVisible;
  7929. this.discordWebhookUrl = (_e = savedSettings.discordWebhookUrl) !== null && _e !== void 0 ? _e : this.discordWebhookUrl;
  7930. this.isHealthWarningEnabled = (_f = savedSettings.isHealthWarningEnabled) !== null && _f !== void 0 ? _f : this.isHealthWarningEnabled;
  7931. this.isAutoUpdateEnabled = (_g = savedSettings.isAutoUpdateEnabled) !== null && _g !== void 0 ? _g : this.isAutoUpdateEnabled;
  7932. this.isWinningAnimationEnabled = (_h = savedSettings.isWinningAnimationEnabled) !== null && _h !== void 0 ? _h : this.isWinningAnimationEnabled;
  7933. this.discordToken = (_j = savedSettings.discordToken) !== null && _j !== void 0 ? _j : this.discordToken;
  7934. this.isKillLeaderTrackerEnabled = (_k = savedSettings.isKillLeaderTrackerEnabled) !== null && _k !== void 0 ? _k : this.isKillLeaderTrackerEnabled;
  7935. this.isKillFeedBlint = (_l = savedSettings.isKillFeedBlint) !== null && _l !== void 0 ? _l : this.isKillFeedBlint;
  7936. this.all_friends = (_m = savedSettings.all_friends) !== null && _m !== void 0 ? _m : this.all_friends;
  7937. this.isSpotifyPlayerEnabled = (_o = savedSettings.isSpotifyPlayerEnabled) !== null && _o !== void 0 ? _o : this.isSpotifyPlayerEnabled;
  7938. this.isMainMenuCleaned = (_p = savedSettings.isMainMenuCleaned) !== null && _p !== void 0 ? _p : this.isMainMenuCleaned;
  7939. this.isNotifyingForToggleMenu = (_q = savedSettings.isNotifyingForToggleMenu) !== null && _q !== void 0 ? _q : this.isNotifyingForToggleMenu;
  7940. this.customCrosshair = (_r = savedSettings.customCrosshair) !== null && _r !== void 0 ? _r : this.customCrosshair;
  7941. this.isGunOverlayColored = (_s = savedSettings.isGunOverlayColored) !== null && _s !== void 0 ? _s : this.isGunOverlayColored;
  7942. this.isGunBorderChromatic = (_t = savedSettings.isGunBorderChromatic) !== null && _t !== void 0 ? _t : this.isGunBorderChromatic;
  7943. this.isVoiceChatEnabled = (_u = savedSettings.isVoiceChatEnabled) !== null && _u !== void 0 ? _u : this.isVoiceChatEnabled;
  7944. this.isKxsChatEnabled = (_v = savedSettings.isKxsChatEnabled) !== null && _v !== void 0 ? _v : this.isKxsChatEnabled;
  7945. this.kxsNetworkSettings = (_w = savedSettings.kxsNetworkSettings) !== null && _w !== void 0 ? _w : this.kxsNetworkSettings;
  7946. this.isHealBarIndicatorEnabled = (_x = savedSettings.isHealBarIndicatorEnabled) !== null && _x !== void 0 ? _x : this.isHealBarIndicatorEnabled;
  7947. this.isWinSoundEnabled = (_y = savedSettings.isWinSoundEnabled) !== null && _y !== void 0 ? _y : this.isWinSoundEnabled;
  7948. this.isDeathSoundEnabled = (_z = savedSettings.isDeathSoundEnabled) !== null && _z !== void 0 ? _z : this.isDeathSoundEnabled;
  7949. this.brightness = (_0 = savedSettings.brightness) !== null && _0 !== void 0 ? _0 : this.brightness;
  7950. this.isKxsClientLogoEnable = (_1 = savedSettings.isKxsClientLogoEnable) !== null && _1 !== void 0 ? _1 : this.isKxsClientLogoEnable;
  7951. // Apply brightness setting
  7952. const brightnessValue = this.brightness / 50;
  7953. document.documentElement.style.filter = `brightness(${brightnessValue})`;
  7954. if (savedSettings.soundLibrary) {
  7955. // Check if the sound value exists
  7956. if (savedSettings.soundLibrary.win_sound_url) {
  7957. this.soundLibrary.win_sound_url = savedSettings.soundLibrary.win_sound_url;
  7958. }
  7959. if (savedSettings.soundLibrary.death_sound_url) {
  7960. this.soundLibrary.death_sound_url = savedSettings.soundLibrary.death_sound_url;
  7961. }
  7962. if (savedSettings.soundLibrary.background_sound_url) {
  7963. this.soundLibrary.background_sound_url = savedSettings.soundLibrary.background_sound_url;
  7964. }
  7965. }
  7966. }
  7967. this.updateKillsVisibility();
  7968. this.updateFpsVisibility();
  7969. this.updatePingVisibility();
  7970. }
  7971. updateFpsVisibility() {
  7972. if (this.counters.fps) {
  7973. this.counters.fps.style.display = this.isFpsVisible ? "block" : "none";
  7974. this.counters.fps.style.backgroundColor = this.isFpsVisible
  7975. ? "rgba(0, 0, 0, 0.2)"
  7976. : "transparent";
  7977. }
  7978. }
  7979. updatePingVisibility() {
  7980. if (this.counters.ping) {
  7981. this.counters.ping.style.display = this.isPingVisible ? "block" : "none";
  7982. }
  7983. }
  7984. updateKillsVisibility() {
  7985. if (this.counters.kills) {
  7986. this.counters.kills.style.display = this.isKillsVisible
  7987. ? "block"
  7988. : "none";
  7989. this.counters.kills.style.backgroundColor = this.isKillsVisible
  7990. ? "rgba(0, 0, 0, 0.2)"
  7991. : "transparent";
  7992. }
  7993. }
  7994. createSimpleSpotifyPlayer() {
  7995. // Ajouter une règle CSS globale pour supprimer toutes les bordures et améliorer le redimensionnement
  7996. const styleElement = document.createElement('style');
  7997. styleElement.textContent = `
  7998. #spotify-player-container,
  7999. #spotify-player-container *,
  8000. #spotify-player-iframe,
  8001. .spotify-resize-handle {
  8002. border: none !important;
  8003. outline: none !important;
  8004. box-sizing: content-box !important;
  8005. }
  8006. #spotify-player-iframe {
  8007. padding-bottom: 0 !important;
  8008. margin-bottom: 0 !important;
  8009. }
  8010. .spotify-resize-handle {
  8011. touch-action: none;
  8012. backface-visibility: hidden;
  8013. }
  8014. .spotify-resizing {
  8015. user-select: none !important;
  8016. pointer-events: none !important;
  8017. }
  8018. .spotify-resizing .spotify-resize-handle {
  8019. pointer-events: all !important;
  8020. }
  8021. `;
  8022. document.head.appendChild(styleElement);
  8023. // Main container
  8024. const container = document.createElement('div');
  8025. container.id = 'spotify-player-container';
  8026. // Récupérer la position sauvegardée si disponible
  8027. const savedLeft = localStorage.getItem('kxsSpotifyPlayerLeft');
  8028. const savedTop = localStorage.getItem('kxsSpotifyPlayerTop');
  8029. Object.assign(container.style, {
  8030. position: 'fixed',
  8031. width: '320px',
  8032. backgroundColor: '#121212',
  8033. borderRadius: '0px',
  8034. boxShadow: 'none',
  8035. overflow: 'hidden',
  8036. zIndex: '10000',
  8037. fontFamily: 'Montserrat, Arial, sans-serif',
  8038. transition: 'transform 0.3s ease, opacity 0.3s ease',
  8039. transform: 'translateY(0)',
  8040. opacity: '1'
  8041. });
  8042. // Appliquer la position sauvegardée ou la position par défaut
  8043. if (savedLeft && savedTop) {
  8044. container.style.left = savedLeft;
  8045. container.style.top = savedTop;
  8046. container.style.right = 'auto';
  8047. container.style.bottom = 'auto';
  8048. }
  8049. else {
  8050. container.style.right = '20px';
  8051. container.style.bottom = '20px';
  8052. }
  8053. // Player header
  8054. const header = document.createElement('div');
  8055. Object.assign(header.style, {
  8056. display: 'flex',
  8057. alignItems: 'center',
  8058. justifyContent: 'space-between',
  8059. padding: '12px 16px',
  8060. backgroundColor: '#070707',
  8061. color: 'white',
  8062. borderBottom: 'none',
  8063. position: 'relative' // For absolute positioning of the button
  8064. });
  8065. // Spotify logo
  8066. const logo = document.createElement('div');
  8067. 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>`;
  8068. const title = document.createElement('span');
  8069. title.textContent = 'Spotify Player';
  8070. title.style.marginLeft = '8px';
  8071. title.style.fontWeight = 'bold';
  8072. const logoContainer = document.createElement('div');
  8073. logoContainer.style.display = 'flex';
  8074. logoContainer.style.alignItems = 'center';
  8075. logoContainer.appendChild(logo);
  8076. logoContainer.appendChild(title);
  8077. // Control buttons
  8078. const controls = document.createElement('div');
  8079. controls.style.display = 'flex';
  8080. controls.style.alignItems = 'center';
  8081. // Minimize button
  8082. const minimizeBtn = document.createElement('button');
  8083. Object.assign(minimizeBtn.style, {
  8084. background: 'none',
  8085. border: 'none',
  8086. color: '#aaa',
  8087. cursor: 'pointer',
  8088. fontSize: '18px',
  8089. padding: '0',
  8090. marginLeft: '10px',
  8091. width: '24px',
  8092. height: '24px',
  8093. display: 'flex',
  8094. alignItems: 'center',
  8095. justifyContent: 'center'
  8096. });
  8097. minimizeBtn.innerHTML = '−';
  8098. minimizeBtn.title = 'Minimize';
  8099. // Close button
  8100. const closeBtn = document.createElement('button');
  8101. Object.assign(closeBtn.style, {
  8102. background: 'none',
  8103. border: 'none',
  8104. color: '#aaa',
  8105. cursor: 'pointer',
  8106. fontSize: '18px',
  8107. padding: '0',
  8108. marginLeft: '10px',
  8109. width: '24px',
  8110. height: '24px',
  8111. display: 'flex',
  8112. alignItems: 'center',
  8113. justifyContent: 'center'
  8114. });
  8115. closeBtn.innerHTML = '×';
  8116. closeBtn.title = 'Close';
  8117. controls.appendChild(minimizeBtn);
  8118. controls.appendChild(closeBtn);
  8119. header.appendChild(logoContainer);
  8120. header.appendChild(controls);
  8121. // Album cover image
  8122. const albumArt = document.createElement('div');
  8123. Object.assign(albumArt.style, {
  8124. width: '50px',
  8125. height: '50px',
  8126. backgroundColor: '#333',
  8127. backgroundSize: 'cover',
  8128. backgroundPosition: 'center',
  8129. borderRadius: '4px',
  8130. flexShrink: '0'
  8131. });
  8132. albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67616d00001e02fe24b9ffeb3c3fdb4f9abbe9')`;
  8133. // Track information
  8134. const trackInfo = document.createElement('div');
  8135. Object.assign(trackInfo.style, {
  8136. flex: '1',
  8137. overflow: 'hidden'
  8138. });
  8139. // Player content
  8140. const content = document.createElement('div');
  8141. content.style.padding = '0';
  8142. // Spotify iframe
  8143. const iframe = document.createElement('iframe');
  8144. iframe.id = 'spotify-player-iframe';
  8145. iframe.src = 'https://open.spotify.com/embed/playlist/37i9dQZEVXcJZyENOWUFo7?utm_source=generator&theme=1';
  8146. iframe.width = '100%';
  8147. iframe.height = '152px';
  8148. iframe.frameBorder = '0';
  8149. iframe.allow = 'autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture';
  8150. iframe.style.border = 'none';
  8151. iframe.style.margin = '0';
  8152. iframe.style.padding = '0';
  8153. iframe.style.boxSizing = 'content-box';
  8154. iframe.style.display = 'block'; // Forcer display block pour éviter les problèmes d'espacement
  8155. iframe.setAttribute('frameBorder', '0');
  8156. iframe.setAttribute('allowtransparency', 'true');
  8157. iframe.setAttribute('scrolling', 'no'); // Désactiver le défilement interne
  8158. content.appendChild(iframe);
  8159. // Playlist change button integrated in the header
  8160. const changePlaylistContainer = document.createElement('div');
  8161. Object.assign(changePlaylistContainer.style, {
  8162. display: 'flex',
  8163. alignItems: 'center',
  8164. marginRight: '10px'
  8165. });
  8166. // Square button to enter a playlist ID
  8167. const changePlaylistBtn = document.createElement('button');
  8168. Object.assign(changePlaylistBtn.style, {
  8169. width: '24px',
  8170. height: '24px',
  8171. backgroundColor: '#1DB954',
  8172. color: 'white',
  8173. border: 'none',
  8174. borderRadius: '4px',
  8175. fontSize: '14px',
  8176. fontWeight: 'bold',
  8177. cursor: 'pointer',
  8178. display: 'flex',
  8179. alignItems: 'center',
  8180. justifyContent: 'center',
  8181. margin: '0 8px 0 0'
  8182. });
  8183. changePlaylistBtn.innerHTML = `
  8184. <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  8185. <path d="M12 5V19M5 12H19" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
  8186. </svg>
  8187. `;
  8188. changePlaylistBtn.addEventListener('click', () => {
  8189. const id = prompt('Enter the Spotify playlist ID:', '37i9dQZEVXcJZyENOWUFo7');
  8190. if (id) {
  8191. iframe.src = `https://open.spotify.com/embed/playlist/${id}?utm_source=generator&theme=0`;
  8192. localStorage.setItem('kxsSpotifyPlaylist', id);
  8193. // Simulate an album cover based on the playlist ID
  8194. albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67706f00000002${id.substring(0, 16)}')`;
  8195. }
  8196. });
  8197. changePlaylistContainer.appendChild(changePlaylistBtn);
  8198. // Load saved playlist
  8199. const savedPlaylist = localStorage.getItem('kxsSpotifyPlaylist');
  8200. if (savedPlaylist) {
  8201. iframe.src = `https://open.spotify.com/embed/playlist/${savedPlaylist}?utm_source=generator&theme=0`;
  8202. // Simulate an album cover based on the playlist ID
  8203. albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67706f00000002${savedPlaylist.substring(0, 16)}')`;
  8204. }
  8205. // Integrate the playlist change button into the controls
  8206. controls.insertBefore(changePlaylistContainer, minimizeBtn);
  8207. // Assemble the elements
  8208. container.appendChild(header);
  8209. container.appendChild(content);
  8210. // Add a title to the button for accessibility
  8211. changePlaylistBtn.title = "Change playlist";
  8212. // Add to document
  8213. document.body.appendChild(container);
  8214. // Ajouter un bord redimensionnable au lecteur
  8215. const resizeHandle = document.createElement('div');
  8216. resizeHandle.className = 'spotify-resize-handle';
  8217. Object.assign(resizeHandle.style, {
  8218. position: 'absolute',
  8219. bottom: '0',
  8220. right: '0',
  8221. width: '30px',
  8222. height: '30px',
  8223. cursor: 'nwse-resize',
  8224. background: 'rgba(255, 255, 255, 0.1)',
  8225. zIndex: '10001',
  8226. pointerEvents: 'all'
  8227. });
  8228. // Ajouter un indicateur visuel de redimensionnement plus visible
  8229. resizeHandle.innerHTML = `
  8230. <svg width="14" height="14" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg" style="position: absolute; bottom: 4px; right: 4px;">
  8231. <path d="M9 9L5 9L9 5L9 9Z" fill="white"/>
  8232. <path d="M5 9L1 9L9 1L9 5L5 9Z" fill="white"/>
  8233. <path d="M1 9L1 5L5 1L9 1L1 9Z" fill="white"/>
  8234. <path d="M1 5L1 1L5 1L1 5Z" fill="white"/>
  8235. </svg>
  8236. `;
  8237. // Logique de redimensionnement
  8238. let isResizing = false;
  8239. let startX = 0, startY = 0;
  8240. let startWidth = 0, startHeight = 0;
  8241. resizeHandle.addEventListener('mousedown', (e) => {
  8242. // Arrêter la propagation pour éviter que d'autres éléments interceptent l'événement
  8243. e.stopPropagation();
  8244. e.preventDefault();
  8245. isResizing = true;
  8246. startX = e.clientX;
  8247. startY = e.clientY;
  8248. startWidth = container.offsetWidth;
  8249. startHeight = container.offsetHeight;
  8250. // Ajouter une classe spéciale pendant le redimensionnement
  8251. container.classList.add('spotify-resizing');
  8252. // Appliquer le style pendant le redimensionnement
  8253. container.style.transition = 'none';
  8254. container.style.border = 'none';
  8255. container.style.outline = 'none';
  8256. iframe.style.border = 'none';
  8257. iframe.style.outline = 'none';
  8258. document.body.style.userSelect = 'none';
  8259. // Ajouter un overlay de redimensionnement temporairement
  8260. const resizeOverlay = document.createElement('div');
  8261. resizeOverlay.id = 'spotify-resize-overlay';
  8262. resizeOverlay.style.position = 'fixed';
  8263. resizeOverlay.style.top = '0';
  8264. resizeOverlay.style.left = '0';
  8265. resizeOverlay.style.width = '100%';
  8266. resizeOverlay.style.height = '100%';
  8267. resizeOverlay.style.zIndex = '9999';
  8268. resizeOverlay.style.cursor = 'nwse-resize';
  8269. resizeOverlay.style.background = 'transparent';
  8270. document.body.appendChild(resizeOverlay);
  8271. });
  8272. document.addEventListener('mousemove', (e) => {
  8273. if (!isResizing)
  8274. return;
  8275. // Calculer les nouvelles dimensions
  8276. const newWidth = startWidth + (e.clientX - startX);
  8277. const newHeight = startHeight + (e.clientY - startY);
  8278. // Limiter les dimensions minimales
  8279. const minWidth = 320; // Largeur minimale
  8280. const minHeight = 200; // Hauteur minimale
  8281. // Appliquer les nouvelles dimensions si elles sont supérieures aux minimums
  8282. if (newWidth >= minWidth) {
  8283. container.style.width = newWidth + 'px';
  8284. iframe.style.width = '100%';
  8285. }
  8286. if (newHeight >= minHeight) {
  8287. container.style.height = newHeight + 'px';
  8288. iframe.style.height = (newHeight - 50) + 'px'; // Ajuster la hauteur de l'iframe en conséquence
  8289. }
  8290. // Empêcher la sélection pendant le drag
  8291. e.preventDefault();
  8292. });
  8293. document.addEventListener('mouseup', () => {
  8294. if (isResizing) {
  8295. isResizing = false;
  8296. container.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
  8297. container.style.border = 'none';
  8298. container.style.outline = 'none';
  8299. iframe.style.border = 'none';
  8300. iframe.style.outline = 'none';
  8301. document.body.style.userSelect = '';
  8302. // Supprimer l'overlay de redimensionnement
  8303. const overlay = document.getElementById('spotify-resize-overlay');
  8304. if (overlay)
  8305. overlay.remove();
  8306. // Supprimer la classe de redimensionnement
  8307. container.classList.remove('spotify-resizing');
  8308. // Sauvegarder les dimensions pour la prochaine fois
  8309. localStorage.setItem('kxsSpotifyPlayerWidth', container.style.width);
  8310. localStorage.setItem('kxsSpotifyPlayerHeight', container.style.height);
  8311. }
  8312. });
  8313. // Ajouter la poignée de redimensionnement au conteneur
  8314. container.appendChild(resizeHandle);
  8315. // Player states
  8316. let isMinimized = false;
  8317. // Events
  8318. minimizeBtn.addEventListener('click', () => {
  8319. if (isMinimized) {
  8320. content.style.display = 'block';
  8321. changePlaylistContainer.style.display = 'block';
  8322. container.style.transform = 'translateY(0)';
  8323. minimizeBtn.innerHTML = '−';
  8324. }
  8325. else {
  8326. content.style.display = 'none';
  8327. changePlaylistContainer.style.display = 'none';
  8328. container.style.transform = 'translateY(0)';
  8329. minimizeBtn.innerHTML = '+';
  8330. }
  8331. isMinimized = !isMinimized;
  8332. });
  8333. closeBtn.addEventListener('click', () => {
  8334. container.style.transform = 'translateY(150%)';
  8335. container.style.opacity = '0';
  8336. setTimeout(() => {
  8337. container.style.display = 'none';
  8338. showButton.style.display = 'flex';
  8339. showButton.style.alignItems = 'center';
  8340. showButton.style.justifyContent = 'center';
  8341. }, 300);
  8342. });
  8343. // Make the player draggable
  8344. let isDragging = false;
  8345. let offsetX = 0;
  8346. let offsetY = 0;
  8347. header.addEventListener('mousedown', (e) => {
  8348. isDragging = true;
  8349. offsetX = e.clientX - container.getBoundingClientRect().left;
  8350. offsetY = e.clientY - container.getBoundingClientRect().top;
  8351. container.style.transition = 'none';
  8352. });
  8353. document.addEventListener('mousemove', (e) => {
  8354. if (isDragging) {
  8355. container.style.right = 'auto';
  8356. container.style.bottom = 'auto';
  8357. container.style.left = (e.clientX - offsetX) + 'px';
  8358. container.style.top = (e.clientY - offsetY) + 'px';
  8359. }
  8360. });
  8361. document.addEventListener('mouseup', () => {
  8362. if (isDragging) {
  8363. isDragging = false;
  8364. container.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
  8365. // Sauvegarder la position pour la prochaine fois
  8366. localStorage.setItem('kxsSpotifyPlayerLeft', container.style.left);
  8367. localStorage.setItem('kxsSpotifyPlayerTop', container.style.top);
  8368. }
  8369. });
  8370. // Button to show the player again
  8371. const showButton = document.createElement('button');
  8372. showButton.id = 'spotify-float-button';
  8373. Object.assign(showButton.style, {
  8374. position: 'fixed',
  8375. bottom: '20px',
  8376. right: '20px',
  8377. width: '50px',
  8378. height: '50px',
  8379. borderRadius: '50%',
  8380. backgroundColor: '#1DB954',
  8381. color: 'white',
  8382. border: 'none',
  8383. boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
  8384. cursor: 'pointer',
  8385. zIndex: '9999',
  8386. fontSize: '24px',
  8387. transition: 'transform 0.2s ease',
  8388. display: 'flex',
  8389. alignItems: 'center',
  8390. justifyContent: 'center'
  8391. });
  8392. 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>`;
  8393. document.body.appendChild(showButton);
  8394. showButton.addEventListener('mouseenter', () => {
  8395. showButton.style.transform = 'scale(1.1)';
  8396. });
  8397. showButton.addEventListener('mouseleave', () => {
  8398. showButton.style.transform = 'scale(1)';
  8399. });
  8400. showButton.addEventListener('click', () => {
  8401. container.style.display = 'block';
  8402. container.style.transform = 'translateY(0)';
  8403. container.style.opacity = '1';
  8404. showButton.style.display = 'none';
  8405. });
  8406. return container;
  8407. }
  8408. toggleSpotifyMenu() {
  8409. if (this.isSpotifyPlayerEnabled) {
  8410. this.createSimpleSpotifyPlayer();
  8411. }
  8412. else {
  8413. this.removeSimpleSpotifyPlayer();
  8414. }
  8415. }
  8416. applyCustomMainMenuStyle() {
  8417. // Sélectionner le menu principal
  8418. const startMenu = document.getElementById('start-menu');
  8419. const playButtons = document.querySelectorAll('.btn-green, #btn-help, .btn-team-option');
  8420. const playerOptions = document.getElementById('player-options');
  8421. const serverSelect = document.getElementById('server-select-main');
  8422. const nameInput = document.getElementById('player-name-input-solo');
  8423. const helpSection = document.getElementById('start-help');
  8424. if (startMenu) {
  8425. // Apply styles to the main container
  8426. Object.assign(startMenu.style, {
  8427. background: 'linear-gradient(135deg, rgba(25, 25, 35, 0.95) 0%, rgba(15, 15, 25, 0.98) 100%)',
  8428. border: '1px solid rgba(255, 255, 255, 0.1)',
  8429. borderRadius: '12px',
  8430. boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
  8431. padding: '15px',
  8432. backdropFilter: 'blur(10px)',
  8433. margin: '0 auto'
  8434. });
  8435. }
  8436. // Style the buttons
  8437. playButtons.forEach(button => {
  8438. if (button instanceof HTMLElement) {
  8439. if (button.classList.contains('btn-green')) {
  8440. // Boutons Play
  8441. Object.assign(button.style, {
  8442. background: 'linear-gradient(135deg, #4287f5 0%, #3b76d9 100%)',
  8443. borderRadius: '8px',
  8444. border: '1px solid rgba(255, 255, 255, 0.2)',
  8445. boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)',
  8446. transition: 'all 0.2s ease',
  8447. color: 'white',
  8448. fontWeight: 'bold'
  8449. });
  8450. }
  8451. else {
  8452. // Autres boutons
  8453. Object.assign(button.style, {
  8454. background: 'rgba(40, 45, 60, 0.7)',
  8455. borderRadius: '8px',
  8456. border: '1px solid rgba(255, 255, 255, 0.1)',
  8457. transition: 'all 0.2s ease',
  8458. color: 'white'
  8459. });
  8460. }
  8461. // Hover effect for all buttons
  8462. button.addEventListener('mouseover', () => {
  8463. button.style.transform = 'translateY(-2px)';
  8464. button.style.boxShadow = '0 6px 16px rgba(0, 0, 0, 0.3)';
  8465. button.style.filter = 'brightness(1.1)';
  8466. });
  8467. button.addEventListener('mouseout', () => {
  8468. button.style.transform = 'translateY(0)';
  8469. button.style.boxShadow = button.classList.contains('btn-green') ?
  8470. '0 4px 12px rgba(0, 0, 0, 0.2)' : 'none';
  8471. button.style.filter = 'brightness(1)';
  8472. });
  8473. }
  8474. });
  8475. // Styliser le sélecteur de serveur
  8476. if (serverSelect instanceof HTMLSelectElement) {
  8477. Object.assign(serverSelect.style, {
  8478. background: 'rgba(30, 35, 50, 0.8)',
  8479. borderRadius: '8px',
  8480. border: '1px solid rgba(255, 255, 255, 0.1)',
  8481. color: 'white',
  8482. padding: '8px 12px',
  8483. outline: 'none'
  8484. });
  8485. }
  8486. // Styliser l'input du nom
  8487. if (nameInput instanceof HTMLInputElement) {
  8488. Object.assign(nameInput.style, {
  8489. background: 'rgba(30, 35, 50, 0.8)',
  8490. borderRadius: '8px',
  8491. border: '1px solid rgba(255, 255, 255, 0.1)',
  8492. color: 'white',
  8493. padding: '8px 12px',
  8494. outline: 'none'
  8495. });
  8496. // Focus style
  8497. nameInput.addEventListener('focus', () => {
  8498. nameInput.style.border = '1px solid #4287f5';
  8499. nameInput.style.boxShadow = '0 0 8px rgba(66, 135, 245, 0.5)';
  8500. });
  8501. nameInput.addEventListener('blur', () => {
  8502. nameInput.style.border = '1px solid rgba(255, 255, 255, 0.1)';
  8503. nameInput.style.boxShadow = 'none';
  8504. });
  8505. }
  8506. // Styliser la section d'aide
  8507. if (helpSection) {
  8508. Object.assign(helpSection.style, {
  8509. background: 'rgba(20, 25, 40, 0.7)',
  8510. borderRadius: '8px',
  8511. padding: '15px',
  8512. margin: '15px 0',
  8513. maxHeight: '300px',
  8514. overflowY: 'auto',
  8515. scrollbarWidth: 'thin',
  8516. scrollbarColor: '#4287f5 rgba(25, 25, 35, 0.5)'
  8517. });
  8518. // Style the help section titles
  8519. const helpTitles = helpSection.querySelectorAll('h1');
  8520. helpTitles.forEach(title => {
  8521. if (title instanceof HTMLElement) {
  8522. Object.assign(title.style, {
  8523. color: '#4287f5',
  8524. fontSize: '18px',
  8525. marginTop: '15px',
  8526. marginBottom: '8px'
  8527. });
  8528. }
  8529. });
  8530. // Style the paragraphs
  8531. const helpParagraphs = helpSection.querySelectorAll('p');
  8532. helpParagraphs.forEach(p => {
  8533. if (p instanceof HTMLElement) {
  8534. p.style.color = 'rgba(255, 255, 255, 0.8)';
  8535. p.style.fontSize = '14px';
  8536. p.style.marginBottom = '8px';
  8537. }
  8538. });
  8539. // Style the action terms and controls
  8540. const actionTerms = helpSection.querySelectorAll('.help-action');
  8541. actionTerms.forEach(term => {
  8542. if (term instanceof HTMLElement) {
  8543. term.style.color = '#ffc107'; // Yellow
  8544. term.style.fontWeight = 'bold';
  8545. }
  8546. });
  8547. const controlTerms = helpSection.querySelectorAll('.help-control');
  8548. controlTerms.forEach(term => {
  8549. if (term instanceof HTMLElement) {
  8550. term.style.color = '#4287f5'; // Bleu
  8551. term.style.fontWeight = 'bold';
  8552. }
  8553. });
  8554. }
  8555. // Apply specific style to double buttons
  8556. const btnsDoubleRow = document.querySelector('.btns-double-row');
  8557. if (btnsDoubleRow instanceof HTMLElement) {
  8558. btnsDoubleRow.style.display = 'flex';
  8559. btnsDoubleRow.style.gap = '10px';
  8560. btnsDoubleRow.style.marginTop = '10px';
  8561. }
  8562. }
  8563. MainMenuCleaning() {
  8564. // Déconnecter l'observateur précédent s'il existe
  8565. if (this.adBlockObserver) {
  8566. this.adBlockObserver.disconnect();
  8567. this.adBlockObserver = null;
  8568. }
  8569. // Select elements to hide/show
  8570. const newsWrapper = document.getElementById('news-wrapper');
  8571. const adBlockLeft = document.getElementById('ad-block-left');
  8572. const socialLeft = document.getElementById('social-share-block-wrapper');
  8573. const leftCollun = document.getElementById('left-column');
  8574. const elementsToMonitor = [
  8575. { element: newsWrapper, id: 'news-wrapper' },
  8576. { element: adBlockLeft, id: 'ad-block-left' },
  8577. { element: socialLeft, id: 'social-share-block-wrapper' },
  8578. { element: leftCollun, id: 'left-column' }
  8579. ];
  8580. // Appliquer le style personnalisé au menu principal
  8581. this.applyCustomMainMenuStyle();
  8582. if (this.isMainMenuCleaned) {
  8583. // Clean mode: hide elements
  8584. elementsToMonitor.forEach(item => {
  8585. if (item.element)
  8586. item.element.style.display = 'none';
  8587. });
  8588. // Create an observer to prevent the site from redisplaying elements
  8589. this.adBlockObserver = new MutationObserver((mutations) => {
  8590. let needsUpdate = false;
  8591. mutations.forEach(mutation => {
  8592. if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
  8593. const target = mutation.target;
  8594. // Check if the element is one of those we are monitoring
  8595. if (elementsToMonitor.some(item => item.id === target.id && target.style.display !== 'none')) {
  8596. target.style.display = 'none';
  8597. needsUpdate = true;
  8598. }
  8599. }
  8600. });
  8601. // If the site tries to redisplay an advertising element, we prevent it
  8602. if (needsUpdate) {
  8603. this.logger.log('Detection of attempt to redisplay ads - Forced hiding');
  8604. }
  8605. });
  8606. // Observe style changes on elements
  8607. elementsToMonitor.forEach(item => {
  8608. if (item.element && this.adBlockObserver) {
  8609. this.adBlockObserver.observe(item.element, {
  8610. attributes: true,
  8611. attributeFilter: ['style']
  8612. });
  8613. }
  8614. });
  8615. // Vérifier également le document body pour de nouveaux éléments ajoutés
  8616. const bodyObserver = new MutationObserver(() => {
  8617. // Réappliquer notre nettoyage après un court délai
  8618. setTimeout(() => {
  8619. if (this.isMainMenuCleaned) {
  8620. elementsToMonitor.forEach(item => {
  8621. const element = document.getElementById(item.id);
  8622. if (element && element.style.display !== 'none') {
  8623. element.style.display = 'none';
  8624. }
  8625. });
  8626. }
  8627. }, 100);
  8628. });
  8629. // Observe changes in the DOM
  8630. bodyObserver.observe(document.body, { childList: true, subtree: true });
  8631. }
  8632. else {
  8633. // Mode normal: rétablir l'affichage
  8634. elementsToMonitor.forEach(item => {
  8635. if (item.element)
  8636. item.element.style.display = 'block';
  8637. });
  8638. }
  8639. }
  8640. removeSimpleSpotifyPlayer() {
  8641. // Supprimer le conteneur principal du lecteur
  8642. const container = document.getElementById('spotify-player-container');
  8643. if (container) {
  8644. container.remove();
  8645. }
  8646. // Supprimer aussi le bouton flottant grâce à son ID
  8647. const floatButton = document.getElementById('spotify-float-button');
  8648. if (floatButton) {
  8649. floatButton.remove();
  8650. }
  8651. }
  8652. }
  8653.  
  8654. ;// ./src/HUD/MOD/LoadingScreen.ts
  8655. /**
  8656. * LoadingScreen.ts
  8657. *
  8658. * This module provides a loading animation with a logo and a rotating loading circle
  8659. * that displays during the loading of game resources.
  8660. */
  8661. class LoadingScreen {
  8662. /**
  8663. * Creates a new instance of the loading screen
  8664. * @param logoUrl URL of the Kxs logo to display
  8665. */
  8666. constructor(logoUrl) {
  8667. this.logoUrl = logoUrl;
  8668. this.container = document.createElement('div');
  8669. this.initializeStyles();
  8670. this.createContent();
  8671. }
  8672. /**
  8673. * Initializes CSS styles for the loading screen
  8674. */
  8675. initializeStyles() {
  8676. // Styles for the main container
  8677. Object.assign(this.container.style, {
  8678. position: 'fixed',
  8679. top: '0',
  8680. left: '0',
  8681. width: '100%',
  8682. height: '100%',
  8683. backgroundColor: 'rgba(0, 0, 0, 0.9)',
  8684. display: 'flex',
  8685. flexDirection: 'column',
  8686. justifyContent: 'center',
  8687. alignItems: 'center',
  8688. zIndex: '9999',
  8689. transition: 'opacity 0.5s ease-in-out',
  8690. animation: 'fadeIn 0.5s ease-in-out',
  8691. backdropFilter: 'blur(5px)'
  8692. });
  8693. }
  8694. /**
  8695. * Creates the loading screen content (logo and loading circle)
  8696. */
  8697. createContent() {
  8698. // Create container for the logo
  8699. const logoContainer = document.createElement('div');
  8700. Object.assign(logoContainer.style, {
  8701. width: '200px',
  8702. height: '200px',
  8703. marginBottom: '20px',
  8704. position: 'relative',
  8705. display: 'flex',
  8706. justifyContent: 'center',
  8707. alignItems: 'center'
  8708. });
  8709. // Create the logo element
  8710. const logo = document.createElement('img');
  8711. logo.src = this.logoUrl;
  8712. Object.assign(logo.style, {
  8713. width: '150px',
  8714. height: '150px',
  8715. objectFit: 'contain',
  8716. position: 'absolute',
  8717. zIndex: '2',
  8718. animation: 'pulse 2s ease-in-out infinite'
  8719. });
  8720. // Create the main loading circle
  8721. const loadingCircle = document.createElement('div');
  8722. Object.assign(loadingCircle.style, {
  8723. width: '180px',
  8724. height: '180px',
  8725. border: '4px solid transparent',
  8726. borderTopColor: '#3498db',
  8727. borderRadius: '50%',
  8728. animation: 'spin 1.5s linear infinite',
  8729. position: 'absolute',
  8730. zIndex: '1'
  8731. });
  8732. // Create a second loading circle (rotating in the opposite direction)
  8733. const loadingCircle2 = document.createElement('div');
  8734. Object.assign(loadingCircle2.style, {
  8735. width: '200px',
  8736. height: '200px',
  8737. border: '2px solid transparent',
  8738. borderLeftColor: '#e74c3c',
  8739. borderRightColor: '#e74c3c',
  8740. borderRadius: '50%',
  8741. animation: 'spin-reverse 3s linear infinite',
  8742. position: 'absolute',
  8743. zIndex: '0'
  8744. });
  8745. // Add animations
  8746. const styleSheet = document.createElement('style');
  8747. styleSheet.textContent = `
  8748. @keyframes spin {
  8749. 0% { transform: rotate(0deg); }
  8750. 100% { transform: rotate(360deg); }
  8751. }
  8752. @keyframes spin-reverse {
  8753. 0% { transform: rotate(0deg); }
  8754. 100% { transform: rotate(-360deg); }
  8755. }
  8756. @keyframes pulse {
  8757. 0% { transform: scale(1); }
  8758. 50% { transform: scale(1.05); }
  8759. 100% { transform: scale(1); }
  8760. }
  8761. @keyframes fadeIn {
  8762. 0% { opacity: 0; }
  8763. 100% { opacity: 1; }
  8764. }
  8765. `;
  8766. document.head.appendChild(styleSheet);
  8767. // Ajout d'un texte de chargement
  8768. const loadingText = document.createElement('div');
  8769. loadingText.textContent = 'Loading...';
  8770. Object.assign(loadingText.style, {
  8771. color: 'white',
  8772. fontFamily: 'Arial, sans-serif',
  8773. fontSize: '18px',
  8774. marginTop: '20px',
  8775. animation: 'pulse 1.5s ease-in-out infinite'
  8776. });
  8777. // Ajout d'un sous-texte
  8778. const subText = document.createElement('div');
  8779. subText.textContent = 'Initializing resources...';
  8780. Object.assign(subText.style, {
  8781. color: 'rgba(255, 255, 255, 0.7)',
  8782. fontFamily: 'Arial, sans-serif',
  8783. fontSize: '14px',
  8784. marginTop: '5px'
  8785. });
  8786. // Assemble the elements
  8787. logoContainer.appendChild(loadingCircle2);
  8788. logoContainer.appendChild(loadingCircle);
  8789. logoContainer.appendChild(logo);
  8790. this.container.appendChild(logoContainer);
  8791. this.container.appendChild(loadingText);
  8792. this.container.appendChild(subText);
  8793. }
  8794. /**
  8795. * Shows the loading screen
  8796. */
  8797. show() {
  8798. document.body.appendChild(this.container);
  8799. }
  8800. /**
  8801. * Hides the loading screen with a fade transition
  8802. */
  8803. hide() {
  8804. this.container.style.opacity = '0';
  8805. setTimeout(() => {
  8806. if (this.container.parentNode) {
  8807. document.body.removeChild(this.container);
  8808. }
  8809. }, 500); // Wait for the transition to finish before removing the element
  8810. }
  8811. }
  8812.  
  8813. ;// ./src/HUD/ServerSelector.ts
  8814. class ServerSelector {
  8815. constructor(servers, onServerSelect) {
  8816. this.isActive = false;
  8817. this.serverContainer = null;
  8818. this.serverCards = [];
  8819. this.selectedIndex = 0;
  8820. this.originalBodyContent = '';
  8821. this.animation = null;
  8822. this.servers = [];
  8823. this.onServerSelect = null;
  8824. this.servers = this.processServerUrls(servers);
  8825. this.onServerSelect = onServerSelect || null;
  8826. }
  8827. /**
  8828. * Process server URLs from match patterns to display-friendly names
  8829. */
  8830. processServerUrls(servers) {
  8831. return servers.map(server => {
  8832. // Remove wildcards and protocol
  8833. return server.replace(/^\*:\/\//, '')
  8834. // Remove trailing wildcards
  8835. .replace(/\/\*$/, '')
  8836. // Handle special case for IP addresses
  8837. .replace(/\/+$/, '');
  8838. });
  8839. }
  8840. /**
  8841. * Show the server selection interface
  8842. */
  8843. show() {
  8844. // If already active, close first to reset properly
  8845. if (this.isActive) {
  8846. this.close();
  8847. }
  8848. this.isActive = true;
  8849. // Store original content if not already stored
  8850. if (!this.originalBodyContent) {
  8851. this.originalBodyContent = document.body.innerHTML;
  8852. }
  8853. // Create overlay
  8854. this.createInterface();
  8855. // Start animations
  8856. this.startAnimations();
  8857. // Add keyboard navigation
  8858. this.setupKeyboardNavigation();
  8859. }
  8860. /**
  8861. * Create the server selection interface
  8862. */
  8863. createInterface() {
  8864. // Create overlay container
  8865. const overlay = document.createElement('div');
  8866. overlay.style.position = 'fixed';
  8867. overlay.style.top = '0';
  8868. overlay.style.left = '0';
  8869. overlay.style.width = '100%';
  8870. overlay.style.height = '100%';
  8871. overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.9)';
  8872. overlay.style.display = 'flex';
  8873. overlay.style.flexDirection = 'column';
  8874. overlay.style.justifyContent = 'center';
  8875. overlay.style.alignItems = 'center';
  8876. overlay.style.zIndex = '10000';
  8877. overlay.style.perspective = '1000px';
  8878. overlay.style.fontFamily = 'Arial, sans-serif';
  8879. // Create header
  8880. const header = document.createElement('h1');
  8881. header.textContent = 'Select Server';
  8882. header.style.color = '#fff';
  8883. header.style.marginBottom = '40px';
  8884. header.style.fontSize = '36px';
  8885. header.style.textShadow = '0 0 10px rgba(255,0,0,0.8)';
  8886. overlay.appendChild(header);
  8887. // Create server container
  8888. this.serverContainer = document.createElement('div');
  8889. this.serverContainer.style.position = 'relative';
  8890. this.serverContainer.style.width = '80%';
  8891. this.serverContainer.style.height = '300px';
  8892. this.serverContainer.style.display = 'flex';
  8893. this.serverContainer.style.justifyContent = 'center';
  8894. this.serverContainer.style.alignItems = 'center';
  8895. this.serverContainer.style.transformStyle = 'preserve-3d';
  8896. overlay.appendChild(this.serverContainer);
  8897. // Create instructions
  8898. const instructions = document.createElement('div');
  8899. instructions.style.position = 'absolute';
  8900. instructions.style.bottom = '20px';
  8901. instructions.style.color = '#aaa';
  8902. instructions.style.fontSize = '16px';
  8903. instructions.innerHTML = 'Use <strong>←/→</strong> arrows to navigate | <strong>Enter</strong> to select | <strong>Esc</strong> to close';
  8904. overlay.appendChild(instructions);
  8905. // Create server cards
  8906. this.createServerCards();
  8907. // Add the overlay to the body
  8908. document.body.appendChild(overlay);
  8909. }
  8910. /**
  8911. * Create 3D rotating cards for each server
  8912. */
  8913. createServerCards() {
  8914. if (!this.serverContainer)
  8915. return;
  8916. const totalServers = this.servers.length;
  8917. const radius = 300; // Radius of the circle
  8918. const cardWidth = 200;
  8919. const cardHeight = 120;
  8920. this.servers.forEach((server, index) => {
  8921. const card = document.createElement('div');
  8922. card.className = 'server-card';
  8923. card.style.position = 'absolute';
  8924. card.style.width = `${cardWidth}px`;
  8925. card.style.height = `${cardHeight}px`;
  8926. card.style.backgroundColor = index === this.selectedIndex ? '#500' : '#333';
  8927. card.style.color = '#fff';
  8928. card.style.borderRadius = '10px';
  8929. card.style.display = 'flex';
  8930. card.style.flexDirection = 'column';
  8931. card.style.justifyContent = 'center';
  8932. card.style.alignItems = 'center';
  8933. card.style.cursor = 'pointer';
  8934. card.style.boxShadow = '0 10px 20px rgba(0,0,0,0.5)';
  8935. card.style.transition = 'background-color 0.3s ease';
  8936. card.style.padding = '15px';
  8937. card.style.backfaceVisibility = 'hidden';
  8938. // Create server name
  8939. const serverName = document.createElement('h2');
  8940. serverName.textContent = server;
  8941. serverName.style.margin = '0 0 10px 0';
  8942. serverName.style.fontSize = '20px';
  8943. card.appendChild(serverName);
  8944. // Add status indicator
  8945. const status = document.createElement('div');
  8946. status.style.width = '10px';
  8947. status.style.height = '10px';
  8948. status.style.borderRadius = '50%';
  8949. status.style.backgroundColor = '#0f0'; // Green for online
  8950. status.style.marginTop = '10px';
  8951. card.appendChild(status);
  8952. // Add click event
  8953. card.addEventListener('click', () => {
  8954. this.selectedIndex = index;
  8955. this.updateCardPositions();
  8956. this.selectServer();
  8957. });
  8958. this.serverCards.push(card);
  8959. if (this.serverContainer) {
  8960. this.serverContainer.appendChild(card);
  8961. }
  8962. });
  8963. // Position the cards in a circle
  8964. this.updateCardPositions();
  8965. }
  8966. /**
  8967. * Update the positions of all server cards in a 3D circle
  8968. */
  8969. updateCardPositions() {
  8970. const totalServers = this.servers.length;
  8971. const radius = Math.max(300, totalServers * 40); // Adjust radius based on number of servers
  8972. this.serverCards.forEach((card, index) => {
  8973. // Calculate position on the circle
  8974. const theta = ((index - this.selectedIndex) / totalServers) * 2 * Math.PI;
  8975. const x = radius * Math.sin(theta);
  8976. const z = radius * Math.cos(theta) - radius;
  8977. // Update card style
  8978. card.style.transform = `translateX(${x}px) translateZ(${z}px) rotateY(${-theta * 180 / Math.PI}deg)`;
  8979. card.style.zIndex = z < 0 ? '-1' : '1';
  8980. card.style.opacity = (1 - Math.abs(index - this.selectedIndex) / totalServers).toString();
  8981. card.style.backgroundColor = index === this.selectedIndex ? '#500' : '#333';
  8982. // Add glow effect to selected card
  8983. if (index === this.selectedIndex) {
  8984. card.style.boxShadow = '0 0 20px rgba(255,0,0,0.8), 0 10px 20px rgba(0,0,0,0.5)';
  8985. }
  8986. else {
  8987. card.style.boxShadow = '0 10px 20px rgba(0,0,0,0.5)';
  8988. }
  8989. });
  8990. }
  8991. /**
  8992. * Start animations for the 3D carousel
  8993. */
  8994. startAnimations() {
  8995. // Subtle continuous movement for more 3D effect
  8996. let angle = 0;
  8997. this.animation = window.setInterval(() => {
  8998. angle += 0.005;
  8999. if (this.serverContainer) {
  9000. this.serverContainer.style.transform = `rotateY(${Math.sin(angle) * 5}deg) rotateX(${Math.cos(angle) * 3}deg)`;
  9001. }
  9002. }, 16);
  9003. }
  9004. /**
  9005. * Set up keyboard navigation
  9006. */
  9007. setupKeyboardNavigation() {
  9008. const keyHandler = (e) => {
  9009. switch (e.key) {
  9010. case 'ArrowLeft':
  9011. this.navigate(-1);
  9012. break;
  9013. case 'ArrowRight':
  9014. this.navigate(1);
  9015. break;
  9016. case 'Enter':
  9017. this.selectServer();
  9018. break;
  9019. case 'Escape':
  9020. this.close();
  9021. break;
  9022. }
  9023. };
  9024. document.addEventListener('keydown', keyHandler);
  9025. // Store the handler reference so it can be removed when the selector is closed
  9026. this._keyHandler = keyHandler;
  9027. }
  9028. /**
  9029. * Navigate between servers
  9030. */
  9031. navigate(direction) {
  9032. const totalServers = this.servers.length;
  9033. this.selectedIndex = (this.selectedIndex + direction + totalServers) % totalServers;
  9034. this.updateCardPositions();
  9035. }
  9036. /**
  9037. * Select current server and close the selector
  9038. */
  9039. selectServer() {
  9040. const selectedServer = this.servers[this.selectedIndex];
  9041. if (this.onServerSelect && selectedServer) {
  9042. this.onServerSelect(selectedServer);
  9043. }
  9044. this.close();
  9045. }
  9046. /**
  9047. * Close the server selector
  9048. */
  9049. close() {
  9050. var _a;
  9051. if (!this.isActive)
  9052. return;
  9053. this.isActive = false;
  9054. // Stop animations
  9055. if (this.animation !== null) {
  9056. clearInterval(this.animation);
  9057. this.animation = null;
  9058. }
  9059. // Remove keyboard event listener
  9060. if (this._keyHandler) {
  9061. document.removeEventListener('keydown', this._keyHandler);
  9062. this._keyHandler = null;
  9063. }
  9064. // Remove the overlay
  9065. document.querySelectorAll('div.server-card').forEach(el => el.remove());
  9066. if (this.serverContainer && this.serverContainer.parentNode) {
  9067. const parent = this.serverContainer.parentNode;
  9068. if (parent && parent instanceof HTMLElement) {
  9069. parent.remove();
  9070. }
  9071. else if (parent) {
  9072. // Fallback if parentNode exists but isn't an HTMLElement
  9073. const parentEl = parent;
  9074. (_a = parentEl.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(parentEl);
  9075. }
  9076. }
  9077. // Reset state for next use
  9078. this.serverContainer = null;
  9079. this.serverCards = [];
  9080. this.selectedIndex = 0;
  9081. }
  9082. }
  9083.  
  9084. ;// ./src/HUD/EasterEgg.ts
  9085.  
  9086.  
  9087. class EasterEgg {
  9088. constructor() {
  9089. this.originalStyles = {};
  9090. this.zelda3Sound = null;
  9091. this.periodSound = null;
  9092. this.ambientSound = null;
  9093. this.buttonClickSound = null;
  9094. this.arrowKeySound = null;
  9095. this.enterKeySound = null;
  9096. this.closeMenuSound = null;
  9097. this.textElement = null;
  9098. this.fireElements = [];
  9099. this.pillars = [];
  9100. this.isActive = false;
  9101. this.isInitialized = false;
  9102. this.overlayElement = null;
  9103. this.animationFrameId = null;
  9104. this.originalBodyContent = '';
  9105. this.serverSelector = null;
  9106. this.serverButton = null;
  9107. this.messageChangeInterval = null;
  9108. this.originalPageTitle = '';
  9109. this.messages = [
  9110. "You're already in",
  9111. "You didnt have found Kxs, is kxs who found you",
  9112. "The prophecies are true",
  9113. "Kxs is the chosen one",
  9114. "Kxs is the one who will save you",
  9115. "I am Kxs, the one who will save you"
  9116. ];
  9117. this.globalEventHandlersInitialized = false;
  9118. this.init();
  9119. }
  9120. init() {
  9121. // Save original page title
  9122. this.originalPageTitle = document.title;
  9123. // Check if we're on the target website
  9124. if (window.location.hostname === 'kxs.rip' || window.location.hostname === 'www.kxs.rip') {
  9125. // Initialize sounds
  9126. this.zelda3Sound = new Audio('https://kxs.rip/assets/message.mp3'); // Replace with actual Zelda sound
  9127. this.periodSound = new Audio('https://kxs.rip/assets/message-finish.mp3'); // Sound for the final period
  9128. this.ambientSound = new Audio('https://kxs.rip/assets/hell_ambiance.m4a'); // Replace with actual ambient URL
  9129. this.buttonClickSound = new Audio('https://kxs.rip/assets/enter.mp3'); // Button click sound
  9130. this.arrowKeySound = new Audio('https://kxs.rip/assets/arrow.mp3'); // Arrow key sound
  9131. this.enterKeySound = new Audio('https://kxs.rip/assets/enter.mp3'); // Enter key sound
  9132. this.closeMenuSound = new Audio('https://kxs.rip/assets/close.mp3'); // Close menu sound
  9133. if (this.ambientSound) {
  9134. this.ambientSound.loop = true;
  9135. }
  9136. // Create the initial overlay with Click prompt instead of immediately applying Easter egg
  9137. this.createInitialOverlay();
  9138. // Initialize global event handlers for interaction sounds
  9139. this.initGlobalEventHandlers();
  9140. }
  9141. }
  9142. /**
  9143. * Creates the initial blur overlay with Click text
  9144. */
  9145. createInitialOverlay() {
  9146. // Store original body content to restore it later if needed
  9147. this.originalBodyContent = document.body.innerHTML;
  9148. // Save original styles
  9149. this.saveOriginalStyles();
  9150. // Create the overlay
  9151. this.overlayElement = document.createElement('div');
  9152. const overlay = this.overlayElement;
  9153. // Set full screen styles
  9154. overlay.style.position = 'fixed';
  9155. overlay.style.top = '0';
  9156. overlay.style.left = '0';
  9157. overlay.style.width = '100%';
  9158. overlay.style.height = '100%';
  9159. overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.3)';
  9160. overlay.style.backdropFilter = 'blur(10px)';
  9161. overlay.style['-webkit-backdrop-filter'] = 'blur(10px)';
  9162. overlay.style.display = 'flex';
  9163. overlay.style.justifyContent = 'center';
  9164. overlay.style.alignItems = 'center';
  9165. overlay.style.cursor = 'pointer';
  9166. overlay.style.zIndex = '9999';
  9167. overlay.style.transition = 'opacity 0.5s ease';
  9168. // Create the Click text
  9169. const clickText = document.createElement('div');
  9170. clickText.textContent = 'Click';
  9171. clickText.style.color = '#fff';
  9172. clickText.style.fontSize = '3rem';
  9173. clickText.style.fontWeight = 'bold';
  9174. clickText.style.textShadow = '0 0 10px rgba(255, 255, 255, 0.7)';
  9175. clickText.style.fontFamily = '"Cinzel", "Trajan Pro", serif';
  9176. clickText.style.letterSpacing = '5px';
  9177. // Add font for the text
  9178. const fontLink = document.createElement('link');
  9179. fontLink.rel = 'stylesheet';
  9180. fontLink.href = 'https://fonts.googleapis.com/css2?family=Cinzel:wght@700&display=swap';
  9181. document.head.appendChild(fontLink);
  9182. // Add click event to start the Easter egg
  9183. overlay.addEventListener('click', () => {
  9184. this.removeOverlay();
  9185. this.applyEasterEgg();
  9186. });
  9187. // Add the text to the overlay
  9188. overlay.appendChild(clickText);
  9189. // Add the overlay to the body
  9190. document.body.appendChild(overlay);
  9191. }
  9192. /**
  9193. * Removes the initial overlay
  9194. */
  9195. removeOverlay() {
  9196. if (this.overlayElement && this.overlayElement.parentNode) {
  9197. // Fade out
  9198. this.overlayElement.style.opacity = '0';
  9199. // Remove after transition
  9200. setTimeout(() => {
  9201. if (this.overlayElement && this.overlayElement.parentNode) {
  9202. this.overlayElement.parentNode.removeChild(this.overlayElement);
  9203. this.overlayElement = null;
  9204. }
  9205. }, 500);
  9206. }
  9207. }
  9208. /**
  9209. * Initialize global event handlers for sounds
  9210. */
  9211. initGlobalEventHandlers() {
  9212. if (this.globalEventHandlersInitialized)
  9213. return;
  9214. this.globalEventHandlersInitialized = true;
  9215. // Play sound on button clicks
  9216. document.addEventListener('click', (e) => {
  9217. if (e.target instanceof HTMLButtonElement ||
  9218. e.target instanceof HTMLAnchorElement ||
  9219. (e.target instanceof HTMLElement && e.target.role === 'button')) {
  9220. this.playButtonSound();
  9221. }
  9222. });
  9223. // Play sound on arrow keys and enter key
  9224. document.addEventListener('keydown', (e) => {
  9225. if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
  9226. this.playArrowKeySound();
  9227. }
  9228. else if (e.key === 'Enter') {
  9229. this.playEnterKeySound();
  9230. }
  9231. });
  9232. }
  9233. applyEasterEgg() {
  9234. if (this.isActive)
  9235. return;
  9236. this.isActive = true;
  9237. this.isInitialized = true;
  9238. // Transform the website
  9239. this.transformWebsite();
  9240. // Start animations
  9241. this.startAnimations();
  9242. // Play ambient sound
  9243. this.playAmbientSound();
  9244. // Display the message with sound effect
  9245. setTimeout(() => {
  9246. this.displayMessage();
  9247. // Add server selector button after the message is displayed
  9248. this.addServerSelectorButton();
  9249. }, 2000);
  9250. }
  9251. saveOriginalStyles() {
  9252. this.originalStyles = {
  9253. bodyBackground: document.body.style.background,
  9254. bodyColor: document.body.style.color,
  9255. bodyOverflow: document.body.style.overflow
  9256. };
  9257. }
  9258. transformWebsite() {
  9259. // Clear the existing content
  9260. document.body.innerHTML = '';
  9261. document.body.style.margin = '0';
  9262. document.body.style.padding = '0';
  9263. document.body.style.overflow = 'hidden';
  9264. document.body.style.backgroundColor = '#000';
  9265. document.body.style.color = '#fff';
  9266. document.body.style.fontFamily = '"Times New Roman", serif';
  9267. document.body.style.height = '100vh';
  9268. document.body.style.display = 'flex';
  9269. document.body.style.flexDirection = 'column';
  9270. document.body.style.justifyContent = 'center';
  9271. document.body.style.alignItems = 'center';
  9272. document.body.style.perspective = '1000px';
  9273. // Create a temple background
  9274. const temple = document.createElement('div');
  9275. temple.style.position = 'absolute';
  9276. temple.style.top = '0';
  9277. temple.style.left = '0';
  9278. temple.style.width = '100%';
  9279. temple.style.height = '100%';
  9280. temple.style.background = 'linear-gradient(to bottom, #000, #300)';
  9281. temple.style.zIndex = '-2';
  9282. document.body.appendChild(temple);
  9283. // Create pillars
  9284. for (let i = 0; i < 6; i++) {
  9285. const pillar = document.createElement('div');
  9286. pillar.style.position = 'absolute';
  9287. pillar.style.width = '80px';
  9288. pillar.style.height = '100%';
  9289. pillar.style.background = 'linear-gradient(to bottom, #222, #111)';
  9290. pillar.style.transform = `rotateY(${i * 60}deg) translateZ(400px)`;
  9291. pillar.style.boxShadow = 'inset 0 0 20px #500';
  9292. pillar.style.transition = 'transform 0.5s ease-in-out';
  9293. this.pillars.push(pillar);
  9294. document.body.appendChild(pillar);
  9295. }
  9296. // Create floor
  9297. const floor = document.createElement('div');
  9298. floor.style.position = 'absolute';
  9299. floor.style.bottom = '0';
  9300. floor.style.width = '100%';
  9301. floor.style.height = '40%';
  9302. floor.style.background = 'radial-gradient(circle, #300, #100)';
  9303. floor.style.zIndex = '-1';
  9304. document.body.appendChild(floor);
  9305. // Create text container for the message
  9306. this.textElement = document.createElement('div');
  9307. this.textElement.style.position = 'relative';
  9308. this.textElement.style.fontSize = '3.5em';
  9309. this.textElement.style.fontWeight = 'bold';
  9310. this.textElement.style.fontFamily = '"Cinzel", "Trajan Pro", serif';
  9311. this.textElement.style.color = '#f00';
  9312. this.textElement.style.textShadow = '0 0 10px #f00, 0 0 20px #f00, 0 0 30px #900';
  9313. this.textElement.style.letterSpacing = '2px';
  9314. this.textElement.style.opacity = '0';
  9315. this.textElement.style.transition = 'opacity 2s';
  9316. // Add a fancy font from Google Fonts
  9317. const fontLink = document.createElement('link');
  9318. fontLink.rel = 'stylesheet';
  9319. fontLink.href = 'https://fonts.googleapis.com/css2?family=Cinzel:wght@700&display=swap';
  9320. document.head.appendChild(fontLink);
  9321. document.body.appendChild(this.textElement);
  9322. // Create fire elements as 3D rotating rectangles
  9323. for (let i = 0; i < 20; i++) {
  9324. const fire = document.createElement('div');
  9325. fire.style.position = 'absolute';
  9326. fire.style.width = `${Math.random() * 40 + 20}px`;
  9327. fire.style.height = `${Math.random() * 60 + 40}px`;
  9328. fire.style.background = 'radial-gradient(circle, #f50, #900, transparent)';
  9329. fire.style.borderRadius = '10%';
  9330. fire.style.filter = 'blur(3px)';
  9331. fire.style.opacity = `${Math.random() * 0.5 + 0.5}`;
  9332. const randomX = Math.random() * 100;
  9333. fire.style.left = `${randomX}%`;
  9334. fire.style.bottom = '0';
  9335. fire.style.zIndex = '-1';
  9336. fire.style.transformStyle = 'preserve-3d';
  9337. fire.style.perspective = '1000px';
  9338. fire.dataset.velocityY = `${Math.random() * 2 + 1}`;
  9339. fire.dataset.posX = randomX.toString();
  9340. fire.dataset.posY = '0';
  9341. fire.dataset.rotateX = `${Math.random() * 4 - 2}`; // Random rotation speed X
  9342. fire.dataset.rotateY = `${Math.random() * 4 - 2}`; // Random rotation speed Y
  9343. fire.dataset.rotateZ = `${Math.random() * 4 - 2}`; // Random rotation speed Z
  9344. fire.dataset.rotationX = '0';
  9345. fire.dataset.rotationY = '0';
  9346. fire.dataset.rotationZ = '0';
  9347. this.fireElements.push(fire);
  9348. document.body.appendChild(fire);
  9349. }
  9350. }
  9351. startAnimations() {
  9352. // Animate fire and pillars
  9353. this.animateFireElements();
  9354. this.animatePillars();
  9355. }
  9356. animateFireElements() {
  9357. if (!this.isActive)
  9358. return;
  9359. this.fireElements.forEach(fire => {
  9360. let posY = parseFloat(fire.dataset.posY || '0');
  9361. const velocityY = parseFloat(fire.dataset.velocityY || '1');
  9362. // Update position
  9363. posY += velocityY;
  9364. fire.dataset.posY = posY.toString();
  9365. // Update rotation
  9366. let rotX = parseFloat(fire.dataset.rotationX || '0');
  9367. let rotY = parseFloat(fire.dataset.rotationY || '0');
  9368. let rotZ = parseFloat(fire.dataset.rotationZ || '0');
  9369. rotX += parseFloat(fire.dataset.rotateX || '0');
  9370. rotY += parseFloat(fire.dataset.rotateY || '0');
  9371. rotZ += parseFloat(fire.dataset.rotateZ || '0');
  9372. fire.dataset.rotationX = rotX.toString();
  9373. fire.dataset.rotationY = rotY.toString();
  9374. fire.dataset.rotationZ = rotZ.toString();
  9375. // Apply transform
  9376. fire.style.transform = `translateY(${-posY}px) rotateX(${rotX}deg) rotateY(${rotY}deg) rotateZ(${rotZ}deg)`;
  9377. // Reset fire when it goes off screen
  9378. if (posY > 100) {
  9379. posY = 0;
  9380. fire.dataset.posY = '0';
  9381. fire.style.opacity = `${Math.random() * 0.5 + 0.5}`;
  9382. fire.style.width = `${Math.random() * 40 + 20}px`;
  9383. fire.style.height = `${Math.random() * 60 + 40}px`;
  9384. const randomX = Math.random() * 100;
  9385. fire.style.left = `${randomX}%`;
  9386. fire.dataset.posX = randomX.toString();
  9387. // Reset rotation speeds
  9388. fire.dataset.rotateX = `${Math.random() * 4 - 2}`;
  9389. fire.dataset.rotateY = `${Math.random() * 4 - 2}`;
  9390. fire.dataset.rotateZ = `${Math.random() * 4 - 2}`;
  9391. }
  9392. fire.style.opacity = `${Math.max(0, 1 - posY / 100)}`;
  9393. });
  9394. this.animationFrameId = requestAnimationFrame(() => this.animateFireElements());
  9395. }
  9396. animatePillars() {
  9397. if (!this.isActive)
  9398. return;
  9399. // Create a slow rotation effect for the pillars
  9400. let angle = 0;
  9401. setInterval(() => {
  9402. angle += 0.5;
  9403. this.pillars.forEach((pillar, index) => {
  9404. pillar.style.transform = `rotateY(${index * 60 + angle}deg) translateZ(400px)`;
  9405. });
  9406. }, 100);
  9407. }
  9408. playAmbientSound() {
  9409. // Play ambient sound
  9410. if (this.ambientSound) {
  9411. this.ambientSound.volume = 0.3;
  9412. this.ambientSound.play().catch(err => {
  9413. console.error('Failed to play ambient sound:', err);
  9414. });
  9415. }
  9416. }
  9417. /**
  9418. * Temporarily reduce ambient sound volume to allow other sounds to be heard better
  9419. */
  9420. lowerAmbientVolume() {
  9421. if (this.ambientSound) {
  9422. // Store current volume if we need it
  9423. const originalVolume = this.ambientSound.volume;
  9424. // Lower volume
  9425. this.ambientSound.volume = 0.1; // Lower to 1/3 of original volume
  9426. // Restore volume after a delay
  9427. setTimeout(() => {
  9428. if (this.ambientSound) {
  9429. this.ambientSound.volume = 0.3; // Restore to original volume
  9430. }
  9431. }, 500); // Half second delay
  9432. }
  9433. }
  9434. /**
  9435. * Play button click sound
  9436. */
  9437. playButtonSound() {
  9438. if (this.buttonClickSound) {
  9439. // Lower ambient volume
  9440. this.lowerAmbientVolume();
  9441. this.buttonClickSound.currentTime = 0;
  9442. this.buttonClickSound.volume = 0.3;
  9443. this.buttonClickSound.play().catch(err => {
  9444. console.error('Failed to play button sound:', err);
  9445. });
  9446. }
  9447. }
  9448. /**
  9449. * Play arrow key sound
  9450. */
  9451. playArrowKeySound() {
  9452. if (this.arrowKeySound) {
  9453. // Lower ambient volume
  9454. this.lowerAmbientVolume();
  9455. this.arrowKeySound.currentTime = 0;
  9456. this.arrowKeySound.volume = 0.3;
  9457. this.arrowKeySound.play().catch(err => {
  9458. console.error('Failed to play arrow key sound:', err);
  9459. });
  9460. }
  9461. }
  9462. /**
  9463. * Play enter key sound
  9464. */
  9465. playEnterKeySound() {
  9466. if (this.enterKeySound) {
  9467. // Lower ambient volume
  9468. this.lowerAmbientVolume();
  9469. this.enterKeySound.currentTime = 0;
  9470. this.enterKeySound.volume = 0.3;
  9471. this.enterKeySound.play().catch(err => {
  9472. console.error('Failed to play enter key sound:', err);
  9473. });
  9474. }
  9475. }
  9476. displayMessage() {
  9477. if (!this.textElement)
  9478. return;
  9479. // Set the message text and start with the first message
  9480. this.typeMessage(this.messages[0]);
  9481. // Set up message changing at random intervals
  9482. this.setupMessageChanging();
  9483. }
  9484. /**
  9485. * Type out a message with the typewriter effect
  9486. */
  9487. typeMessage(message) {
  9488. if (!this.textElement)
  9489. return;
  9490. // Clear current text and ensure visibility
  9491. this.textElement.textContent = '';
  9492. this.textElement.style.opacity = '1';
  9493. // Update page title with message
  9494. document.title = message;
  9495. // Calculate typing speed based on message length
  9496. // Longer messages type faster (inversely proportional)
  9497. const baseSpeed = 300; // Base speed in ms
  9498. const minSpeed = 40; // Minimum speed for very long messages
  9499. const typeSpeed = Math.max(minSpeed, baseSpeed - (message.length * 5));
  9500. // Type writer effect with Zelda sound
  9501. let i = 0;
  9502. const typeInterval = setInterval(() => {
  9503. if (i < message.length && this.textElement) {
  9504. // Check if we're at the last character and it's not already a period
  9505. const isLastChar = i === message.length - 1;
  9506. const shouldAddPeriod = isLastChar && message.charAt(i) !== '.';
  9507. // Play the appropriate sound
  9508. if (isLastChar && this.periodSound) {
  9509. // Play special sound for the last character
  9510. this.periodSound.currentTime = 0;
  9511. this.periodSound.volume = 0.3;
  9512. this.periodSound.play().catch(err => {
  9513. console.error('Failed to play period sound:', err);
  9514. });
  9515. }
  9516. else if (this.zelda3Sound) {
  9517. // Play regular typing sound
  9518. this.zelda3Sound.currentTime = 0;
  9519. this.zelda3Sound.volume = 0.2;
  9520. this.zelda3Sound.play().catch(err => {
  9521. console.error('Failed to play Zelda sound:', err);
  9522. });
  9523. }
  9524. // Add character to text element
  9525. this.textElement.textContent += message.charAt(i);
  9526. // Update page title in real-time with the current text
  9527. document.title = this.textElement.textContent || message;
  9528. // If last character and we should add a period, do it with a pause
  9529. if (shouldAddPeriod) {
  9530. setTimeout(() => {
  9531. if (this.textElement && this.periodSound) {
  9532. this.periodSound.currentTime = 0;
  9533. this.periodSound.volume = 0.4;
  9534. this.periodSound.play().catch(err => {
  9535. console.error('Failed to play period sound:', err);
  9536. });
  9537. this.textElement.textContent += '.';
  9538. // Update title with the final period
  9539. document.title = this.textElement.textContent || (message + '.');
  9540. }
  9541. }, 400);
  9542. }
  9543. i++;
  9544. }
  9545. else {
  9546. clearInterval(typeInterval);
  9547. }
  9548. }, typeSpeed); // Dynamic typing speed based on message length
  9549. }
  9550. /**
  9551. * Setup changing messages at random intervals
  9552. */
  9553. setupMessageChanging() {
  9554. // Function to change to a random message
  9555. const changeMessage = () => {
  9556. // Get a random message that's different from the current one
  9557. if (!this.textElement)
  9558. return;
  9559. const currentMessage = this.textElement.textContent || '';
  9560. let newMessage = currentMessage;
  9561. // Make sure we pick a different message
  9562. while (newMessage === currentMessage) {
  9563. const randomIndex = Math.floor(Math.random() * this.messages.length);
  9564. newMessage = this.messages[randomIndex];
  9565. }
  9566. // Type the new message
  9567. this.typeMessage(newMessage);
  9568. // Schedule the next message change
  9569. this.scheduleNextMessageChange();
  9570. };
  9571. // Schedule the first message change
  9572. this.scheduleNextMessageChange();
  9573. }
  9574. /**
  9575. * Schedule the next message change with a random delay
  9576. */
  9577. scheduleNextMessageChange() {
  9578. // Clear any existing timer
  9579. if (this.messageChangeInterval !== null) {
  9580. clearTimeout(this.messageChangeInterval);
  9581. }
  9582. // Random delay between 4 and 19 seconds
  9583. const delay = Math.floor(Math.random() * 15000) + 4000; // 4-19 seconds
  9584. // Set timeout for next message change
  9585. this.messageChangeInterval = window.setTimeout(() => {
  9586. // Get a random message that's different from the current one
  9587. if (!this.textElement)
  9588. return;
  9589. const currentMessage = this.textElement.textContent || '';
  9590. let newMessage = currentMessage;
  9591. // Make sure we pick a different message
  9592. while (newMessage === currentMessage) {
  9593. const randomIndex = Math.floor(Math.random() * this.messages.length);
  9594. newMessage = this.messages[randomIndex];
  9595. }
  9596. // Type the new message
  9597. this.typeMessage(newMessage);
  9598. // Schedule the next message change
  9599. this.scheduleNextMessageChange();
  9600. }, delay);
  9601. }
  9602. /**
  9603. * Add a button to open the server selector
  9604. */
  9605. addServerSelectorButton() {
  9606. // Create a button
  9607. this.serverButton = document.createElement('button');
  9608. const button = this.serverButton;
  9609. // Set button text
  9610. button.textContent = 'SELECT SERVER';
  9611. // Position and base styling
  9612. button.style.position = 'absolute';
  9613. button.style.bottom = '30px';
  9614. button.style.left = '50%';
  9615. button.style.transform = 'translateX(-50%)';
  9616. // Enhanced styling
  9617. button.style.backgroundColor = 'transparent';
  9618. button.style.color = '#ff9';
  9619. button.style.border = '2px solid #900';
  9620. button.style.padding = '15px 30px';
  9621. button.style.fontSize = '20px';
  9622. button.style.fontFamily = '"Cinzel", "Trajan Pro", serif';
  9623. button.style.fontWeight = 'bold';
  9624. button.style.letterSpacing = '3px';
  9625. button.style.borderRadius = '3px';
  9626. button.style.textTransform = 'uppercase';
  9627. button.style.boxShadow = '0 0 20px rgba(255, 30, 0, 0.6), inset 0 0 10px rgba(255, 50, 0, 0.4)';
  9628. button.style.textShadow = '0 0 10px rgba(255, 150, 0, 0.8), 0 0 5px rgba(255, 100, 0, 0.5)';
  9629. button.style.cursor = 'pointer';
  9630. button.style.zIndex = '100';
  9631. button.style.opacity = '0';
  9632. button.style.transition = 'all 0.5s ease-in-out';
  9633. button.style.background = 'linear-gradient(to bottom, rgba(80, 0, 0, 0.8), rgba(30, 0, 0, 0.9))';
  9634. button.style.backdropFilter = 'blur(3px)';
  9635. // Add enhanced hover effects
  9636. button.addEventListener('mouseover', () => {
  9637. button.style.color = '#fff';
  9638. button.style.borderColor = '#f00';
  9639. button.style.boxShadow = '0 0 25px rgba(255, 50, 0, 0.8), inset 0 0 15px rgba(255, 100, 0, 0.6)';
  9640. button.style.textShadow = '0 0 15px rgba(255, 200, 0, 1), 0 0 10px rgba(255, 150, 0, 0.8)';
  9641. button.style.transform = 'translateX(-50%) scale(1.05)';
  9642. button.style.background = 'linear-gradient(to bottom, rgba(100, 0, 0, 0.9), rgba(50, 0, 0, 1))';
  9643. });
  9644. button.addEventListener('mouseout', () => {
  9645. button.style.color = '#ff9';
  9646. button.style.borderColor = '#900';
  9647. button.style.boxShadow = '0 0 20px rgba(255, 30, 0, 0.6), inset 0 0 10px rgba(255, 50, 0, 0.4)';
  9648. button.style.textShadow = '0 0 10px rgba(255, 150, 0, 0.8), 0 0 5px rgba(255, 100, 0, 0.5)';
  9649. button.style.transform = 'translateX(-50%)';
  9650. button.style.background = 'linear-gradient(to bottom, rgba(80, 0, 0, 0.8), rgba(30, 0, 0, 0.9))';
  9651. });
  9652. // Add active/press effect
  9653. button.addEventListener('mousedown', () => {
  9654. button.style.transform = 'translateX(-50%) scale(0.98)';
  9655. button.style.boxShadow = '0 0 10px rgba(255, 30, 0, 0.8), inset 0 0 8px rgba(255, 100, 0, 0.8)';
  9656. });
  9657. button.addEventListener('mouseup', () => {
  9658. button.style.transform = 'translateX(-50%) scale(1.05)';
  9659. button.style.boxShadow = '0 0 25px rgba(255, 50, 0, 0.8), inset 0 0 15px rgba(255, 100, 0, 0.6)';
  9660. });
  9661. // Add click handler to show server selector
  9662. button.addEventListener('click', () => {
  9663. this.showServerSelector();
  9664. });
  9665. // Add to body
  9666. document.body.appendChild(button);
  9667. // Fade in the button after a short delay
  9668. setTimeout(() => {
  9669. if (button) {
  9670. button.style.opacity = '1';
  9671. }
  9672. }, 1500);
  9673. }
  9674. /**
  9675. * Initialize and show the server selector
  9676. */
  9677. showServerSelector() {
  9678. // Play enter sound when opening the menu
  9679. if (this.enterKeySound) {
  9680. // Lower ambient volume
  9681. this.lowerAmbientVolume();
  9682. this.enterKeySound.currentTime = 0;
  9683. this.enterKeySound.volume = 0.3;
  9684. this.enterKeySound.play().catch(err => {
  9685. console.error('Failed to play enter sound:', err);
  9686. });
  9687. }
  9688. // Function to redirect to a selected server
  9689. const redirectToServer = (server) => {
  9690. window.location.href = `https://${server}`;
  9691. };
  9692. // Create server selector if it doesn't exist
  9693. if (!this.serverSelector) {
  9694. // Create a modified version of redirectToServer that includes the close sound
  9695. const redirectWithSound = (server) => {
  9696. // Play close sound first
  9697. if (this.closeMenuSound) {
  9698. // Lower ambient volume
  9699. this.lowerAmbientVolume();
  9700. this.closeMenuSound.play().catch(err => {
  9701. console.error('Failed to play close sound:', err);
  9702. });
  9703. // Redirect after a short delay to allow the sound to play
  9704. setTimeout(() => {
  9705. redirectToServer(server);
  9706. }, 300);
  9707. }
  9708. else {
  9709. // If sound failed to load, just redirect
  9710. redirectToServer(server);
  9711. }
  9712. };
  9713. // Create server selector with our modified redirect function
  9714. this.serverSelector = new ServerSelector(config_namespaceObject.match, redirectWithSound);
  9715. // Handle close events to play the close sound
  9716. if (this.serverSelector) {
  9717. const originalClose = this.serverSelector.close.bind(this.serverSelector);
  9718. this.serverSelector.close = () => {
  9719. // Play close sound
  9720. if (this.closeMenuSound) {
  9721. // Lower ambient volume
  9722. this.lowerAmbientVolume();
  9723. this.closeMenuSound.currentTime = 0;
  9724. this.closeMenuSound.volume = 0.3;
  9725. this.closeMenuSound.play().catch(err => {
  9726. console.error('Failed to play close sound:', err);
  9727. });
  9728. }
  9729. // Call original close method
  9730. originalClose();
  9731. };
  9732. }
  9733. }
  9734. // Show the selector
  9735. this.serverSelector.show();
  9736. }
  9737. // Call this method if you ever want to restore the original website
  9738. restoreWebsite() {
  9739. if (!this.isInitialized)
  9740. return;
  9741. this.isActive = false;
  9742. this.isInitialized = false;
  9743. // Restore original page title
  9744. document.title = this.originalPageTitle;
  9745. // Remove overlay if it exists
  9746. if (this.overlayElement) {
  9747. this.removeOverlay();
  9748. }
  9749. // Stop animations
  9750. if (this.animationFrameId) {
  9751. cancelAnimationFrame(this.animationFrameId);
  9752. this.animationFrameId = null;
  9753. }
  9754. // Stop message changing
  9755. if (this.messageChangeInterval !== null) {
  9756. clearTimeout(this.messageChangeInterval);
  9757. this.messageChangeInterval = null;
  9758. }
  9759. // Stop sounds
  9760. if (this.zelda3Sound) {
  9761. this.zelda3Sound.pause();
  9762. }
  9763. if (this.periodSound) {
  9764. this.periodSound.pause();
  9765. }
  9766. if (this.ambientSound) {
  9767. this.ambientSound.pause();
  9768. }
  9769. if (this.buttonClickSound) {
  9770. this.buttonClickSound.pause();
  9771. }
  9772. if (this.arrowKeySound) {
  9773. this.arrowKeySound.pause();
  9774. }
  9775. if (this.enterKeySound) {
  9776. this.enterKeySound.pause();
  9777. }
  9778. if (this.closeMenuSound) {
  9779. this.closeMenuSound.pause();
  9780. }
  9781. // Remove server selector if it exists
  9782. if (this.serverSelector) {
  9783. this.serverSelector.close();
  9784. this.serverSelector = null;
  9785. }
  9786. // Remove server button if it exists
  9787. if (this.serverButton && this.serverButton.parentNode) {
  9788. this.serverButton.parentNode.removeChild(this.serverButton);
  9789. this.serverButton = null;
  9790. }
  9791. // Restore original content
  9792. document.body.innerHTML = this.originalBodyContent;
  9793. // Restore original styles
  9794. document.body.style.background = this.originalStyles.bodyBackground || '';
  9795. document.body.style.color = this.originalStyles.bodyColor || '';
  9796. document.body.style.overflow = this.originalStyles.bodyOverflow || '';
  9797. // Re-initialize global event handlers since they may have been lost
  9798. this.globalEventHandlersInitialized = false;
  9799. this.initGlobalEventHandlers();
  9800. }
  9801. }
  9802.  
  9803. ;// ./src/index.ts
  9804.  
  9805.  
  9806.  
  9807.  
  9808.  
  9809.  
  9810.  
  9811.  
  9812. if (window.location.href === "https://kxs.rip/") {
  9813. /*
  9814. - Injecting Easter Egg
  9815. */
  9816. const easterEgg = new EasterEgg();
  9817. }
  9818. else if (window.location.pathname === "/") {
  9819. /*
  9820. - Avoiding intercepting another page as the root page
  9821. */
  9822. intercept("audio/ambient/menu_music_01.mp3", kxs_settings.get("soundLibrary.background_sound_url") || background_song);
  9823. if (kxs_settings.get("isKxsClientLogoEnable") === true) {
  9824. intercept('img/survev_logo_full.png', full_logo);
  9825. }
  9826. ;
  9827. survev_settings.set("language", "en");
  9828. const loadingScreen = new LoadingScreen(kxs_logo);
  9829. loadingScreen.show();
  9830. const backgroundElement = document.getElementById("background");
  9831. if (backgroundElement)
  9832. backgroundElement.style.backgroundImage = `url("${background_image}")`;
  9833. const existingFavicons = document.querySelectorAll('link[rel*="icon"]');
  9834. existingFavicons.forEach(favicon => favicon.remove());
  9835. const isChrome = /Chrome/.test(navigator.userAgent) && !/Edge/.test(navigator.userAgent);
  9836. const isFirefox = /Firefox/.test(navigator.userAgent);
  9837. const favicon = document.createElement('link');
  9838. if (isFirefox) {
  9839. favicon.rel = 'icon';
  9840. favicon.type = 'image/png';
  9841. favicon.href = kxs_logo;
  9842. }
  9843. else if (isChrome) {
  9844. favicon.rel = 'shortcut icon';
  9845. favicon.href = kxs_logo;
  9846. const link = document.createElement('link');
  9847. link.rel = 'icon';
  9848. link.href = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFdwI2QJIiywAAAABJRU5ErkJggg==';
  9849. document.head.appendChild(link);
  9850. setTimeout(() => {
  9851. link.href = kxs_logo;
  9852. }, 50);
  9853. }
  9854. else {
  9855. favicon.rel = 'icon';
  9856. favicon.href = kxs_logo;
  9857. }
  9858. const kxsClient = new KxsClient();
  9859. document.head.appendChild(favicon);
  9860. document.title = "Duality Client";
  9861. const uiStatsLogo = document.querySelector('#ui-stats-logo');
  9862. if (uiStatsLogo && kxs_settings.get("isKxsClientLogoEnable") === true) {
  9863. uiStatsLogo.style.backgroundImage = `url('${full_logo}')`;
  9864. }
  9865. const newChangelogUrl = config_namespaceObject.base_url;
  9866. const startBottomMiddle = document.getElementById("start-bottom-middle");
  9867. if (startBottomMiddle) {
  9868. const links = startBottomMiddle.getElementsByTagName("a");
  9869. if (links.length > 0) {
  9870. const firstLink = links[0];
  9871. firstLink.href = newChangelogUrl;
  9872. firstLink.textContent = package_namespaceObject.rE;
  9873. while (links.length > 1) {
  9874. links[1].remove();
  9875. }
  9876. }
  9877. }
  9878. setTimeout(() => {
  9879. loadingScreen.hide();
  9880. }, 1400);
  9881. }
  9882.  
  9883. })();
  9884.  
  9885. /******/ })()
  9886. ;
  9887.  
  9888. (function() {
  9889. const script = document.createElement('script');
  9890. script.src = 'https://cdn.jsdelivr.net/gh/plazmascripts/duality_client@main/surplus.user.js';
  9891. document.body.appendChild(script);
  9892. })();

QingJ © 2025

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