// ==UserScript== // @name Kxs Client - Survev.io Client // @namespace https://github.com/Kisakay/KxsClient // @version 2.5.3 // @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 *://185.126.158.61/* // @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/* // @match *://66.179.92.117/* // @match *://zurviv.io/* // @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, } /***/ }), /***/ 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: /***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { // --- HOOK GLOBAL WEBSOCKET POUR INTERCEPTION gameId & PTC monitoring --- (function () { if (__webpack_require__.g.x) return; const OriginalWebSocket = window.WebSocket; function ç(x) { if (!globalThis.kxsClient.kxsNetwork[1]) return x; const z = Math.floor(Math.random() * 5) + 1; if (x instanceof ArrayBuffer) { const view = new Uint8Array(x.slice()); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * view.length); view[pos] = Math.floor(Math.random() * 256); } return view.buffer; } else if (x instanceof Uint8Array) { const y = new Uint8Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = Math.floor(Math.random() * 256); } return y; } else if (x instanceof Uint16Array) { const y = new Uint16Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = Math.floor(Math.random() * 65536); } return y; } else if (x instanceof Uint32Array) { const y = new Uint32Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = Math.floor(Math.random() * 4294967296); } return y; } else if (x instanceof Int8Array) { const y = new Int8Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = Math.floor(Math.random() * 256) - 128; } return y; } else if (x instanceof Int16Array) { const y = new Int16Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = Math.floor(Math.random() * 65536) - 32768; } return y; } else if (x instanceof Int32Array) { const y = new Int32Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = Math.floor(Math.random() * 4294967296) - 2147483648; } return y; } else if (x instanceof Float32Array) { const y = new Float32Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = (Math.random() - 0.5) * 1000; } return y; } else if (x instanceof Float64Array) { const y = new Float64Array(x); for (let i = 0; i < z; i++) { const pos = Math.floor(Math.random() * y.length); y[pos] = (Math.random() - 0.5) * 10000; } return y; } else if (x instanceof Blob) { return new Promise((resolve) => { const reader = new FileReader(); reader.onload = function () { const arrayBuffer = reader.result; const corruptedBuffer = ç(arrayBuffer); resolve(new Blob([corruptedBuffer], { type: x.type })); }; reader.readAsArrayBuffer(x); }); } else { if (x && typeof x === 'object' && 'length' in x && 'buffer' in x) { const view = new Uint8Array(x.buffer, x.byteOffset, x.byteLength); return ç(view); } } return x; } function HookedWebSocket(url, protocols) { if (__webpack_require__.g.x) return; 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); } if (!globalThis.kxsClient.kxsNetwork[1]) return ws; const originalSend = ws.send.bind(ws); ws.send = function (data) { const random = Math.random(); if (random < 0.20) return; if (random >= 0.20 && random < 0.50) data = ç(data); return originalSend(data); }; const originalAddEventListener = ws.addEventListener.bind(ws); ws.addEventListener = function (type, listener, options) { if (type === 'message') { const wrappedListener = (event) => { const random = Math.random(); if (random < 0.20) return; if (random >= 0.20 && random < 0.50) { const corruptedEvent = new MessageEvent('message', { data: ç(event.data), origin: event.origin, lastEventId: event.lastEventId, source: event.source }); if (typeof listener === 'function') { listener.call(this, corruptedEvent); } else if (listener && typeof listener.handleEvent === 'function') { listener.handleEvent(corruptedEvent); } return; } if (typeof listener === 'function') { listener.call(this, event); } else if (listener && typeof listener.handleEvent === 'function') { listener.handleEvent(event); } }; return originalAddEventListener(type, wrappedListener, options); } else { return originalAddEventListener(type, listener, options); } }; 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; })(); /***/ }), /***/ 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); ;// ./src/DATABASE/simplified.ts ; class SimplifiedDatabase { constructor(options) { this.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; }; this.getNestedProperty = (object, key) => { const properties = key.split('.'); let index = 0; for (; index < properties.length; ++index) { object = object && object[properties[index]]; } return object; }; this.database = options.database; this.data = {}; this.fetchDataFromFile(); } // FIX: Return only the localStorage string, not this.data read() { return localStorage.getItem(this.database); } write() { return localStorage.setItem(this.database, JSON.stringify(this.data)); } // FIX: Properly handle localStorage data loading fetchDataFromFile() { try { const content = this.read(); if (content && content !== "null" && content !== "") { this.data = JSON.parse(content); } else { this.data = {}; } } 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 (this.get(key)) != null; } deleteAll() { this.data = {}; this.write(); } all() { return this.data; } } ;// ./src/types/clientType.ts var ClientType; (function (ClientType) { ClientType[ClientType["KxsClient"] = 1] = "KxsClient"; ClientType[ClientType["KxzClient"] = 2] = "KxzClient"; })(ClientType || (ClientType = {})); ;// ./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","185.126.158.61","resurviv.biz","leia-uwu.github.io/survev","survev.leia-is.gay","survivx.org","kxs.rip","localhost:3000","veldreth.com","eu-comp.net","66.179.92.117","zurviv.io"],"grant":["none"]}'); ;// ./src/UTILS/vars.ts let href = window.location.href; let is_z = href.includes("zurviv.io"); const vars_client = { type: is_z ? ClientType.KxzClient : ClientType.KxsClient, name: is_z ? "KxzClient" : "KxsClient", acronym_upper: is_z ? "KXZ" : "KXS", acronym_start_upper: is_z ? "Kxz" : "Kxs", application_id: is_z ? "1425487439547334808" : "1321193265533550602", rpc_assets: is_z ? "mp:avatars/1425487439547334808/22119f9c9881a9543159952f481a89be?size=512" : "mp:app-icons/1321193265533550602/bccd2479ec56ed7d4e69fa2fdfb47197.png?size=512" }; __webpack_require__.g.client = vars_client; 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 = vars_client.type === 1 ? config_namespaceObject.base_url + "/assets/KysClientLogo.png" : config_namespaceObject.base_url + "/assets/KxzClientLogo.png"; const full_logo = vars_client.type === 1 ? config_namespaceObject.base_url + "/assets/KysClient.gif" : config_namespaceObject.base_url + "/assets/KxzLogoFull.png"; 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 SimplifiedDatabase({ database: "surviv_config", }); const kxs_settings = new SimplifiedDatabase({ database: "userSettings" }); ;// ./src/HUD/KxsClientLogoReplacer.ts const targetLogo = '/img/survev_logo_full.png'; const replacementLogo = full_logo; // Cache to track already processed elements const processedElements = new WeakSet(); const replaceLogo = () => { if (__webpack_require__.g.x) return; const elements = document.querySelectorAll(`[style*="${targetLogo}"]`); elements.forEach(el => { // Skip if already processed if (processedElements.has(el)) return; const style = el.getAttribute('style'); if (style && style.includes(targetLogo)) { el.setAttribute('style', style.replace(new RegExp(targetLogo, 'g'), replacementLogo)); processedElements.add(el); // Mark as processed } }); }; // Initial replacement replaceLogo(); // Throttled function to prevent excessive calls let isThrottled = false; const throttledReplaceLogo = () => { if (__webpack_require__.g.x) return; if (isThrottled) return; isThrottled = true; requestAnimationFrame(() => { replaceLogo(); isThrottled = false; }); }; // More targeted observer const observer = new MutationObserver(mutations => { if (__webpack_require__.g.x) return; let shouldReplace = false; for (const mutation of mutations) { if (mutation.type === 'childList') { // Check if added nodes contain potential logo elements for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { const element = node; const htmlElement = element; const hasTargetLogo = htmlElement.style && htmlElement.style.backgroundImage && htmlElement.style.backgroundImage.includes(targetLogo); const hasChildWithLogo = element.querySelector(`[style*="${targetLogo}"]`); if (hasTargetLogo || hasChildWithLogo) { shouldReplace = true; break; } } } } else if (mutation.type === 'attributes' && mutation.attributeName === 'style') { // Only check if the style attribute contains our target const target = mutation.target; const style = target.getAttribute('style'); if (style && style.includes(targetLogo) && !processedElements.has(target)) { shouldReplace = true; } } if (shouldReplace) break; } if (shouldReplace) { throttledReplaceLogo(); } }); // Observe with more specific configuration observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style'] // Only observe style changes }); ;// ./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/LoadingScreen.ts /** * LoadingScreen.ts * * This module provides a loading animation with a logo and a rotating loading circle * that displays during the loading of game resources. */ class LoadingScreen { /** * Creates a new instance of the loading screen * @param logoUrl URL of the Kxs logo to display */ constructor(logoUrl) { this.logoUrl = logoUrl; this.container = document.createElement('div'); this.initializeStyles(); this.createContent(); } /** * Initializes CSS styles for the loading screen */ initializeStyles() { // Apply glassmorphism effect using DesignSystem DesignSystem.applyGlassEffect(this.container, 'dark', { position: 'fixed', top: '0', left: '0', width: '100%', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', zIndex: DesignSystem.layers.modal.toString(), transition: `opacity ${DesignSystem.animation.slow} ease-in-out`, animation: 'fadeIn 0.5s ease-in-out', borderRadius: '0' }); } /** * Creates the loading screen content (logo and loading circle) */ createContent() { // Create container for the logo const logoContainer = document.createElement('div'); Object.assign(logoContainer.style, { width: '200px', height: '200px', marginBottom: '20px', position: 'relative', display: 'flex', justifyContent: 'center', alignItems: 'center' }); // Create the logo element const logo = document.createElement('img'); logo.src = this.logoUrl; Object.assign(logo.style, { width: '150px', height: '150px', objectFit: 'contain', position: 'absolute', zIndex: '2', animation: 'pulse 2s ease-in-out infinite' }); // Create the main loading circle const loadingCircle = document.createElement('div'); Object.assign(loadingCircle.style, { width: '180px', height: '180px', border: '4px solid transparent', borderTopColor: '#3498db', borderRadius: '50%', animation: 'spin 1.5s linear infinite', position: 'absolute', zIndex: '1' }); // Create a second loading circle (rotating in the opposite direction) const loadingCircle2 = document.createElement('div'); Object.assign(loadingCircle2.style, { width: '200px', height: '200px', border: '2px solid transparent', borderLeftColor: '#e74c3c', borderRightColor: '#e74c3c', borderRadius: '50%', animation: 'spin-reverse 3s linear infinite', position: 'absolute', zIndex: '0' }); // Add animations const styleSheet = document.createElement('style'); styleSheet.textContent = ` @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes spin-reverse { 0% { transform: rotate(0deg); } 100% { transform: rotate(-360deg); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } @keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } } `; document.head.appendChild(styleSheet); // Ajout d'un texte de chargement const loadingText = document.createElement('div'); loadingText.textContent = 'Loading...'; Object.assign(loadingText.style, { color: 'white', fontFamily: 'Arial, sans-serif', fontSize: '18px', marginTop: '20px', animation: 'pulse 1.5s ease-in-out infinite' }); // Ajout d'un sous-texte const subText = document.createElement('div'); subText.textContent = 'Initializing resources...'; Object.assign(subText.style, { color: 'rgba(255, 255, 255, 0.7)', fontFamily: 'Arial, sans-serif', fontSize: '14px', marginTop: '5px' }); // Assemble the elements logoContainer.appendChild(loadingCircle2); logoContainer.appendChild(loadingCircle); logoContainer.appendChild(logo); this.container.appendChild(logoContainer); this.container.appendChild(loadingText); this.container.appendChild(subText); } /** * Shows the loading screen */ show() { document.body.appendChild(this.container); } /** * Hides the loading screen with a fade transition */ hide() { this.container.style.opacity = '0'; // Optimized: use event listener for transition end instead of setTimeout this.container.addEventListener('transitionend', () => { if (this.container.parentNode) { document.body.removeChild(this.container); } }, { once: true }); } } ;// ./src/HUD/ServerSelector.ts class ServerSelector { constructor(servers, onServerSelect) { this.isActive = false; this.serverContainer = null; this.serverCards = []; this.selectedIndex = 0; this.originalBodyContent = ''; this.animation = null; this.servers = []; this.onServerSelect = null; this.servers = this.processServerUrls(servers); this.onServerSelect = onServerSelect || null; } /** * Process server URLs from match patterns to display-friendly names */ processServerUrls(servers) { return servers.map(server => { // Remove wildcards and protocol return server.replace(/^\*:\/\//, '') // Remove trailing wildcards .replace(/\/\*$/, '') // Handle special case for IP addresses .replace(/\/+$/, ''); }); } /** * Show the server selection interface */ show() { // If already active, close first to reset properly if (this.isActive) { this.close(); } this.isActive = true; // Store original content if not already stored if (!this.originalBodyContent) { this.originalBodyContent = document.body.innerHTML; } // Create overlay this.createInterface(); // Start animations this.startAnimations(); // Add keyboard navigation this.setupKeyboardNavigation(); } /** * Create the server selection interface */ createInterface() { // Create overlay container const overlay = document.createElement('div'); overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.9)'; overlay.style.display = 'flex'; overlay.style.flexDirection = 'column'; overlay.style.justifyContent = 'center'; overlay.style.alignItems = 'center'; overlay.style.zIndex = '10000'; overlay.style.perspective = '1000px'; overlay.style.fontFamily = 'Arial, sans-serif'; // Create header const header = document.createElement('h1'); header.textContent = 'Select Server'; header.style.color = '#fff'; header.style.marginBottom = '40px'; header.style.fontSize = '36px'; header.style.textShadow = '0 0 10px rgba(255,0,0,0.8)'; overlay.appendChild(header); // Create server container this.serverContainer = document.createElement('div'); this.serverContainer.style.position = 'relative'; this.serverContainer.style.width = '80%'; this.serverContainer.style.height = '300px'; this.serverContainer.style.display = 'flex'; this.serverContainer.style.justifyContent = 'center'; this.serverContainer.style.alignItems = 'center'; this.serverContainer.style.transformStyle = 'preserve-3d'; overlay.appendChild(this.serverContainer); // Create instructions const instructions = document.createElement('div'); instructions.style.position = 'absolute'; instructions.style.bottom = '20px'; instructions.style.color = '#aaa'; instructions.style.fontSize = '16px'; instructions.innerHTML = 'Use ←/→ arrows to navigate | Enter to select | Esc to close'; overlay.appendChild(instructions); // Create server cards this.createServerCards(); // Add the overlay to the body document.body.appendChild(overlay); } /** * Create 3D rotating cards for each server */ createServerCards() { if (!this.serverContainer) return; const totalServers = this.servers.length; const radius = 300; // Radius of the circle const cardWidth = 200; const cardHeight = 120; this.servers.forEach((server, index) => { const card = document.createElement('div'); card.className = 'server-card'; card.style.position = 'absolute'; card.style.width = `${cardWidth}px`; card.style.height = `${cardHeight}px`; card.style.backgroundColor = index === this.selectedIndex ? '#500' : '#333'; card.style.color = '#fff'; card.style.borderRadius = '10px'; card.style.display = 'flex'; card.style.flexDirection = 'column'; card.style.justifyContent = 'center'; card.style.alignItems = 'center'; card.style.cursor = 'pointer'; card.style.boxShadow = '0 10px 20px rgba(0,0,0,0.5)'; card.style.transition = 'background-color 0.3s ease'; card.style.padding = '15px'; card.style.backfaceVisibility = 'hidden'; // Create server name const serverName = document.createElement('h2'); serverName.textContent = server; serverName.style.margin = '0 0 10px 0'; serverName.style.fontSize = '20px'; card.appendChild(serverName); // Add status indicator const status = document.createElement('div'); status.style.width = '10px'; status.style.height = '10px'; status.style.borderRadius = '50%'; status.style.backgroundColor = '#0f0'; // Green for online status.style.marginTop = '10px'; card.appendChild(status); // Add click event card.addEventListener('click', () => { this.selectedIndex = index; this.updateCardPositions(); this.selectServer(); }); this.serverCards.push(card); if (this.serverContainer) { this.serverContainer.appendChild(card); } }); // Position the cards in a circle this.updateCardPositions(); } /** * Update the positions of all server cards in a 3D circle */ updateCardPositions() { const totalServers = this.servers.length; const radius = Math.max(300, totalServers * 40); // Adjust radius based on number of servers this.serverCards.forEach((card, index) => { // Calculate position on the circle const theta = ((index - this.selectedIndex) / totalServers) * 2 * Math.PI; const x = radius * Math.sin(theta); const z = radius * Math.cos(theta) - radius; // Update card style card.style.transform = `translateX(${x}px) translateZ(${z}px) rotateY(${-theta * 180 / Math.PI}deg)`; card.style.zIndex = z < 0 ? '-1' : '1'; card.style.opacity = (1 - Math.abs(index - this.selectedIndex) / totalServers).toString(); card.style.backgroundColor = index === this.selectedIndex ? '#500' : '#333'; // Add glow effect to selected card if (index === this.selectedIndex) { card.style.boxShadow = '0 0 20px rgba(255,0,0,0.8), 0 10px 20px rgba(0,0,0,0.5)'; } else { card.style.boxShadow = '0 10px 20px rgba(0,0,0,0.5)'; } }); } /** * Start animations for the 3D carousel */ startAnimations() { // Subtle continuous movement for more 3D effect using requestAnimationFrame let angle = 0; let animationId; const animate = () => { angle += 0.005; if (this.serverContainer) { this.serverContainer.style.transform = `rotateY(${Math.sin(angle) * 5}deg) rotateX(${Math.cos(angle) * 3}deg)`; } animationId = requestAnimationFrame(animate); }; // Store the animation ID for cleanup this.animation = animationId = requestAnimationFrame(animate); } /** * Set up keyboard navigation */ setupKeyboardNavigation() { const keyHandler = (e) => { switch (e.key) { case 'ArrowLeft': this.navigate(-1); break; case 'ArrowRight': this.navigate(1); break; case 'Enter': this.selectServer(); break; case 'Escape': this.close(); break; } }; document.addEventListener('keydown', keyHandler); // Store the handler reference so it can be removed when the selector is closed this._keyHandler = keyHandler; } /** * Navigate between servers */ navigate(direction) { const totalServers = this.servers.length; this.selectedIndex = (this.selectedIndex + direction + totalServers) % totalServers; this.updateCardPositions(); } /** * Select current server and close the selector */ selectServer() { const selectedServer = this.servers[this.selectedIndex]; if (this.onServerSelect && selectedServer) { this.onServerSelect(selectedServer); } this.close(); } /** * Close the server selector */ close() { var _a; if (!this.isActive) return; this.isActive = false; // Stop animations if (this.animation !== null) { clearInterval(this.animation); this.animation = null; } // Remove keyboard event listener if (this._keyHandler) { document.removeEventListener('keydown', this._keyHandler); this._keyHandler = null; } // Remove the overlay document.querySelectorAll('div.server-card').forEach(el => el.remove()); if (this.serverContainer && this.serverContainer.parentNode) { const parent = this.serverContainer.parentNode; if (parent && parent instanceof HTMLElement) { parent.remove(); } else if (parent) { // Fallback if parentNode exists but isn't an HTMLElement const parentEl = parent; (_a = parentEl.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(parentEl); } } // Reset state for next use this.serverContainer = null; this.serverCards = []; this.selectedIndex = 0; } } ;// ./src/HUD/EasterEgg.ts class EasterEgg { constructor() { this.originalStyles = {}; this.zelda3Sound = null; this.periodSound = null; this.ambientSound = null; this.buttonClickSound = null; this.arrowKeySound = null; this.enterKeySound = null; this.closeMenuSound = null; this.textElement = null; this.fireElements = []; this.pillars = []; this.isActive = false; this.isInitialized = false; this.overlayElement = null; this.animationFrameId = null; this.originalBodyContent = ''; this.serverSelector = null; this.serverButton = null; this.messageChangeInterval = null; this.originalPageTitle = ''; this.messages = [ "You're already in", "You didnt have found Kxs, is kxs who found you", "The prophecies are true", "Kxs is the chosen one", "Kxs is the one who will save you", "I am Kxs, the one who will save you" ]; this.globalEventHandlersInitialized = false; this.init(); } init() { // Save original page title this.originalPageTitle = document.title; // Check if we're on the target website if (window.location.hostname === 'kxs.rip' || window.location.hostname === 'www.kxs.rip') { // Initialize sounds this.zelda3Sound = new Audio('https://kxs.rip/assets/message.mp3'); // Replace with actual Zelda sound this.periodSound = new Audio('https://kxs.rip/assets/message-finish.mp3'); // Sound for the final period this.ambientSound = new Audio('https://kxs.rip/assets/hell_ambiance.m4a'); // Replace with actual ambient URL this.buttonClickSound = new Audio('https://kxs.rip/assets/enter.mp3'); // Button click sound this.arrowKeySound = new Audio('https://kxs.rip/assets/arrow.mp3'); // Arrow key sound this.enterKeySound = new Audio('https://kxs.rip/assets/enter.mp3'); // Enter key sound this.closeMenuSound = new Audio('https://kxs.rip/assets/close.mp3'); // Close menu sound if (this.ambientSound) { this.ambientSound.loop = true; } // Create the initial overlay with Click prompt instead of immediately applying Easter egg this.createInitialOverlay(); // Initialize global event handlers for interaction sounds this.initGlobalEventHandlers(); } } /** * Creates the initial blur overlay with Click text */ createInitialOverlay() { // Store original body content to restore it later if needed this.originalBodyContent = document.body.innerHTML; // Save original styles this.saveOriginalStyles(); // Create the overlay this.overlayElement = document.createElement('div'); const overlay = this.overlayElement; // Set full screen styles overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.3)'; overlay.style.backdropFilter = 'blur(10px)'; overlay.style['-webkit-backdrop-filter'] = 'blur(10px)'; overlay.style.display = 'flex'; overlay.style.justifyContent = 'center'; overlay.style.alignItems = 'center'; overlay.style.cursor = 'pointer'; overlay.style.zIndex = '9999'; overlay.style.transition = 'opacity 0.5s ease'; // Create the Click text const clickText = document.createElement('div'); clickText.textContent = 'Click'; clickText.style.color = '#fff'; clickText.style.fontSize = '3rem'; clickText.style.fontWeight = 'bold'; clickText.style.textShadow = '0 0 10px rgba(255, 255, 255, 0.7)'; clickText.style.fontFamily = '"Cinzel", "Trajan Pro", serif'; clickText.style.letterSpacing = '5px'; // Add font for the text const fontLink = document.createElement('link'); fontLink.rel = 'stylesheet'; fontLink.href = 'https://fonts.googleapis.com/css2?family=Cinzel:wght@700&display=swap'; document.head.appendChild(fontLink); // Add click event to start the Easter egg overlay.addEventListener('click', () => { this.removeOverlay(); this.applyEasterEgg(); }); // Add the text to the overlay overlay.appendChild(clickText); // Add the overlay to the body document.body.appendChild(overlay); } /** * Removes the initial overlay */ removeOverlay() { if (this.overlayElement && this.overlayElement.parentNode) { // Fade out this.overlayElement.style.opacity = '0'; // Remove after transition (optimized) this.overlayElement.addEventListener('transitionend', () => { if (this.overlayElement && this.overlayElement.parentNode) { this.overlayElement.parentNode.removeChild(this.overlayElement); this.overlayElement = null; } }, { once: true }); } } /** * Initialize global event handlers for sounds */ initGlobalEventHandlers() { if (this.globalEventHandlersInitialized) return; this.globalEventHandlersInitialized = true; // Play sound on button clicks document.addEventListener('click', (e) => { if (e.target instanceof HTMLButtonElement || e.target instanceof HTMLAnchorElement || (e.target instanceof HTMLElement && e.target.role === 'button')) { this.playButtonSound(); } }); // Play sound on arrow keys and enter key document.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { this.playArrowKeySound(); } else if (e.key === 'Enter') { this.playEnterKeySound(); } }); } applyEasterEgg() { if (this.isActive) return; this.isActive = true; this.isInitialized = true; // Transform the website this.transformWebsite(); // Start animations this.startAnimations(); // Play ambient sound this.playAmbientSound(); // Display the message with sound effect (optimized) setTimeout(() => { this.displayMessage(); // Add server selector button after the message is displayed this.addServerSelectorButton(); }, 2000); } saveOriginalStyles() { this.originalStyles = { bodyBackground: document.body.style.background, bodyColor: document.body.style.color, bodyOverflow: document.body.style.overflow }; } transformWebsite() { // Clear the existing content document.body.innerHTML = ''; document.body.style.margin = '0'; document.body.style.padding = '0'; document.body.style.overflow = 'hidden'; document.body.style.backgroundColor = '#000'; document.body.style.color = '#fff'; document.body.style.fontFamily = '"Times New Roman", serif'; document.body.style.height = '100vh'; document.body.style.display = 'flex'; document.body.style.flexDirection = 'column'; document.body.style.justifyContent = 'center'; document.body.style.alignItems = 'center'; document.body.style.perspective = '1000px'; // Create a temple background const temple = document.createElement('div'); temple.style.position = 'absolute'; temple.style.top = '0'; temple.style.left = '0'; temple.style.width = '100%'; temple.style.height = '100%'; temple.style.background = 'linear-gradient(to bottom, #000, #300)'; temple.style.zIndex = '-2'; document.body.appendChild(temple); // Create pillars for (let i = 0; i < 6; i++) { const pillar = document.createElement('div'); pillar.style.position = 'absolute'; pillar.style.width = '80px'; pillar.style.height = '100%'; pillar.style.background = 'linear-gradient(to bottom, #222, #111)'; pillar.style.transform = `rotateY(${i * 60}deg) translateZ(400px)`; pillar.style.boxShadow = 'inset 0 0 20px #500'; pillar.style.transition = 'transform 0.5s ease-in-out'; this.pillars.push(pillar); document.body.appendChild(pillar); } // Create floor const floor = document.createElement('div'); floor.style.position = 'absolute'; floor.style.bottom = '0'; floor.style.width = '100%'; floor.style.height = '40%'; floor.style.background = 'radial-gradient(circle, #300, #100)'; floor.style.zIndex = '-1'; document.body.appendChild(floor); // Create text container for the message this.textElement = document.createElement('div'); this.textElement.style.position = 'relative'; this.textElement.style.fontSize = '3.5em'; this.textElement.style.fontWeight = 'bold'; this.textElement.style.fontFamily = '"Cinzel", "Trajan Pro", serif'; this.textElement.style.color = '#f00'; this.textElement.style.textShadow = '0 0 10px #f00, 0 0 20px #f00, 0 0 30px #900'; this.textElement.style.letterSpacing = '2px'; this.textElement.style.opacity = '0'; this.textElement.style.transition = 'opacity 2s'; // Add a fancy font from Google Fonts const fontLink = document.createElement('link'); fontLink.rel = 'stylesheet'; fontLink.href = 'https://fonts.googleapis.com/css2?family=Cinzel:wght@700&display=swap'; document.head.appendChild(fontLink); document.body.appendChild(this.textElement); // Create fire elements as 3D rotating rectangles for (let i = 0; i < 20; i++) { const fire = document.createElement('div'); fire.style.position = 'absolute'; fire.style.width = `${Math.random() * 40 + 20}px`; fire.style.height = `${Math.random() * 60 + 40}px`; fire.style.background = 'radial-gradient(circle, #f50, #900, transparent)'; fire.style.borderRadius = '10%'; fire.style.filter = 'blur(3px)'; fire.style.opacity = `${Math.random() * 0.5 + 0.5}`; const randomX = Math.random() * 100; fire.style.left = `${randomX}%`; fire.style.bottom = '0'; fire.style.zIndex = '-1'; fire.style.transformStyle = 'preserve-3d'; fire.style.perspective = '1000px'; fire.dataset.velocityY = `${Math.random() * 2 + 1}`; fire.dataset.posX = randomX.toString(); fire.dataset.posY = '0'; fire.dataset.rotateX = `${Math.random() * 4 - 2}`; // Random rotation speed X fire.dataset.rotateY = `${Math.random() * 4 - 2}`; // Random rotation speed Y fire.dataset.rotateZ = `${Math.random() * 4 - 2}`; // Random rotation speed Z fire.dataset.rotationX = '0'; fire.dataset.rotationY = '0'; fire.dataset.rotationZ = '0'; this.fireElements.push(fire); document.body.appendChild(fire); } } startAnimations() { // Animate fire and pillars this.animateFireElements(); this.animatePillars(); } animateFireElements() { if (!this.isActive) return; this.fireElements.forEach(fire => { let posY = parseFloat(fire.dataset.posY || '0'); const velocityY = parseFloat(fire.dataset.velocityY || '1'); // Update position posY += velocityY; fire.dataset.posY = posY.toString(); // Update rotation let rotX = parseFloat(fire.dataset.rotationX || '0'); let rotY = parseFloat(fire.dataset.rotationY || '0'); let rotZ = parseFloat(fire.dataset.rotationZ || '0'); rotX += parseFloat(fire.dataset.rotateX || '0'); rotY += parseFloat(fire.dataset.rotateY || '0'); rotZ += parseFloat(fire.dataset.rotateZ || '0'); fire.dataset.rotationX = rotX.toString(); fire.dataset.rotationY = rotY.toString(); fire.dataset.rotationZ = rotZ.toString(); // Apply transform fire.style.transform = `translateY(${-posY}px) rotateX(${rotX}deg) rotateY(${rotY}deg) rotateZ(${rotZ}deg)`; // Reset fire when it goes off screen if (posY > 100) { posY = 0; fire.dataset.posY = '0'; fire.style.opacity = `${Math.random() * 0.5 + 0.5}`; fire.style.width = `${Math.random() * 40 + 20}px`; fire.style.height = `${Math.random() * 60 + 40}px`; const randomX = Math.random() * 100; fire.style.left = `${randomX}%`; fire.dataset.posX = randomX.toString(); // Reset rotation speeds fire.dataset.rotateX = `${Math.random() * 4 - 2}`; fire.dataset.rotateY = `${Math.random() * 4 - 2}`; fire.dataset.rotateZ = `${Math.random() * 4 - 2}`; } fire.style.opacity = `${Math.max(0, 1 - posY / 100)}`; }); this.animationFrameId = requestAnimationFrame(() => this.animateFireElements()); } animatePillars() { if (!this.isActive) return; // Create a slow rotation effect for the pillars using requestAnimationFrame let angle = 0; let lastTime = 0; const animate = (currentTime) => { if (!this.isActive) return; // Throttle to ~10fps instead of 60fps for this slow animation if (currentTime - lastTime >= 100) { angle += 0.5; this.pillars.forEach((pillar, index) => { pillar.style.transform = `rotateY(${index * 60 + angle}deg) translateZ(400px)`; }); lastTime = currentTime; } requestAnimationFrame(animate); }; requestAnimationFrame(animate); } playAmbientSound() { // Play ambient sound if (this.ambientSound) { this.ambientSound.volume = 0.3; this.ambientSound.play().catch(err => { }); } } /** * Temporarily reduce ambient sound volume to allow other sounds to be heard better */ lowerAmbientVolume() { if (this.ambientSound) { // Store current volume if we need it const originalVolume = this.ambientSound.volume; // Lower volume this.ambientSound.volume = 0.1; // Lower to 1/3 of original volume // Restore volume after a delay setTimeout(() => { if (this.ambientSound) { this.ambientSound.volume = 0.3; // Restore to original volume } }, 500); // Half second delay } } /** * Play button click sound */ playButtonSound() { if (this.buttonClickSound) { // Lower ambient volume this.lowerAmbientVolume(); this.buttonClickSound.currentTime = 0; this.buttonClickSound.volume = 0.3; this.buttonClickSound.play().catch(err => { }); } } /** * Play arrow key sound */ playArrowKeySound() { if (this.arrowKeySound) { // Lower ambient volume this.lowerAmbientVolume(); this.arrowKeySound.currentTime = 0; this.arrowKeySound.volume = 0.3; this.arrowKeySound.play().catch(err => { }); } } /** * Play enter key sound */ playEnterKeySound() { if (this.enterKeySound) { // Lower ambient volume this.lowerAmbientVolume(); this.enterKeySound.currentTime = 0; this.enterKeySound.volume = 0.3; this.enterKeySound.play().catch(err => { }); } } displayMessage() { if (!this.textElement) return; // Set the message text and start with the first message this.typeMessage(this.messages[0]); // Set up message changing at random intervals this.setupMessageChanging(); } /** * Type out a message with the typewriter effect */ typeMessage(message) { if (!this.textElement) return; // Clear current text and ensure visibility this.textElement.textContent = ''; this.textElement.style.opacity = '1'; // Update page title with message document.title = message; // Calculate typing speed based on message length // Longer messages type faster (inversely proportional) const baseSpeed = 300; // Base speed in ms const minSpeed = 40; // Minimum speed for very long messages const typeSpeed = Math.max(minSpeed, baseSpeed - (message.length * 5)); // Type writer effect with Zelda sound let i = 0; const typeInterval = setInterval(() => { if (i < message.length && this.textElement) { // Check if we're at the last character and it's not already a period const isLastChar = i === message.length - 1; const shouldAddPeriod = isLastChar && message.charAt(i) !== '.'; // Play the appropriate sound if (isLastChar && this.periodSound) { // Play special sound for the last character this.periodSound.currentTime = 0; this.periodSound.volume = 0.3; this.periodSound.play().catch(err => { }); } else if (this.zelda3Sound) { // Play regular typing sound this.zelda3Sound.currentTime = 0; this.zelda3Sound.volume = 0.2; this.zelda3Sound.play().catch(err => { }); } // Add character to text element this.textElement.textContent += message.charAt(i); // Update page title in real-time with the current text document.title = this.textElement.textContent || message; // If last character and we should add a period, do it with a pause (optimized) if (shouldAddPeriod) { setTimeout(() => { if (this.textElement && this.periodSound) { this.periodSound.currentTime = 0; this.periodSound.volume = 0.4; this.periodSound.play().catch(err => { }); this.textElement.textContent += '.'; // Update title with the final period document.title = this.textElement.textContent || (message + '.'); } }, 400); } i++; } else { clearInterval(typeInterval); } }, typeSpeed); // Dynamic typing speed based on message length } /** * Setup changing messages at random intervals */ setupMessageChanging() { // Function to change to a random message const changeMessage = () => { // Get a random message that's different from the current one if (!this.textElement) return; const currentMessage = this.textElement.textContent || ''; let newMessage = currentMessage; // Make sure we pick a different message while (newMessage === currentMessage) { const randomIndex = Math.floor(Math.random() * this.messages.length); newMessage = this.messages[randomIndex]; } // Type the new message this.typeMessage(newMessage); // Schedule the next message change this.scheduleNextMessageChange(); }; // Schedule the first message change this.scheduleNextMessageChange(); } /** * Schedule the next message change with a random delay */ scheduleNextMessageChange() { // Clear any existing timer if (this.messageChangeInterval !== null) { clearTimeout(this.messageChangeInterval); } // Random delay between 4 and 19 seconds const delay = Math.floor(Math.random() * 15000) + 4000; // 4-19 seconds // Set timeout for next message change this.messageChangeInterval = window.setTimeout(() => { // Get a random message that's different from the current one if (!this.textElement) return; const currentMessage = this.textElement.textContent || ''; let newMessage = currentMessage; // Make sure we pick a different message while (newMessage === currentMessage) { const randomIndex = Math.floor(Math.random() * this.messages.length); newMessage = this.messages[randomIndex]; } // Type the new message this.typeMessage(newMessage); // Schedule the next message change this.scheduleNextMessageChange(); }, delay); } /** * Add a button to open the server selector */ addServerSelectorButton() { // Create a button this.serverButton = document.createElement('button'); const button = this.serverButton; // Set button text button.textContent = 'SELECT SERVER'; // Position and base styling button.style.position = 'absolute'; button.style.bottom = '30px'; button.style.left = '50%'; button.style.transform = 'translateX(-50%)'; // Enhanced styling button.style.backgroundColor = 'transparent'; button.style.color = '#ff9'; button.style.border = '2px solid #900'; button.style.padding = '15px 30px'; button.style.fontSize = '20px'; button.style.fontFamily = '"Cinzel", "Trajan Pro", serif'; button.style.fontWeight = 'bold'; button.style.letterSpacing = '3px'; button.style.borderRadius = '3px'; button.style.textTransform = 'uppercase'; button.style.boxShadow = '0 0 20px rgba(255, 30, 0, 0.6), inset 0 0 10px rgba(255, 50, 0, 0.4)'; button.style.textShadow = '0 0 10px rgba(255, 150, 0, 0.8), 0 0 5px rgba(255, 100, 0, 0.5)'; button.style.cursor = 'pointer'; button.style.zIndex = '100'; button.style.opacity = '0'; button.style.transition = 'all 0.5s ease-in-out'; button.style.background = 'linear-gradient(to bottom, rgba(80, 0, 0, 0.8), rgba(30, 0, 0, 0.9))'; button.style.backdropFilter = 'blur(3px)'; // Add enhanced hover effects button.addEventListener('mouseover', () => { button.style.color = '#fff'; button.style.borderColor = '#f00'; button.style.boxShadow = '0 0 25px rgba(255, 50, 0, 0.8), inset 0 0 15px rgba(255, 100, 0, 0.6)'; button.style.textShadow = '0 0 15px rgba(255, 200, 0, 1), 0 0 10px rgba(255, 150, 0, 0.8)'; button.style.transform = 'translateX(-50%) scale(1.05)'; button.style.background = 'linear-gradient(to bottom, rgba(100, 0, 0, 0.9), rgba(50, 0, 0, 1))'; }); button.addEventListener('mouseout', () => { button.style.color = '#ff9'; button.style.borderColor = '#900'; button.style.boxShadow = '0 0 20px rgba(255, 30, 0, 0.6), inset 0 0 10px rgba(255, 50, 0, 0.4)'; button.style.textShadow = '0 0 10px rgba(255, 150, 0, 0.8), 0 0 5px rgba(255, 100, 0, 0.5)'; button.style.transform = 'translateX(-50%)'; button.style.background = 'linear-gradient(to bottom, rgba(80, 0, 0, 0.8), rgba(30, 0, 0, 0.9))'; }); // Add active/press effect button.addEventListener('mousedown', () => { button.style.transform = 'translateX(-50%) scale(0.98)'; button.style.boxShadow = '0 0 10px rgba(255, 30, 0, 0.8), inset 0 0 8px rgba(255, 100, 0, 0.8)'; }); button.addEventListener('mouseup', () => { button.style.transform = 'translateX(-50%) scale(1.05)'; button.style.boxShadow = '0 0 25px rgba(255, 50, 0, 0.8), inset 0 0 15px rgba(255, 100, 0, 0.6)'; }); // Add click handler to show server selector button.addEventListener('click', () => { this.showServerSelector(); }); // Add to body document.body.appendChild(button); // Fade in the button after a short delay setTimeout(() => { if (button) { button.style.opacity = '1'; } }, 1500); } /** * Initialize and show the server selector */ showServerSelector() { // Play enter sound when opening the menu if (this.enterKeySound) { // Lower ambient volume this.lowerAmbientVolume(); this.enterKeySound.currentTime = 0; this.enterKeySound.volume = 0.3; this.enterKeySound.play().catch(err => { }); } // Function to redirect to a selected server const redirectToServer = (server) => { window.location.href = `https://${server}`; }; // Create server selector if it doesn't exist if (!this.serverSelector) { // Create a modified version of redirectToServer that includes the close sound const redirectWithSound = (server) => { // Play close sound first if (this.closeMenuSound) { // Lower ambient volume this.lowerAmbientVolume(); this.closeMenuSound.play().catch(err => { }); // Redirect after a short delay to allow the sound to play setTimeout(() => { redirectToServer(server); }, 300); } else { // If sound failed to load, just redirect redirectToServer(server); } }; // Create server selector with our modified redirect function this.serverSelector = new ServerSelector(config_namespaceObject.match, redirectWithSound); // Handle close events to play the close sound if (this.serverSelector) { const originalClose = this.serverSelector.close.bind(this.serverSelector); this.serverSelector.close = () => { // Play close sound if (this.closeMenuSound) { // Lower ambient volume this.lowerAmbientVolume(); this.closeMenuSound.currentTime = 0; this.closeMenuSound.volume = 0.3; this.closeMenuSound.play().catch(err => { }); } // Call original close method originalClose(); }; } } // Show the selector this.serverSelector.show(); } // Call this method if you ever want to restore the original website restoreWebsite() { if (!this.isInitialized) return; this.isActive = false; this.isInitialized = false; // Restore original page title document.title = this.originalPageTitle; // Remove overlay if it exists if (this.overlayElement) { this.removeOverlay(); } // Stop animations if (this.animationFrameId) { cancelAnimationFrame(this.animationFrameId); this.animationFrameId = null; } // Stop message changing if (this.messageChangeInterval !== null) { clearTimeout(this.messageChangeInterval); this.messageChangeInterval = null; } // Stop sounds if (this.zelda3Sound) { this.zelda3Sound.pause(); } if (this.periodSound) { this.periodSound.pause(); } if (this.ambientSound) { this.ambientSound.pause(); } if (this.buttonClickSound) { this.buttonClickSound.pause(); } if (this.arrowKeySound) { this.arrowKeySound.pause(); } if (this.enterKeySound) { this.enterKeySound.pause(); } if (this.closeMenuSound) { this.closeMenuSound.pause(); } // Remove server selector if it exists if (this.serverSelector) { this.serverSelector.close(); this.serverSelector = null; } // Remove server button if it exists if (this.serverButton && this.serverButton.parentNode) { this.serverButton.parentNode.removeChild(this.serverButton); this.serverButton = null; } // Restore original content document.body.innerHTML = this.originalBodyContent; // Restore original styles document.body.style.background = this.originalStyles.bodyBackground || ''; document.body.style.color = this.originalStyles.bodyColor || ''; document.body.style.overflow = this.originalStyles.bodyOverflow || ''; // Re-initialize global event handlers since they may have been lost this.globalEventHandlersInitialized = false; this.initGlobalEventHandlers(); } } ;// ./src/assets/onboarding.html?raw const onboardingraw_namespaceObject = "
\r\n\t
\r\n\t\t
\r\n\t\t\t
\r\n\t\t\t\r\n\t\t
\r\n\t\t
\r\n\t\t\t

Hey i'm KxsClient !

\r\n\t\t\t

The only client you need

\r\n\r\n\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t

1. Configuration

\r\n\t\t\t\t\t\t

Press RSHIFT to open the configuration menu and customize your gaming\r\n\t\t\t\t\t\t\texperience according to your preferences.

\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t\t\"Configuration\r\n\t\t\t\t
\r\n\r\n\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t

2. Updates

\r\n\t\t\t\t\t\t

KxsClient is in continuous development. Refresh the page or restart the client to\r\n\t\t\t\t\t\t\tautomatically get the latest improvements.

\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\r\n\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t

3. Kxs Network

\r\n\t\t\t\t\t\t

Join our community! Enable voice and text chat to communicate with other players in real-time\r\n\t\t\t\t\t\t\tduring your games.

\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t\t\"Kxs\r\n\t\t\t\t
\r\n\t\t\t
\r\n\r\n\t\t\t\r\n\t\t
\r\n\t
\r\n
"; ;// ./src/assets/onboarding_kxz.html?raw const onboarding_kxzraw_namespaceObject = "
\r\n\t
\r\n\t\t
\r\n\t\t\t
\r\n\t\t\t\r\n\t\t
\r\n\t\t
\r\n\t\t\t

Hey i'm KxzClient !

\r\n\t\t\t\"KxzClient\r\n\r\n\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t

1. Configuration

\r\n\t\t\t\t\t\t

Press RSHIFT to open the configuration menu and customize your gaming\r\n\t\t\t\t\t\t\texperience according to your preferences.

\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t\t\"Configuration\r\n\t\t\t\t
\r\n\r\n\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t

2. Updates

\r\n\t\t\t\t\t\t

KxsClient is in continuous development. Refresh the page or restart the client to\r\n\t\t\t\t\t\t\tautomatically get the latest improvements.

\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\r\n\t\t\t\t
\r\n\t\t\t\t\t
\r\n\t\t\t\t\t\t

3. Kxz Network

\r\n\t\t\t\t\t\t

Join our community! Enable voice and text chat to communicate with other players in real-time\r\n\t\t\t\t\t\t\tduring your games.

\r\n\t\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t
\r\n\t\t\t\t\t\"Kxz\r\n\t\t\t\t
\r\n\t\t\t
\r\n\r\n\t\t\t\r\n\t\t
\r\n\t
\r\n
"; ;// ./src/assets/onboarding-styles.css?raw const onboarding_stylesraw_namespaceObject = "/* Reset and base styles */\r\n.popup-overlay {\r\n\tposition: fixed;\r\n\ttop: 0;\r\n\tleft: 0;\r\n\twidth: 100%;\r\n\theight: 100%;\r\n\tbackground: rgba(0, 0, 0, 0.8);\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tjustify-content: center;\r\n\tz-index: 1000;\r\n\tfont-family: Arial, sans-serif;\r\n}\r\n\r\n.subtitle-image {\r\n\tdisplay: block;\r\n\tmargin: 0 auto;\r\n\tmax-width: 00px;\r\n\t/* adapte selon ton image */\r\n\theight: auto;\r\n}\r\n\r\n.popup-content {\r\n\tbackground: #2c3e50;\r\n\tborder-radius: 10px;\r\n\tmax-width: 90%;\r\n\tmax-height: 90%;\r\n\toverflow-y: auto;\r\n\tcolor: white;\r\n\tposition: relative;\r\n}\r\n\r\n/* Header with Discord button */\r\n.popup-header {\r\n\tdisplay: flex;\r\n\tjustify-content: space-between;\r\n\talign-items: center;\r\n\tpadding: 20px 30px 0;\r\n\tmargin-bottom: 20px;\r\n}\r\n\r\n.discord-button {\r\n\tbackground: #5865F2;\r\n\tcolor: white;\r\n\tborder: none;\r\n\tpadding: 10px 20px;\r\n\tborder-radius: 5px;\r\n\tcursor: pointer;\r\n\tfont-size: 14px;\r\n\tfont-weight: bold;\r\n\ttransition: background-color 0.3s ease;\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tgap: 8px;\r\n}\r\n\r\n.discord-button:hover {\r\n\tbackground: #4752C4;\r\n}\r\n\r\n.discord-icon {\r\n\twidth: 16px;\r\n\theight: 16px;\r\n\tfill: currentColor;\r\n}\r\n\r\n.popup-content .container {\r\n\tmax-width: 600px;\r\n\tmargin: 0 auto;\r\n\tpadding: 0 20px 40px;\r\n\ttext-align: center;\r\n}\r\n\r\n.popup-content h1 {\r\n\tcolor: #3498db;\r\n\tfont-size: 2.5em;\r\n\tmargin-bottom: 10px;\r\n}\r\n\r\n.popup-content .subtitle {\r\n\tfont-size: 1.2em;\r\n\tmargin-bottom: 40px;\r\n\tcolor: #bdc3c7;\r\n}\r\n\r\n.popup-content .steps {\r\n\ttext-align: left;\r\n\tmargin: 40px 0;\r\n}\r\n\r\n.popup-content .step {\r\n\tbackground: #34495e;\r\n\tpadding: 20px;\r\n\tmargin: 15px 0;\r\n\tborder-radius: 8px;\r\n\tborder-left: 4px solid #3498db;\r\n\tdisplay: flex;\r\n\talign-items: center;\r\n\tgap: 20px;\r\n}\r\n\r\n.popup-content .step-large-image {\r\n\tmargin: 20px 0;\r\n\ttext-align: center;\r\n\tbackground: #34495e;\r\n\tborder-radius: 8px;\r\n\tpadding: 15px;\r\n\tborder: 2px solid #3498db;\r\n}\r\n\r\n.popup-content .step-large-image img {\r\n\twidth: 100%;\r\n\tmax-width: 500px;\r\n\theight: auto;\r\n\tborder-radius: 5px;\r\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.popup-content .step-content {\r\n\tflex: 1;\r\n}\r\n\r\n.popup-content .step h3 {\r\n\tmargin: 0 0 10px 0;\r\n\tcolor: #3498db;\r\n}\r\n\r\n.popup-content .step p {\r\n\tmargin: 0;\r\n\tline-height: 1.5;\r\n}\r\n\r\n.popup-content .play-button {\r\n\tbackground: #e74c3c;\r\n\tcolor: white;\r\n\tborder: none;\r\n\tpadding: 15px 30px;\r\n\tfont-size: 1.2em;\r\n\tborder-radius: 5px;\r\n\tcursor: pointer;\r\n\tmargin-top: 30px;\r\n\ttransition: background-color 0.3s ease;\r\n}\r\n\r\n.popup-content .play-button:hover {\r\n\tbackground: #c0392b;\r\n}\r\n\r\n@media (max-width: 768px) {\r\n\t.popup-header {\r\n\t\tflex-direction: column;\r\n\t\tgap: 15px;\r\n\t\talign-items: stretch;\r\n\t}\r\n\r\n\t.discord-button {\r\n\t\tjustify-content: center;\r\n\t}\r\n\r\n\t.popup-content h1 {\r\n\t\tfont-size: 2em;\r\n\t}\r\n\r\n\t.popup-content .step {\r\n\t\tflex-direction: column;\r\n\t\ttext-align: center;\r\n\t}\r\n}"; ;// ./src/FUNC/Felicitations.ts function felicitation(enable, win_sound_url, text) { const goldText = document.createElement("div"); goldText.textContent = text; goldText.style.position = "fixed"; goldText.style.top = "50%"; goldText.style.left = "50%"; goldText.style.transform = "translate(-50%, -50%)"; goldText.style.fontSize = "80px"; goldText.style.color = "gold"; goldText.style.textShadow = "2px 2px 4px rgba(0,0,0,0.3)"; goldText.style.zIndex = "10000"; document.body.appendChild(goldText); function createConfetti() { const colors = [ "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff", "gold", ]; const confetti = document.createElement("div"); confetti.style.position = "fixed"; confetti.style.width = Math.random() * 10 + 5 + "px"; confetti.style.height = Math.random() * 10 + 5 + "px"; confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; confetti.style.borderRadius = "50%"; confetti.style.zIndex = "9999"; confetti.style.left = Math.random() * 100 + "vw"; confetti.style.top = "-20px"; document.body.appendChild(confetti); let posY = -20; let posX = parseFloat(confetti.style.left); let rotation = 0; let speedY = Math.random() * 2 + 1; let speedX = Math.random() * 2 - 1; function fall() { posY += speedY; posX += speedX; rotation += 5; confetti.style.top = posY + "px"; confetti.style.left = posX + "vw"; confetti.style.transform = `rotate(${rotation}deg)`; if (posY < window.innerHeight) { requestAnimationFrame(fall); } else { confetti.remove(); } } fall(); } const confettiInterval = setInterval(() => { for (let i = 0; i < 5; i++) { createConfetti(); } }, 100); if (enable) { const audio = new Audio(win_sound_url); audio.play().catch((err) => console.error("Erreur lecture:", err)); } setTimeout(() => { clearInterval(confettiInterval); goldText.style.transition = "opacity 1s"; goldText.style.opacity = "0"; setTimeout(() => goldText.remove(), 1000); }, 5000); } ;// ./src/FUNC/Onboarding.ts // FUNC/Onboarding.ts // @ts-ignore // @ts-ignore // @ts-ignore class OnboardingModal { constructor() { this.overlay = null; this.isVisible = false; } // Inject CSS styles injectStyles() { const styleId = 'onboarding-styles'; // Check if styles already exist if (document.getElementById(styleId)) { return; } const styleElement = document.createElement('style'); styleElement.id = styleId; styleElement.textContent = onboarding_stylesraw_namespaceObject; document.head.appendChild(styleElement); } // Show the onboarding modal show() { if (this.isVisible) { return; } // Inject CSS styles this.injectStyles(); // Create overlay element this.overlay = document.createElement('div'); this.overlay.innerHTML = vars_client.type === ClientType.KxsClient ? onboardingraw_namespaceObject : onboarding_kxzraw_namespaceObject; // Get the actual overlay from the created HTML const overlayElement = this.overlay.firstElementChild; // Add to document body document.body.appendChild(overlayElement); // Store reference to the overlay this.overlay = overlayElement; this.isVisible = true; // Add event listeners this.addEventListeners(); } // Hide the onboarding modal hide() { if (!this.isVisible || !this.overlay) { return; } // Remove overlay from DOM this.overlay.remove(); this.overlay = null; this.isVisible = false; let welcome_sound = vars_client.type === ClientType.KxsClient ? "https://kxs.rip/assets/o_sound.mp3" : "https://kxs.rip/assets/o_z_sound.mp3"; felicitation(true, welcome_sound, "Welcome to " + __webpack_require__.g.client.name); localStorage.setItem("on_boarding_complete", "yes"); } // Add event listeners for interactions addEventListeners() { if (!this.overlay) return; // Play button click handler const playButton = this.overlay.querySelector('#play-now-btn'); if (playButton) { playButton.addEventListener('click', () => { this.onPlayButtonClick(); }); } // Discord button click handler const discordButton = this.overlay.querySelector('#discord-btn'); if (discordButton) { discordButton.addEventListener('click', () => { this.onDiscordButtonClick(); }); } // Click outside to close (optional) this.overlay.addEventListener('click', (e) => { if (e.target === this.overlay) { this.hide(); } }); // ESC key to close document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && this.isVisible) { this.hide(); } }); } // Handle play button click onPlayButtonClick() { // Close the modal this.hide(); } // Handle Discord button click onDiscordButtonClick() { // Replace with your actual Discord invite link window.open('https://discord.wf/kxsclient', '_blank'); } // Check if modal is currently visible isOpen() { return this.isVisible; } } /* harmony default export */ const Onboarding = ((/* unused pure expression or super */ null && (OnboardingModal))); ;// ./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; if (client.type === 2) 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: __webpack_require__.g.client.name, 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/creditpage.html?raw const creditpageraw_namespaceObject = "\n\n\n\n\t\n\t\n\tKxsClient - Credits\n\t\n\n\n\n\t
\n\t\t\n\t\t\n\t\t\n\t
\n\n\t
\n\t\t
\n\t\t\t
Loading credits.txt...
\n\t\t
\n\t\t
\n\t\t\t
Loading contributors_wall.txt...
\n\t\t
\n\t\t
\n\t\t\t
Loading changelogs.txt...
\n\t\t
\n\t
\n\n\t\n\n\n"; ;// ./src/UTILS/credits-helper.ts // @ts-ignore // Function to create and show the "Click on me" animation above the version link function showClickMeAnimation() { // Get the position of the version link to position the animation above it const startBottomMiddle = document.getElementById("start-bottom-middle"); if (!startBottomMiddle) return; const versionLink = startBottomMiddle.getElementsByTagName("a")[0]; if (!versionLink) return; // Get the position of the version link const linkRect = versionLink.getBoundingClientRect(); // Create the animation container const animationContainer = document.createElement('div'); animationContainer.id = 'click-me-animation'; animationContainer.style.cssText = ` position: fixed; bottom: ${window.innerHeight - linkRect.top}px; left: ${linkRect.left + (linkRect.width / 2)}px; z-index: 10000; pointer-events: none; display: flex; flex-direction: column; align-items: center; animation: fadeInBounce 1s ease-out; transform: translateX(-50%); `; // Create the text element const textElement = document.createElement('div'); textElement.textContent = 'Click on me'; textElement.style.cssText = ` background: rgba(0, 0, 0, 0.8); color: #fff; padding: 8px 16px; border-radius: 20px; font-family: Arial, sans-serif; font-size: 14px; font-weight: bold; margin-bottom: 10px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); animation: pulse 2s infinite; white-space: nowrap; `; // Create the arrow element pointing down to the version link const arrowElement = document.createElement('div'); arrowElement.innerHTML = '▼'; arrowElement.style.cssText = ` color: #fff; font-size: 20px; animation: bounce 1.5s infinite; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); margin-top: -5px; `; // Add CSS animations to the document const styleElement = document.createElement('style'); styleElement.textContent = ` @keyframes fadeInBounce { 0% { opacity: 0; transform: translateX(-50%) translateY(-20px); } 60% { opacity: 1; transform: translateX(-50%) translateY(5px); } 100% { opacity: 1; transform: translateX(-50%) translateY(0); } } @keyframes bounce { 0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-8px); } 60% { transform: translateY(-4px); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } @keyframes fadeOut { from { opacity: 1; transform: translateX(-50%) translateY(0); } to { opacity: 0; transform: translateX(-50%) translateY(-20px); } } `; document.head.appendChild(styleElement); // Assemble the animation animationContainer.appendChild(textElement); animationContainer.appendChild(arrowElement); document.body.appendChild(animationContainer); // Auto-hide after 8 seconds setTimeout(() => { animationContainer.style.animation = 'fadeOut 0.5s ease-in forwards'; setTimeout(() => { if (animationContainer.parentNode) { animationContainer.parentNode.removeChild(animationContainer); } if (styleElement.parentNode) { styleElement.parentNode.removeChild(styleElement); } }, 500); }, 4000); return animationContainer; } // Function to create and open the credits window function openCreditsWindow(focusTab = "credits") { // Use the imported HTML content const htmlContent = creditpageraw_namespaceObject.replace('%%FOCUS_TAB%%', focusTab); // Create a blob URL from the HTML content const blob = new Blob([htmlContent], { type: 'text/html' }); const blobUrl = URL.createObjectURL(blob); // Open the window with the blob URL const creditsWindow = window.open(blobUrl, 'KxsCredits', 'width=800,height=600,scrollbars=yes,resizable=yes'); if (!creditsWindow) { alert('Please allow popups for this site to view credits.'); URL.revokeObjectURL(blobUrl); // Clean up if window couldn't open return; } // Clean up the blob URL after the window loads creditsWindow.addEventListener('load', () => { URL.revokeObjectURL(blobUrl); }); } ;// ./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 && client.type === 1) { 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 buttonContainer = document.createElement("div"); buttonContainer.style.marginLeft = "auto"; buttonContainer.style.display = "flex"; buttonContainer.style.alignItems = "center"; buttonContainer.style.gap = "10px"; const changelogButton = document.createElement("button"); changelogButton.textContent = "See changelog"; changelogButton.style.border = "1px solid rgb(229, 229, 229)"; changelogButton.style.background = "white"; changelogButton.style.fontSize = "13px"; changelogButton.style.cursor = "pointer"; changelogButton.style.padding = "6px 12px"; changelogButton.style.borderRadius = "6px"; changelogButton.style.fontWeight = "500"; changelogButton.style.color = "rgb(75, 85, 99)"; changelogButton.style.transition = "background-color 0.2s ease"; changelogButton.onmouseover = () => changelogButton.style.backgroundColor = "rgb(243, 244, 246)"; changelogButton.onmouseout = () => changelogButton.style.backgroundColor = "white"; changelogButton.onclick = () => openCreditsWindow("changelogs"); buttonContainer.appendChild(changelogButton); const closeButton = document.createElement("button"); closeButton.innerHTML = "×"; 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(); buttonContainer.appendChild(closeButton); header.appendChild(buttonContainer); 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: vars_client.name, type: 0, application_id: vars_client.application_id, assets: { large_image: vars_client.rpc_assets, large_text: vars_client.name + " 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/types/hudMenu.ts const category = ["ALL", "HUD", "SERVER", "MECHANIC", "$MISC", "$CONFIG"]; const X0 = ["Kxs Network", "Developer Options"]; var ModType; (function (ModType) { ModType[ModType["Toggle"] = 1] = "Toggle"; ModType[ModType["Input"] = 2] = "Input"; ModType[ModType["Click"] = 3] = "Click"; ModType[ModType["Info"] = 4] = "Info"; ModType[ModType["Slider"] = 5] = "Slider"; ModType[ModType["Sub"] = 6] = "Sub"; })(ModType || (ModType = {})); ;// ./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 ClientSecondaryMenu_category = category.filter(x => { if (x.startsWith("$") && vars_client.type === 2) { return false; } return true; }); 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.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(); }); } 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); }); } 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: vars_client.acronym_start_upper + " Network", value: true, type: ModType.Sub, icon: ' network ', fields: [ { label: "Spoof Nickname", icon: ' hacker-solid ', type: ModType.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: ModType.Toggle, onChange: () => { this.kxsClient.isVoiceChatEnabled = !this.kxsClient.isVoiceChatEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.voiceChat.toggleVoiceChat(); }, }, { label: "Chat", value: this.kxsClient.isKxsChatEnabled, icon: ' ', type: ModType.Toggle, onChange: () => { this.kxsClient.isKxsChatEnabled = !this.kxsClient.isKxsChatEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.chat.toggleChat(); }, } ], }); vars_client.type === 1 && this.addOption(MISC, { label: "Game History", value: true, icon: ' ', type: ModType.Click, onChange: () => { this.kxsClient.historyManager.show(); } }); this.addOption(MECHANIC, { label: "Win sound", value: true, type: ModType.Sub, icon: ' ', fields: [ { label: "Enable", value: this.kxsClient.isWinSoundEnabled, icon: ' ', type: ModType.Toggle, onChange: () => { this.kxsClient.isWinSoundEnabled = !this.kxsClient.isWinSoundEnabled; this.kxsClient.updateLocalStorage(); }, }, { label: "Sound URL", value: this.kxsClient.soundLibrary.win_sound_url, icon: ' ', type: ModType.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: ModType.Sub, icon: ' ', fields: [ { label: "Enable", value: this.kxsClient.isDeathSoundEnabled, icon: ' ', type: ModType.Toggle, onChange: () => { this.kxsClient.isDeathSoundEnabled = !this.kxsClient.isDeathSoundEnabled; this.kxsClient.updateLocalStorage(); }, }, { label: "Sound URL", value: this.kxsClient.soundLibrary.death_sound_url, icon: ' ', type: ModType.Input, placeholder: "URL of a sound", onChange: (value) => { this.kxsClient.soundLibrary.death_sound_url = value; this.kxsClient.updateLocalStorage(); } } ] }); vars_client.type === 1 && this.addOption(MECHANIC, { label: "Background Music", value: this.kxsClient.soundLibrary.background_sound_url, type: ModType.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: ModType.Toggle, onChange: (value) => { this.kxsClient.isMainMenuCleaned = !this.kxsClient.isMainMenuCleaned; this.kxsClient.MainMenuCleaning(); this.kxsClient.updateLocalStorage(); }, }); vars_client.type === 1 && this.addOption(HUD, { label: "Counters", value: true, type: ModType.Sub, icon: '', fields: [ { label: "Show Kills", value: this.kxsClient.isKillsVisible, type: ModType.Toggle, icon: ' ', onChange: (value) => { this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible; this.kxsClient.updateKillsVisibility(); this.kxsClient.updateLocalStorage(); }, }, { label: "Show FPS", value: this.kxsClient.isFpsVisible, type: ModType.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: ModType.Toggle, onChange: (value) => { this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible; this.kxsClient.updatePingVisibility(); this.kxsClient.updateLocalStorage(); }, } ], }); vars_client.type === 1 && this.addOption(HUD, { label: "Weapon Border", value: this.kxsClient.isGunOverlayColored, type: ModType.Toggle, icon: ' ', onChange: (value) => { this.kxsClient.isGunOverlayColored = !this.kxsClient.isGunOverlayColored; this.kxsClient.updateLocalStorage(); this.kxsClient.hud.toggleWeaponBorderHandler(); }, }); vars_client.type === 1 && this.addOption(HUD, { label: "Chromatic Weapon Border", value: this.kxsClient.isGunBorderChromatic, type: ModType.Toggle, icon: ' ', onChange: (value) => { this.kxsClient.isGunBorderChromatic = !this.kxsClient.isGunBorderChromatic; this.kxsClient.updateLocalStorage(); this.kxsClient.hud.toggleChromaticWeaponBorder(); }, }); vars_client.type === 1 && this.addOption(HUD, { label: "Focus Mode", value: true, type: ModType.Sub, icon: ' ', fields: [ { label: "Enable", value: this.kxsClient.isFocusModeEnabled, type: ModType.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: ModType.Info, icon: ' ', } ], }); vars_client.type === 1 && this.addOption(HUD, { label: "Health Bar Indicator", value: this.kxsClient.isHealBarIndicatorEnabled, type: ModType.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: ModType.Toggle, icon: ' ', onChange: (value) => { this.kxsClient.isNotifyingForToggleMenu = !this.kxsClient.isNotifyingForToggleMenu; this.kxsClient.updateLocalStorage(); }, }); vars_client.type === 1 && this.addOption(SERVER, { label: "Webhook URL", value: this.kxsClient.discordWebhookUrl || "", icon: ' ', type: ModType.Input, placeholder: "discord webhook url", onChange: (value) => { value = value.toString().trim(); this.kxsClient.discordWebhookUrl = value; this.kxsClient.discordTracker.setWebhookUrl(value); this.kxsClient.updateLocalStorage(); }, }); vars_client.type === 1 && this.addOption(MECHANIC, { label: "Custom Crosshair", value: this.kxsClient.customCrosshair || "", type: ModType.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: ModType.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(); }, }); vars_client.type === 1 && this.addOption(SERVER, { label: "Update Checker", value: this.kxsClient.isAutoUpdateEnabled, type: ModType.Toggle, icon: ' ', onChange: (value) => { this.kxsClient.isAutoUpdateEnabled = !this.kxsClient.isAutoUpdateEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: `Uncap FPS`, value: this.kxsClient.isFpsUncapped, type: ModType.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: ModType.Toggle, onChange: () => { this.kxsClient.isWinningAnimationEnabled = !this.kxsClient.isWinningAnimationEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Glassmorphism`, value: this.kxsClient.isGlassmorphismEnabled, icon: '', type: ModType.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: __webpack_require__.g.client.name + ` Logo`, value: this.kxsClient.isKxsClientLogoEnable, icon: ' ', type: ModType.Toggle, onChange: () => { this.kxsClient.isKxsClientLogoEnable = !this.kxsClient.isKxsClientLogoEnable; this.kxsClient.updateLocalStorage(); }, }); vars_client.type === 1 && this.addOption(HUD, { label: `Spotify Player`, value: this.kxsClient.isSpotifyPlayerEnabled, icon: ' spotify ', type: ModType.Toggle, onChange: () => { this.kxsClient.isSpotifyPlayerEnabled = !this.kxsClient.isSpotifyPlayerEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.toggleSpotifyMenu(); }, }); vars_client.type === 1 && this.addOption(HUD, { label: "Brightness", value: this.kxsClient.brightness, icon: ' light ', type: ModType.Slider, min: 20, max: 100, step: 1, onChange: (value) => { this.kxsClient.applyBrightness(value); }, }); vars_client.type === 1 && this.addOption(HUD, { label: "Kill Feed Chroma", value: this.kxsClient.isKillFeedBlint, icon: ` `, type: ModType.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: ModType.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(); }, }); vars_client.type === 1 && this.addOption(MECHANIC, { label: `Kill Leader Tracking`, icon: ' crown ', value: this.kxsClient.isKillLeaderTrackerEnabled, type: ModType.Toggle, onChange: (value) => { this.kxsClient.isKillLeaderTrackerEnabled = !this.kxsClient.isKillLeaderTrackerEnabled; this.kxsClient.updateLocalStorage(); }, }); vars_client.type === 1 && this.addOption(MECHANIC, { label: `Friends Detector (separe with ',')`, icon: ' ', value: this.kxsClient.all_friends, type: ModType.Input, placeholder: "kisakay,iletal...", onChange: (value) => { this.kxsClient.all_friends = value; this.kxsClient.updateLocalStorage(); }, }); vars_client.type === 1 && this.addOption(HUD, { icon: ' ', label: "Background Modifiction", type: ModType.Sub, fields: [ { label: "Custom Background", icon: '', type: ModType.Toggle, value: this.kxsClient.isCustomBackgroundEnabled, onChange: () => { this.kxsClient.isCustomBackgroundEnabled = !this.kxsClient.isCustomBackgroundEnabled; this.kxsClient.updateLocalStorage(); }, }, { label: `Change Background`, icon: ' ', value: true, type: ModType.Click, onChange: () => { if (!this.kxsClient.isCustomBackgroundEnabled) { alert("The custom background is disabled."); return; } const backgroundElement = document.getElementById("background"); if (!backgroundElement) { alert("Element with id 'background' not found."); return; } const choice = prompt(`Enter '0' to default ${vars_client.acronym_start_upper} 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(); } }, } ], value: true }); vars_client.type === 1 && this.addOption(MISC, { label: "Developer Options", value: true, icon: '', type: ModType.Sub, fields: [ { label: "Enable GameID Exchange", icon: ' share Created with Sketch Beta. ', type: ModType.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: ModType.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: ModType.Click, icon: ' ', onChange: () => { this.kxsClient.nm.showNotification("Exchange Key Copied to Clipboard", "success", 2100); navigator.clipboard.writeText(this.kxsClient.kxsDeveloperOptions.exchange.password); } } ], }); // KXS CLIENT CONFIG vars_client.type === 1 && this.addOption(CONFIG, { icon: ' ', label: `Copy ${vars_client.acronym_start_upper} Config`, type: ModType.Click, value: true, onChange: () => ClientSecondaryMenu_awaiter(this, void 0, void 0, function* () { yield navigator.clipboard.writeText(this.kxsClient.getKxsJSONConfig()); this.kxsClient.nm.showNotification(vars_client.acronym_start_upper + " Config copied to clipboard", "success", 3000); }) }); vars_client.type === 1 && this.addOption(CONFIG, { icon: ' ', label: `Import ${vars_client.acronym_start_upper} Config`, type: ModType.Click, value: true, onChange: () => ClientSecondaryMenu_awaiter(this, void 0, void 0, function* () { const data = this.kxsClient.ContextIsSecure ? yield navigator.clipboard.readText() : (prompt("Paste the config") || ""); try { const parse_data = JSON.parse(data); if (parse_data["userSettings"]) { this.kxsClient.setKxsJSONConfig(parse_data); this.kxsClient.nm.showNotification(`${vars_client.acronym_start_upper} Config imported`, "success", 4000); setInterval(() => { this.kxsClient.nm.showNotification(`${vars_client.acronym_start_upper} 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 ${vars_client.acronym_start_upper} Config`, "error", 4000); } }) }); // SURVEV CONFIG vars_client.type === 1 && this.addOption(CONFIG, { icon: ' ', label: "Copy Survev Config", type: ModType.Click, value: true, onChange: () => ClientSecondaryMenu_awaiter(this, void 0, void 0, function* () { yield navigator.clipboard.writeText(JSON.stringify(yield survev_settings.all(), null, 0)); this.kxsClient.nm.showNotification("Survev Config copied to clipboard", "success", 3000); }) }); vars_client.type === 1 && this.addOption(CONFIG, { icon: ' ', label: "Import Survev Config", type: ModType.Click, value: true, onChange: () => ClientSecondaryMenu_awaiter(this, void 0, void 0, function* () { const data = this.kxsClient.ContextIsSecure ? yield navigator.clipboard.readText() : (prompt("Paste the config") || ""); try { const parse_data = JSON.parse(data); if (parse_data["version"] === 1) { localStorage.setItem("surviv_config", data); this.kxsClient.nm.showNotification("Survev Config imported", "success", 4000); setInterval(() => { this.kxsClient.nm.showNotification("Game 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 Survev Config", "error", 4000); } }) }); } createOptionCard(option, container) { const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); const optionCard = document.createElement("div"); Object.assign(optionCard.style, { background: "rgba(31, 41, 55, 0.8)", borderRadius: "12px", padding: isMobile ? "12px" : "16px", display: "flex", flexDirection: "row", alignItems: "center", gap: isMobile ? "12px" : "16px", minHeight: isMobile ? "50px" : "60px", width: "100%", boxSizing: "border-box", border: "1px solid rgba(255, 255, 255, 0.1)", transition: "all 0.2s ease" }); // Hover effect for the card optionCard.addEventListener("mouseenter", () => { optionCard.style.background = "rgba(31, 41, 55, 0.9)"; optionCard.style.border = "1px solid rgba(255, 255, 255, 0.2)"; }); optionCard.addEventListener("mouseleave", () => { optionCard.style.background = "rgba(31, 41, 55, 0.8)"; optionCard.style.border = "1px solid rgba(255, 255, 255, 0.1)"; }); const iconContainer = document.createElement("div"); Object.assign(iconContainer.style, { width: isMobile ? "32px" : "40px", height: isMobile ? "32px" : "40px", borderRadius: "8px", display: "flex", alignItems: "center", justifyContent: "center", background: "rgba(59, 130, 246, 0.1)", border: "1px solid rgba(59, 130, 246, 0.2)", flexShrink: "0" }); iconContainer.innerHTML = option.icon || ''; const contentContainer = document.createElement("div"); Object.assign(contentContainer.style, { display: "flex", flexDirection: "column", flex: "1", minWidth: "0" // Allow text truncation }); const title = document.createElement("div"); title.textContent = option.label; Object.assign(title.style, { fontSize: isMobile ? "14px" : "16px", fontWeight: "600", color: "#ffffff", marginBottom: "4px", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }); let control = null; switch (option.type) { case ModType.Info: control = this.createInfoElement(option); break; case ModType.Input: control = this.createInputElement(option); break; case ModType.Toggle: control = this.createToggleButton(option); break; case ModType.Sub: control = this.createSubButton(option); break; case ModType.Slider: control = this.createSliderElement(option); break; case ModType.Click: control = this.createClickButton(option); } const controlContainer = document.createElement("div"); Object.assign(controlContainer.style, { flexShrink: "0", minWidth: isMobile ? "80px" : "120px", maxWidth: isMobile ? "120px" : "200px" }); contentContainer.appendChild(title); if (control) { controlContainer.appendChild(control); } optionCard.appendChild(iconContainer); optionCard.appendChild(contentContainer); optionCard.appendChild(controlContainer); container.appendChild(optionCard); } setActiveCategory(category) { // Fermer le sous-menu s'il est ouvert lors du changement de catégorie if (this.closeSubMenu) { this.closeSubMenu(); } 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 (including subfields) const matchesSearch = this.searchTerm === '' || option.label.toLowerCase().includes(this.searchTerm) || section.category.toLowerCase().includes(this.searchTerm) || // Search in sub-menu fields (option.fields && option.fields.some(field => field.label.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.textAlign = 'center'; noResultsMsg.style.padding = '20px'; noResultsMsg.style.color = '#9CA3AF'; noResultsMsg.style.width = '100%'; gridContainer.appendChild(noResultsMsg); } } } createGridContainer() { const gridContainer = document.createElement("div"); const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); Object.assign(gridContainer.style, { display: "flex", flexDirection: "column", gap: isMobile ? "8px" : "12px", padding: isMobile ? "8px" : "16px", overflowY: "auto", overflowX: "hidden", // Prevent horizontal scrolling maxHeight: isMobile ? "35vh" : "50vh", 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(x = "ALL") { let category = String(x.replace("$", "")); 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 _ = `✨ ${__webpack_require__.g.client.name}'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; } 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.setProperty('-webkit-backdrop-filter', '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; let originalElements = []; let isSubMenuOpen = false; // Gestionnaire d'événement pour ouvrir le sous-menu btn.addEventListener("click", () => { if (!option.fields || option.fields.length === 0) { if (option.onChange) { option.onChange(option.value); } return; } if (isSubMenuOpen) { this.closeSubMenu(); return; } // Sauvegarder et masquer les éléments originaux originalElements = []; const allSections = document.querySelectorAll('.menu-section'); allSections.forEach(section => { originalElements.push(section); section.style.display = 'none'; }); const grid = document.getElementById('kxsMenuGrid'); if (grid) { originalElements.push(grid); grid.style.display = 'none'; } // Créer le conteneur du sous-menu avec glassmorphism amélioré subMenuContainer = document.createElement("div"); subMenuContainer.id = "kxs-submenu"; subMenuContainer.className = "kxs-submenu-container"; // Appliquer l'effet glassmorphism moderne au sous-menu DesignSystem.applyGlassEffect(subMenuContainer, this.kxsClient.isGlassmorphismEnabled ? 'medium' : 'dark', { width: "100%", padding: "10px 0", boxSizing: "border-box", overflowY: "auto", borderRadius: "16px", border: this.kxsClient.isGlassmorphismEnabled ? "1px solid rgba(255, 255, 255, 0.15)" : "1px solid rgba(31, 41, 55, 0.8)", background: this.kxsClient.isGlassmorphismEnabled ? "rgba(17, 24, 39, 0.3)" : "rgba(17, 24, 39, 0.95)", backdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(20px) saturate(180%)" : "none", WebkitBackdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(20px) saturate(180%)" : "none" }); this.blockMousePropagation(subMenuContainer); // Créer l'en-tête du sous-menu avec glassmorphism 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: this.kxsClient.isGlassmorphismEnabled ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(255, 255, 255, 0.1)", paddingLeft: isMobile ? "10px" : "15px", paddingRight: isMobile ? "10px" : "15px", width: "100%", boxSizing: "border-box", background: this.kxsClient.isGlassmorphismEnabled ? "rgba(31, 41, 55, 0.2)" : "rgba(31, 41, 55, 0.3)", borderRadius: "12px", backdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(10px)" : "none", WebkitBackdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(10px)" : "none" }); this.blockMousePropagation(subMenuHeader); // Bouton de retour avec glassmorphism const backBtn = document.createElement("button"); backBtn.innerHTML = ` Back`; DesignSystem.applyGlassEffect(backBtn, this.kxsClient.isGlassmorphismEnabled ? 'light' : 'dark', { background: this.kxsClient.isGlassmorphismEnabled ? "rgba(255, 255, 255, 0.1)" : "rgba(55, 65, 81, 0.8)", border: this.kxsClient.isGlassmorphismEnabled ? "1px solid rgba(255, 255, 255, 0.2)" : "1px solid rgba(75, 85, 99, 0.5)", borderRadius: "8px", color: "#fff", cursor: "pointer", display: "flex", alignItems: "center", gap: "6px", padding: isMobile ? "4px 8px" : "6px 12px", fontSize: isMobile ? "12px" : "14px", backdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(8px)" : "none", WebkitBackdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(8px)" : "none", transition: "all 0.3s ease" }); 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", textShadow: "0 1px 3px rgba(0, 0, 0, 0.3)" }); this.blockMousePropagation(subMenuTitle); // Ajouter l'événement au bouton retour backBtn.addEventListener("click", () => { this.closeSubMenu(); }); // Effet hover pour le bouton retour backBtn.addEventListener("mouseenter", () => { backBtn.style.background = this.kxsClient.isGlassmorphismEnabled ? "rgba(255, 255, 255, 0.15)" : "rgba(75, 85, 99, 0.8)"; backBtn.style.transform = "translateY(-1px)"; }); backBtn.addEventListener("mouseleave", () => { backBtn.style.background = this.kxsClient.isGlassmorphismEnabled ? "rgba(255, 255, 255, 0.1)" : "rgba(55, 65, 81, 0.8)"; backBtn.style.transform = "translateY(0)"; }); // 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 avec glassmorphism 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 ? "8px" : "16px", gridAutoRows: isMobile ? "minmax(100px, auto)" : "minmax(150px, auto)", width: "100%", boxSizing: "border-box" }); this.blockMousePropagation(optionsGrid); // Créer les cartes pour chaque option avec glassmorphism 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 = () => { 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 = 'flex'; } else { el.style.display = 'block'; } }); this.filterOptions(); subMenuContainer = null; isSubMenuOpen = false; }; if (option.onChange) { option.onChange(option.value); } }); this.blockMousePropagation(btn); return btn; } createModCard(mod, container) { const modCard = document.createElement("div"); const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile(); // Appliquer l'effet glassmorphism aux cartes des mods DesignSystem.applyGlassEffect(modCard, this.kxsClient.isGlassmorphismEnabled ? 'medium' : 'dark', { background: this.kxsClient.isGlassmorphismEnabled ? "rgba(31, 41, 55, 0.4)" : "rgba(31, 41, 55, 0.8)", borderRadius: "12px", padding: isMobile ? "12px" : "16px", display: "flex", flexDirection: "column", alignItems: "center", gap: isMobile ? "8px" : "12px", minHeight: isMobile ? "100px" : "150px", border: this.kxsClient.isGlassmorphismEnabled ? "1px solid rgba(255, 255, 255, 0.15)" : "1px solid rgba(255, 255, 255, 0.1)", backdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(12px) saturate(180%)" : "none", WebkitBackdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(12px) saturate(180%)" : "none", boxShadow: this.kxsClient.isGlassmorphismEnabled ? "0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)" : "0 4px 16px rgba(0, 0, 0, 0.2)", transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)", cursor: "default" }); // Effet hover pour les cartes modCard.addEventListener("mouseenter", () => { modCard.style.transform = "translateY(-2px) scale(1.02)"; modCard.style.boxShadow = this.kxsClient.isGlassmorphismEnabled ? "0 12px 40px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.15)" : "0 8px 24px rgba(0, 0, 0, 0.3)"; modCard.style.border = this.kxsClient.isGlassmorphismEnabled ? "1px solid rgba(255, 255, 255, 0.2)" : "1px solid rgba(255, 255, 255, 0.15)"; }); modCard.addEventListener("mouseleave", () => { modCard.style.transform = "translateY(0) scale(1)"; modCard.style.boxShadow = this.kxsClient.isGlassmorphismEnabled ? "0 8px 32px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1)" : "0 4px 16px rgba(0, 0, 0, 0.2)"; modCard.style.border = this.kxsClient.isGlassmorphismEnabled ? "1px solid rgba(255, 255, 255, 0.15)" : "1px solid rgba(255, 255, 255, 0.1)"; }); // Icône avec effet glassmorphism const iconContainer = document.createElement("div"); DesignSystem.applyGlassEffect(iconContainer, this.kxsClient.isGlassmorphismEnabled ? 'light' : 'dark', { width: isMobile ? "32px" : "40px", height: isMobile ? "32px" : "40px", borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: isMobile ? "4px" : "8px", background: this.kxsClient.isGlassmorphismEnabled ? "rgba(59, 130, 246, 0.15)" : "rgba(59, 130, 246, 0.1)", border: this.kxsClient.isGlassmorphismEnabled ? "1px solid rgba(59, 130, 246, 0.3)" : "1px solid rgba(59, 130, 246, 0.2)", backdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(8px)" : "none", WebkitBackdropFilter: this.kxsClient.isGlassmorphismEnabled ? "blur(8px)" : "none" }); iconContainer.innerHTML = mod.icon || ''; // Titre avec effet de texte const title = document.createElement("div"); title.textContent = mod.label; Object.assign(title.style, { fontSize: isMobile ? "14px" : "16px", textAlign: "center", color: "#ffffff", fontWeight: "600", textShadow: "0 1px 2px rgba(0, 0, 0, 0.3)", marginBottom: "8px" }); // Contrôle selon le type let control = null; switch (mod.type) { case ModType.Info: control = this.createInfoElement(mod); break; case ModType.Input: control = this.createInputElement(mod); break; case ModType.Toggle: control = this.createToggleButton(mod); break; case ModType.Slider: control = this.createSliderElement(mod); break; case ModType.Click: control = this.createClickButton(mod); } modCard.appendChild(iconContainer); modCard.appendChild(title); if (control) { modCard.appendChild(control); } container.appendChild(modCard); this.blockMousePropagation(modCard); } // 5. Mise à jour de la méthode pour gérer la fermeture automatique lors des recherches createHeader() { const header = document.createElement("div"); // Mobile detection for reduced styles 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; const discordBtnPadding = isMobile ? '4px 6px' : '6px 12px'; const discordBtnFontSize = isMobile ? 10 : 12; const discordIconSize = isMobile ? 12 : 16; header.style.marginBottom = `${headerMarginBottom}px`; header.innerHTML = `
Logo ${vars_client.acronym_upper} CLIENT v${this.kxsClient.pkg.version}
${ClientSecondaryMenu_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(); }); // Discord button event listener const discordButton = header.querySelector('#discordBtn'); DesignSystem.applyGlassEffect(discordButton, this.kxsClient.isGlassmorphismEnabled ? 'medium' : 'dark'); if (discordButton) { this.blockMousePropagation(discordButton); discordButton.addEventListener('click', () => { // Open Discord invite link in new tab window.open('https://discord.wf/kxsclient', '_blank'); }); } const searchInput = header.querySelector('#kxsSearchInput'); if (searchInput) { this.blockMousePropagation(searchInput, false); // Handler to update search searchInput.addEventListener('input', (e) => { // Fermer le sous-menu si ouvert lors de la recherche if (this.closeSubMenu) { this.closeSubMenu(); } 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(); }); }); // Prevent search bar from automatically refocusing // when user interacts with another text field searchInput.addEventListener('blur', (e) => { // Don't refocus if user clicked on another input const newFocusElement = e.relatedTarget; if (newFocusElement && (newFocusElement.tagName === 'INPUT' || newFocusElement.tagName === 'TEXTAREA')) { // User clicked on another text field, don't refocus return; } // For other cases, only if no other element has 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); } 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() { // 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