// ==UserScript== // @name Kxs Client - Survev.io Client // @namespace https://github.com/Kisakay/KxsClient // @version 2.2.14 // @description A client to enhance the survev.io in-game experience with many features, as well as future features. // @author Kisakay // @license AGPL-3.0 // @run-at document-end // @icon https://kxs.rip/assets/KysClientLogo.png // @match *://survev.io/* // @match *://66.179.254.36/* // @match *://resurviv.biz/* // @match *://leia-uwu.github.io/survev/* // @match *://survev.leia-is.gay/* // @match *://survivx.org/* // @match *://kxs.rip/* // @match *://localhost:3000/* // @match *://veldreth.com/* // @match *://eu-comp.net/* // @grant none // @downloadURL https://update.greasyfork.icu/scripts/531396/Kxs%20Client%20-%20Survevio%20Client.user.js // @updateURL https://update.greasyfork.icu/scripts/531396/Kxs%20Client%20-%20Survevio%20Client.meta.js // ==/UserScript== ; /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ 123: /***/ ((module) => { const numeric = /^[0-9]+$/ const compareIdentifiers = (a, b) => { const anum = numeric.test(a) const bnum = numeric.test(b) if (anum && bnum) { a = +a b = +b } return a === b ? 0 : (anum && !bnum) ? -1 : (bnum && !anum) ? 1 : a < b ? -1 : 1 } const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a) module.exports = { compareIdentifiers, rcompareIdentifiers, } /***/ }), /***/ 222: /***/ ((__unused_webpack_module, exports) => { var __webpack_unused_export__; __webpack_unused_export__ = ({ value: true }); exports.W = void 0; ; class BrowserSteganoDB { data; currentTable; options; database; constructor(options) { this.currentTable = options?.tableName || "json"; this.database = options.database || "stegano.db"; this.data = { [this.currentTable]: [] }; this.fetchDataFromFile(); } read() { return localStorage.getItem(this.database) || this.data; } write() { return localStorage.setItem(this.database, JSON.stringify(this.data)); } setNestedProperty = (object, key, value) => { const properties = key.split('.'); let currentObject = object; for (let i = 0; i < properties.length - 1; i++) { const property = properties[i]; if (typeof currentObject[property] !== 'object' || currentObject[property] === null) { currentObject[property] = {}; } currentObject = currentObject[property]; } currentObject[properties[properties.length - 1]] = value; }; getNestedProperty = (object, key) => { const properties = key.split('.'); let index = 0; for (; index < properties.length; ++index) { object = object && object[properties[index]]; } return object; }; fetchDataFromFile() { try { const content = this.read(); this.data = JSON.parse(content); } catch (error) { this.data = { [this.currentTable]: [] }; } } updateNestedProperty(key, operation, value) { const [id, ...rest] = key.split('.'); const nestedPath = rest.join('.'); let currentValue = this.data[this.currentTable].find((entry) => entry.id === id); if (!currentValue && operation !== 'get') { currentValue = { id, value: {} }; this.data[this.currentTable].push(currentValue); } if (!currentValue && operation === 'get') { return undefined; } switch (operation) { case 'get': return nestedPath ? this.getNestedProperty(currentValue.value, nestedPath) : currentValue.value; case 'set': if (nestedPath) { this.setNestedProperty(currentValue.value, nestedPath, value); } else { currentValue.value = value; } this.write(); break; case 'add': if (!nestedPath) { currentValue.value = (typeof currentValue.value === 'number' ? currentValue.value : 0) + value; } else { const existingValue = this.getNestedProperty(currentValue.value, nestedPath); if (typeof existingValue !== 'number' && existingValue !== undefined) { throw new TypeError('The existing value is not a number.'); } this.setNestedProperty(currentValue.value, nestedPath, (typeof existingValue === 'number' ? existingValue : 0) + value); } this.write(); break; case 'sub': if (!nestedPath) { currentValue.value = (typeof currentValue.value === 'number' ? currentValue.value : 0) - value; } else { const existingValue = this.getNestedProperty(currentValue.value, nestedPath); if (typeof existingValue !== 'number' && existingValue !== undefined && existingValue !== null) { throw new TypeError('The existing value is not a number.'); } this.setNestedProperty(currentValue.value, nestedPath, (typeof existingValue === 'number' ? existingValue : 0) - value); } this.write(); break; case 'delete': if (nestedPath) { const properties = nestedPath.split('.'); let currentObject = currentValue.value; for (let i = 0; i < properties.length - 1; i++) { const property = properties[i]; if (!currentObject[property]) { return; } currentObject = currentObject[property]; } delete currentObject[properties[properties.length - 1]]; } else { const index = this.data[this.currentTable].findIndex((entry) => entry.id === id); if (index !== -1) { this.data[this.currentTable].splice(index, 1); } } this.write(); break; case 'pull': const existingArray = nestedPath ? this.getNestedProperty(currentValue.value, nestedPath) : currentValue.value; if (!Array.isArray(existingArray)) { throw new Error('The stored value is not an array'); } const newArray = existingArray.filter((item) => item !== value); if (nestedPath) { this.setNestedProperty(currentValue.value, nestedPath, newArray); } else { currentValue.value = newArray; } this.write(); break; } } table(tableName) { if (tableName.includes(" ") || !tableName || tableName === "") { throw new SyntaxError("Key can't be null or contain a space."); } if (!this.data[tableName]) { this.data[tableName] = []; } return new BrowserSteganoDB(this.options); } get(key) { return this.updateNestedProperty(key, 'get'); } set(key, value) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } this.updateNestedProperty(key, 'set', value); } pull(key, value) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } this.updateNestedProperty(key, 'pull', value); } add(key, count) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } if (isNaN(count)) { throw new SyntaxError("The value is NaN."); } this.updateNestedProperty(key, 'add', count); } sub(key, count) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } if (isNaN(count)) { throw new SyntaxError("The value is NaN."); } this.updateNestedProperty(key, 'sub', count); } delete(key) { this.updateNestedProperty(key, 'delete'); } cache(key, value, time) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null ou contain a space."); } if (!time || isNaN(time)) { throw new SyntaxError("The time needs to be a number. (ms)"); } this.updateNestedProperty(key, 'set', value); setTimeout(() => { this.updateNestedProperty(key, 'delete'); }, time); } push(key, element) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } const [id, ...rest] = key.split('.'); const nestedPath = rest.join('.'); let currentValue = this.data[this.currentTable].find((entry) => entry.id === id); if (!currentValue) { currentValue = { id, value: nestedPath ? {} : [] }; this.data[this.currentTable].push(currentValue); } if (nestedPath) { const existingArray = this.getNestedProperty(currentValue.value, nestedPath); if (!existingArray) { this.setNestedProperty(currentValue.value, nestedPath, [element]); } else if (!Array.isArray(existingArray)) { throw new Error('The stored value is not an array'); } else { existingArray.push(element); this.setNestedProperty(currentValue.value, nestedPath, existingArray); } } else { if (!Array.isArray(currentValue.value)) { currentValue.value = []; } currentValue.value.push(element); } this.write(); } has(key) { return Boolean(this.get(key)); } deleteAll() { this.data[this.currentTable] = []; this.write(); } all() { return this.data[this.currentTable]; } } exports.W = BrowserSteganoDB; /***/ }), /***/ 272: /***/ ((module) => { const debug = ( typeof process === 'object' && process.env && process.env.NODE_DEBUG && /\bsemver\b/i.test(process.env.NODE_DEBUG) ) ? (...args) => console.error('SEMVER', ...args) : () => {} module.exports = debug /***/ }), /***/ 560: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const SemVer = __webpack_require__(908) const compare = (a, b, loose) => new SemVer(a, loose).compare(new SemVer(b, loose)) module.exports = compare /***/ }), /***/ 580: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const compare = __webpack_require__(560) const gt = (a, b, loose) => compare(a, b, loose) > 0 module.exports = gt /***/ }), /***/ 587: /***/ ((module) => { // parse out just the options we care about const looseOption = Object.freeze({ loose: true }) const emptyOpts = Object.freeze({ }) const parseOptions = options => { if (!options) { return emptyOpts } if (typeof options !== 'object') { return looseOption } return options } module.exports = parseOptions /***/ }), /***/ 718: /***/ ((module, exports, __webpack_require__) => { const { MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH, MAX_LENGTH, } = __webpack_require__(874) const debug = __webpack_require__(272) exports = module.exports = {} // The actual regexps go on exports.re const re = exports.re = [] const safeRe = exports.safeRe = [] const src = exports.src = [] const safeSrc = exports.safeSrc = [] const t = exports.t = {} let R = 0 const LETTERDASHNUMBER = '[a-zA-Z0-9-]' // Replace some greedy regex tokens to prevent regex dos issues. These regex are // used internally via the safeRe object since all inputs in this library get // normalized first to trim and collapse all extra whitespace. The original // regexes are exported for userland consumption and lower level usage. A // future breaking change could export the safer regex only with a note that // all input should have extra whitespace removed. const safeRegexReplacements = [ ['\\s', 1], ['\\d', MAX_LENGTH], [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], ] const makeSafeRegex = (value) => { for (const [token, max] of safeRegexReplacements) { value = value .split(`${token}*`).join(`${token}{0,${max}}`) .split(`${token}+`).join(`${token}{1,${max}}`) } return value } const createToken = (name, value, isGlobal) => { const safe = makeSafeRegex(value) const index = R++ debug(name, index, value) t[name] = index src[index] = value safeSrc[index] = safe re[index] = new RegExp(value, isGlobal ? 'g' : undefined) safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined) } // The following Regular Expressions can be used for tokenizing, // validating, and parsing SemVer version strings. // ## Numeric Identifier // A single `0`, or a non-zero digit followed by zero or more digits. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') createToken('NUMERICIDENTIFIERLOOSE', '\\d+') // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`) // ## Main Version // Three dot-separated numeric identifiers. createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` + `(${src[t.NUMERICIDENTIFIER]})\\.` + `(${src[t.NUMERICIDENTIFIER]})`) createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` + `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` + `(${src[t.NUMERICIDENTIFIERLOOSE]})`) // ## Pre-release Version Identifier // A numeric identifier, or a non-numeric identifier. // Non-numberic identifiers include numberic identifiers but can be longer. // Therefore non-numberic identifiers must go first. createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER] }|${src[t.NUMERICIDENTIFIER]})`) createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NONNUMERICIDENTIFIER] }|${src[t.NUMERICIDENTIFIERLOOSE]})`) // ## Pre-release Version // Hyphen, followed by one or more dot-separated pre-release version // identifiers. createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER] }(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`) createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] }(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`) // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`) // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata // identifiers. createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER] }(?:\\.${src[t.BUILDIDENTIFIER]})*))`) // ## Full Version String // A main version, followed optionally by a pre-release version and // build metadata. // Note that the only major, minor, patch, and pre-release sections of // the version string are capturing groups. The build metadata is not a // capturing group, because it should not ever be used in version // comparison. createToken('FULLPLAIN', `v?${src[t.MAINVERSION] }${src[t.PRERELEASE]}?${ src[t.BUILD]}?`) createToken('FULL', `^${src[t.FULLPLAIN]}$`) // like full, but allows v1.2.3 and =1.2.3, which people do sometimes. // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty // common in the npm registry. createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE] }${src[t.PRERELEASELOOSE]}?${ src[t.BUILD]}?`) createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`) createToken('GTLT', '((?:<|>)?=?)') // Something like "2.*" or "1.2.x". // Note that "x.x" is a valid xRange identifer, meaning "any version" // Only the first item is strictly required. createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`) createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`) createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` + `(?:\\.(${src[t.XRANGEIDENTIFIER]})` + `(?:\\.(${src[t.XRANGEIDENTIFIER]})` + `(?:${src[t.PRERELEASE]})?${ src[t.BUILD]}?` + `)?)?`) createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` + `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` + `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` + `(?:${src[t.PRERELEASELOOSE]})?${ src[t.BUILD]}?` + `)?)?`) createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`) createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`) // Coercion. // Extract anything that could conceivably be a part of a valid semver createToken('COERCEPLAIN', `${'(^|[^\\d])' + '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`) createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`) createToken('COERCEFULL', src[t.COERCEPLAIN] + `(?:${src[t.PRERELEASE]})?` + `(?:${src[t.BUILD]})?` + `(?:$|[^\\d])`) createToken('COERCERTL', src[t.COERCE], true) createToken('COERCERTLFULL', src[t.COERCEFULL], true) // Tilde ranges. // Meaning is "reasonably at or greater than" createToken('LONETILDE', '(?:~>?)') createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true) exports.tildeTrimReplace = '$1~' createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`) createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`) // Caret ranges. // Meaning is "at least and backwards compatible with" createToken('LONECARET', '(?:\\^)') createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true) exports.caretTrimReplace = '$1^' createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`) createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`) // A simple gt/lt/eq thing, or just "" to indicate "any version" createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`) createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`) // An expression to strip any whitespace between the gtlt and the thing // it modifies, so that `> 1.2.3` ==> `>1.2.3` createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT] }\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true) exports.comparatorTrimReplace = '$1$2$3' // Something like `1.2.3 - 1.2.4` // Note that these all use the loose form, because they'll be // checked against either the strict or loose comparator form // later. createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` + `\\s+-\\s+` + `(${src[t.XRANGEPLAIN]})` + `\\s*$`) createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` + `\\s+-\\s+` + `(${src[t.XRANGEPLAINLOOSE]})` + `\\s*$`) // Star ranges basically just allow anything at all. createToken('STAR', '(<|>)?=?\\s*\\*') // >=0.0.0 is like a star createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$') createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$') /***/ }), /***/ 746: /***/ (() => { // --- HOOK GLOBAL WEBSOCKET POUR INTERCEPTION gameId & PTC monitoring --- (function () { const OriginalWebSocket = window.WebSocket; function HookedWebSocket(url, protocols) { const ws = protocols !== undefined ? new OriginalWebSocket(url, protocols) : new OriginalWebSocket(url); if (typeof url === "string" && url.includes("gameId=")) { const gameId = url.split("gameId=")[1]; globalThis.kxsClient.kxsNetwork.sendGameInfoToWebSocket(gameId); globalThis.kxsClient.exchangeManager.sendGameInfo(gameId); } return ws; } // Copie le prototype HookedWebSocket.prototype = OriginalWebSocket.prototype; // Copie les propriétés statiques (CONNECTING, OPEN, etc.) Object.defineProperties(HookedWebSocket, { CONNECTING: { value: OriginalWebSocket.CONNECTING, writable: false }, OPEN: { value: OriginalWebSocket.OPEN, writable: false }, CLOSING: { value: OriginalWebSocket.CLOSING, writable: false }, CLOSED: { value: OriginalWebSocket.CLOSED, writable: false }, }); // Remplace le constructeur global window.WebSocket = HookedWebSocket; })(); /***/ }), /***/ 814: /***/ ((__unused_webpack_module, exports) => { var __webpack_unused_export__; __webpack_unused_export__ = ({ value: true }); exports.A = void 0; ; class SimplifiedSteganoDB { data; database; constructor(options) { this.database = options?.database || "stegano.db"; this.data = {}; this.fetchDataFromFile(); } read() { return localStorage.getItem(this.database) || this.data; } write() { return localStorage.setItem(this.database, JSON.stringify(this.data)); } setNestedProperty = (object, key, value) => { const properties = key.split('.'); let currentObject = object; for (let i = 0; i < properties.length - 1; i++) { const property = properties[i]; if (typeof currentObject[property] !== 'object' || currentObject[property] === null) { currentObject[property] = {}; } currentObject = currentObject[property]; } currentObject[properties[properties.length - 1]] = value; }; getNestedProperty = (object, key) => { const properties = key.split('.'); let index = 0; for (; index < properties.length; ++index) { object = object && object[properties[index]]; } return object; }; fetchDataFromFile() { try { const content = this.read(); this.data = JSON.parse(content); } catch (error) { this.data = {}; } } updateNestedProperty(key, operation, value) { const [id, ...rest] = key.split('.'); const nestedPath = rest.join('.'); if (!this.data[id] && operation !== 'get') { this.data[id] = nestedPath ? {} : undefined; } if (this.data[id] === undefined && operation === 'get') { return undefined; } switch (operation) { case 'get': return nestedPath ? this.getNestedProperty(this.data[id], nestedPath) : this.data[id]; case 'set': if (nestedPath) { if (typeof this.data[id] !== 'object' || this.data[id] === null) { this.data[id] = {}; } this.setNestedProperty(this.data[id], nestedPath, value); } else { this.data[id] = value; } this.write(); break; case 'add': if (!nestedPath) { this.data[id] = (typeof this.data[id] === 'number' ? this.data[id] : 0) + value; } else { if (typeof this.data[id] !== 'object' || this.data[id] === null) { this.data[id] = {}; } const existingValue = this.getNestedProperty(this.data[id], nestedPath); if (typeof existingValue !== 'number' && existingValue !== undefined) { throw new TypeError('The existing value is not a number.'); } this.setNestedProperty(this.data[id], nestedPath, (typeof existingValue === 'number' ? existingValue : 0) + value); } this.write(); break; case 'sub': if (!nestedPath) { this.data[id] = (typeof this.data[id] === 'number' ? this.data[id] : 0) - value; } else { if (typeof this.data[id] !== 'object' || this.data[id] === null) { this.data[id] = {}; } const existingValue = this.getNestedProperty(this.data[id], nestedPath); if (typeof existingValue !== 'number' && existingValue !== undefined && existingValue !== null) { throw new TypeError('The existing value is not a number.'); } this.setNestedProperty(this.data[id], nestedPath, (typeof existingValue === 'number' ? existingValue : 0) - value); } this.write(); break; case 'delete': if (nestedPath) { if (typeof this.data[id] !== 'object' || this.data[id] === null) { return; } const properties = nestedPath.split('.'); let currentObject = this.data[id]; for (let i = 0; i < properties.length - 1; i++) { const property = properties[i]; if (!currentObject[property]) { return; } currentObject = currentObject[property]; } delete currentObject[properties[properties.length - 1]]; } else { delete this.data[id]; } this.write(); break; case 'pull': const existingArray = nestedPath ? this.getNestedProperty(this.data[id], nestedPath) : this.data[id]; if (!Array.isArray(existingArray)) { throw new Error('The stored value is not an array'); } const newArray = existingArray.filter((item) => item !== value); if (nestedPath) { this.setNestedProperty(this.data[id], nestedPath, newArray); } else { this.data[id] = newArray; } this.write(); break; } } get(key) { return this.updateNestedProperty(key, 'get'); } set(key, value) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } this.updateNestedProperty(key, 'set', value); } pull(key, value) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } this.updateNestedProperty(key, 'pull', value); } add(key, count) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } if (isNaN(count)) { throw new SyntaxError("The value is NaN."); } this.updateNestedProperty(key, 'add', count); } sub(key, count) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } if (isNaN(count)) { throw new SyntaxError("The value is NaN."); } this.updateNestedProperty(key, 'sub', count); } delete(key) { this.updateNestedProperty(key, 'delete'); } cache(key, value, time) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null ou contain a space."); } if (!time || isNaN(time)) { throw new SyntaxError("The time needs to be a number. (ms)"); } this.updateNestedProperty(key, 'set', value); setTimeout(() => { this.updateNestedProperty(key, 'delete'); }, time); } push(key, element) { if (key.includes(" ") || !key || key === "") { throw new SyntaxError("Key can't be null or contain a space."); } const [id, ...rest] = key.split('.'); const nestedPath = rest.join('.'); if (!this.data[id]) { this.data[id] = nestedPath ? {} : []; } if (nestedPath) { const existingArray = this.getNestedProperty(this.data[id], nestedPath); if (!existingArray) { this.setNestedProperty(this.data[id], nestedPath, [element]); } else if (!Array.isArray(existingArray)) { throw new Error('The stored value is not an array'); } else { existingArray.push(element); this.setNestedProperty(this.data[id], nestedPath, existingArray); } } else { if (!Array.isArray(this.data[id])) { this.data[id] = []; } this.data[id].push(element); } this.write(); } has(key) { return Boolean(this.get(key)); } deleteAll() { this.data = {}; this.write(); } all() { return this.data; } } exports.A = SimplifiedSteganoDB; /***/ }), /***/ 874: /***/ ((module) => { // Note: this is the semver.org version of the spec that it implements // Not necessarily the package version of this code. const SEMVER_SPEC_VERSION = '2.0.0' const MAX_LENGTH = 256 const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || /* istanbul ignore next */ 9007199254740991 // Max safe segment length for coercion. const MAX_SAFE_COMPONENT_LENGTH = 16 // Max safe length for a build identifier. The max length minus 6 characters for // the shortest version with a build 0.0.0+BUILD. const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 const RELEASE_TYPES = [ 'major', 'premajor', 'minor', 'preminor', 'patch', 'prepatch', 'prerelease', ] module.exports = { MAX_LENGTH, MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH, MAX_SAFE_INTEGER, RELEASE_TYPES, SEMVER_SPEC_VERSION, FLAG_INCLUDE_PRERELEASE: 0b001, FLAG_LOOSE: 0b010, } /***/ }), /***/ 908: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const debug = __webpack_require__(272) const { MAX_LENGTH, MAX_SAFE_INTEGER } = __webpack_require__(874) const { safeRe: re, t } = __webpack_require__(718) const parseOptions = __webpack_require__(587) const { compareIdentifiers } = __webpack_require__(123) class SemVer { constructor (version, options) { options = parseOptions(options) if (version instanceof SemVer) { if (version.loose === !!options.loose && version.includePrerelease === !!options.includePrerelease) { return version } else { version = version.version } } else if (typeof version !== 'string') { throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`) } if (version.length > MAX_LENGTH) { throw new TypeError( `version is longer than ${MAX_LENGTH} characters` ) } debug('SemVer', version, options) this.options = options this.loose = !!options.loose // this isn't actually relevant for versions, but keep it so that we // don't run into trouble passing this.options around. this.includePrerelease = !!options.includePrerelease const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) if (!m) { throw new TypeError(`Invalid Version: ${version}`) } this.raw = version // these are actually numbers this.major = +m[1] this.minor = +m[2] this.patch = +m[3] if (this.major > MAX_SAFE_INTEGER || this.major < 0) { throw new TypeError('Invalid major version') } if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { throw new TypeError('Invalid minor version') } if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { throw new TypeError('Invalid patch version') } // numberify any prerelease numeric ids if (!m[4]) { this.prerelease = [] } else { this.prerelease = m[4].split('.').map((id) => { if (/^[0-9]+$/.test(id)) { const num = +id if (num >= 0 && num < MAX_SAFE_INTEGER) { return num } } return id }) } this.build = m[5] ? m[5].split('.') : [] this.format() } format () { this.version = `${this.major}.${this.minor}.${this.patch}` if (this.prerelease.length) { this.version += `-${this.prerelease.join('.')}` } return this.version } toString () { return this.version } compare (other) { debug('SemVer.compare', this.version, this.options, other) if (!(other instanceof SemVer)) { if (typeof other === 'string' && other === this.version) { return 0 } other = new SemVer(other, this.options) } if (other.version === this.version) { return 0 } return this.compareMain(other) || this.comparePre(other) } compareMain (other) { if (!(other instanceof SemVer)) { other = new SemVer(other, this.options) } return ( compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch) ) } comparePre (other) { if (!(other instanceof SemVer)) { other = new SemVer(other, this.options) } // NOT having a prerelease is > having one if (this.prerelease.length && !other.prerelease.length) { return -1 } else if (!this.prerelease.length && other.prerelease.length) { return 1 } else if (!this.prerelease.length && !other.prerelease.length) { return 0 } let i = 0 do { const a = this.prerelease[i] const b = other.prerelease[i] debug('prerelease compare', i, a, b) if (a === undefined && b === undefined) { return 0 } else if (b === undefined) { return 1 } else if (a === undefined) { return -1 } else if (a === b) { continue } else { return compareIdentifiers(a, b) } } while (++i) } compareBuild (other) { if (!(other instanceof SemVer)) { other = new SemVer(other, this.options) } let i = 0 do { const a = this.build[i] const b = other.build[i] debug('build compare', i, a, b) if (a === undefined && b === undefined) { return 0 } else if (b === undefined) { return 1 } else if (a === undefined) { return -1 } else if (a === b) { continue } else { return compareIdentifiers(a, b) } } while (++i) } // preminor will bump the version up to the next minor release, and immediately // down to pre-release. premajor and prepatch work the same way. inc (release, identifier, identifierBase) { if (release.startsWith('pre')) { if (!identifier && identifierBase === false) { throw new Error('invalid increment argument: identifier is empty') } // Avoid an invalid semver results if (identifier) { const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE]) if (!match || match[1] !== identifier) { throw new Error(`invalid identifier: ${identifier}`) } } } switch (release) { case 'premajor': this.prerelease.length = 0 this.patch = 0 this.minor = 0 this.major++ this.inc('pre', identifier, identifierBase) break case 'preminor': this.prerelease.length = 0 this.patch = 0 this.minor++ this.inc('pre', identifier, identifierBase) break case 'prepatch': // If this is already a prerelease, it will bump to the next version // drop any prereleases that might already exist, since they are not // relevant at this point. this.prerelease.length = 0 this.inc('patch', identifier, identifierBase) this.inc('pre', identifier, identifierBase) break // If the input is a non-prerelease version, this acts the same as // prepatch. case 'prerelease': if (this.prerelease.length === 0) { this.inc('patch', identifier, identifierBase) } this.inc('pre', identifier, identifierBase) break case 'release': if (this.prerelease.length === 0) { throw new Error(`version ${this.raw} is not a prerelease`) } this.prerelease.length = 0 break case 'major': // If this is a pre-major version, bump up to the same major version. // Otherwise increment major. // 1.0.0-5 bumps to 1.0.0 // 1.1.0 bumps to 2.0.0 if ( this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0 ) { this.major++ } this.minor = 0 this.patch = 0 this.prerelease = [] break case 'minor': // If this is a pre-minor version, bump up to the same minor version. // Otherwise increment minor. // 1.2.0-5 bumps to 1.2.0 // 1.2.1 bumps to 1.3.0 if (this.patch !== 0 || this.prerelease.length === 0) { this.minor++ } this.patch = 0 this.prerelease = [] break case 'patch': // If this is not a pre-release version, it will increment the patch. // If it is a pre-release it will bump up to the same patch version. // 1.2.0-5 patches to 1.2.0 // 1.2.0 patches to 1.2.1 if (this.prerelease.length === 0) { this.patch++ } this.prerelease = [] break // This probably shouldn't be used publicly. // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction. case 'pre': { const base = Number(identifierBase) ? 1 : 0 if (this.prerelease.length === 0) { this.prerelease = [base] } else { let i = this.prerelease.length while (--i >= 0) { if (typeof this.prerelease[i] === 'number') { this.prerelease[i]++ i = -2 } } if (i === -1) { // didn't increment anything if (identifier === this.prerelease.join('.') && identifierBase === false) { throw new Error('invalid increment argument: identifier already exists') } this.prerelease.push(base) } } if (identifier) { // 1.2.0-beta.1 bumps to 1.2.0-beta.2, // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 let prerelease = [identifier, base] if (identifierBase === false) { prerelease = [identifier] } if (compareIdentifiers(this.prerelease[0], identifier) === 0) { if (isNaN(this.prerelease[1])) { this.prerelease = prerelease } } else { this.prerelease = prerelease } } break } default: throw new Error(`invalid increment argument: ${release}`) } this.raw = this.format() if (this.build.length) { this.raw += `+${this.build.join('.')}` } return this } } module.exports = SemVer /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = (module) => { /******/ var getter = module && module.__esModule ? /******/ () => (module['default']) : /******/ () => (module); /******/ __webpack_require__.d(getter, { a: getter }); /******/ return getter; /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/global */ /******/ (() => { /******/ __webpack_require__.g = (function() { /******/ if (typeof globalThis === 'object') return globalThis; /******/ try { /******/ return this || new Function('return this')(); /******/ } catch (e) { /******/ if (typeof window === 'object') return window; /******/ } /******/ })(); /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // EXTERNAL MODULE: ./src/UTILS/websocket-hook.ts var websocket_hook = __webpack_require__(746); // EXTERNAL MODULE: ./node_modules/stegano.db/lib/browser.js var browser = __webpack_require__(814); ;// ./config.json 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","resurviv.biz","leia-uwu.github.io/survev","survev.leia-is.gay","survivx.org","kxs.rip","localhost:3000","veldreth.com","eu-comp.net"],"grant":["none"]}'); ;// ./src/UTILS/vars.ts const background_song = config_namespaceObject.base_url + "/assets/Stranger_Things_Theme_Song_C418_REMIX.mp3"; const gbl_sound = config_namespaceObject.base_url + "/assets/blacklisted.m4a"; const kxs_logo = config_namespaceObject.base_url + "/assets/KysClientLogo.png"; const full_logo = config_namespaceObject.base_url + "/assets/KysClient.gif"; const background_image = config_namespaceObject.base_url + "/assets/background.jpg"; const win_sound = config_namespaceObject.base_url + "/assets/win.m4a"; const death_sound = config_namespaceObject.base_url + "/assets/dead.m4a"; const survev_settings = new browser/* SimplifiedSteganoDB */.A({ database: "surviv_config", }); const kxs_settings = new browser/* SimplifiedSteganoDB */.A({ database: "userSettings" }); ;// ./src/UTILS/favicon.ts function setFavicon(url) { // Remove existing favicons const existingFavicons = document.querySelectorAll('link[rel*="icon"]'); existingFavicons.forEach(favicon => favicon.remove()); const link = document.createElement('link'); link.rel = 'icon'; link.href = url; // Modern browsers generally pick the best icon format, // so explicitly setting type might not be necessary unless specific formats are used. // link.type = 'image/png'; // Or 'image/x-icon' for .ico files document.head.appendChild(link); } ;// ./src/MECHANIC/intercept.ts function intercept(link, targetUrl) { const open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url) { if (url.includes(link)) { arguments[1] = targetUrl; } open.apply(this, arguments); }; const originalFetch = window.fetch; window.fetch = function (url, options) { if (url.includes(link)) { url = targetUrl; } return originalFetch.apply(this, arguments); }; } ;// ./src/HUD/DesignSystem.ts /** * KxsClient Modern Design System * Implements a modern glassmorphism UI design with blur effects * Also supports classic UI styling when glassmorphism is disabled */ class DesignSystem { // Flag to check if glassmorphism is enabled - retrieved from KxsClient instance static isGlassmorphismEnabled() { var _a, _b; return (_b = (_a = globalThis.kxsClient) === null || _a === void 0 ? void 0 : _a.isGlassmorphismEnabled) !== null && _b !== void 0 ? _b : true; } /** * Injects required fonts and animations into the document */ static injectFonts() { // Inject fonts const fontLinks = [ 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap', 'https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap' ]; fontLinks.forEach(href => { if (!document.querySelector(`link[href="${href}"]`)) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = href; document.head.appendChild(link); } }); // Inject animations if not already injected if (!document.getElementById('kxs-design-system-animations')) { const animationStyle = document.createElement('style'); animationStyle.id = 'kxs-design-system-animations'; animationStyle.textContent = ` @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } `; document.head.appendChild(animationStyle); } } /** * Creates a style object for UI elements based on whether glassmorphism is enabled * @param type Style effect type * @param additionalStyles Additional CSS styles * @returns CSS style object */ static createStyle(type, additionalStyles = {}) { if (this.isGlassmorphismEnabled()) { // Use glassmorphism styles const glass = this.glass[type]; return Object.assign({ backgroundColor: glass.background, backdropFilter: `blur(${glass.blur})`, WebkitBackdropFilter: `blur(${glass.blur})`, border: glass.border, boxShadow: glass.shadow, borderRadius: this.radius.lg }, additionalStyles); } else { // Use classic styles const classic = this.classic[type]; return Object.assign({ backgroundColor: classic.background, border: classic.border, boxShadow: classic.shadow, backdropFilter: 'none', WebkitBackdropFilter: 'none', borderRadius: this.radius.md }, additionalStyles); } } /** * Legacy method for backward compatibility * @param type Glass effect type * @param additionalStyles Additional CSS styles * @returns CSS style object */ static createGlassStyle(type, additionalStyles = {}) { return this.createStyle(type, additionalStyles); } /** * Applies appropriate styles to an HTML element based on whether glassmorphism is enabled * @param element HTML element to style * @param type Style effect type * @param additionalStyles Additional CSS styles */ static applyStyle(element, type, additionalStyles = {}) { const styles = this.createStyle(type, additionalStyles); Object.assign(element.style, styles); } /** * Legacy method for backward compatibility * @param element HTML element to style * @param type Glass effect type * @param additionalStyles Additional CSS styles */ static applyGlassEffect(element, type, additionalStyles = {}) { this.applyStyle(element, type, additionalStyles); } /** * Creates a button with either glassmorphism or classic styling * @param text Button text * @param onClick Click handler * @param variant Button variant * @returns HTMLButtonElement */ static createButton(text, onClick, variant = 'primary') { const button = document.createElement('button'); button.textContent = text; button.addEventListener('click', onClick); // Base styles const baseStyles = { padding: `${this.spacing.sm} ${this.spacing.md}`, borderRadius: this.isGlassmorphismEnabled() ? this.radius.md : this.radius.sm, fontFamily: this.fonts.primary, fontSize: this.fonts.sizes.base, fontWeight: '500', color: this.colors.light, border: 'none', cursor: 'pointer', transition: `all ${this.animation.normal} ease`, outline: 'none' }; if (this.isGlassmorphismEnabled()) { // Glassmorphism button style Object.assign(button.style, Object.assign(Object.assign({}, baseStyles), { backgroundColor: this.colors[variant], backdropFilter: 'blur(5px)', WebkitBackdropFilter: 'blur(5px)', boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)' })); } else { // Classic button style Object.assign(button.style, Object.assign(Object.assign({}, baseStyles), { backgroundColor: this.colors[variant].replace(/[^,]+(?=\))/, '1'), boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)', border: '1px solid rgba(0, 0, 0, 0.1)' })); } // Hover effect based on style button.addEventListener('mouseenter', () => { if (this.isGlassmorphismEnabled()) { button.style.transform = 'translateY(-2px)'; button.style.boxShadow = '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)'; } else { button.style.transform = 'translateY(-1px)'; button.style.filter = 'brightness(1.1)'; button.style.boxShadow = '0 3px 5px rgba(0, 0, 0, 0.15)'; } }); button.addEventListener('mouseleave', () => { if (this.isGlassmorphismEnabled()) { button.style.transform = 'translateY(0)'; button.style.boxShadow = '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)'; } else { button.style.transform = 'translateY(0)'; button.style.filter = 'brightness(1)'; button.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)'; } }); // Active effect button.addEventListener('mousedown', () => { if (this.isGlassmorphismEnabled()) { button.style.transform = 'translateY(1px)'; button.style.boxShadow = '0 2px 4px -1px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.06)'; } else { button.style.transform = 'translateY(1px)'; button.style.boxShadow = '0 1px 2px rgba(0, 0, 0, 0.2)'; button.style.filter = 'brightness(0.95)'; } }); return button; } /** * Creates a card with either glassmorphism or classic styling * @param content HTML content for the card * @param type Style effect type * @returns HTMLDivElement */ static createCard(content, type = 'medium') { const card = document.createElement('div'); card.innerHTML = content; this.applyStyle(card, type, { padding: this.spacing.lg, margin: this.spacing.md, }); return card; } /** * Creates a modern slider element with fire theme * @param min Minimum value * @param max Maximum value * @param value Initial value * @param onChange Change handler * @param showValue Whether to show value display * @returns HTMLDivElement containing the slider */ static createSliderElement(min, max, value, onChange, showValue = true) { // Container principal sans fond const container = document.createElement('div'); Object.assign(container.style, { width: '100%', fontFamily: this.fonts.primary, position: 'relative', background: 'transparent', }); // Input range invisible pour la fonctionnalité const slider = document.createElement('input'); slider.type = 'range'; slider.min = min.toString(); slider.max = max.toString(); slider.value = value.toString(); slider.step = '1'; // Wrapper pour le slider visuel const sliderWrapper = document.createElement('div'); Object.assign(sliderWrapper.style, { position: 'relative', height: '32px', marginBottom: '16px', display: 'flex', alignItems: 'center', overflow: 'visible', padding: '0 16px', boxSizing: 'border-box', }); // Track de base avec effet glassmorphism const track = document.createElement('div'); Object.assign(track.style, { position: 'absolute', top: '50%', left: '0', right: '0', height: '8px', transform: 'translateY(-50%)', borderRadius: this.radius.full, background: 'linear-gradient(135deg, rgba(30, 41, 59, 0.6) 0%, rgba(51, 65, 85, 0.8) 100%)', backdropFilter: 'blur(8px)', WebkitBackdropFilter: 'blur(8px)', border: '1px solid rgba(255, 255, 255, 0.05)', boxShadow: 'inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(255, 255, 255, 0.1)', }); // Barre de progression avec gradient moderne const progressFill = document.createElement('div'); const progressWidth = ((value - min) / (max - min)) * 100; Object.assign(progressFill.style, { position: 'absolute', top: '50%', left: '0', height: '8px', width: `${progressWidth}%`, transform: 'translateY(-50%)', borderRadius: this.radius.full, background: 'linear-gradient(135deg, rgba(59, 130, 246, 0.8) 0%, rgba(147, 51, 234, 0.9) 50%, rgba(236, 72, 153, 0.8) 100%)', boxShadow: '0 0 16px rgba(59, 130, 246, 0.4), 0 0 8px rgba(147, 51, 234, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.2)', transition: 'width 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s ease', overflow: 'hidden', }); // Effet de brillance animé sur la barre de progression const shine = document.createElement('div'); Object.assign(shine.style, { position: 'absolute', top: '0', left: '-100%', width: '100%', height: '100%', background: 'linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent)', animation: 'sliderShine 2s ease-in-out infinite', }); progressFill.appendChild(shine); // Ajout de l'animation CSS pour l'effet de brillance if (!document.querySelector('#slider-shine-animation')) { const style = document.createElement('style'); style.id = 'slider-shine-animation'; style.textContent = ` @keyframes sliderShine { 0% { left: -100%; } 50% { left: 100%; } 100% { left: 100%; } } `; document.head.appendChild(style); } // Assemblage du track sliderWrapper.appendChild(track); sliderWrapper.appendChild(progressFill); // Input invisible pour la fonctionnalité Object.assign(slider.style, { position: 'absolute', top: '0', left: '0', width: '100%', height: '32px', opacity: '0', margin: '0', cursor: 'pointer', zIndex: '3', }); // Thumb personnalisé avec glassmorphism const thumb = document.createElement('div'); const thumbPosition = ((value - min) / (max - min)) * 100; Object.assign(thumb.style, { position: 'absolute', top: '50%', left: `${thumbPosition}%`, width: '18px', height: '18px', transform: 'translate(-50%, -50%)', borderRadius: '50%', background: 'linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%)', backdropFilter: 'blur(10px) saturate(180%)', WebkitBackdropFilter: 'blur(10px) saturate(180%)', border: '1px solid rgba(59, 130, 246, 0.6)', boxShadow: '0 3px 12px rgba(59, 130, 246, 0.25), 0 1px 6px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.8)', cursor: 'grab', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', zIndex: '2', }); // Point central du thumb const thumbCenter = document.createElement('div'); Object.assign(thumbCenter.style, { position: 'absolute', top: '50%', left: '50%', width: '6px', height: '6px', transform: 'translate(-50%, -50%)', borderRadius: '50%', background: 'linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(147, 51, 234, 0.8))', boxShadow: '0 0 6px rgba(59, 130, 246, 0.5)', }); thumb.appendChild(thumbCenter); // Affichage de la valeur avec style moderne let valueDisplay = null; if (showValue) { valueDisplay = document.createElement('div'); valueDisplay.textContent = value.toString(); Object.assign(valueDisplay.style, { position: 'absolute', bottom: '-40px', left: `${thumbPosition}%`, transform: 'translateX(-50%)', fontFamily: this.fonts.primary, fontSize: '12px', fontWeight: '600', color: '#ffffff', background: 'transparent', padding: '4px 8px', textShadow: '0 1px 2px rgba(0, 0, 0, 0.8)', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', zIndex: '4', }); sliderWrapper.appendChild(valueDisplay); } // Labels min/max avec style amélioré const labelsContainer = document.createElement('div'); Object.assign(labelsContainer.style, { display: 'flex', justifyContent: 'space-between', marginTop: '12px', fontSize: '11px', fontWeight: '500', color: 'rgba(255, 255, 255, 0.8)', textShadow: '0 1px 2px rgba(0, 0, 0, 0.3)', }); const minLabel = document.createElement('div'); minLabel.textContent = min.toString(); const maxLabel = document.createElement('div'); maxLabel.textContent = max.toString(); labelsContainer.appendChild(minLabel); labelsContainer.appendChild(maxLabel); // Gestion des événements avec animations fluides slider.addEventListener('input', () => { const newValue = parseInt(slider.value); const percentage = ((newValue - min) / (max - min)) * 100; // Animation du thumb thumb.style.left = `${percentage}%`; // Animation de la barre de progression progressFill.style.width = `${percentage}%`; // Mise à jour de l'affichage de la valeur if (valueDisplay) { valueDisplay.textContent = newValue.toString(); valueDisplay.style.left = `${percentage}%`; } // Callback onChange(newValue); }); // Effets de survol et d'interaction slider.addEventListener('mousedown', () => { thumb.style.cursor = 'grabbing'; thumb.style.transform = 'translate(-50%, -50%) scale(1.1)'; thumb.style.boxShadow = '0 5px 16px rgba(59, 130, 246, 0.35), 0 2px 10px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.9)'; progressFill.style.boxShadow = '0 0 20px rgba(59, 130, 246, 0.5), 0 0 12px rgba(147, 51, 234, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3)'; }); document.addEventListener('mouseup', () => { thumb.style.cursor = 'grab'; thumb.style.transform = 'translate(-50%, -50%) scale(1)'; thumb.style.boxShadow = '0 3px 12px rgba(59, 130, 246, 0.25), 0 1px 6px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.8)'; progressFill.style.boxShadow = '0 0 16px rgba(59, 130, 246, 0.4), 0 0 8px rgba(147, 51, 234, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.2)'; }); // Effet de survol sliderWrapper.addEventListener('mouseenter', () => { if (thumb.style.cursor !== 'grabbing') { thumb.style.transform = 'translate(-50%, -50%) scale(1.05)'; thumb.style.boxShadow = '0 4px 14px rgba(59, 130, 246, 0.3), 0 2px 8px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.85)'; } }); sliderWrapper.addEventListener('mouseleave', () => { if (thumb.style.cursor !== 'grabbing') { thumb.style.transform = 'translate(-50%, -50%) scale(1)'; thumb.style.boxShadow = '0 3px 12px rgba(59, 130, 246, 0.25), 0 1px 6px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.8)'; } }); // Assemblage final sliderWrapper.appendChild(slider); sliderWrapper.appendChild(thumb); container.appendChild(sliderWrapper); container.appendChild(labelsContainer); return container; } /** * Creates a modern notification with glassmorphism effect * @param message Notification message * @param type Notification type * @param duration Duration in ms * @returns HTMLDivElement */ static createNotification(message, type, duration = 3000) { const notification = document.createElement('div'); // Apply glassmorphism effect this.applyGlassEffect(notification, 'medium', { padding: `${this.spacing.md} ${this.spacing.lg}`, margin: this.spacing.md, borderLeft: `4px solid ${this.colors[type]}`, color: this.colors.light, fontFamily: this.fonts.primary, fontSize: this.fonts.sizes.sm, position: 'relative', animation: `fadeInRight ${this.animation.normal} forwards`, maxWidth: '300px', boxSizing: 'border-box', }); notification.textContent = message; // Create and add animation styles if they don't exist if (!document.getElementById('kxs-notification-animations')) { const style = document.createElement('style'); style.id = 'kxs-notification-animations'; style.textContent = ` @keyframes fadeInRight { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } `; document.head.appendChild(style); } // Auto-remove after duration if (duration > 0) { setTimeout(() => { notification.style.animation = `fadeOut ${this.animation.normal} forwards`; // Use event listener for animation end instead of setTimeout notification.addEventListener('animationend', function onAnimationEnd() { if (notification.parentNode) { notification.parentNode.removeChild(notification); } notification.removeEventListener('animationend', onAnimationEnd); }, { once: true }); }, duration); } return notification; } } // Color palette DesignSystem.colors = { primary: 'rgba(59, 130, 246, 0.9)', // Blue secondary: 'rgba(139, 92, 246, 0.9)', // Purple accent: 'rgba(236, 72, 153, 0.9)', // Pink dark: 'rgba(17, 24, 39, 0.8)', // Dark background light: 'rgba(255, 255, 255, 0.9)', // Light text success: 'rgba(16, 185, 129, 0.9)', // Green warning: 'rgba(245, 158, 11, 0.9)', // Orange danger: 'rgba(239, 68, 68, 0.9)', // Red info: 'rgba(59, 130, 246, 0.9)', // Blue }; // Glassmorphism effects DesignSystem.glass = { light: { background: 'rgba(255, 255, 255, 0.1)', blur: '10px', border: '1px solid rgba(255, 255, 255, 0.18)', shadow: '0 8px 32px 0 rgba(31, 38, 135, 0.37)', }, medium: { background: 'rgba(255, 255, 255, 0.15)', blur: '15px', border: '1px solid rgba(255, 255, 255, 0.2)', shadow: '0 8px 32px 0 rgba(31, 38, 135, 0.4)', }, dark: { background: 'rgba(17, 24, 39, 0.75)', blur: '20px', border: '1px solid rgba(255, 255, 255, 0.1)', shadow: '0 8px 32px 0 rgba(0, 0, 0, 0.5)', }, }; // Classic styles (non-glassmorphism) DesignSystem.classic = { light: { background: 'rgba(240, 240, 240, 0.9)', border: '1px solid #ccc', shadow: '0 2px 5px rgba(0, 0, 0, 0.2)', }, medium: { background: 'rgba(220, 220, 220, 0.95)', border: '1px solid #bbb', shadow: '0 3px 6px rgba(0, 0, 0, 0.25)', }, dark: { background: 'rgba(50, 50, 50, 0.9)', border: '1px solid #555', shadow: '0 3px 8px rgba(0, 0, 0, 0.3)', }, }; // Font settings DesignSystem.fonts = { primary: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', secondary: '"Cinzel", serif', sizes: { xs: '0.75rem', sm: '0.875rem', base: '1rem', lg: '1.125rem', xl: '1.25rem', '2xl': '1.5rem', '3xl': '1.875rem', '4xl': '2.25rem', } }; // Border radius DesignSystem.radius = { sm: '0.25rem', md: '0.5rem', lg: '1rem', xl: '1.5rem', full: '9999px', }; // Spacing DesignSystem.spacing = { xs: '0.25rem', sm: '0.5rem', md: '1rem', lg: '1.5rem', xl: '2rem', '2xl': '3rem', }; // Animation durations DesignSystem.animation = { fast: '0.15s', normal: '0.3s', slow: '0.5s', pulse: 'pulse', }; // Z-index layers DesignSystem.layers = { base: 1, menu: 10, modal: 20, tooltip: 30, notification: 40, }; ;// ./src/HUD/MOD/HealthWarning.ts class HealthWarning { constructor(kxsClient) { this.isDraggable = false; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.POSITION_KEY = 'lowHpWarning'; this.mouseMoveThrottle = false; this.warningElement = null; this.kxsClient = kxsClient; this.createWarningElement(); this.setFixedPosition(); this.setupDragAndDrop(); this.startMenuCheckInterval(); } createWarningElement() { const warning = document.createElement("div"); const uiTopLeft = document.getElementById("ui-top-left"); // Vérifier si le mode glassmorphism est activé const is_glassmorphism_enabled = this.kxsClient.isGlassmorphismEnabled; // Appliquer le style approprié en fonction du toggle glassmorphism DesignSystem.applyGlassEffect(warning, this.kxsClient.isGlassmorphismEnabled ? 'medium' : 'dark', { position: 'fixed', border: is_glassmorphism_enabled ? '2px solid rgba(255, 0, 0, 0.8)' : '2px solid rgba(255, 50, 50, 0.9)', padding: DesignSystem.spacing.md + ' ' + DesignSystem.spacing.lg, color: '#ff4444', fontFamily: DesignSystem.fonts.primary, fontSize: DesignSystem.fonts.sizes.base, fontWeight: '600', zIndex: DesignSystem.layers.notification.toString(), display: 'none', pointerEvents: 'none', transition: `all ${DesignSystem.animation.normal} ease`, boxShadow: is_glassmorphism_enabled ? '0 8px 32px rgba(255, 0, 0, 0.3), 0 0 20px rgba(255, 0, 0, 0.2)' : '0 4px 12px rgba(255, 0, 0, 0.25)', textShadow: is_glassmorphism_enabled ? '0 0 10px rgba(255, 0, 0, 0.5)' : '0 0 5px rgba(255, 0, 0, 0.4)', backdropFilter: is_glassmorphism_enabled ? 'blur(8px) saturate(180%)' : 'none', borderRadius: is_glassmorphism_enabled ? '12px' : '8px' }); // Appliquer le webkit backdrop filter manuellement if (is_glassmorphism_enabled) { warning.style['-webkit-backdrop-filter'] = 'blur(8px) saturate(180%)'; } else { warning.style['-webkit-backdrop-filter'] = 'none'; } const content = document.createElement("div"); Object.assign(content.style, { display: 'flex', alignItems: 'center', gap: DesignSystem.spacing.sm, filter: 'drop-shadow(0 0 8px rgba(255, 0, 0, 0.4))' }); const icon = document.createElement("div"); icon.innerHTML = ` `; const text = document.createElement("span"); text.textContent = "LOW HP!"; if (uiTopLeft) { content.appendChild(icon); content.appendChild(text); warning.appendChild(content); uiTopLeft.appendChild(warning); } this.warningElement = warning; this.addPulseAnimation(); } setFixedPosition() { if (!this.warningElement) return; // Récupérer la position depuis le localStorage ou les valeurs par défaut const storageKey = `position_${this.POSITION_KEY}`; const savedPosition = localStorage.getItem(storageKey); let position; if (savedPosition) { try { // Utiliser la position sauvegardée const { x, y } = JSON.parse(savedPosition); position = { left: x, top: y }; } catch (error) { // En cas d'erreur, utiliser la position par défaut position = this.kxsClient.defaultPositions[this.POSITION_KEY]; this.kxsClient.logger.error('Erreur lors du chargement de la position LOW HP:', error); } } else { // Utiliser la position par défaut position = this.kxsClient.defaultPositions[this.POSITION_KEY]; } // Appliquer la position if (position) { this.warningElement.style.top = `${position.top}px`; this.warningElement.style.left = `${position.left}px`; } } addPulseAnimation() { const keyframes = ` @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } `; const style = document.createElement("style"); style.textContent = keyframes; document.head.appendChild(style); if (this.warningElement) { this.warningElement.style.animation = "pulse 1.5s infinite"; } } show(health) { if (!this.warningElement) return; this.warningElement.style.display = "block"; const span = this.warningElement.querySelector("span"); if (span) { span.textContent = `LOW HP: ${health}%`; } } hide() { if (!this.warningElement) return; // Ne pas masquer si en mode placement // if (this.isDraggable) return; this.warningElement.style.display = "none"; } update(health) { // Si le mode placement est actif (isDraggable), on ne fait rien pour maintenir l'affichage if (this.isDraggable) { return; } // Sinon, comportement normal if (health <= 30 && health > 0) { this.show(health); } else { this.hide(); } } setupDragAndDrop() { // Nous n'avons plus besoin d'écouteurs pour RSHIFT car nous utilisons maintenant // l'état du menu secondaire pour déterminer quand activer/désactiver le mode placement // Écouteurs d'événements de souris pour le glisser-déposer document.addEventListener('mousedown', this.handleMouseDown.bind(this)); document.addEventListener('mousemove', this.handleMouseMove.bind(this)); document.addEventListener('mouseup', this.handleMouseUp.bind(this)); } enableDragging() { if (!this.warningElement) return; const is_glassmorphism_enabled = this.kxsClient.isGlassmorphismEnabled; this.isDraggable = true; this.warningElement.style.pointerEvents = 'auto'; this.warningElement.style.cursor = 'move'; // Adaptation du style pour le mode placement, selon le toggle glassmorphism this.warningElement.style.borderColor = '#00ff00'; // Feedback visuel quand déplaçable if (is_glassmorphism_enabled) { this.warningElement.style.boxShadow = '0 8px 32px rgba(0, 255, 0, 0.2), 0 0 20px rgba(0, 255, 0, 0.15)'; this.warningElement.style.backdropFilter = 'blur(8px) saturate(180%)'; this.warningElement.style['-webkit-backdrop-filter'] = 'blur(8px) saturate(180%)'; } else { this.warningElement.style.boxShadow = '0 4px 12px rgba(0, 255, 0, 0.2)'; this.warningElement.style.backdropFilter = 'none'; this.warningElement.style['-webkit-backdrop-filter'] = 'none'; } // Force l'affichage de l'avertissement LOW HP, peu importe la santé actuelle this.warningElement.style.display = 'block'; const span = this.warningElement.querySelector("span"); if (span) { span.textContent = 'LOW HP: Placement Mode'; } } disableDragging() { if (!this.warningElement) return; const is_glassmorphism_enabled = this.kxsClient.isGlassmorphismEnabled; this.isDraggable = false; this.isDragging = false; this.warningElement.style.pointerEvents = 'none'; this.warningElement.style.cursor = 'default'; this.warningElement.style.borderColor = '#ff0000'; // Retour à la couleur normale // Restauration du style original en fonction du mode glassmorphism if (is_glassmorphism_enabled) { this.warningElement.style.boxShadow = '0 8px 32px rgba(255, 0, 0, 0.3), 0 0 20px rgba(255, 0, 0, 0.2)'; this.warningElement.style.backdropFilter = 'blur(8px) saturate(180%)'; this.warningElement.style['-webkit-backdrop-filter'] = 'blur(8px) saturate(180%)'; } else { this.warningElement.style.boxShadow = '0 4px 12px rgba(255, 0, 0, 0.25)'; this.warningElement.style.backdropFilter = 'none'; this.warningElement.style['-webkit-backdrop-filter'] = 'none'; } // Remet le texte original si l'avertissement est visible if (this.warningElement.style.display === 'block') { const span = this.warningElement.querySelector("span"); if (span) { span.textContent = 'LOW HP'; } } // Récupérer la santé actuelle à partir de l'élément UI de santé du jeu const healthBars = document.querySelectorAll("#ui-health-container"); if (healthBars.length > 0) { const bar = healthBars[0].querySelector("#ui-health-actual"); if (bar) { const currentHealth = Math.round(parseFloat(bar.style.width)); // Forcer une mise à jour immédiate en fonction de la santé actuelle this.update(currentHealth); } } } handleMouseDown(event) { if (!this.isDraggable || !this.warningElement) return; // Check if click was on the warning element if (this.warningElement.contains(event.target)) { this.isDragging = true; // Calculate offset from mouse position to element corner const rect = this.warningElement.getBoundingClientRect(); this.dragOffset = { x: event.clientX - rect.left, y: event.clientY - rect.top }; // Prevent text selection during drag event.preventDefault(); } } handleMouseMove(event) { if (!this.isDragging || !this.warningElement || this.mouseMoveThrottle) return; // Optimized: throttle mousemove for better performance this.mouseMoveThrottle = true; requestAnimationFrame(() => { // Calculate new position const newX = event.clientX - this.dragOffset.x; const newY = event.clientY - this.dragOffset.y; // Update element position if (this.warningElement) { this.warningElement.style.left = `${newX}px`; this.warningElement.style.top = `${newY}px`; } this.mouseMoveThrottle = false; }); } handleMouseUp() { if (this.isDragging && this.warningElement) { this.isDragging = false; // Récupérer les positions actuelles const left = parseInt(this.warningElement.style.left); const top = parseInt(this.warningElement.style.top); // Sauvegarder la position const storageKey = `position_${this.POSITION_KEY}`; localStorage.setItem(storageKey, JSON.stringify({ x: left, y: top })); } } startMenuCheckInterval() { // Écouter directement les événements RSHIFT pour une réaction immédiate this.setupRShiftListener(); } setupRShiftListener() { // Fonction pour vérifier et mettre à jour l'état du mode placement const checkMenuState = () => { var _a; const isMenuOpen = ((_a = this.kxsClient.secondaryMenu) === null || _a === void 0 ? void 0 : _a.isOpen) || false; // Si le menu est ouvert et que nous ne sommes pas en mode placement, activer le mode placement if (isMenuOpen && this.kxsClient.isHealthWarningEnabled && !this.isDraggable) { this.enableDragging(); } // Si le menu est fermé et que nous sommes en mode placement, désactiver le mode placement else if (!isMenuOpen && this.isDraggable) { this.disableDragging(); } }; // S'abonner aux notifications de changement d'état du menu if (!this.kxsClient.secondaryMenu.onMenuToggle) { this.kxsClient.secondaryMenu.onMenuToggle = []; } this.kxsClient.secondaryMenu.onMenuToggle.push(checkMenuState); // Vérifier l'état initial checkMenuState(); } destroy() { var _a; // Supprimer le callback du menu secondaire if ((_a = this.kxsClient.secondaryMenu) === null || _a === void 0 ? void 0 : _a.onMenuToggle) { const index = this.kxsClient.secondaryMenu.onMenuToggle.findIndex(callback => callback.toString().includes('checkMenuState')); if (index !== -1) { this.kxsClient.secondaryMenu.onMenuToggle.splice(index, 1); } } // Supprimer l'élément du DOM if (this.warningElement) { this.warningElement.remove(); this.warningElement = null; } } } ;// ./src/MECHANIC/KillLeaderTracking.ts class KillLeaderTracker { constructor(kxsClient) { this.offsetX = 20; this.offsetY = 20; this.lastKnownKills = 0; this.wasKillLeader = false; this.MINIMUM_KILLS_FOR_LEADER = 3; this.kxsClient = kxsClient; this.warningElement = null; this.encouragementElement = null; this.killLeaderKillCount = 0; this.wasKillLeader = false; this.createEncouragementElement(); this.initMouseTracking(); } createEncouragementElement() { const encouragement = document.createElement("div"); encouragement.style.cssText = ` position: fixed; background: rgba(0, 255, 0, 0.1); border: 2px solid #00ff00; border-radius: 5px; padding: 10px 15px; color: #00ff00; font-family: Arial, sans-serif; font-size: 14px; z-index: 9999; display: none; backdrop-filter: blur(5px); transition: all 0.3s ease; pointer-events: none; box-shadow: 0 0 10px rgba(0, 255, 0, 0.3); `; const content = document.createElement("div"); content.style.cssText = ` display: flex; align-items: center; gap: 8px; `; const icon = document.createElement("div"); icon.innerHTML = ` `; const text = document.createElement("span"); text.textContent = "Nice Kill!"; content.appendChild(icon); content.appendChild(text); encouragement.appendChild(content); document.body.appendChild(encouragement); this.encouragementElement = encouragement; this.addEncouragementAnimation(); } initMouseTracking() { document.addEventListener("mousemove", (e) => { this.updateElementPosition(this.warningElement, e); this.updateElementPosition(this.encouragementElement, e); }); } updateElementPosition(element, e) { if (!element || element.style.display === "none") return; const x = e.clientX + this.offsetX; const y = e.clientY + this.offsetY; const rect = element.getBoundingClientRect(); const maxX = window.innerWidth - rect.width; const maxY = window.innerHeight - rect.height; const finalX = Math.min(Math.max(0, x), maxX); const finalY = Math.min(Math.max(0, y), maxY); element.style.transform = `translate(${finalX}px, ${finalY}px)`; } addEncouragementAnimation() { const keyframes = ` @keyframes encouragementPulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.8; } 100% { transform: scale(1); opacity: 1; } } @keyframes fadeInOut { 0% { opacity: 0; transform: translateY(20px); } 10% { opacity: 1; transform: translateY(0); } 90% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(-20px); } } `; const style = document.createElement("style"); style.textContent = keyframes; document.head.appendChild(style); if (this.encouragementElement) { this.encouragementElement.style.animation = "fadeInOut 3s forwards"; } } showEncouragement(killsToLeader, isDethrone = false, noKillLeader = false) { if (!this.encouragementElement) return; let message; if (isDethrone && killsToLeader !== 0) { message = "Oh no! You've been dethroned!"; this.encouragementElement.style.borderColor = "#ff0000"; this.encouragementElement.style.color = "#ff0000"; this.encouragementElement.style.background = "rgba(255, 0, 0, 0.1)"; } else if (noKillLeader) { const killsNeeded = this.MINIMUM_KILLS_FOR_LEADER - this.lastKnownKills; message = `Nice Kill! Get ${killsNeeded} more kills to become the first Kill Leader!`; } else { message = killsToLeader <= 0 ? "You're the Kill Leader! 👑" : `Nice Kill! ${killsToLeader} more to become Kill Leader!`; } const span = this.encouragementElement.querySelector("span"); if (span) span.textContent = message; this.encouragementElement.style.display = "block"; this.encouragementElement.style.animation = "fadeInOut 3s forwards"; setTimeout(() => { if (this.encouragementElement) { this.encouragementElement.style.display = "none"; // Reset colors this.encouragementElement.style.borderColor = "#00ff00"; this.encouragementElement.style.color = "#00ff00"; this.encouragementElement.style.background = "rgba(0, 255, 0, 0.1)"; } }, 7000); } isKillLeader() { const killLeaderNameElement = document.querySelector("#ui-kill-leader-name"); return this.kxsClient.getUsername() === (killLeaderNameElement === null || killLeaderNameElement === void 0 ? void 0 : killLeaderNameElement.textContent); } update(myKills) { if (!this.kxsClient.isKillLeaderTrackerEnabled) return; const killLeaderElement = document.querySelector("#ui-kill-leader-count"); this.killLeaderKillCount = parseInt((killLeaderElement === null || killLeaderElement === void 0 ? void 0 : killLeaderElement.textContent) || "0", 10); if (myKills > this.lastKnownKills) { if (this.killLeaderKillCount === 0) { // Pas encore de kill leader, encourager le joueur à atteindre 3 kills this.showEncouragement(0, false, true); } else if (this.killLeaderKillCount < this.MINIMUM_KILLS_FOR_LEADER) { // Ne rien faire si le kill leader n'a pas atteint le minimum requis return; } else if (this.isKillLeader() && myKills > 0) { this.showEncouragement(0); this.wasKillLeader = true; } else { const killsNeeded = this.killLeaderKillCount + 1 - myKills; this.showEncouragement(killsNeeded); } } else if (this.wasKillLeader && !this.isKillLeader()) { // Détroné this.showEncouragement(0, true); this.wasKillLeader = false; } this.lastKnownKills = myKills; } } ;// ./src/HUD/GridSystem.ts class GridSystem { constructor() { this.gridSize = 20; // Size of each grid cell this.snapThreshold = 15; // Distance in pixels to trigger snap this.gridVisible = false; this.magneticEdges = true; this.counterElements = {}; this.gridContainer = this.createGridOverlay(); this.setupKeyBindings(); } createGridOverlay() { const container = document.createElement("div"); container.id = "grid-overlay"; Object.assign(container.style, { position: "fixed", top: "0", left: "0", width: "100%", height: "100%", pointerEvents: "none", zIndex: "9999", display: "none", opacity: "0.2", }); // Create vertical lines for (let x = this.gridSize; x < window.innerWidth; x += this.gridSize) { const vLine = document.createElement("div"); Object.assign(vLine.style, { position: "absolute", left: `${x}px`, top: "0", width: "1px", height: "100%", backgroundColor: "#4CAF50", }); container.appendChild(vLine); } // Create horizontal lines for (let y = this.gridSize; y < window.innerHeight; y += this.gridSize) { const hLine = document.createElement("div"); Object.assign(hLine.style, { position: "absolute", left: "0", top: `${y}px`, width: "100%", height: "1px", backgroundColor: "#4CAF50", }); container.appendChild(hLine); } document.body.appendChild(container); return container; } setupKeyBindings() { document.addEventListener("keydown", (e) => { if (e.key === "g" && e.altKey) { this.toggleGrid(); } }); } toggleGrid() { this.gridVisible = !this.gridVisible; this.gridContainer.style.display = this.gridVisible ? "block" : "none"; } registerCounter(id, element) { if (element) { this.counterElements[id] = element; } else { delete this.counterElements[id]; } } snapToGrid(element, x, y) { const rect = element.getBoundingClientRect(); const elementWidth = rect.width; const elementHeight = rect.height; // Snap to grid let snappedX = Math.round(x / this.gridSize) * this.gridSize; let snappedY = Math.round(y / this.gridSize) * this.gridSize; // Edge snapping if (this.magneticEdges) { const screenEdges = { left: 0, right: window.innerWidth - elementWidth, center: (window.innerWidth - elementWidth) / 2, top: 0, bottom: window.innerHeight - elementHeight, middle: (window.innerHeight - elementHeight) / 2, }; // Snap to horizontal edges if (Math.abs(x - screenEdges.left) < this.snapThreshold) { snappedX = screenEdges.left; } else if (Math.abs(x - screenEdges.right) < this.snapThreshold) { snappedX = screenEdges.right; } else if (Math.abs(x - screenEdges.center) < this.snapThreshold) { snappedX = screenEdges.center; } // Snap to vertical edges if (Math.abs(y - screenEdges.top) < this.snapThreshold) { snappedY = screenEdges.top; } else if (Math.abs(y - screenEdges.bottom) < this.snapThreshold) { snappedY = screenEdges.bottom; } else if (Math.abs(y - screenEdges.middle) < this.snapThreshold) { snappedY = screenEdges.middle; } } return { x: snappedX, y: snappedY }; } highlightNearestGridLine(x, y) { if (!this.gridVisible) return; // Remove existing highlights const highlights = document.querySelectorAll(".grid-highlight"); highlights.forEach((h) => h.remove()); // Create highlight for nearest vertical line const nearestX = Math.round(x / this.gridSize) * this.gridSize; if (Math.abs(x - nearestX) < this.snapThreshold) { const vHighlight = document.createElement("div"); Object.assign(vHighlight.style, { position: "absolute", left: `${nearestX}px`, top: "0", width: "2px", height: "100%", backgroundColor: "#FFD700", zIndex: "10000", pointerEvents: "none", }); vHighlight.classList.add("grid-highlight"); this.gridContainer.appendChild(vHighlight); } // Create highlight for nearest horizontal line const nearestY = Math.round(y / this.gridSize) * this.gridSize; if (Math.abs(y - nearestY) < this.snapThreshold) { const hHighlight = document.createElement("div"); Object.assign(hHighlight.style, { position: "absolute", left: "0", top: `${nearestY}px`, width: "100%", height: "2px", backgroundColor: "#FFD700", zIndex: "10000", pointerEvents: "none", }); hHighlight.classList.add("grid-highlight"); this.gridContainer.appendChild(hHighlight); } } } ;// ./src/SERVER/DiscordTracking.ts var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const stuff_emojis = { main_weapon: "🔫", secondary_weapon: "🔫", grenades: "💣", melees: "🔪", soda: "🥤", medkit: "🩹", bandage: "🩹", pills: "💊", backpack: "🎒", chest: "📦", helmet: "⛑️" }; class WebhookValidator { static isValidWebhookUrl(url = '') { return url.startsWith("https://"); } static isWebhookAlive(webhookUrl) { return __awaiter(this, void 0, void 0, function* () { try { // First check if the URL format is valid if (!this.isValidWebhookUrl(webhookUrl)) { throw new Error("Invalid webhook URL format"); } // Test the webhook with a GET request (Discord allows GET on webhooks) const response = yield fetch(webhookUrl, { method: "GET", headers: { "Content-Type": "application/json", }, }); // Discord returns 200 for valid webhooks return response.status === 200; } catch (error) { return false; } }); } static testWebhook(webhookUrl) { return __awaiter(this, void 0, void 0, function* () { try { if (!webhookUrl) { return { isValid: false, message: "Please enter a webhook URL", }; } if (!this.isValidWebhookUrl(webhookUrl)) { return { isValid: false, message: "Invalid Discord webhook URL format", }; } const isAlive = yield this.isWebhookAlive(webhookUrl); return { isValid: isAlive, message: isAlive ? "Webhook is valid and working!" : "Webhook is not responding or has been deleted", }; } catch (error) { return { isValid: false, message: "Error testing webhook connection", }; } }); } } class DiscordTracking { constructor(kxsClient, webhookUrl) { this.kxsClient = kxsClient; this.webhookUrl = webhookUrl; } setWebhookUrl(webhookUrl) { this.webhookUrl = webhookUrl; } validateCurrentWebhook() { return __awaiter(this, void 0, void 0, function* () { return WebhookValidator.isWebhookAlive(this.webhookUrl); }); } sendWebhookMessage(message) { return __awaiter(this, void 0, void 0, function* () { if (!WebhookValidator.isValidWebhookUrl(this.webhookUrl)) { return; } this.kxsClient.nm.showNotification("Sending Discord message...", "info", 2300); try { const response = yield fetch(this.webhookUrl, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(message), }); if (!response.ok) { throw new Error(`Discord Webhook Error: ${response.status}`); } } catch (error) { this.kxsClient.logger.error("Error sending Discord message:", error); } }); } getEmbedColor(isWin) { return isWin ? 0x2ecc71 : 0xe74c3c; // Green for victory, red for defeat } trackGameEnd(result) { return __awaiter(this, void 0, void 0, function* () { const title = result.isWin ? "🏆 VICTORY ROYALE!" : `${result.position} - Game Over`; const embed = { title, description: `${result.username}'s Match`, color: this.getEmbedColor(result.isWin), fields: [ { name: "💀 Eliminations", value: result.kills.toString(), inline: true, }, ], }; if (result.duration) { embed.fields.push({ name: "⏱️ Duration", value: result.duration, inline: true, }); } if (result.damageDealt) { embed.fields.push({ name: "💥 Damage Dealt", value: Math.round(result.damageDealt).toString(), inline: true, }); } if (result.damageTaken) { embed.fields.push({ name: "💢 Damage Taken", value: Math.round(result.damageTaken).toString(), inline: true, }); } if (result.username) { embed.fields.push({ name: "📝 Username", value: result.username, inline: true, }); } if (result.stuff) { for (const [key, value] of Object.entries(result.stuff)) { if (value) { embed.fields.push({ name: `${stuff_emojis[key]} ${key.replace("_", " ").toUpperCase()}`, value, inline: true, }); } } } const message = { username: "KxsClient", avatar_url: kxs_logo, content: result.isWin ? "🎉 New Victory!" : "Match Ended", embeds: [embed], }; yield this.sendWebhookMessage(message); }); } } ;// ./src/FUNC/StatsParser.ts class StatsParser { static cleanNumber(str) { return parseInt(str.replace(/[^\d.-]/g, "")) || 0; } /** * Extract the full duration string including the unit */ static extractDuration(str) { const match = str.match(/(\d+\s*[smh])/i); return match ? match[1].trim() : "0s"; } static parse(statsText, rankContent) { let stats = { username: "Player", kills: 0, damageDealt: 0, damageTaken: 0, duration: "", position: "#unknown", }; // Handle developer format const devPattern = /Developer.*?Kills(\d+).*?Damage Dealt(\d+).*?Damage Taken(\d+).*?Survived(\d+\s*[smh])/i; const devMatch = statsText.match(devPattern); if (devMatch) { return { username: "Player", kills: this.cleanNumber(devMatch[1]), damageDealt: this.cleanNumber(devMatch[2]), damageTaken: this.cleanNumber(devMatch[3]), duration: devMatch[4].trim(), // Keep the full duration string with unit position: rankContent.replace("##", "#"), }; } // Handle template format const templatePattern = /%username%.*?Kills%kills_number%.*?Dealt%number_dealt%.*?Taken%damage_taken%.*?Survived%duration%/; const templateMatch = statsText.match(templatePattern); if (templateMatch) { const parts = statsText.split(/Kills|Dealt|Taken|Survived/); if (parts.length >= 5) { return { username: parts[0].trim(), kills: this.cleanNumber(parts[1]), damageDealt: this.cleanNumber(parts[2]), damageTaken: this.cleanNumber(parts[3]), duration: this.extractDuration(parts[4]), // Extract full duration with unit position: rankContent.replace("##", "#"), }; } } // Generic parsing as fallback const usernameMatch = statsText.match(/^([^0-9]+)/); if (usernameMatch) { stats.username = usernameMatch[1].trim(); } const killsMatch = statsText.match(/Kills[^0-9]*(\d+)/i); if (killsMatch) { stats.kills = this.cleanNumber(killsMatch[1]); } const dealtMatch = statsText.match(/Dealt[^0-9]*(\d+)/i); if (dealtMatch) { stats.damageDealt = this.cleanNumber(dealtMatch[1]); } const takenMatch = statsText.match(/Taken[^0-9]*(\d+)/i); if (takenMatch) { stats.damageTaken = this.cleanNumber(takenMatch[1]); } // Extract survival time with unit const survivalMatch = statsText.match(/Survived[^0-9]*(\d+\s*[smh])/i); if (survivalMatch) { stats.duration = survivalMatch[1].trim(); } stats.position = rankContent.replace("##", "#"); return stats; } } // EXTERNAL MODULE: ./node_modules/semver/functions/gt.js var gt = __webpack_require__(580); var gt_default = /*#__PURE__*/__webpack_require__.n(gt); ;// ./src/FUNC/UpdateChecker.ts var UpdateChecker_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; class UpdateChecker { constructor(kxsClient) { this.remoteScriptUrl = `${config_namespaceObject.api_url}/cors/${encodeURIComponent(config_namespaceObject.base_url + "/download/latest-dev.js")}`; this.kxsClient = kxsClient; if (this.kxsClient.isAutoUpdateEnabled) { this.checkForUpdate(); } } copyScriptToClipboard() { return UpdateChecker_awaiter(this, void 0, void 0, function* () { try { const response = yield fetch(this.remoteScriptUrl, { method: "GET", headers: { "cache-control": "no-cache, no-store, must-revalidate", "pragma": "no-cache", "expires": "0" } }); if (!response.ok) { throw new Error("Error retrieving script: " + response.statusText); } const scriptContent = yield response.text(); yield navigator.clipboard.writeText(scriptContent); this.kxsClient.nm.showNotification("Script copied to clipboard!", "success", 2300); } catch (error) { throw new Error("Error copying script to clipboard: " + error); } }); } getNewScriptVersion() { return UpdateChecker_awaiter(this, void 0, void 0, function* () { try { const response = yield fetch(this.remoteScriptUrl, { method: "GET", headers: { "cache-control": "no-cache, no-store, must-revalidate", "pragma": "no-cache", "expires": "0" } }); if (!response.ok) { throw new Error("Error retrieving remote script: " + response.statusText); } const scriptContent = yield response.text(); const versionMatch = scriptContent.match(/\/\/\s*@version\s+([\d.]+)/); if (versionMatch && versionMatch[1]) { return versionMatch[1]; } else { throw new Error("Script version was not found in the file."); } } catch (error) { throw new Error("Error retrieving remote script: " + error); } }); } checkForUpdate() { return UpdateChecker_awaiter(this, void 0, void 0, function* () { const localScriptVersion = yield this.getCurrentScriptVersion(); const hostedScriptVersion = yield this.getNewScriptVersion(); this.hostedScriptVersion = hostedScriptVersion; // Vérifie si la version hébergée est supérieure à la version locale if (gt_default()(hostedScriptVersion, localScriptVersion)) { this.displayUpdateNotification(); } else { this.kxsClient.nm.showNotification("Client is up to date", "success", 2300); } }); } displayUpdateNotification() { const modal = document.createElement("div"); modal.style.position = "fixed"; modal.style.top = "50%"; modal.style.left = "50%"; modal.style.transform = "translate(-50%, -50%)"; modal.style.backgroundColor = "rgb(250, 250, 250)"; modal.style.borderRadius = "10px"; modal.style.padding = "20px"; modal.style.width = "500px"; modal.style.boxShadow = "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"; modal.style.border = "1px solid rgb(229, 229, 229)"; modal.style.zIndex = "10000"; const header = document.createElement("div"); header.style.display = "flex"; header.style.alignItems = "center"; header.style.marginBottom = "15px"; const title = document.createElement("h3"); title.textContent = "Update Available"; title.style.margin = "0"; title.style.fontSize = "18px"; title.style.fontWeight = "600"; header.appendChild(title); const closeButton = document.createElement("button"); closeButton.innerHTML = "×"; closeButton.style.marginLeft = "auto"; closeButton.style.border = "none"; closeButton.style.background = "none"; closeButton.style.fontSize = "24px"; closeButton.style.cursor = "pointer"; closeButton.style.padding = "0 5px"; closeButton.onclick = () => modal.remove(); header.appendChild(closeButton); const content = document.createElement("div"); content.innerHTML = `

A new version of KxsClient is available!

Current version: ${this.getCurrentScriptVersion()} | New version: ${this.hostedScriptVersion}

To update, follow these steps:

  1. Click "Copy Script" below
  2. Open your script manager (Tampermonkey, Violentmonkey, etc.)
  3. Overwrite the current script with the new one and paste the content
  4. Save the script (Ctrl+S or Cmd+S)
  5. Reload the game page
`; content.style.color = "rgb(75, 85, 99)"; content.style.fontSize = "14px"; content.style.lineHeight = "1.5"; const updateButton = document.createElement("button"); updateButton.textContent = "Copy Script"; updateButton.style.backgroundColor = "rgb(79, 70, 229)"; updateButton.style.color = "white"; updateButton.style.padding = "10px 16px"; updateButton.style.borderRadius = "6px"; updateButton.style.border = "none"; updateButton.style.cursor = "pointer"; updateButton.style.width = "100%"; updateButton.style.fontWeight = "500"; updateButton.style.fontSize = "15px"; updateButton.style.transition = "background-color 0.2s ease"; updateButton.onmouseover = () => updateButton.style.backgroundColor = "rgb(67, 56, 202)"; updateButton.onmouseout = () => updateButton.style.backgroundColor = "rgb(79, 70, 229)"; updateButton.onclick = () => UpdateChecker_awaiter(this, void 0, void 0, function* () { try { yield this.copyScriptToClipboard(); updateButton.textContent = "Script copied!"; updateButton.style.backgroundColor = "rgb(16, 185, 129)"; setTimeout(() => { if (updateButton.isConnected) { updateButton.textContent = "Copy Script"; updateButton.style.backgroundColor = "rgb(79, 70, 229)"; } }, 3000); } catch (error) { this.kxsClient.nm.showNotification("Error: " + error.message, "error", 5000); } }); modal.appendChild(header); modal.appendChild(content); modal.appendChild(updateButton); document.body.appendChild(modal); } getCurrentScriptVersion() { return this.kxsClient.pkg.version; } } ;// ./src/SERVER/DiscordRichPresence.ts class DiscordWebSocket { constructor(kxsClient, token) { this.ws = null; this.heartbeatInterval = 0; this.sequence = null; this.isAuthenticated = false; this.kxsClient = kxsClient; } connect() { if (this.kxsClient.discordToken === "" || this.kxsClient.discordToken === null || this.kxsClient.discordToken === undefined) { return; } this.ws = new WebSocket('wss://gateway.discord.gg/?v=9&encoding=json'); this.ws.onopen = () => { }; this.ws.onmessage = (event) => { const data = JSON.parse(event.data); this.handleMessage(data); }; this.ws.onerror = (error) => { }; this.ws.onclose = () => { clearInterval(this.heartbeatInterval); this.isAuthenticated = false; }; } identify() { const payload = { op: 2, d: { token: this.kxsClient.discordToken, properties: { $os: 'linux', $browser: 'chrome', $device: 'chrome' }, presence: { activities: [{ name: "KxsClient", type: 0, application_id: "1321193265533550602", assets: { large_image: "mp:app-icons/1321193265533550602/bccd2479ec56ed7d4e69fa2fdfb47197.png?size=512", large_text: "KxsClient v" + this.kxsClient.pkg.version, } }], status: 'online', afk: false } } }; this.send(payload); } handleMessage(data) { switch (data.op) { case 10: // Hello const { heartbeat_interval } = data.d; this.startHeartbeat(heartbeat_interval); this.identify(); break; case 11: // Heartbeat ACK this.kxsClient.logger.log('[RichPresence] Heartbeat acknowledged'); break; case 0: // Dispatch this.sequence = data.s; if (data.t === 'READY') { this.isAuthenticated = true; this.kxsClient.nm.showNotification('Started Discord RPC', 'success', 3000); } break; } } startHeartbeat(interval) { this.heartbeatInterval = setInterval(() => { this.send({ op: 1, d: this.sequence }); }, interval); } send(data) { var _a; if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) { this.ws.send(JSON.stringify(data)); } } disconnect() { if (this.ws) { clearInterval(this.heartbeatInterval); this.ws.close(); } } } ;// ./src/HUD/MOD/NotificationManager.ts class NotificationManager { constructor() { this.notifications = []; this.NOTIFICATION_HEIGHT = 65; // Height + margin this.addGlobalStyles(); } static getInstance() { if (!NotificationManager.instance) { NotificationManager.instance = new NotificationManager(); } return NotificationManager.instance; } addGlobalStyles() { const styleSheet = document.createElement("style"); styleSheet.textContent = ` @keyframes slideIn { 0% { transform: translateX(-120%); opacity: 0; } 50% { transform: translateX(10px); opacity: 0.8; } 100% { transform: translateX(0); opacity: 1; } } @keyframes slideOut { 0% { transform: translateX(0); opacity: 1; } 50% { transform: translateX(10px); opacity: 0.8; } 100% { transform: translateX(-120%); opacity: 0; } } @keyframes slideLeft { from { transform-origin: right; transform: scaleX(1); } to { transform-origin: right; transform: scaleX(0); } } @keyframes bounce { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } } `; document.head.appendChild(styleSheet); } updateNotificationPositions() { this.notifications.forEach((notification, index) => { const topPosition = 20 + (index * this.NOTIFICATION_HEIGHT); notification.style.top = `${topPosition}px`; }); } removeNotification(notification) { const index = this.notifications.indexOf(notification); if (index > -1) { this.notifications.splice(index, 1); this.updateNotificationPositions(); } } getIconConfig(type) { const configs = { success: { color: '#4CAF50', svg: ` ` }, error: { color: '#F44336', svg: ` ` }, info: { color: '#FFD700', svg: ` ` } }; return configs[type]; } showNotification(message, type, duration = 5000) { const notification = document.createElement("div"); // Apply styles using DesignSystem with dark theme to match the rest of the interface DesignSystem.applyGlassEffect(notification, __webpack_require__.g.kxsClient.isGlassmorphismEnabled ? 'light' : 'dark', { position: "fixed", top: "20px", left: "20px", padding: DesignSystem.spacing.md + " " + DesignSystem.spacing.lg, color: "white", zIndex: DesignSystem.layers.notification.toString(), minWidth: "200px", display: "flex", alignItems: "center", gap: DesignSystem.spacing.sm, transform: "translateX(-120%)", opacity: "0", fontFamily: DesignSystem.fonts.primary }); // Create icon const icon = document.createElement("div"); Object.assign(icon.style, { width: "20px", height: "20px", display: "flex", alignItems: "center", justifyContent: "center", animation: "bounce 0.5s ease-in-out" }); const iconConfig = this.getIconConfig(type); icon.style.color = iconConfig.color; icon.innerHTML = iconConfig.svg; // Create message const messageDiv = document.createElement("div"); messageDiv.textContent = message; messageDiv.style.flex = "1"; // Create progress bar with appropriate style based on glassmorphism setting const progressBar = document.createElement("div"); if (__webpack_require__.g.kxsClient.isGlassmorphismEnabled) { // Glassmorphism progress bar style Object.assign(progressBar.style, { height: "4px", background: "rgba(255, 255, 255, 0.3)", backdropFilter: "blur(5px)", webkitBackdropFilter: "blur(5px)", borderRadius: `0 0 ${DesignSystem.radius.lg} ${DesignSystem.radius.lg}`, width: "100%", position: "absolute", bottom: "0", left: "0", animation: `slideLeft ${duration}ms linear forwards` }); } else { // Classic progress bar style Object.assign(progressBar.style, { height: "3px", background: type === "success" ? "#4CAF50" : type === "error" ? "#F44336" : "#2196F3", opacity: "0.7", borderRadius: `0 0 ${DesignSystem.radius.md} ${DesignSystem.radius.md}`, width: "100%", position: "absolute", bottom: "0", left: "0", animation: `slideLeft ${duration}ms linear forwards` }); } // Assemble notification notification.appendChild(icon); notification.appendChild(messageDiv); notification.appendChild(progressBar); document.body.appendChild(notification); // Add to stack and update positions this.notifications.push(notification); this.updateNotificationPositions(); // Entrance animation requestAnimationFrame(() => { notification.style.transition = "all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55)"; notification.style.animation = "slideIn 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards"; }); // Exit animation and cleanup (optimized) setTimeout(() => { notification.style.animation = "slideOut 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards"; // Use event listener for animation end instead of setTimeout notification.addEventListener('animationend', () => { this.removeNotification(notification); notification.remove(); }, { once: true }); }, duration); } } ;// ./src/HUD/ClientSecondaryMenu.ts var ClientSecondaryMenu_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; const category = ["ALL", "HUD", "SERVER", "MECHANIC", "MISC", "CONFIG"]; const X0 = ["Kxs Network", "Developer Options"]; class KxsClientSecondaryMenu { constructor(kxsClient) { this.searchTerm = ''; // Fonction pour fermer un sous-menu this.closeSubMenu = () => { }; // Callbacks pour notifier les changements d'état du menu this.onMenuToggle = []; this.mouseMoveListener = (e) => { if (this.isDragging) { // Optimized: use requestAnimationFrame for smooth dragging requestAnimationFrame(() => { const x = e.clientX - this.dragOffset.x; const y = e.clientY - this.dragOffset.y; this.menu.style.transform = 'none'; this.menu.style.left = `${x}px`; this.menu.style.top = `${y}px`; }); } }; this.mouseUpListener = () => { this.isDragging = false; this.menu.style.cursor = "grab"; }; this.kxsClient = kxsClient; this.isClientMenuVisible = false; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.sections = []; this.allOptions = []; this.activeCategory = "ALL"; this.isOpen = false; this.menu = document.createElement("div"); this.initMenu(); this.addShiftListener(); this.addDragListeners(); this.loadOption(); } initMenu() { this.menu.id = "kxsClientMenu"; this.applyMenuStyles(); this.createHeader(); this.createGridContainer(); document.body.appendChild(this.menu); this.menu.style.display = "none"; // Empêcher la propagation des événements souris (clics et molette) vers la page web // Utiliser la phase de bouillonnement (bubbling) au lieu de la phase de capture // pour permettre aux éléments enfants de recevoir les événements d'abord this.menu.addEventListener('click', (e) => { e.stopPropagation(); }); this.menu.addEventListener('wheel', (e) => { e.stopPropagation(); }); // Nous ne gérons pas mousedown et mouseup ici car ils sont gérés dans addDragListeners() } applyMenuStyles() { const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); // Injecter les polices et animations du DesignSystem DesignSystem.injectFonts(); // Apply appropriate styling based on the glassmorphism toggle DesignSystem.applyStyle(this.menu, 'dark', { position: "fixed", left: "50%", top: "50%", transform: "translate(-50%, -50%)", width: isMobile ? "85%" : "55%", maxWidth: "650px", maxHeight: "70vh", color: "#fff", padding: isMobile ? "10px" : "18px", zIndex: "10000", display: "none", flexDirection: "column", fontFamily: DesignSystem.fonts.primary, cursor: "grab", userSelect: "none", overflow: "hidden", boxSizing: "border-box", transition: `all ${DesignSystem.animation.normal} ease`, borderRadius: this.kxsClient.isGlassmorphismEnabled ? "14px" : "10px" }); // Styles réduits pour mobile if (isMobile) { Object.assign(this.menu.style, { padding: "8px", borderRadius: this.kxsClient.isGlassmorphismEnabled ? "12px" : "8px", width: "75vw", maxWidth: "80vw", fontSize: "11px", maxHeight: "65vh", top: "5%" }); // Add specific mobile styles based on glassmorphism toggle if (this.kxsClient.isGlassmorphismEnabled) { // Glassmorphism mobile optimisé pour les performances Object.assign(this.menu.style, { backdropFilter: "blur(10px) saturate(140%)", WebkitBackdropFilter: "blur(10px) saturate(140%)", willChange: "transform, opacity", backgroundColor: "rgba(15, 23, 42, 0.9)", boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)" }); } else { // Classic style mobile Object.assign(this.menu.style, { backgroundColor: "rgba(40, 40, 40, 0.97)", boxShadow: "0 6px 16px rgba(0, 0, 0, 0.4)" }); } } } blockMousePropagation(element, preventDefault = true) { ['click', 'mousedown', 'mouseup', 'dblclick', 'contextmenu', 'wheel'].forEach(eventType => { element.addEventListener(eventType, (e) => { e.stopPropagation(); if (preventDefault && (eventType === 'contextmenu' || eventType === 'wheel' || element.tagName !== 'INPUT')) { e.preventDefault(); } }, false); }); } createHeader() { const header = document.createElement("div"); // Détection mobile pour styles réduits const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); const logoSize = isMobile ? 20 : 30; const titleFontSize = isMobile ? 12 : 20; const headerGap = isMobile ? 4 : 10; const headerMarginBottom = isMobile ? 8 : 20; const closeBtnPadding = isMobile ? 2 : 6; const closeBtnFontSize = isMobile ? 12 : 18; header.style.marginBottom = `${headerMarginBottom}px`; header.innerHTML = `
Logo KXS CLIENT v${this.kxsClient.pkg.version}
${category.map(cat => ` `).join('')}
`; header.querySelectorAll('.category-btn').forEach(btn => { this.blockMousePropagation(btn); btn.addEventListener('click', (e) => { const category = e.target.dataset.category; if (category) { this.setActiveCategory(category); } }); }); const closeButton = header.querySelector('button'); closeButton === null || closeButton === void 0 ? void 0 : closeButton.addEventListener('click', () => { this.toggleMenuVisibility(); }); const searchInput = header.querySelector('#kxsSearchInput'); if (searchInput) { this.blockMousePropagation(searchInput, false); // Gestionnaire pour mettre à jour la recherche searchInput.addEventListener('input', (e) => { this.searchTerm = e.target.value.toLowerCase(); this.filterOptions(); }); // Prevent keys from being interpreted by the game // We only block the propagation of keyboard events, except for special keys ['keydown', 'keyup', 'keypress'].forEach(eventType => { searchInput.addEventListener(eventType, (e) => { const keyEvent = e; // Don't block special keys (Escape, Shift) if (keyEvent.key === 'Escape' || (keyEvent.key === 'Shift' && keyEvent.location === 2)) { return; // Let the event propagate normally } // Block propagation for all other keys e.stopPropagation(); }); }); // Éviter que la barre de recherche ne reprenne automatiquement le focus // lorsque l'utilisateur interagit avec un autre champ de texte searchInput.addEventListener('blur', (e) => { // Ne pas reprendre le focus si l'utilisateur clique sur un autre input const newFocusElement = e.relatedTarget; if (newFocusElement && (newFocusElement.tagName === 'INPUT' || newFocusElement.tagName === 'TEXTAREA')) { // L'utilisateur a cliqué sur un autre champ de texte, ne pas reprendre le focus return; } // Pour les autres cas, seulement si aucun autre élément n'a le focus (optimized) requestAnimationFrame(() => { const activeElement = document.activeElement; if (this.isClientMenuVisible && activeElement && activeElement !== searchInput && activeElement.tagName !== 'INPUT' && activeElement.tagName !== 'TEXTAREA') { searchInput.focus(); } }); }); } this.menu.appendChild(header); } clearMenu() { const gridContainer = document.getElementById('kxsMenuGrid'); if (gridContainer) { gridContainer.innerHTML = ''; } // Reset search term when clearing menu this.searchTerm = ''; const searchInput = document.getElementById('kxsSearchInput'); if (searchInput) { searchInput.value = ''; } } loadOption() { // Clear existing options to avoid duplicates this.allOptions = []; let HUD = this.addSection("HUD"); let MECHANIC = this.addSection("MECHANIC"); let SERVER = this.addSection("SERVER"); let MISC = this.addSection("MISC"); let CONFIG = this.addSection("CONFIG"); this.addOption(SERVER, { label: "Kxs Network", value: true, type: "sub", icon: ' network ', fields: [ { label: "Spoof Nickname", icon: ' hacker-solid ', type: "toggle", value: this.kxsClient.kxsNetworkSettings.nickname_anonymized, onChange: () => { this.kxsClient.kxsNetworkSettings.nickname_anonymized = !this.kxsClient.kxsNetworkSettings.nickname_anonymized; this.kxsClient.updateLocalStorage(); } }, { label: "Voice Chat", value: this.kxsClient.isVoiceChatEnabled, icon: ' ', type: "toggle", onChange: () => { this.kxsClient.isVoiceChatEnabled = !this.kxsClient.isVoiceChatEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.voiceChat.toggleVoiceChat(); }, }, { label: "Chat", value: this.kxsClient.isKxsChatEnabled, icon: ' ', type: "toggle", onChange: () => { this.kxsClient.isKxsChatEnabled = !this.kxsClient.isKxsChatEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.chat.toggleChat(); }, } ], }); this.addOption(MISC, { label: "Game History", value: true, icon: ' ', type: "click", onChange: () => { this.kxsClient.historyManager.show(); } }); this.addOption(MECHANIC, { label: "Win sound", value: true, type: "sub", icon: ' ', fields: [ { label: "Enable", value: this.kxsClient.isWinSoundEnabled, icon: ' ', type: "toggle", onChange: () => { this.kxsClient.isWinSoundEnabled = !this.kxsClient.isWinSoundEnabled; this.kxsClient.updateLocalStorage(); }, }, { label: "Sound URL", value: this.kxsClient.soundLibrary.win_sound_url, icon: ' ', type: "input", placeholder: "URL of a sound", onChange: (value) => { this.kxsClient.soundLibrary.win_sound_url = value; this.kxsClient.updateLocalStorage(); } } ] }); this.addOption(MECHANIC, { label: "Death sound", value: true, type: "sub", icon: ' ', fields: [ { label: "Enable", value: this.kxsClient.isDeathSoundEnabled, icon: ' ', type: "toggle", onChange: () => { this.kxsClient.isDeathSoundEnabled = !this.kxsClient.isDeathSoundEnabled; this.kxsClient.updateLocalStorage(); }, }, { label: "Sound URL", value: this.kxsClient.soundLibrary.death_sound_url, icon: ' ', type: "input", placeholder: "URL of a sound", onChange: (value) => { this.kxsClient.soundLibrary.death_sound_url = value; this.kxsClient.updateLocalStorage(); } } ] }); this.addOption(MECHANIC, { label: "Background Music", value: this.kxsClient.soundLibrary.background_sound_url, type: "input", icon: ' ', placeholder: background_song, onChange: (value) => { this.kxsClient.soundLibrary.background_sound_url = value; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Clean Main Menu", value: this.kxsClient.isMainMenuCleaned, icon: ' clean ', type: "toggle", onChange: (value) => { this.kxsClient.isMainMenuCleaned = !this.kxsClient.isMainMenuCleaned; this.kxsClient.MainMenuCleaning(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Counters", value: true, type: "sub", icon: '', fields: [ { label: "Show Kills", value: this.kxsClient.isKillsVisible, type: "toggle", icon: ' ', onChange: (value) => { this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible; this.kxsClient.updateKillsVisibility(); this.kxsClient.updateLocalStorage(); }, }, { label: "Show FPS", value: this.kxsClient.isFpsVisible, type: "toggle", icon: '', onChange: (value) => { this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible; this.kxsClient.updateFpsVisibility(); this.kxsClient.updateLocalStorage(); }, }, { label: "Show Ping", value: this.kxsClient.isPingVisible, icon: '', type: "toggle", onChange: (value) => { this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible; this.kxsClient.updatePingVisibility(); this.kxsClient.updateLocalStorage(); }, } ], }); this.addOption(HUD, { label: "Weapon Border", value: this.kxsClient.isGunOverlayColored, type: "toggle", icon: ' ', onChange: (value) => { this.kxsClient.isGunOverlayColored = !this.kxsClient.isGunOverlayColored; this.kxsClient.updateLocalStorage(); this.kxsClient.hud.toggleWeaponBorderHandler(); }, }); this.addOption(HUD, { label: "Chromatic Weapon Border", value: this.kxsClient.isGunBorderChromatic, type: "toggle", icon: ' ', onChange: (value) => { this.kxsClient.isGunBorderChromatic = !this.kxsClient.isGunBorderChromatic; this.kxsClient.updateLocalStorage(); this.kxsClient.hud.toggleChromaticWeaponBorder(); }, }); this.addOption(HUD, { label: "Focus Mode", value: true, type: "sub", icon: ' ', fields: [ { label: "Enable", value: this.kxsClient.isFocusModeEnabled, type: "toggle", icon: ' ', onChange: (value) => { this.kxsClient.isFocusModeEnabled = !this.kxsClient.isFocusModeEnabled; if (!this.kxsClient.isFocusModeEnabled) { this.kxsClient.currentFocusModeState = false; this.kxsClient.hud.toggleFocusMode(); } this.kxsClient.updateLocalStorage(); }, }, { label: "Focus Mode", value: (() => { const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform); return `Press ${isMac ? 'Command+F (⌘+F)' : 'Ctrl+F'} to toggle Focus Mode.\nWhen enabled, the HUD will dim and notifications will appear.`; })(), type: "info", icon: ' ', } ], }); this.addOption(HUD, { label: "Health Bar Indicator", value: this.kxsClient.isHealBarIndicatorEnabled, type: "toggle", icon: ' ', onChange: () => { this.kxsClient.isHealBarIndicatorEnabled = !this.kxsClient.isHealBarIndicatorEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Message Open/Close RSHIFT Menu", value: this.kxsClient.isNotifyingForToggleMenu, type: "toggle", icon: ' ', onChange: (value) => { this.kxsClient.isNotifyingForToggleMenu = !this.kxsClient.isNotifyingForToggleMenu; this.kxsClient.updateLocalStorage(); }, }); this.addOption(SERVER, { label: "Webhook URL", value: this.kxsClient.discordWebhookUrl || "", icon: ' ', type: "input", placeholder: "discord webhook url", onChange: (value) => { value = value.toString().trim(); this.kxsClient.discordWebhookUrl = value; this.kxsClient.discordTracker.setWebhookUrl(value); this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: "Custom Crosshair", value: this.kxsClient.customCrosshair || "", type: "input", icon: ' crosshair ', placeholder: "URL of png,gif,svg", onChange: (value) => { this.kxsClient.customCrosshair = value; this.kxsClient.updateLocalStorage(); this.kxsClient.hud.loadCustomCrosshair(); }, }); this.addOption(MECHANIC, { label: "Heal Warning", value: this.kxsClient.isHealthWarningEnabled, type: "toggle", icon: ' health ', onChange: (value) => { var _a, _b; this.kxsClient.isHealthWarningEnabled = !this.kxsClient.isHealthWarningEnabled; if (this.kxsClient.isHealthWarningEnabled) { // Always enter placement mode when enabling from RSHIFT menu (_a = this.kxsClient.healWarning) === null || _a === void 0 ? void 0 : _a.enableDragging(); } else { (_b = this.kxsClient.healWarning) === null || _b === void 0 ? void 0 : _b.hide(); } this.kxsClient.updateLocalStorage(); }, }); this.addOption(SERVER, { label: "Update Checker", value: this.kxsClient.isAutoUpdateEnabled, type: "toggle", icon: ' ', onChange: (value) => { this.kxsClient.isAutoUpdateEnabled = !this.kxsClient.isAutoUpdateEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: `Uncap FPS`, value: this.kxsClient.isFpsUncapped, type: "toggle", icon: ' ic_fluent_fps_960_24_filled Created with Sketch. ', onChange: () => { this.kxsClient.isFpsUncapped = !this.kxsClient.isFpsUncapped; this.kxsClient.setAnimationFrameCallback(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Winning Animation`, value: this.kxsClient.isWinningAnimationEnabled, icon: ' ', type: "toggle", onChange: () => { this.kxsClient.isWinningAnimationEnabled = !this.kxsClient.isWinningAnimationEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Glassmorphism`, value: this.kxsClient.isGlassmorphismEnabled, icon: '', type: "toggle", onChange: () => { this.kxsClient.isGlassmorphismEnabled = !this.kxsClient.isGlassmorphismEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.nm.showNotification("You need to reload the page to see the changes", "info", 1900); }, }); this.addOption(HUD, { label: `KxsClient Logo`, value: this.kxsClient.isKxsClientLogoEnable, icon: ' ', type: "toggle", onChange: () => { this.kxsClient.isKxsClientLogoEnable = !this.kxsClient.isKxsClientLogoEnable; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Spotify Player`, value: this.kxsClient.isSpotifyPlayerEnabled, icon: ' spotify ', type: "toggle", onChange: () => { this.kxsClient.isSpotifyPlayerEnabled = !this.kxsClient.isSpotifyPlayerEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.toggleSpotifyMenu(); }, }); this.addOption(HUD, { label: "Brightness", value: this.kxsClient.brightness, icon: ' light ', type: "slider", min: 20, max: 100, step: 1, onChange: (value) => { this.kxsClient.applyBrightness(value); }, }); this.addOption(HUD, { label: "Kill Feed Chroma", value: this.kxsClient.isKillFeedBlint, icon: ` `, type: "toggle", onChange: () => { this.kxsClient.isKillFeedBlint = !this.kxsClient.isKillFeedBlint; this.kxsClient.updateLocalStorage(); this.kxsClient.hud.toggleKillFeed(); }, }); this.addOption(SERVER, { label: `Rich Presence (Account token required)`, value: this.kxsClient.discordToken || "", icon: ' ', type: "input", placeholder: "Your discord account token", onChange: (value) => { value = value.toString().trim(); this.kxsClient.discordToken = this.kxsClient.parseToken(value); this.kxsClient.discordRPC.disconnect(); this.kxsClient.discordRPC.connect(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: `Kill Leader Tracking`, icon: ' crown ', value: this.kxsClient.isKillLeaderTrackerEnabled, type: "toggle", onChange: (value) => { this.kxsClient.isKillLeaderTrackerEnabled = !this.kxsClient.isKillLeaderTrackerEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: `Friends Detector (separe with ',')`, icon: ' ', value: this.kxsClient.all_friends, type: "input", placeholder: "kisakay,iletal...", onChange: (value) => { this.kxsClient.all_friends = value; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Change Background`, icon: ' ', value: true, type: "click", onChange: () => { const backgroundElement = document.getElementById("background"); if (!backgroundElement) { alert("Element with id 'background' not found."); return; } const choice = prompt("Enter '0' to default Kxs background, '1' to provide a URL or '2' to upload a local image:"); if (choice === "0") { localStorage.removeItem("lastBackgroundUrl"); localStorage.removeItem("lastBackgroundFile"); localStorage.removeItem("lastBackgroundType"); localStorage.removeItem("lastBackgroundValue"); backgroundElement.style.backgroundImage = `url(${background_image})`; } else if (choice === "1") { const newBackgroundUrl = prompt("Enter the URL of the new background image:"); if (newBackgroundUrl) { backgroundElement.style.backgroundImage = `url(${newBackgroundUrl})`; this.kxsClient.saveBackgroundToLocalStorage(newBackgroundUrl); alert("Background updated successfully!"); } } else if (choice === "2") { const fileInput = document.createElement("input"); fileInput.type = "file"; fileInput.accept = "image/*"; fileInput.onchange = (event) => { var _a, _b; const file = (_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]; if (file) { const reader = new FileReader(); reader.onload = () => { backgroundElement.style.backgroundImage = `url(${reader.result})`; this.kxsClient.saveBackgroundToLocalStorage(file); alert("Background updated successfully!"); }; reader.readAsDataURL(file); } }; fileInput.click(); } }, }); this.addOption(MISC, { label: "Developer Options", value: true, icon: '', type: "sub", fields: [ { label: "Enable GameID Exchange", icon: ' share Created with Sketch Beta. ', type: "toggle", value: this.kxsClient.kxsDeveloperOptions.enableGameIDExchange, onChange: () => { this.kxsClient.kxsDeveloperOptions.enableGameIDExchange = !this.kxsClient.kxsDeveloperOptions.enableGameIDExchange; this.kxsClient.updateLocalStorage(); } }, { label: "Renew Exchange Key", value: true, type: "click", icon: ' reset ', onChange: () => ClientSecondaryMenu_awaiter(this, void 0, void 0, function* () { const new_password = this.kxsClient.generateRandomPassword(); this.kxsClient.kxsDeveloperOptions.exchange.password = new_password; this.kxsClient.updateLocalStorage(); this.kxsClient.nm.showNotification("New Exchange Key Generated (pasted to clipboard)", "success", 2100); yield navigator.clipboard.writeText(new_password); }) }, { label: "Copy Exchange Key", value: this.kxsClient.kxsDeveloperOptions.exchange.password, type: "click", icon: ' ', onChange: () => { this.kxsClient.nm.showNotification("Exchange Key Copied to Clipboard", "success", 2100); navigator.clipboard.writeText(this.kxsClient.kxsDeveloperOptions.exchange.password); } } ], }); this.addOption(CONFIG, { icon: ' ', label: "Copy Kxs Config", type: "click", value: true, onChange: () => ClientSecondaryMenu_awaiter(this, void 0, void 0, function* () { yield navigator.clipboard.writeText(this.kxsClient.getKxsJSONConfig()); this.kxsClient.nm.showNotification("Kxs Config copied to clipboard", "success", 3000); }) }); this.addOption(CONFIG, { icon: ' ', label: "Import Kxs Config", type: "click", value: true, onChange: () => ClientSecondaryMenu_awaiter(this, void 0, void 0, function* () { const data = yield navigator.clipboard.readText(); try { const parse_data = JSON.parse(data); if (parse_data["userSettings"]) { this.kxsClient.setKxsJSONConfig(parse_data); this.kxsClient.nm.showNotification("Kxs Config imported", "success", 4000); setInterval(() => { this.kxsClient.nm.showNotification("Kxs reloading soon...", "info", 4000); }, 1000); // Reload the webpage setTimeout(() => { window.location.reload(); }, 4000); } } catch (_a) { this.kxsClient.nm.showNotification("The current configuration in the clipboard is not a valid Kxs Config", "error", 4000); } }) }); } createOptionCard(option, container) { const optionCard = document.createElement("div"); Object.assign(optionCard.style, { background: "rgba(31, 41, 55, 0.8)", borderRadius: "10px", padding: "16px", display: "flex", flexDirection: "column", alignItems: "center", gap: "12px", minHeight: "150px", }); const iconContainer = document.createElement("div"); Object.assign(iconContainer.style, { width: "48px", height: "48px", borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: "8px" }); iconContainer.innerHTML = option.icon || ''; const title = document.createElement("div"); title.textContent = option.label; title.style.fontSize = "16px"; title.style.textAlign = "center"; let control = null; switch (option.type) { case "info": control = this.createInfoElement(option); break; case "input": control = this.createInputElement(option); break; case "toggle": control = this.createToggleButton(option); break; case "sub": control = this.createSubButton(option); break; case "slider": control = this.createSliderElement(option); break; case "click": control = this.createClickButton(option); } optionCard.appendChild(iconContainer); optionCard.appendChild(title); optionCard.appendChild(control); container.appendChild(optionCard); } setActiveCategory(category) { this.activeCategory = category; this.filterOptions(); // Update button styles this.menu.querySelectorAll('.category-btn').forEach(btn => { const btnCategory = btn.dataset.category; btn.style.background = btnCategory === category ? '#3B82F6' : 'rgba(55, 65, 81, 0.8)'; }); } filterOptions() { const gridContainer = document.getElementById('kxsMenuGrid'); if (gridContainer) { // Clear existing content gridContainer.innerHTML = ''; // Get unique options based on category and search term const displayedOptions = new Set(); this.sections.forEach(section => { if (this.activeCategory === 'ALL' || section.category === this.activeCategory) { section.options.forEach((option) => { // Create a unique key for each option if ((this.kxsClient.kxsNetwork["1"] === true) && X0.includes(option.label)) { return; } const optionKey = `${option.label}-${section.category}`; // Check if option matches search term const matchesSearch = this.searchTerm === '' || option.label.toLowerCase().includes(this.searchTerm) || section.category.toLowerCase().includes(this.searchTerm); if (!displayedOptions.has(optionKey) && matchesSearch) { displayedOptions.add(optionKey); this.createOptionCard(option, gridContainer); } }); } }); // Show a message if no options match the search if (displayedOptions.size === 0 && this.searchTerm !== '') { const noResultsMsg = document.createElement('div'); noResultsMsg.textContent = `No results found for "${this.searchTerm}"`; noResultsMsg.style.gridColumn = '1 / -1'; noResultsMsg.style.textAlign = 'center'; noResultsMsg.style.padding = '20px'; noResultsMsg.style.color = '#9CA3AF'; gridContainer.appendChild(noResultsMsg); } } } createGridContainer() { const gridContainer = document.createElement("div"); const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); Object.assign(gridContainer.style, { display: "grid", gridTemplateColumns: isMobile ? "repeat(4, 1fr)" : "repeat(3, 1fr)", gap: isMobile ? "5px" : "16px", padding: isMobile ? "2px" : "16px", gridAutoRows: isMobile ? "minmax(38px, auto)" : "minmax(150px, auto)", overflowY: "auto", overflowX: "hidden", // Prevent horizontal scrolling maxHeight: isMobile ? "28vh" : "calc(3 * 150px + 2 * 16px)", width: "100%", boxSizing: "border-box" // Include padding in width calculation }); gridContainer.id = "kxsMenuGrid"; this.menu.appendChild(gridContainer); } addOption(section, option) { section.options.push(option); // Store all options for searching this.allOptions.push(option); } addSection(category = "ALL") { const section = { options: [], category }; const sectionElement = document.createElement("div"); sectionElement.className = "menu-section"; sectionElement.style.display = this.activeCategory === "ALL" || this.activeCategory === category ? "block" : "none"; section.element = sectionElement; this.sections.push(section); this.menu.appendChild(sectionElement); return section; } createToggleButton(option) { // Créer le bouton principal const btn = document.createElement("button"); const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); // Créer l'indicateur (point vert/rouge) const indicator = document.createElement("div"); // Appliquer le style de base au bouton avec glassmorphism moderne btn.style.width = "100%"; btn.style.padding = isMobile ? "8px 14px" : "12px 18px"; btn.style.height = isMobile ? "32px" : "42px"; btn.style.background = "linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.08) 100%)"; btn.style.backdropFilter = "blur(16px) saturate(180%)"; btn.style['-webkit-backdrop-filter'] = "blur(16px) saturate(180%)"; btn.style.border = "1px solid rgba(255, 255, 255, 0.18)"; btn.style.borderRadius = "12px"; btn.style.color = "#ffffff"; btn.style.cursor = "pointer"; btn.style.transition = "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"; btn.style.fontSize = isMobile ? "11px" : "14px"; btn.style.fontWeight = "500"; btn.style.letterSpacing = "0.3px"; btn.style.fontFamily = "'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"; btn.style.position = "relative"; btn.style.display = "flex"; btn.style.alignItems = "center"; btn.style.justifyContent = "space-between"; btn.style.textAlign = "left"; btn.style.boxShadow = "0 4px 16px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.1)"; btn.style.textShadow = "0 1px 2px rgba(0, 0, 0, 0.3)"; btn.style.overflow = "hidden"; // Appliquer le style à l'indicateur avec effet glassmorphism indicator.style.width = isMobile ? "10px" : "12px"; indicator.style.height = isMobile ? "10px" : "12px"; indicator.style.borderRadius = "50%"; indicator.style.marginLeft = "12px"; indicator.style.flexShrink = "0"; indicator.style.transition = "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"; indicator.style.border = "2px solid rgba(255, 255, 255, 0.2)"; indicator.style.backdropFilter = "blur(8px)"; indicator.style['-webkit-backdrop-filter'] = "blur(8px)"; // Créer un conteneur pour le texte const textSpan = document.createElement("span"); textSpan.style.flexGrow = "1"; // Fonction pour mettre à jour l'apparence du bouton const updateButtonState = () => { const isEnabled = option.value; // Mettre à jour le texte textSpan.textContent = isEnabled ? "ENABLED" : "DISABLED"; // Mettre à jour le style du bouton avec glassmorphism btn.style.background = isEnabled ? "linear-gradient(135deg, rgba(74, 222, 128, 0.15) 0%, rgba(34, 197, 94, 0.12) 100%)" : "linear-gradient(135deg, rgba(248, 113, 113, 0.15) 0%, rgba(239, 68, 68, 0.12) 100%)"; btn.style.border = isEnabled ? "1px solid rgba(74, 222, 128, 0.3)" : "1px solid rgba(248, 113, 113, 0.3)"; btn.style.boxShadow = isEnabled ? "0 4px 16px rgba(74, 222, 128, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1)" : "0 4px 16px rgba(248, 113, 113, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1)"; // Mettre à jour l'indicateur avec effet glassmorphism indicator.style.background = isEnabled ? "radial-gradient(circle, #4ade80 0%, #22c55e 100%)" : "radial-gradient(circle, #f87171 0%, #ef4444 100%)"; indicator.style.boxShadow = isEnabled ? "0 0 12px rgba(74, 222, 128, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3)" : "0 0 12px rgba(248, 113, 113, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3)"; }; // Ajouter les éléments au DOM btn.appendChild(textSpan); btn.appendChild(indicator); // Définir l'état initial updateButtonState(); // Gérer les événements de survol avec glassmorphism amélioré btn.addEventListener("mouseenter", () => { const isEnabled = option.value; btn.style.transform = "translateY(-3px) scale(1.02)"; btn.style.backdropFilter = "blur(20px) saturate(200%)"; btn.style['-webkit-backdrop-filter'] = "blur(20px) saturate(200%)"; btn.style.boxShadow = isEnabled ? "0 8px 24px rgba(74, 222, 128, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 0 0 1px rgba(74, 222, 128, 0.2)" : "0 8px 24px rgba(248, 113, 113, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 0 0 1px rgba(248, 113, 113, 0.2)"; btn.style.border = isEnabled ? "1px solid rgba(74, 222, 128, 0.4)" : "1px solid rgba(248, 113, 113, 0.4)"; }); btn.addEventListener("mouseleave", () => { const isEnabled = option.value; btn.style.transform = "translateY(0) scale(1)"; btn.style.backdropFilter = "blur(16px) saturate(180%)"; btn.style['-webkit-backdrop-filter'] = "blur(16px) saturate(180%)"; btn.style.boxShadow = isEnabled ? "0 4px 16px rgba(74, 222, 128, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1)" : "0 4px 16px rgba(248, 113, 113, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1)"; btn.style.border = isEnabled ? "1px solid rgba(74, 222, 128, 0.3)" : "1px solid rgba(248, 113, 113, 0.3)"; }); // Gérer le clic btn.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); // Inverser la valeur const newValue = !option.value; option.value = newValue; // Mettre à jour l'apparence updateButtonState(); // Ajouter une animation de pulsation (optimized) btn.style.animation = `${DesignSystem.animation.pulse} 0.5s ease`; // Use event listener for animation end instead of setTimeout btn.addEventListener('animationend', function onAnimationEnd() { btn.style.animation = ''; btn.removeEventListener('animationend', onAnimationEnd); }, { once: true }); // Appeler le gestionnaire onChange if (option.onChange) { option.onChange(newValue); } return false; }); this.blockMousePropagation(btn); return btn; } createClickButton(option) { const btn = document.createElement("button"); const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); // Appliquer un style glassmorphism moderne btn.style.width = "100%"; btn.style.padding = isMobile ? "6px 8px" : "10px 12px"; btn.style.height = isMobile ? "32px" : "auto"; btn.style.minHeight = isMobile ? "32px" : "40px"; btn.style.background = "linear-gradient(135deg, rgba(66, 135, 245, 0.15) 0%, rgba(59, 118, 217, 0.12) 100%)"; btn.style.backdropFilter = "blur(16px) saturate(180%)"; btn.style['-webkit-backdrop-filter'] = "blur(16px) saturate(180%)"; btn.style.border = "1px solid rgba(66, 135, 245, 0.25)"; btn.style.borderRadius = "12px"; btn.style.color = "#ffffff"; btn.style.cursor = "pointer"; btn.style.transition = "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)"; btn.style.fontSize = isMobile ? "11px" : "13px"; btn.style.fontWeight = "500"; btn.style.letterSpacing = "0.2px"; btn.style.fontFamily = "'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"; btn.style.boxShadow = "0 4px 16px rgba(66, 135, 245, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1)"; btn.style.textShadow = "0 1px 2px rgba(0, 0, 0, 0.3)"; btn.style.position = "relative"; btn.style.overflow = "hidden"; btn.style.whiteSpace = "nowrap"; btn.style.textOverflow = "ellipsis"; btn.style.display = "flex"; btn.style.alignItems = "center"; btn.style.justifyContent = "center"; btn.style.textAlign = "center"; btn.textContent = option.label; // Ajouter les effets hover modernes btn.addEventListener("mouseenter", () => { btn.style.transform = "translateY(-3px) scale(1.02)"; btn.style.backdropFilter = "blur(20px) saturate(200%)"; btn.style['-webkit-backdrop-filter'] = "blur(20px) saturate(200%)"; btn.style.boxShadow = "0 8px 24px rgba(66, 135, 245, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 0 0 1px rgba(66, 135, 245, 0.2)"; btn.style.border = "1px solid rgba(66, 135, 245, 0.35)"; }); btn.addEventListener("mouseleave", () => { btn.style.transform = "translateY(0) scale(1)"; btn.style.backdropFilter = "blur(16px) saturate(180%)"; btn.style['-webkit-backdrop-filter'] = "blur(16px) saturate(180%)"; btn.style.boxShadow = "0 4px 16px rgba(66, 135, 245, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1)"; btn.style.border = "1px solid rgba(66, 135, 245, 0.25)"; }); // Ajouter l'effet actif btn.addEventListener("mousedown", () => { btn.style.transform = "translateY(-1px) scale(0.98)"; btn.style.boxShadow = "0 2px 8px rgba(66, 135, 245, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.2)"; }); btn.addEventListener("click", () => { var _a; // Add ripple effect for feedback const ripple = document.createElement("span"); Object.assign(ripple.style, { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", background: "rgba(255, 255, 255, 0.3)", borderRadius: "50%", width: "0", height: "0", animation: "ripple 0.6s linear", zIndex: "1" }); // Add ripple animation if it doesn't exist if (!document.getElementById("kxs-ripple-animation")) { const style = document.createElement("style"); style.id = "kxs-ripple-animation"; style.textContent = ` @keyframes ripple { to { width: 200px; height: 200px; opacity: 0; } } `; document.head.appendChild(style); } btn.appendChild(ripple); // Use event listener for animation end instead of setTimeout ripple.addEventListener('animationend', () => { ripple.remove(); }, { once: true }); (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, true); }); this.blockMousePropagation(btn); return btn; } addShiftListener() { // Gestionnaire pour la touche Shift (ouverture du menu) window.addEventListener("keydown", (event) => { if (event.key === "Shift" && event.location == 2) { this.clearMenu(); this.toggleMenuVisibility(); // Ensure options are displayed after loading this.filterOptions(); if (this.kxsClient.getUsername() === "debug") { let _ = `✨ KxsClient's Features\n\r`; this.allOptions.forEach(x => { var _a; _ += `* ${x.label} (${x.placeholder || "No description"}) - ${x.type}\n` + `${((_a = x.fields) === null || _a === void 0 ? void 0 : _a.map(field => { return `- Name: ${field.label}\n- Type: ${field.type}\n\n`; }).join("")) || "Not SubMenu Found\n"}\n`; }); navigator.clipboard.writeText(_); } } }); // Gestionnaire séparé pour la touche Échap avec capture en phase de capture // pour intercepter l'événement avant qu'il n'atteigne le jeu document.addEventListener("keydown", (event) => { if (event.key === "Escape" && this.isClientMenuVisible) { // Fermer le menu si la touche Échap est pressée et que le menu est visible this.toggleMenuVisibility(); // Empêcher la propagation ET l'action par défaut event.stopPropagation(); event.preventDefault(); // Arrêter la propagation de l'événement return false; } }, true); // true = phase de capture } createInputElement(option) { const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); // Create container for input with label effect const container = document.createElement("div"); Object.assign(container.style, { position: "relative", width: "100%", margin: "4px 0" }); // Create the input element const input = document.createElement("input"); input.type = "text"; input.value = String(option.value); if (option.placeholder) { input.placeholder = option.placeholder; } // Apply glassmorphism effect to input DesignSystem.applyGlassEffect(input, 'dark', { width: "100%", padding: isMobile ? "6px 8px" : "8px 10px", background: "rgba(17, 24, 39, 0.7)", borderRadius: "6px", color: "#FFAE00", // Gold color fontSize: isMobile ? "12px" : "14px", fontFamily: DesignSystem.fonts.primary, boxSizing: "border-box", border: "1px solid rgba(255, 174, 0, 0.3)", transition: `all ${DesignSystem.animation.normal} ease`, outline: "none" }); // Add focus effects input.addEventListener("focus", () => { input.style.boxShadow = "0 0 0 2px rgba(255, 174, 0, 0.2)"; input.style.border = "1px solid rgba(255, 174, 0, 0.5)"; }); input.addEventListener("blur", () => { input.style.boxShadow = "none"; input.style.border = "1px solid rgba(255, 174, 0, 0.3)"; }); input.addEventListener("change", () => { var _a; option.value = input.value; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, input.value); // Visual feedback on change (optimized) input.style.animation = "glow 0.5s ease"; // Use event listener for animation end instead of setTimeout input.addEventListener('animationend', function onAnimationEnd() { input.style.animation = ""; input.removeEventListener('animationend', onAnimationEnd); }, { once: true }); }); // Add glow animation if it doesn't exist if (!document.getElementById("kxs-input-animations")) { const style = document.createElement("style"); style.id = "kxs-input-animations"; style.textContent = ` @keyframes glow { 0% { box-shadow: 0 0 0 0 rgba(255, 174, 0, 0.4); } 50% { box-shadow: 0 0 10px 3px rgba(255, 174, 0, 0.4); } 100% { box-shadow: 0 0 0 0 rgba(255, 174, 0, 0.4); } } `; document.head.appendChild(style); } // Prevent key propagation to the game input.addEventListener("keydown", (e) => { e.stopPropagation(); }); input.addEventListener("keyup", (e) => { e.stopPropagation(); }); input.addEventListener("keypress", (e) => { e.stopPropagation(); }); this.blockMousePropagation(input); container.appendChild(input); return container; } createSliderElement(option) { // Create the slider using DesignSystem with proper event handling const sliderElement = DesignSystem.createSliderElement(option.min || 0, option.max || 100, Number(option.value), (newValue) => { var _a; option.value = newValue; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, newValue); }, true); // Prevent mouse events from propagating to the game const sliderInput = sliderElement.querySelector("input"); if (sliderInput) { this.blockMousePropagation(sliderInput, false); } // Apply consistent styling Object.assign(sliderElement.style, { width: "100%", margin: "5px 0" }); return sliderElement; } createInfoElement(option) { const info = document.createElement("div"); info.textContent = String(option.value); Object.assign(info.style, { color: "#b0b0b0", fontSize: "12px", fontStyle: "italic", marginTop: "2px", marginLeft: "6px", marginBottom: "2px", flex: "1 1 100%", whiteSpace: "pre-line" }); this.blockMousePropagation(info); return info; } // Crée un bouton pour ouvrir un sous-menu de configuration de mode createSubButton(option) { const btn = document.createElement("button"); const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); // Apply modern glassmorphism effect DesignSystem.applyGlassEffect(btn, 'dark', { width: "100%", padding: isMobile ? "6px 8px" : "12px 16px", height: isMobile ? "32px" : "auto", background: "linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(37, 99, 235, 0.25) 100%)", backdropFilter: "blur(12px) saturate(180%)", WebkitBackdropFilter: "blur(12px) saturate(180%)", border: "1px solid rgba(59, 130, 246, 0.3)", borderRadius: isMobile ? "8px" : "12px", color: "#ffffff", cursor: "pointer", transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)", fontSize: isMobile ? "11px" : "14px", fontWeight: "600", letterSpacing: "0.5px", fontFamily: DesignSystem.fonts.primary, boxShadow: "0 4px 16px rgba(59, 130, 246, 0.2), 0 2px 8px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1)", textShadow: "0 1px 2px rgba(0, 0, 0, 0.3)", position: "relative", overflow: "hidden" }); // Add a subtle icon to indicate configuration btn.innerHTML = ` CONFIGURE `; // Add sophisticated hover effects btn.addEventListener("mouseenter", () => { btn.style.transform = "translateY(-2px) scale(1.02)"; btn.style.backdropFilter = "blur(16px) saturate(200%)"; btn.style.setProperty('-webkit-backdrop-filter', 'blur(16px) saturate(200%)'); btn.style.boxShadow = "0 8px 24px rgba(59, 130, 246, 0.35), 0 4px 12px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.2)"; btn.style.border = "1px solid rgba(59, 130, 246, 0.5)"; }); btn.addEventListener("mouseleave", () => { btn.style.transform = "translateY(0) scale(1)"; btn.style.backdropFilter = "blur(12px) saturate(180%)"; btn.style.backdropFilter = "blur(12px) saturate(180%)"; btn.style.boxShadow = "0 4px 16px rgba(59, 130, 246, 0.2), 0 2px 8px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1)"; btn.style.border = "1px solid rgba(59, 130, 246, 0.3)"; }); // Add active effect btn.addEventListener("mousedown", () => { btn.style.transform = "translateY(0) scale(0.98)"; btn.style.boxShadow = "0 2px 8px rgba(59, 130, 246, 0.3), 0 1px 4px rgba(0, 0, 0, 0.2), inset 0 2px 4px rgba(0, 0, 0, 0.1)"; }); // Variables pour le sous-menu let subMenuContainer = null; // Sauvegarde des éléments originaux à masquer/afficher let originalElements = []; let isSubMenuOpen = false; // Gestionnaire d'événement pour ouvrir le sous-menu btn.addEventListener("click", () => { // Si aucun champ n'est défini, ne rien faire if (!option.fields || option.fields.length === 0) { if (option.onChange) { option.onChange(option.value); } return; } // Si le sous-menu est déjà ouvert, le fermer if (isSubMenuOpen) { this.closeSubMenu(); return; } // Trouver tous les éléments principaux à masquer originalElements = []; const allSections = document.querySelectorAll('.menu-section'); allSections.forEach(section => { originalElements.push(section); section.style.display = 'none'; }); // Masquer aussi le conteneur de la grille const grid = document.getElementById('kxsMenuGrid'); if (grid) { originalElements.push(grid); grid.style.display = 'none'; } // Créer le conteneur du sous-menu subMenuContainer = document.createElement("div"); subMenuContainer.id = "kxs-submenu"; subMenuContainer.className = "kxs-submenu-container"; Object.assign(subMenuContainer.style, { width: "100%", padding: "10px 0", boxSizing: "border-box", overflowY: "auto", background: "rgba(17, 24, 39, 0.95)" }); this.blockMousePropagation(subMenuContainer); // Créer l'en-tête du sous-menu const subMenuHeader = document.createElement("div"); Object.assign(subMenuHeader.style, { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: isMobile ? "10px" : "15px", paddingBottom: isMobile ? "5px" : "10px", borderBottom: "1px solid rgba(255, 255, 255, 0.1)", paddingLeft: isMobile ? "10px" : "15px", paddingRight: isMobile ? "10px" : "15px", width: "100%", boxSizing: "border-box" }); this.blockMousePropagation(subMenuHeader); // Bouton de retour const backBtn = document.createElement("button"); backBtn.innerHTML = ` Back`; Object.assign(backBtn.style, { background: "none", border: "none", color: "#fff", cursor: "pointer", display: "flex", alignItems: "center", gap: "6px", padding: "5px", fontSize: isMobile ? "12px" : "14px" }); this.blockMousePropagation(backBtn); // Titre du sous-menu const subMenuTitle = document.createElement("h3"); subMenuTitle.textContent = option.label; Object.assign(subMenuTitle.style, { margin: "0", color: "#fff", fontSize: isMobile ? "16px" : "20px", fontWeight: "bold", textAlign: "center", flex: "1" }); this.blockMousePropagation(subMenuTitle); // Ajouter l'événement au bouton retour pour fermer le sous-menu backBtn.addEventListener("click", () => { this.closeSubMenu(); }); // Assembler l'en-tête subMenuHeader.appendChild(backBtn); subMenuHeader.appendChild(subMenuTitle); subMenuHeader.appendChild(document.createElement("div")); // Espace vide pour l'équilibre subMenuContainer.appendChild(subMenuHeader); // Créer la grille pour les options const optionsGrid = document.createElement("div"); Object.assign(optionsGrid.style, { display: "grid", gridTemplateColumns: isMobile ? "repeat(2, 1fr)" : "repeat(3, 1fr)", gap: isMobile ? "8px" : "16px", padding: isMobile ? "4px" : "16px", gridAutoRows: isMobile ? "minmax(100px, auto)" : "minmax(150px, auto)", width: "100%", boxSizing: "border-box" }); this.blockMousePropagation(optionsGrid); // Créer les cartes pour chaque option option.fields.forEach(mod => { this.createModCard(mod, optionsGrid); }); subMenuContainer.appendChild(optionsGrid); // Ajouter le sous-menu au menu principal this.menu.appendChild(subMenuContainer); isSubMenuOpen = true; // Définir la méthode pour fermer le sous-menu this.closeSubMenu = () => { // Supprimer le sous-menu if (subMenuContainer && subMenuContainer.parentElement) { subMenuContainer.parentElement.removeChild(subMenuContainer); } // Réafficher tous les éléments originaux originalElements.forEach(el => { if (el.id === 'kxsMenuGrid') { el.style.display = 'grid'; } else { el.style.display = 'block'; } }); // Réinitialiser les états this.filterOptions(); // S'assurer que les options sont correctement filtrées subMenuContainer = null; isSubMenuOpen = false; }; // Appeler le callback si défini if (option.onChange) { option.onChange(option.value); } }); this.blockMousePropagation(btn); return btn; } // Crée une carte pour un mod dans le sous-menu createModCard(mod, container) { const modCard = document.createElement("div"); const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); Object.assign(modCard.style, { background: "rgba(31, 41, 55, 0.8)", borderRadius: "10px", padding: isMobile ? "10px" : "16px", display: "flex", flexDirection: "column", alignItems: "center", gap: isMobile ? "8px" : "12px", minHeight: isMobile ? "100px" : "150px", }); // Icône const iconContainer = document.createElement("div"); Object.assign(iconContainer.style, { width: isMobile ? "32px" : "48px", height: isMobile ? "32px" : "48px", borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: isMobile ? "4px" : "8px" }); iconContainer.innerHTML = mod.icon || ''; // Titre const title = document.createElement("div"); title.textContent = mod.label; title.style.fontSize = isMobile ? "14px" : "16px"; title.style.textAlign = "center"; // Contrôle selon le type let control = null; switch (mod.type) { case "info": control = this.createInfoElement(mod); break; case "input": control = this.createInputElement(mod); break; case "toggle": control = this.createToggleButton(mod); break; case "slider": control = this.createSliderElement(mod); break; case "click": control = this.createClickButton(mod); } modCard.appendChild(iconContainer); modCard.appendChild(title); if (control) { modCard.appendChild(control); } container.appendChild(modCard); this.blockMousePropagation(modCard); } addDragListeners() { this.menu.addEventListener('mousedown', (e) => { // Ne pas arrêter la propagation si l'événement vient d'un élément interactif if (e.target instanceof HTMLElement && e.target.matches("input, select, button, svg, path")) { // Laisser l'événement se propager aux éléments interactifs return; } // Empêcher la propagation de l'événement mousedown vers la page web e.stopPropagation(); // Activer le drag & drop seulement si on clique sur une zone non interactive if (e.target instanceof HTMLElement && !e.target.matches("input, select, button, svg, path")) { this.isDragging = true; const rect = this.menu.getBoundingClientRect(); this.dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top }; this.menu.style.cursor = "grabbing"; } }); // Optimized: use throttled mousemove for better performance let mouseMoveThrottle = false; document.addEventListener('mousemove', (e) => { if (this.isDragging && !mouseMoveThrottle) { mouseMoveThrottle = true; requestAnimationFrame(() => { const x = e.clientX - this.dragOffset.x; const y = e.clientY - this.dragOffset.y; this.menu.style.transform = 'none'; this.menu.style.left = `${x}px`; this.menu.style.top = `${y}px`; mouseMoveThrottle = false; }); } }); document.addEventListener('mouseup', (e) => { // Arrêter le drag & drop const wasDragging = this.isDragging; this.isDragging = false; this.menu.style.cursor = "grab"; // Empêcher la propagation de l'événement mouseup vers la page web // seulement si l'événement vient du menu et n'est pas un élément interactif if (this.menu.contains(e.target)) { if (wasDragging || !(e.target instanceof HTMLElement && e.target.matches("input, select, button, svg, path"))) { e.stopPropagation(); } } }); } toggleMenuVisibility() { this.isClientMenuVisible = !this.isClientMenuVisible; // Mettre à jour la propriété publique en même temps this.isOpen = this.isClientMenuVisible; if (this.kxsClient.isNotifyingForToggleMenu) { this.kxsClient.nm.showNotification(this.isClientMenuVisible ? "Opening menu..." : "Closing menu...", "info", 1100); } this.menu.style.display = this.isClientMenuVisible ? "block" : "none"; // If opening the menu, make sure to display options if (this.isClientMenuVisible) { this.filterOptions(); } // Notifier immédiatement tous les callbacks enregistrés this.onMenuToggle.forEach(callback => { try { callback(); } catch (error) { return; } }); } destroy() { // Remove global event listeners document.removeEventListener('mousemove', this.mouseMoveListener); document.removeEventListener('mouseup', this.mouseUpListener); // Supprimer tous les écouteurs d'événements keydown du document // Nous ne pouvons pas supprimer directement l'écouteur anonyme, mais ce n'est pas grave // car la vérification isClientMenuVisible empêchera toute action une fois le menu détruit // Remove all event listeners from menu elements const removeAllListeners = (element) => { var _a; const clone = element.cloneNode(true); (_a = element.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(clone, element); }; // Clean up all buttons and inputs in the menu this.menu.querySelectorAll('button, input').forEach(element => { removeAllListeners(element); }); // Remove the menu from DOM this.menu.remove(); // Clear all sections this.sections.forEach(section => { if (section.element) { removeAllListeners(section.element); section.element.remove(); delete section.element; } section.options = []; }); this.sections = []; // Reset all class properties this.isClientMenuVisible = false; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.activeCategory = "ALL"; // Clear references this.menu = null; this.kxsClient = null; } getMenuVisibility() { return this.isClientMenuVisible; } } ;// ./src/SERVER/Ping.ts class PingTest { constructor() { this.ping = 0; this.ws = null; this.sendTime = 0; this.retryCount = 0; this.isConnecting = false; this.isWebSocket = true; this.url = ""; this.region = ""; this.hasPing = false; this.reconnectTimer = null; this.keepAliveTimer = null; this.connectionCheckTimer = null; this.ptcDataBuf = new ArrayBuffer(1); this.waitForServerSelectElements(); this.startKeepAlive(); } startKeepAlive() { // Annuler l'ancien timer si existant if (this.keepAliveTimer) { clearInterval(this.keepAliveTimer); } this.keepAliveTimer = setInterval(() => { var _a, _b, _c; if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) { this.ws.send(this.ptcDataBuf); } 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) { // Redémarrer la connexion si elle est fermée this.restart(); } }, 5000); // envoie toutes les 5s } waitForServerSelectElements() { const checkInterval = setInterval(() => { const teamSelect = document.getElementById("team-server-select"); const mainSelect = document.getElementById("server-select-main"); const selectedValue = (teamSelect === null || teamSelect === void 0 ? void 0 : teamSelect.value) || (mainSelect === null || mainSelect === void 0 ? void 0 : mainSelect.value); if ((teamSelect || mainSelect) && selectedValue) { clearInterval(checkInterval); this.setServerFromDOM(); this.attachRegionChangeListener(); } }, 100); // Vérifie toutes les 100ms } setServerFromDOM() { const selectedServer = this.detectSelectedServer(); if (!selectedServer) return; const { region, url } = selectedServer; this.region = region; this.url = `wss://${url}/ptc`; this.start(); } detectSelectedServer() { const currentUrl = window.location.href; const isSpecialUrl = /\/#\w+/.test(currentUrl); const teamSelectElement = document.getElementById("team-server-select"); const mainSelectElement = document.getElementById("server-select-main"); const region = isSpecialUrl && teamSelectElement ? teamSelectElement.value : (mainSelectElement === null || mainSelectElement === void 0 ? void 0 : mainSelectElement.value) || "NA"; const servers = [ { region: "NA", url: "usr.mathsiscoolfun.com:8001" }, { region: "EU", url: "eur.mathsiscoolfun.com:8001" }, { region: "Asia", url: "asr.mathsiscoolfun.com:8001" }, { region: "SA", url: "sa.mathsiscoolfun.com:8001" }, ]; const selectedServer = servers.find((s) => s.region.toUpperCase() === region.toUpperCase()); if (!selectedServer) return undefined; return selectedServer; } attachRegionChangeListener() { const teamSelectElement = document.getElementById("team-server-select"); const mainSelectElement = document.getElementById("server-select-main"); const onChange = () => { const selectedServer = this.detectSelectedServer(); if (!selectedServer) return; const { region } = selectedServer; if (region !== this.region) { this.restart(); } }; teamSelectElement === null || teamSelectElement === void 0 ? void 0 : teamSelectElement.addEventListener("change", onChange); mainSelectElement === null || mainSelectElement === void 0 ? void 0 : mainSelectElement.addEventListener("change", onChange); } start() { if (this.isConnecting) return; this.isConnecting = true; this.startWebSocketPing(); // Vérifier régulièrement l'état de la connexion this.startConnectionCheck(); } startConnectionCheck() { // Annuler l'ancien timer si existant if (this.connectionCheckTimer) { clearInterval(this.connectionCheckTimer); } // Vérifier l'état de la connexion toutes les 10 secondes this.connectionCheckTimer = setInterval(() => { // Si on n'a pas de ping valide ou que la connexion est fermée, on tente de reconnecter if (!this.hasPing || !this.ws || this.ws.readyState !== WebSocket.OPEN) { this.restart(); } }, 10000); } startWebSocketPing() { if (this.ws || !this.url) return; const ws = new WebSocket(this.url); ws.binaryType = "arraybuffer"; ws.onopen = () => { this.ws = ws; this.retryCount = 0; this.isConnecting = false; this.sendPing(); setTimeout(() => { var _a; if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) !== WebSocket.OPEN) { this.restart(); } }, 3000); // 3s pour sécuriser }; ws.onmessage = () => { this.hasPing = true; const elapsed = (Date.now() - this.sendTime) / 1e3; this.ping = Math.round(elapsed * 1000); setTimeout(() => this.sendPing(), 1000); }; ws.onerror = (error) => { this.ping = 0; this.hasPing = false; this.retryCount++; // Tentative immédiate mais avec backoff exponentiel const retryDelay = Math.min(1000 * Math.pow(2, this.retryCount - 1), 10000); // Annuler tout timer de reconnexion existant if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); } this.reconnectTimer = setTimeout(() => { this.ws = null; // S'assurer que l'ancienne connexion est effacée this.startWebSocketPing(); }, retryDelay); }; ws.onclose = (event) => { this.hasPing = false; this.ws = null; this.isConnecting = false; // Tentative de reconnexion après une fermeture if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); } this.reconnectTimer = setTimeout(() => { this.start(); }, 2000); // Attendre 2 secondes avant de reconnecter }; } sendPing() { var _a, _b; if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.sendTime = Date.now(); this.ws.send(this.ptcDataBuf); } 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) { // Si la WebSocket est fermée au moment d'envoyer le ping, on tente de reconnecter this.restart(); } } stop() { // Annuler tous les timers if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } if (this.keepAliveTimer) { clearInterval(this.keepAliveTimer); this.keepAliveTimer = null; } if (this.connectionCheckTimer) { clearInterval(this.connectionCheckTimer); this.connectionCheckTimer = null; } if (this.ws) { this.ws.onclose = null; this.ws.onerror = null; this.ws.onmessage = null; this.ws.onopen = null; this.ws.close(); this.ws = null; } this.isConnecting = false; this.retryCount = 0; this.hasPing = false; } restart() { this.stop(); setTimeout(() => { this.setServerFromDOM(); }, 500); // Petit délai pour éviter les problèmes de rebond } /** * Retourne le ping actuel. Ne touche jamais à la websocket ici ! * Si le ping n'est pas dispo, retourne -1 (jamais null). * La reconnexion doit être gérée ailleurs (timer, event, etc). */ getPingResult() { if (this.ws && this.ws.readyState === WebSocket.OPEN && this.hasPing) { return { region: this.region, ping: this.ping, }; } else { // Si on détecte un problème ici, planifier une reconnexion if (!this.reconnectTimer && (!this.ws || this.ws.readyState !== WebSocket.CONNECTING)) { this.reconnectTimer = setTimeout(() => this.restart(), 1000); } return { region: this.region, ping: -1, // -1 indique que le ping n'est pas dispo, mais jamais null }; } } } ;// ./src/HUD/ClientHUD.ts class KxsClientHUD { constructor(kxsClient) { this.healthAnimations = []; this.lastHealthValue = 100; this.hudOpacityObservers = []; this.weaponBorderObservers = []; this.ctrlFocusTimer = null; this.killFeedObserver = null; this.kxsClient = kxsClient; this.frameCount = 0; this.fps = 0; this.kills = 0; this.isMenuVisible = true; this.pingManager = new PingTest(); this.allDivToHide = [ '#ui-medical-interactive > div', '#ui-ammo-interactive > div', '#ui-weapon-container .ui-weapon-switch', '#ui-killfeed', '#ui-killfeed-contents', '.killfeed-div', '.killfeed-text', '#ui-kill-leader-container', '#ui-kill-leader-wrapper', '#ui-kill-leader-name', '#ui-kill-leader-icon', '#ui-kill-leader-count', '#ui-leaderboard-wrapper', '#ui-leaderboard', '#ui-leaderboard-alive', '#ui-leaderboard-alive-faction', '.ui-leaderboard-header', '#ui-kill-counter-wrapper', '#ui-kill-counter', '.ui-player-kills', '.ui-kill-counter-header', '#ui-bottom-center-right', '#ui-armor-helmet', '#ui-armor-chest', '#ui-armor-backpack', '.ui-armor-counter', '.ui-armor-counter-inner', '.ui-armor-level', '.ui-armor-image', '.ui-loot-image', ]; if (this.kxsClient.isPingVisible) { this.initCounter("ping", "Ping", "45ms"); } if (this.kxsClient.isFpsVisible) { this.initCounter("fps", "FPS", "60"); } if (this.kxsClient.isKillsVisible) { this.initCounter("kills", "Kills", "0"); } if (this.kxsClient.isGunOverlayColored) { this.toggleWeaponBorderHandler(); } this.updateCountersDraggableState(); this.startUpdateLoop(); this.escapeMenu(); this.initFriendDetector(); if (this.kxsClient.isKillFeedBlint) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', this.initKillFeed); } else { this.initKillFeed(); } } if (this.kxsClient.customCrosshair !== null) { this.loadCustomCrosshair(); } this.setupCtrlFocusModeListener(); } setupCtrlFocusModeListener() { // Déterminer la plateforme une seule fois à l'initialisation const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform); // Utiliser un flag pour suivre l'état des touches let modifierKeyPressed = false; document.addEventListener('keydown', (e) => { // Détecter si la touche modificatrice est pressée (Command sur macOS, Ctrl sur Windows/Linux) if ((isMac && e.key === 'Meta') || (!isMac && e.key === 'Control')) { modifierKeyPressed = true; } // Activer le mode focus seulement si F est pressé pendant que la touche modificatrice est déjà enfoncée if (modifierKeyPressed && e.code === 'KeyF' && this.kxsClient.isFocusModeEnabled) { e.preventDefault(); // Empêcher le comportement par défaut (recherche) this.kxsClient.currentFocusModeState = !this.kxsClient.currentFocusModeState; this.kxsClient.hud.toggleFocusMode(); this.kxsClient.nm.showNotification("Focus mode toggled", "info", 1200); } }); // Réinitialiser le flag quand la touche modificatrice est relâchée document.addEventListener('keyup', (e) => { if ((isMac && e.key === 'Meta') || (!isMac && e.key === 'Control')) { modifierKeyPressed = false; } }); } initFriendDetector() { // Initialize friends list let all_friends = this.kxsClient.all_friends.split(',') || []; if (all_friends.length >= 1) { // Create a cache for detected friends // Structure will be: { "friendName": timestamp } const friendsCache = {}; // Cache duration in milliseconds (4 minutes = 240000 ms) const cacheDuration = 4 * 60 * 1000; // Select the element containing kill feeds const killfeedContents = document.querySelector('#ui-killfeed-contents'); if (killfeedContents) { // Keep track of last seen content for each div const lastSeenContent = { "ui-killfeed-0": "", "ui-killfeed-1": "", "ui-killfeed-2": "", "ui-killfeed-3": "", "ui-killfeed-4": "", "ui-killfeed-5": "" }; // Function to check if a friend is in the text with cache management const checkForFriends = (text, divId) => { // If the text is identical to the last seen, ignore // @ts-ignore if (text === lastSeenContent[divId]) return; // Update the last seen content // @ts-ignore lastSeenContent[divId] = text; // Ignore empty messages if (!text.trim()) return; // Current timestamp const currentTime = Date.now(); // Check if a friend is mentioned for (let friend of all_friends) { if (friend !== "" && text.includes(friend)) { // Check if the friend is in the cache and if the cache is still valid // @ts-ignore const lastSeen = friendsCache[friend]; if (!lastSeen || (currentTime - lastSeen > cacheDuration)) { // Update the cache // @ts-ignore friendsCache[friend] = currentTime; // Display notification this.kxsClient.nm.showNotification(`[FriendDetector] ${friend} is in this game`, "info", 2300); } break; } } }; // Function to check all kill feeds const checkAllKillfeeds = () => { all_friends = this.kxsClient.all_friends.split(',') || []; for (let i = 0; i <= 5; i++) { const divId = `ui-killfeed-${i}`; const killDiv = document.getElementById(divId); if (killDiv) { const textElement = killDiv.querySelector('.killfeed-text'); if (textElement && textElement.textContent) { checkForFriends(textElement.textContent, divId); } } } }; // Observe style or text changes in the entire container const observer = new MutationObserver(() => { checkAllKillfeeds(); }); // Start observing with a configuration that detects all changes observer.observe(killfeedContents, { childList: true, // Observe changes to child elements subtree: true, // Observe the entire tree characterData: true, // Observe text changes attributes: true // Observe attribute changes (like style/opacity) }); // Check current content immediately checkAllKillfeeds(); } else { this.kxsClient.logger.error("Killfeed-contents element not found"); } } } initKillFeed() { this.applyCustomStyles(); this.setupObserver(); } toggleKillFeed() { if (this.kxsClient.isKillFeedBlint) { this.initKillFeed(); // <-- injecte le CSS custom et observer } else { this.resetKillFeed(); // <-- supprime styles et contenu } } /** * Réinitialise le Kill Feed à l'état par défaut (vide) */ /** * Supprime tous les styles custom KillFeed injectés par applyCustomStyles */ resetKillFeedStyles() { // Supprime tous les