// ==UserScript== // @name Kxs Client - Survev.io Client // @namespace https://github.com/Kisakay/KxsClient // @version 1.2.2 // @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 ://zurviv.io/ // @match ://expandedwater.online/ // @match ://localhost:3000/ // @match ://surviv.wf/ // @match ://resurviv.biz/ // @match ://82.67.125.203/ // @match ://leia-uwu.github.io/survev/ // @match ://50v50.online/ // @match ://eu-comp.net/ // @match ://survev.leia-is.gay/ // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM.getValue // @grant GM.setValue // @downloadURL none // ==/UserScript== ; /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ /***/ 908: /***/ ((module, __unused_webpack_exports, __webpack_require__) => { const debug = __webpack_require__(272) const { MAX_LENGTH, MAX_SAFE_INTEGER } = __webpack_require__(874) const { safeRe: re, safeSrc: src, 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 r = new RegExp(`^${this.options.loose ? src[t.PRERELEASELOOSE] : src[t.PRERELEASE]}$`) const match = `-${identifier}`.match(r) 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 /***/ }), /***/ 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 /***/ }), /***/ 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, } /***/ }), /***/ 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 /***/ }), /***/ 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, } /***/ }), /***/ 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. createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER] }|${src[t.NONNUMERICIDENTIFIER]})`) createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE] }|${src[t.NONNUMERICIDENTIFIER]})`) // ## 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*$') /***/ }), /***/ 891: /***/ ((module) => { "use strict"; module.exports = /*#__PURE__*/JSON.parse('{"base_url":"https://kxs.rip","fileName":"KxsClient.user.js","match":["://survev.io/*","*://66.179.254.36/","://zurviv.io/","://expandedwater.online/","://localhost:3000/","://surviv.wf/","://resurviv.biz/","://82.67.125.203/","://leia-uwu.github.io/survev/","://50v50.online/","://eu-comp.net/","://survev.leia-is.gay/"],"grant":["GM_xmlhttpRequest","GM_info","GM.getValue","GM.setValue"]}'); /***/ }), /***/ 330: /***/ ((module) => { "use strict"; module.exports = /*#__PURE__*/JSON.parse('{"name":"kxsclient","version":"1.2.2","main":"index.js","namespace":"https://github.com/Kisakay/KxsClient","icon":"https://kxs.rip/assets/KysClientLogo.png","placeholder":"Kxs Client - Survev.io Client","scripts":{"test":"echo \\"Error: no test specified\\" && exit 1","commits":"oco --yes; npm version patch; git push;"},"keywords":[],"author":"Kisakay","license":"AGPL-3.0","description":"A client to enhance the survev.io in-game experience with many features, as well as future features.","devDependencies":{"@types/semver":"^7.7.0","@types/tampermonkey":"^5.0.4","ts-loader":"^9.5.1","typescript":"^5.7.2","webpack":"^5.97.1","webpack-cli":"^5.1.4"},"dependencies":{"semver":"^7.7.1"}}'); /***/ }) /******/ }); /************************************************************************/ /******/ // 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/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // This entry needs to be wrapped in an IIFE because it needs to be in strict mode. (() => { "use strict"; // EXPORTS __webpack_require__.d(__webpack_exports__, { h3: () => (/* binding */ background_image), fW: () => (/* binding */ kxs_logo) }); // UNUSED EXPORTS: background_song ;// ./src/ButtonManager.ts class MenuButton { constructor(params) { var _a; this.isEnabled = (_a = params.initialState) !== null && _a !== void 0 ? _a : false; this.button = this.createButton(params); } createButton(params) { const button = document.createElement("button"); // Set initial text this.updateButtonText(button, params.text); // Set styles Object.assign(button.style, { backgroundColor: this.getBackgroundColor(params.color), border: "none", color: "#fff", padding: "10px", borderRadius: "5px", width: "100%", marginBottom: "10px", fontSize: "14px", cursor: "pointer", }); // Set click handler button.onclick = () => { this.isEnabled = !this.isEnabled; params.onClick(); if (params.updateText !== false) { this.updateButtonText(button, params.text); this.updateButtonColor(button, params.color); } }; return button; } updateButtonText(button, baseText) { button.textContent = `${baseText} ${this.isEnabled ? "✅" : "❌"}`; } getBackgroundColor(color) { if (color) return color; return this.isEnabled ? "#4CAF50" : "#FF0000"; } updateButtonColor(button, color) { button.style.backgroundColor = this.getBackgroundColor(color); } getElement() { return this.button; } setState(enabled) { this.isEnabled = enabled; this.updateButtonColor(this.button); } } class MenuManager { constructor(menu) { this.buttons = {}; this.menu = menu; } addToggleButton(params) { const button = new MenuButton({ text: params.text, initialState: params.initialState, color: params.color, onClick: params.onClick, updateText: params.updateText, }); this.buttons[params.id] = button; this.menu.appendChild(button.getElement()); return button; } addButton(params) { var _a; const button = document.createElement("button"); // Set initial text button.textContent = params.text; // Set styles Object.assign(button.style, { backgroundColor: (_a = params.color) !== null && _a !== void 0 ? _a : "#007BFF", // Default color border: "none", color: "#fff", padding: "10px", borderRadius: "5px", width: "100%", marginBottom: "10px", fontSize: "14px", cursor: "pointer", }); // Set click handler button.onclick = params.onClick; this.menu.appendChild(button); } getButton(id) { return this.buttons[id]; } } ;// ./src/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 this.DISCORD_WEBHOOK_REGEX.test(url); } 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) { console.error("Error validating webhook:", 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", }; } }); } } WebhookValidator.DISCORD_WEBHOOK_REGEX = /^https:\/\/discord\.com\/api\/webhooks\/\d+\/[\w-]+$/; 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) { console.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: result.username, content: result.isWin ? "🎉 New Victory!" : "Match Ended", embeds: [embed], }; yield this.sendWebhookMessage(message); }); } } ;// ./src/ClientMainMenu.ts var ClientMainMenu_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 KxsMainClientMenu { constructor(kxsClient) { this.kxsClient = kxsClient; // this.setupKeyListeners(); // this.initMenu(); } initMenu() { this.menu = document.createElement("div"); this.menu.id = "kxsMenu"; Object.assign(this.menu.style, { backgroundColor: "rgba(0, 0, 0, 0.8)", padding: "15px", marginLeft: "15px", borderRadius: "10px", boxShadow: "0 4px 10px rgba(0, 0, 0, 0.6)", zIndex: "10001", width: "250px", fontFamily: "Arial, sans-serif", color: "#fff", maxHeight: "400px", overflowY: "auto", }); const title = document.createElement("h2"); title.textContent = "Kxs Client"; title.style.margin = "0 0 10px"; title.style.textAlign = "center"; title.style.fontSize = "18px"; title.style.color = "#FFAE00"; this.menu.appendChild(title); window.onload = () => { const savedBackground = localStorage.getItem("backgroundImage"); if (savedBackground) { const backgroundElement = document.getElementById("background"); if (backgroundElement) { backgroundElement.style.backgroundImage = `url(${savedBackground})`; } } }; const startRowTop = document.getElementById("start-row-top"); if (startRowTop) { startRowTop.appendChild(this.menu); } this.menuManager = new MenuManager(this.menu); this.menuManager.addToggleButton({ id: "fps", text: "Show FPS", initialState: this.kxsClient.isFpsVisible, onClick: () => { this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible; this.kxsClient.updateFpsVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.menuManager.addToggleButton({ id: "ping", text: `Show Ping`, initialState: this.kxsClient.isPingVisible, onClick: () => { this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible; this.kxsClient.updatePingVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.menuManager.addToggleButton({ id: "kills", text: `Show Kills`, initialState: this.kxsClient.isKillsVisible, onClick: () => { this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible; this.kxsClient.updateKillsVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.menuManager.addToggleButton({ id: "uncapFps", text: `Uncap FPS`, initialState: this.kxsClient.isFpsUncapped, onClick: () => { this.kxsClient.updateLocalStorage(); this.kxsClient.toggleFpsUncap(); }, }); this.menuManager.addButton({ id: "hideShow", text: "👀 Hide/Show Menu [P]", color: "#6F42C1", onClick: () => this.toggleMenuVisibility(), }); this.menuManager.addButton({ id: "background", text: `🎨 Change Background`, color: "#007BFF", onClick: () => { const backgroundElement = document.getElementById("background"); if (!backgroundElement) { alert("Element with id 'background' not found."); return; } const choice = prompt("Enter '1' to provide a URL or '2' to upload a local image:"); if (choice === "1") { const newBackgroundUrl = prompt("Enter the URL of the new background image:"); if (newBackgroundUrl) { backgroundElement.style.backgroundImage = `url(${newBackgroundUrl})`; this.kxsClient.saveBackgroundToLocalStorage(newBackgroundUrl); alert("Background updated successfully!"); } } else if (choice === "2") { const fileInput = document.createElement("input"); fileInput.type = "file"; fileInput.accept = "image/*"; fileInput.onchange = (event) => { var _a, _b; const file = (_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]; if (file) { const reader = new FileReader(); reader.onload = () => { backgroundElement.style.backgroundImage = `url(${reader.result})`; this.kxsClient.saveBackgroundToLocalStorage(file); alert("Background updated successfully!"); }; reader.readAsDataURL(file); } }; fileInput.click(); } }, }); this.menuManager.addButton({ id: "webhook", text: `🕸️ Change Discord Webhook`, color: "#007BFF", onClick: () => ClientMainMenu_awaiter(this, void 0, void 0, function* () { const choice = prompt("enter the new discord webhook url:"); if (choice) { const result = yield WebhookValidator.testWebhook(choice); if (result.isValid) { this.kxsClient.discordWebhookUrl = choice; this.kxsClient.discordTracker.setWebhookUrl(choice); this.kxsClient.updateLocalStorage(); alert(result.message); } else { alert(result.message); } } }), }); } setupKeyListeners() { document.addEventListener("keydown", (event) => { if (event.key.toLowerCase() === "p") { this.toggleMenuVisibility(); } }); } toggleMenuVisibility() { var _a; const isVisible = ((_a = this.menu) === null || _a === void 0 ? void 0 : _a.style.display) !== "none"; this.menu.style.display = isVisible ? "none" : "block"; } } ;// ./src/Ping.ts class PingTest { constructor(selectedServer) { this.ptcDataBuf = new ArrayBuffer(1); this.test = { region: selectedServer.region, url: selectedServer.url.startsWith("ws://") || selectedServer.url.startsWith("wss://") ? selectedServer.url : `https://${selectedServer.url}`, // Store the base URL without /ping for HTTP ping: 0, // Initialize to 0 instead of 9999 ws: null, sendTime: 0, retryCount: 0, isConnecting: false, isWebSocket: selectedServer.url.startsWith("ws://") || selectedServer.url.startsWith("wss://"), }; } //check to see if urls match getMatchingGameUrl() { const gameUrls = [ "*://survev.io/*", "*://66.179.254.36/*", "*://zurviv.io/*", "*://expandedwater.online/*", "*://localhost:3000/*", "*://surviv.wf/*", "*://resurviv.biz/*", "*://82.67.125.203/*", "*://leia-uwu.github.io/survev/*", "*://50v50.online/*", "*://eu-comp.net/*", "*://survev.leia-is.gay/*" ]; const currentDomain = window.location.hostname; for (let i = 0; i < gameUrls.length; i++) { const url = new URL(gameUrls[i].replace('*://', 'http://')); if (currentDomain === url.hostname) { return gameUrls[i]; } } console.warn("No matching game URL found for the current domain"); return null; } startPingTest() { if (this.test.isConnecting) return; this.test.isConnecting = true; // We don't need to replace the URL with a matching game URL // because we want to test the ping to the specific server selected // The URL was already properly set in the constructor if (this.test.isWebSocket) { try { const ws = new WebSocket(this.test.url); ws.binaryType = "arraybuffer"; ws.onopen = () => { this.test.ws = ws; this.test.isConnecting = false; this.test.retryCount = 0; this.sendPing(); }; ws.onmessage = (event) => { if (this.test.sendTime === 0) return; const elapsed = Date.now() - this.test.sendTime; this.test.ping = Math.min(Math.round(elapsed), 999); setTimeout(() => this.sendPing(), 250); }; ws.onerror = (error) => { console.error("WebSocket error:", error); this.handleConnectionError(); }; ws.onclose = () => { this.test.ws = null; this.test.isConnecting = false; if (this.test.retryCount < 3) { setTimeout(() => this.startPingTest(), 1000); } }; } catch (error) { console.error("Failed to create WebSocket:", error); this.handleConnectionError(); } } else { this.sendHttpPing(); } } sendHttpPing() { // Use image loading technique to avoid CORS issues this.test.sendTime = Date.now(); // Create a new image element const img = new Image(); // Set up load and error handlers img.onload = () => { const elapsed = Date.now() - this.test.sendTime; this.test.ping = Math.min(Math.round(elapsed), 999); setTimeout(() => this.sendHttpPing(), 250); }; img.onerror = () => { // Even if the image fails to load, we can still measure the time it took to fail // This gives us an approximate ping time const elapsed = Date.now() - this.test.sendTime; this.test.ping = Math.min(Math.round(elapsed), 999); setTimeout(() => this.sendHttpPing(), 250); }; // Add a cache-busting parameter to prevent caching const cacheBuster = Date.now(); const baseUrl = this.test.url.replace('/ping', ''); img.src = `${baseUrl}/favicon.ico?cb=${cacheBuster}`; } handleConnectionError() { this.test.ping = 0; this.test.isConnecting = false; this.test.retryCount++; if (this.test.ws) { this.test.ws.close(); this.test.ws = null; } if (this.test.retryCount < 3) { setTimeout(() => this.startPingTest(), 1000); } } sendPing() { if (this.test.isWebSocket) { if (!this.test.ws || this.test.ws.readyState !== WebSocket.OPEN) { this.handleConnectionError(); return; } try { this.test.sendTime = Date.now(); this.test.ws.send(this.ptcDataBuf); } catch (error) { console.error("Failed to send ping:", error); this.handleConnectionError(); } } } getPingResult() { return { region: this.test.region, ping: this.test.ping || 0, }; } } class PingManager { constructor() { this.currentServer = null; this.pingTest = null; } startPingTest() { 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 ? mainSelectElement.value : null; if (!region || region === this.currentServer) return; this.currentServer = region; this.resetPing(); 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((server) => region.toUpperCase() === server.region.toUpperCase()); if (selectedServer) { this.pingTest = new PingTest(selectedServer); this.pingTest.startPingTest(); } } resetPing() { var _a; if ((_a = this.pingTest) === null || _a === void 0 ? void 0 : _a.test.ws) { this.pingTest.test.ws.close(); this.pingTest.test.ws = null; } this.pingTest = null; } getPingResult() { var _a; return ((_a = this.pingTest) === null || _a === void 0 ? void 0 : _a.getPingResult()) || { region: "", ping: 0 }; } } ;// ./src/ClientHUD.ts class KxsClientHUD { constructor(kxsClient) { this.healthAnimations = []; this.lastHealthValue = 100; this.kxsClient = kxsClient; this.frameCount = 0; this.fps = 0; this.kills = 0; this.isMenuVisible = true; this.pingManager = new PingManager(); 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"); } this.setupWeaponBorderHandler(); this.startUpdateLoop(); this.escapeMenu(); this.initFriendDetector(); if (this.kxsClient.isKillFeedBlint) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', this.initKillFeed); } else { this.initKillFeed(); } } } 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(); console.log("Friend detector initialized with 4-minute cache"); } else { console.warn("Killfeed-contents element not found"); } } } initKillFeed() { this.applyCustomStyles(); this.setupObserver(); } escapeMenu() { const customStyles = ` .ui-game-menu-desktop { background: linear-gradient(135deg, rgba(25, 25, 35, 0.95) 0%, rgba(15, 15, 25, 0.98) 100%) !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; border-radius: 12px !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important; padding: 20px !important; backdrop-filter: blur(10px) !important; max-width: 350px !important; /* max-height: 80vh !important; */ /* Optional: Limit the maximum height */ margin: auto !important; box-sizing: border-box !important; overflow-y: auto !important; /* Allow vertical scrolling if necessary */ } .ui-game-menu-desktop::-webkit-scrollbar { width: 8px !important; } .ui-game-menu-desktop::-webkit-scrollbar-track { background: rgba(25, 25, 35, 0.5) !important; border-radius: 10px !important; } .ui-game-menu-desktop::-webkit-scrollbar-thumb { background-color: #4287f5 !important; border-radius: 10px !important; border: 2px solid rgba(25, 25, 35, 0.5) !important; } .ui-game-menu-desktop::-webkit-scrollbar-thumb:hover { background-color: #5a9eff !important; } .ui-game-menu-desktop { scrollbar-width: thin !important; scrollbar-color: #4287f5 rgba(25, 25, 35, 0.5) !important; } .kxs-header { display: flex; align-items: center; justify-content: flex-start; margin-bottom: 20px; padding: 10px; border-bottom: 2px solid rgba(255, 255, 255, 0.1); } .kxs-logo { width: 30px; height: 30px; margin-right: 10px; border-radius: 6px; } .kxs-title { font-size: 20px; font-weight: 700; color: #ffffff; text-transform: uppercase; text-shadow: 0 0 10px rgba(66, 135, 245, 0.5); font-family: 'Arial', sans-serif; letter-spacing: 2px; } .kxs-title span { color: #4287f5; } .btn-game-menu { background: linear-gradient(135deg, rgba(66, 135, 245, 0.1) 0%, rgba(66, 135, 245, 0.2) 100%) !important; border: 1px solid rgba(66, 135, 245, 0.3) !important; border-radius: 8px !important; color: #ffffff !important; transition: all 0.3s ease !important; margin: 5px 0 !important; padding: 12px !important; font-weight: 600 !important; width: 100% !important; text-align: center !important; display: block !important; box-sizing: border-box !important; } .btn-game-menu:hover { background: linear-gradient(135deg, rgba(66, 135, 245, 0.2) 0%, rgba(66, 135, 245, 0.3) 100%) !important; transform: translateY(-2px) !important; box-shadow: 0 4px 12px rgba(66, 135, 245, 0.2) !important; } .slider-container { background: rgba(66, 135, 245, 0.1) !important; border-radius: 8px !important; padding: 10px 15px !important; margin: 10px 0 !important; width: 100% !important; box-sizing: border-box !important; } .slider-text { color: #ffffff !important; font-size: 14px !important; margin-bottom: 8px !important; text-align: center !important; } .slider { -webkit-appearance: none !important; width: 100% !important; height: 6px !important; border-radius: 3px !important; background: rgba(66, 135, 245, 0.3) !important; outline: none !important; margin: 10px 0 !important; } .slider::-webkit-slider-thumb { -webkit-appearance: none !important; width: 16px !important; height: 16px !important; border-radius: 50% !important; background: #4287f5 !important; cursor: pointer !important; transition: all 0.3s ease !important; } .slider::-webkit-slider-thumb:hover { transform: scale(1.2) !important; box-shadow: 0 0 10px rgba(66, 135, 245, 0.5) !important; } .btns-game-double-row { display: flex !important; justify-content: center !important; gap: 10px !important; margin-bottom: 10px !important; width: 100% !important; } .btn-game-container { flex: 1 !important; } `; const addCustomStyles = () => { const styleElement = document.createElement('style'); styleElement.textContent = customStyles; document.head.appendChild(styleElement); }; const addKxsHeader = () => { const menuContainer = document.querySelector('#ui-game-menu'); if (!menuContainer) return; const header = document.createElement('div'); header.className = 'kxs-header'; const title = document.createElement('span'); title.className = 'kxs-title'; title.innerHTML = 'Kxs CLIENT'; header.appendChild(title); menuContainer.insertBefore(header, menuContainer.firstChild); }; if (document.querySelector('#ui-game-menu')) { addCustomStyles(); addKxsHeader(); } } handleMessage(element) { if (element instanceof HTMLElement && element.classList.contains('killfeed-div')) { const killfeedText = element.querySelector('.killfeed-text'); if (killfeedText instanceof HTMLElement) { if (killfeedText.textContent && killfeedText.textContent.trim() !== '') { if (!killfeedText.hasAttribute('data-glint')) { killfeedText.setAttribute('data-glint', 'true'); element.style.opacity = '1'; setTimeout(() => { element.style.opacity = '0'; }, 5000); } } else { element.style.opacity = '0'; } } } } setupObserver() { const killfeedContents = document.getElementById('ui-killfeed-contents'); if (killfeedContents) { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.target instanceof HTMLElement && mutation.target.classList.contains('killfeed-text')) { const parentDiv = mutation.target.closest('.killfeed-div'); if (parentDiv) { this.handleMessage(parentDiv); } } mutation.addedNodes.forEach((node) => { if (node instanceof HTMLElement) { this.handleMessage(node); } }); }); }); observer.observe(killfeedContents, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['style', 'class'] }); killfeedContents.querySelectorAll('.killfeed-div').forEach(this.handleMessage); } } applyCustomStyles() { const customStyles = document.createElement('style'); if (this.kxsClient.isKillFeedBlint) { customStyles.innerHTML = ` @import url('https://fonts.googleapis.com/css2?family=Oxanium:wght@600&display=swap'); .killfeed-div { position: absolute !important; padding: 5px 10px !important; background: rgba(0, 0, 0, 0.7) !important; border-radius: 5px !important; transition: opacity 0.5s ease-out !important; } .killfeed-text { font-family: 'Oxanium', sans-serif !important; font-weight: bold !important; font-size: 16px !important; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5) !important; background: linear-gradient(90deg, rgb(255, 0, 0), rgb(255, 127, 0), rgb(255, 255, 0), rgb(0, 255, 0), rgb(0, 0, 255), rgb(75, 0, 130), rgb(148, 0, 211), rgb(255, 0, 0)); background-size: 200%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; animation: glint 3s linear infinite; } @keyframes glint { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } .killfeed-div .killfeed-text:empty { display: none !important; } `; } else { customStyles.innerHTML = ` .killfeed-div { position: absolute; padding: 5px 10px; background: rgba(0, 0, 0, 0.7); border-radius: 5px; transition: opacity 0.5s ease-out; } .killfeed-text { font-family: inherit; font-weight: normal; font-size: inherit; color: inherit; text-shadow: none; background: none; } .killfeed-div .killfeed-text:empty { display: none; } `; } document.head.appendChild(customStyles); } handleResize() { const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; for (const name of ['fps', 'kills', 'ping']) { const counterContainer = document.getElementById(`${name}CounterContainer`); if (!counterContainer) continue; const counter = this.kxsClient.counters[name]; if (!counter) continue; const rect = counterContainer.getBoundingClientRect(); const savedPosition = this.getSavedPosition(name); let newPosition = this.calculateSafePosition(savedPosition, rect.width, rect.height, viewportWidth, viewportHeight); this.applyPosition(counterContainer, newPosition); this.savePosition(name, newPosition); } } calculateSafePosition(currentPosition, elementWidth, elementHeight, viewportWidth, viewportHeight) { let { left, top } = currentPosition; if (left + elementWidth > viewportWidth) { left = viewportWidth - elementWidth; } if (left < 0) { left = 0; } if (top + elementHeight > viewportHeight) { top = viewportHeight - elementHeight; } if (top < 0) { top = 0; } return { left, top }; } getSavedPosition(name) { const savedPosition = localStorage.getItem(`${name}CounterPosition`); if (savedPosition) { try { return JSON.parse(savedPosition); } catch (_a) { return this.kxsClient.defaultPositions[name]; } } return this.kxsClient.defaultPositions[name]; } applyPosition(element, position) { element.style.left = `${position.left}px`; element.style.top = `${position.top}px`; } savePosition(name, position) { localStorage.setItem(`${name}CounterPosition`, JSON.stringify(position)); } startUpdateLoop() { var _a; const now = performance.now(); const delta = now - this.kxsClient.lastFrameTime; this.frameCount++; if (delta >= 1000) { this.fps = Math.round((this.frameCount * 1000) / delta); this.frameCount = 0; this.kxsClient.lastFrameTime = now; this.kills = this.kxsClient.getKills(); if (this.kxsClient.isFpsVisible && this.kxsClient.counters.fps) { this.kxsClient.counters.fps.textContent = `FPS: ${this.fps}`; } if (this.kxsClient.isKillsVisible && this.kxsClient.counters.kills) { this.kxsClient.counters.kills.textContent = `Kills: ${this.kills}`; } if (this.kxsClient.isPingVisible && this.kxsClient.counters.ping && this.pingManager) { const result = this.pingManager.getPingResult(); this.kxsClient.counters.ping.textContent = `PING: ${result.ping} ms`; } } this.pingManager.startPingTest(); if (this.kxsClient.animationFrameCallback) { this.kxsClient.animationFrameCallback(() => this.startUpdateLoop()); } this.updateUiElements(); this.updateBoostBars(); this.updateHealthBars(); (_a = this.kxsClient.kill_leader) === null || _a === void 0 ? void 0 : _a.update(this.kills); } initCounter(name, label, initialText) { const counter = document.createElement("div"); counter.id = `${name}Counter`; const counterContainer = document.createElement("div"); counterContainer.id = `${name}CounterContainer`; Object.assign(counterContainer.style, { position: "absolute", left: `${this.kxsClient.defaultPositions[name].left}px`, top: `${this.kxsClient.defaultPositions[name].top}px`, zIndex: "10000", }); Object.assign(counter.style, { color: "white", backgroundColor: "rgba(0, 0, 0, 0.2)", borderRadius: "5px", fontFamily: "Arial, sans-serif", padding: "5px 10px", pointerEvents: "none", cursor: "default", width: `${this.kxsClient.defaultSizes[name].width}px`, height: `${this.kxsClient.defaultSizes[name].height}px`, display: "flex", alignItems: "center", justifyContent: "center", textAlign: "center", resize: "both", overflow: "hidden", }); counter.textContent = `${label}: ${initialText}`; counterContainer.appendChild(counter); const uiTopLeft = document.getElementById("ui-top-left"); if (uiTopLeft) { uiTopLeft.appendChild(counterContainer); } const adjustFontSize = () => { const { width, height } = counter.getBoundingClientRect(); const size = Math.min(width, height) * 0.4; counter.style.fontSize = `${size}px`; }; new ResizeObserver(adjustFontSize).observe(counter); counter.addEventListener("mousedown", (event) => { if (event.button === 1) { this.resetCounter(name, label, initialText); event.preventDefault(); } }); this.kxsClient.makeDraggable(counterContainer, `${name}CounterPosition`); this.kxsClient.counters[name] = counter; } resetCounter(name, label, initialText) { const counter = this.kxsClient.counters[name]; const container = document.getElementById(`${name}CounterContainer`); if (!counter || !container) return; // Reset only this counter's position and size Object.assign(container.style, { left: `${this.kxsClient.defaultPositions[name].left}px`, top: `${this.kxsClient.defaultPositions[name].top}px`, }); Object.assign(counter.style, { width: `${this.kxsClient.defaultSizes[name].width}px`, height: `${this.kxsClient.defaultSizes[name].height}px`, fontSize: "18px", }); counter.textContent = `${label}: ${initialText}`; // Clear the saved position for this counter only localStorage.removeItem(`${name}CounterPosition`); } updateBoostBars() { const boostCounter = document.querySelector("#ui-boost-counter"); if (boostCounter) { const boostBars = boostCounter.querySelectorAll(".ui-boost-base .ui-bar-inner"); let totalBoost = 0; const weights = [25, 25, 40, 10]; boostBars.forEach((bar, index) => { const width = parseFloat(bar.style.width); if (!isNaN(width)) { totalBoost += width * (weights[index] / 100); } }); const averageBoost = Math.round(totalBoost); let boostDisplay = boostCounter.querySelector(".boost-display"); if (!boostDisplay) { boostDisplay = document.createElement("div"); boostDisplay.classList.add("boost-display"); Object.assign(boostDisplay.style, { position: "absolute", bottom: "75px", right: "335px", color: "#FF901A", backgroundColor: "rgba(0, 0, 0, 0.4)", padding: "5px 10px", borderRadius: "5px", fontFamily: "Arial, sans-serif", fontSize: "14px", zIndex: "10", textAlign: "center", }); boostCounter.appendChild(boostDisplay); } boostDisplay.textContent = `AD: ${averageBoost}%`; } } setupWeaponBorderHandler() { const weaponContainers = Array.from(document.getElementsByClassName("ui-weapon-switch")); weaponContainers.forEach((container) => { if (container.id === "ui-weapon-id-4") { container.style.border = "3px solid #2f4032"; } else { container.style.border = "3px solid #FFFFFF"; } }); const weaponNames = Array.from(document.getElementsByClassName("ui-weapon-name")); const WEAPON_COLORS = { ORANGE: '#FFAE00', BLUE: '#007FFF', GREEN: '#0f690d', RED: '#FF0000', BLACK: '#000000', OLIVE: '#808000', ORANGE_RED: '#FF4500', PURPLE: '#800080', TEAL: '#008080', BROWN: '#A52A2A', PINK: '#FFC0CB', DEFAULT: '#FFFFFF' }; const WEAPON_COLOR_MAPPING = { ORANGE: ['CZ-3A1', 'G18C', 'M9', 'M93R', 'MAC-10', 'MP5', 'P30L', 'DUAL P30L', 'UMP9', 'VECTOR', 'VSS', 'FLAMETHROWER'], BLUE: ['AK-47', 'OT-38', 'OTS-38', 'M39 EMR', 'DP-28', 'MOSIN-NAGANT', 'SCAR-H', 'SV-98', 'M1 GARAND', 'PKP PECHENEG', 'AN-94', 'BAR M1918', 'BLR 81', 'SVD-63', 'M134', 'WATER GUN', 'GROZA', 'GROZA-S'], GREEN: ['FAMAS', 'M416', 'M249', 'QBB-97', 'MK 12 SPR', 'M4A1-S', 'SCOUT ELITE', 'L86A2'], RED: ['M870', 'MP220', 'SAIGA-12', 'SPAS-12', 'USAS-12', 'SUPER 90', 'LASR GUN', 'M1100'], BLACK: ['DEAGLE 50', 'RAINBOW BLASTER'], OLIVE: ['AWM-S', 'MK 20 SSR'], ORANGE_RED: ['FLARE GUN'], PURPLE: ['MODEL 94', 'PEACEMAKER', 'VECTOR (.45 ACP)', 'M1911', 'M1A1', 'MK45G'], TEAL: ['M79'], BROWN: ['POTATO CANNON', 'SPUD GUN'], PINK: ['HEART CANNON'], DEFAULT: [] }; weaponNames.forEach((weaponNameElement) => { const weaponContainer = weaponNameElement.closest(".ui-weapon-switch"); const observer = new MutationObserver(() => { var _a, _b, _c; const weaponName = ((_b = (_a = weaponNameElement.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === null || _b === void 0 ? void 0 : _b.toUpperCase()) || ''; const colorKey = (((_c = Object.entries(WEAPON_COLOR_MAPPING) .find(([_, weapons]) => weapons.includes(weaponName))) === null || _c === void 0 ? void 0 : _c[0]) || 'DEFAULT'); if (weaponContainer && weaponContainer.id !== "ui-weapon-id-4") { weaponContainer.style.border = `3px solid ${WEAPON_COLORS[colorKey]}`; } }); observer.observe(weaponNameElement, { childList: true, characterData: true, subtree: true }); }); } updateUiElements() { const currentUrl = window.location.href; const isSpecialUrl = /\/#\w+/.test(currentUrl); const playerOptions = document.getElementById("player-options"); const teamMenuContents = document.getElementById("team-menu-contents"); const startMenuContainer = document.querySelector("#start-menu .play-button-container"); // Update counters draggable state based on LSHIFT menu visibility this.updateCountersDraggableState(); if (!playerOptions) return; if (isSpecialUrl && teamMenuContents && playerOptions.parentNode !== teamMenuContents) { teamMenuContents.appendChild(playerOptions); } else if (!isSpecialUrl && startMenuContainer && playerOptions.parentNode !== startMenuContainer) { const firstChild = startMenuContainer.firstChild; startMenuContainer.insertBefore(playerOptions, firstChild); } const teamMenu = document.getElementById("team-menu"); if (teamMenu) { teamMenu.style.height = "355px"; } const menuBlocks = document.querySelectorAll(".menu-block"); menuBlocks.forEach((block) => { block.style.maxHeight = "355px"; }); //scalable? } updateMenuButtonText() { const hideButton = document.getElementById("hideMenuButton"); hideButton.textContent = this.isMenuVisible ? "Hide Menu [P]" : "Show Menu [P]"; } updateHealthBars() { const healthBars = document.querySelectorAll("#ui-health-container"); healthBars.forEach((container) => { var _a, _b; const bar = container.querySelector("#ui-health-actual"); if (bar) { const currentHealth = Math.round(parseFloat(bar.style.width)); let percentageText = container.querySelector(".health-text"); // Create or update percentage text if (!percentageText) { percentageText = document.createElement("span"); percentageText.classList.add("health-text"); Object.assign(percentageText.style, { width: "100%", textAlign: "center", marginTop: "5px", color: "#333", fontSize: "20px", fontWeight: "bold", position: "absolute", zIndex: "10", }); container.appendChild(percentageText); } // Check for health change if (currentHealth !== this.lastHealthValue) { const healthChange = currentHealth - this.lastHealthValue; if (healthChange !== 0) { this.showHealthChangeAnimation(container, healthChange); } this.lastHealthValue = currentHealth; } if (this.kxsClient.isHealthWarningEnabled) { (_a = this.kxsClient.healWarning) === null || _a === void 0 ? void 0 : _a.update(currentHealth); } else { (_b = this.kxsClient.healWarning) === null || _b === void 0 ? void 0 : _b.hide(); } percentageText.textContent = `${currentHealth}%`; // Update animations this.updateHealthAnimations(); } }); } showHealthChangeAnimation(container, change) { const animation = document.createElement("div"); const isPositive = change > 0; Object.assign(animation.style, { position: "absolute", color: isPositive ? "#2ecc71" : "#e74c3c", fontSize: "24px", fontWeight: "bold", fontFamily: "Arial, sans-serif", textShadow: "2px 2px 4px rgba(0,0,0,0.3)", pointerEvents: "none", zIndex: "100", opacity: "1", top: "50%", right: "-80px", // Position à droite de la barre de vie transform: "translateY(-50%)", // Centre verticalement whiteSpace: "nowrap", // Empêche le retour à la ligne }); // Check if change is a valid number before displaying it if (!isNaN(change)) { animation.textContent = `${isPositive ? "+" : ""}${change} HP`; } else { // Skip showing animation if change is NaN return; } container.appendChild(animation); this.healthAnimations.push({ element: animation, startTime: performance.now(), duration: 1500, // Animation duration in milliseconds value: change, }); } updateCountersDraggableState() { var _a; const isMenuOpen = ((_a = this.kxsClient.secondaryMenu) === null || _a === void 0 ? void 0 : _a.getMenuVisibility()) || false; const counters = ['fps', 'kills', 'ping']; counters.forEach(name => { const counter = document.getElementById(`${name}Counter`); if (counter) { // Mise à jour des propriétés de draggabilité counter.style.pointerEvents = isMenuOpen ? 'auto' : 'none'; counter.style.cursor = isMenuOpen ? 'move' : 'default'; // Mise à jour de la possibilité de redimensionnement counter.style.resize = isMenuOpen ? 'both' : 'none'; } }); } updateHealthAnimations() { const currentTime = performance.now(); this.healthAnimations = this.healthAnimations.filter(animation => { const elapsed = currentTime - animation.startTime; const progress = Math.min(elapsed / animation.duration, 1); if (progress < 1) { // Update animation position and opacity // Maintenant l'animation se déplace horizontalement vers la droite const translateX = progress * 20; // Déplacement horizontal Object.assign(animation.element.style, { transform: `translateY(-50%) translateX(${translateX}px)`, opacity: String(1 - progress), }); return true; } else { // Remove completed animation animation.element.remove(); return false; } }); } } ;// ./src/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/HealthWarning.ts class HealthWarning { constructor(kxsClient) { this.warningElement = null; this.kxsClient = kxsClient; this.createWarningElement(); this.setFixedPosition(); } createWarningElement() { const warning = document.createElement("div"); const uiTopLeft = document.getElementById("ui-top-left"); warning.style.cssText = ` position: fixed; background: rgba(0, 0, 0, 0.8); border: 2px solid #ff0000; border-radius: 5px; padding: 10px 15px; color: #ff0000; font-family: Arial, sans-serif; font-size: 14px; z-index: 9999; display: none; backdrop-filter: blur(5px); pointer-events: none; `; 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 = "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; // Example this.warningElement.style.top = "742px"; this.warningElement.style.left = "285px"; //OR use transform use somethin like, this.warningElement.style.transform = `translate(20px, 20px)`; } 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; this.warningElement.style.display = "none"; } update(health) { if (health <= 30 && health > 0) { this.show(health); } else { this.hide(); } } } ;// ./src/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) { 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.getPlayerName() === (killLeaderNameElement === null || killLeaderNameElement === void 0 ? void 0 : killLeaderNameElement.textContent); } update(myKills) { if (!this.kxsClient.isKillLeaderTrackerEnabled) return; const killLeaderElement = document.querySelector("#ui-kill-leader-count"); this.killLeaderKillCount = parseInt((killLeaderElement === null || killLeaderElement === void 0 ? void 0 : killLeaderElement.textContent) || "0", 10); if (myKills > this.lastKnownKills) { if (this.killLeaderKillCount === 0) { // Pas encore de kill leader, encourager le joueur à atteindre 3 kills this.showEncouragement(0, false, true); } else if (this.killLeaderKillCount < this.MINIMUM_KILLS_FOR_LEADER) { // Ne rien faire si le kill leader n'a pas atteint le minimum requis return; } else if (this.isKillLeader()) { 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/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.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"; } 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/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/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()); }); }; const packageInfo = __webpack_require__(330); const config = __webpack_require__(891); class UpdateChecker { constructor(kxsClient) { this.remoteScriptUrl = config.base_url + "/download/latest-dev.js"; this.kxsClient = kxsClient; if (this.kxsClient.isAutoUpdateEnabled) { this.checkForUpdate(); } } downloadScript() { return UpdateChecker_awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { GM.xmlHttpRequest({ method: "GET", url: this.remoteScriptUrl, headers: { "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0" }, nocache: true, responseType: "blob", onload: (response) => { if (response.status === 200) { const blob = new Blob([response.response], { type: 'application/javascript' }); const downloadUrl = window.URL.createObjectURL(blob); const downloadLink = document.createElement('a'); downloadLink.href = downloadUrl; downloadLink.download = 'KxsClient.user.js'; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); window.URL.revokeObjectURL(downloadUrl); resolve(); } else { reject(new Error("Error downloading script: " + response.statusText)); } }, onerror: (error) => { reject(new Error("Error during script download: " + error)); } }); }); }); } getNewScriptVersion() { return UpdateChecker_awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { GM.xmlHttpRequest({ method: "GET", url: this.remoteScriptUrl, headers: { "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0" }, nocache: true, onload: (response) => { if (response.status === 200) { const scriptContent = response.responseText; const versionMatch = scriptContent.match(/\/\/\s*@version\s+([\d.]+)/); if (versionMatch && versionMatch[1]) { resolve(versionMatch[1]); } else { reject(new Error("Script version was not found in the file.")); } } else { reject(new Error("Error retrieving remote script: " + response.statusText)); } }, onerror: (error) => { reject(new Error("Error during remote script request: " + 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 = "400px"; 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)"; const header = document.createElement("div"); header.style.display = "flex"; header.style.alignItems = "center"; header.style.marginBottom = "15px"; const title = document.createElement("h3"); title.textContent = "Download Update"; title.style.margin = "0"; title.style.fontSize = "16px"; title.style.fontWeight = "600"; header.appendChild(title); const closeButton = document.createElement("button"); closeButton.innerHTML = "×"; closeButton.style.marginLeft = "auto"; closeButton.style.border = "none"; closeButton.style.background = "none"; closeButton.style.fontSize = "20px"; closeButton.style.cursor = "pointer"; closeButton.style.padding = "0 5px"; closeButton.onclick = () => modal.remove(); header.appendChild(closeButton); const content = document.createElement("div"); content.innerHTML = `A new version of KxsClient is available!
Locale: ${this.getCurrentScriptVersion()} | On web: ${this.hostedScriptVersion}
Click the button below to update now.`; content.style.marginBottom = "20px"; content.style.color = "rgb(75, 85, 99)"; const updateButton = document.createElement("button"); updateButton.textContent = "Update Now"; updateButton.style.backgroundColor = "rgb(59, 130, 246)"; updateButton.style.color = "white"; updateButton.style.padding = "8px 16px"; updateButton.style.borderRadius = "6px"; updateButton.style.border = "none"; updateButton.style.cursor = "pointer"; updateButton.style.width = "100%"; updateButton.onclick = () => UpdateChecker_awaiter(this, void 0, void 0, function* () { try { yield this.downloadScript(); this.kxsClient.nm.showNotification("Download started", "success", 2300); modal.remove(); } catch (error) { this.kxsClient.nm.showNotification("Download failed: " + error.message, "info", 5000); } }); modal.appendChild(header); modal.appendChild(content); modal.appendChild(updateButton); document.body.appendChild(modal); } getCurrentScriptVersion() { return packageInfo.version; } } ;// ./src/DiscordRichPresence.ts const DiscordRichPresence_packageInfo = __webpack_require__(330); 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 = () => { console.log('WebSocket connection established'); }; this.ws.onmessage = (event) => { const data = JSON.parse(event.data); this.handleMessage(data); }; this.ws.onerror = (error) => { this.kxsClient.nm.showNotification('WebSocket error: ' + error.type, 'error', 5000); }; this.ws.onclose = () => { this.kxsClient.nm.showNotification('Disconnected from Discord gateway', 'info', 5000); clearInterval(this.heartbeatInterval); this.isAuthenticated = false; }; } identify() { const payload = { op: 2, d: { token: this.kxsClient.discordToken, properties: { $os: 'linux', $browser: 'chrome', $device: 'chrome' }, presence: { activities: [{ name: "KxsClient", type: 0, application_id: "1321193265533550602", assets: { large_image: "mp:app-assets/1321193265533550602/1322173537326338058.png?size=512", large_text: "KxsClient v" + DiscordRichPresence_packageInfo.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(); this.kxsClient.nm.showNotification('Started Discord RPC', 'success', 3000); break; case 11: // Heartbeat ACK console.log('Heartbeat acknowledged'); break; case 0: // Dispatch this.sequence = data.s; if (data.t === 'READY') { this.isAuthenticated = true; this.kxsClient.nm.showNotification('Connected to Discord gateway', 'success', 2500); } 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/NotificationManager.ts class NotificationManager { constructor() { this.notifications = []; this.NOTIFICATION_HEIGHT = 65; // Height + margin this.NOTIFICATION_MARGIN = 10; 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"); // Base styles Object.assign(notification.style, { position: "fixed", top: "20px", left: "20px", padding: "12px 20px", backgroundColor: "#333333", color: "white", zIndex: "9999", minWidth: "200px", borderRadius: "4px", display: "flex", alignItems: "center", gap: "10px", transform: "translateX(-120%)", opacity: "0", boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)" }); // 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 const progressBar = document.createElement("div"); Object.assign(progressBar.style, { height: "4px", backgroundColor: "#e6f3ff", 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 setTimeout(() => { notification.style.animation = "slideOut 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards"; setTimeout(() => { this.removeNotification(notification); notification.remove(); }, 500); }, duration); } } ;// ./src/ClientSecondaryMenu.ts class KxsLegacyClientSecondaryMenu { constructor(kxsClient) { this.kxsClient = kxsClient; this.isClientMenuVisible = false; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.sections = []; this.menu = document.createElement("div"); this.boundShiftListener = this.handleShiftPress.bind(this); this.boundMouseDownListener = this.handleMouseDown.bind(this); this.boundMouseMoveListener = this.handleMouseMove.bind(this); this.boundMouseUpListener = this.handleMouseUp.bind(this); this.initMenu(); this.addShiftListener(); this.addDragListeners(); } handleShiftPress(event) { if (event.key === "Shift" && event.location == 2) { this.clearMenu(); this.toggleMenuVisibility(); this.loadOption(); } } handleMouseDown(e) { if (e.target instanceof HTMLElement && !e.target.matches("input, select, button")) { 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"; } } handleMouseMove(e) { if (!this.isDragging) return; e.preventDefault(); const newX = e.clientX - this.dragOffset.x; const newY = e.clientY - this.dragOffset.y; const maxX = window.innerWidth - this.menu.offsetWidth; const maxY = window.innerHeight - this.menu.offsetHeight; this.menu.style.left = `${Math.max(0, Math.min(newX, maxX))}px`; this.menu.style.top = `${Math.max(0, Math.min(newY, maxY))}px`; } handleMouseUp() { this.isDragging = false; this.menu.style.cursor = "move"; } initMenu() { this.menu.id = "kxsMenuIG"; this.applyMenuStyles(); this.createHeader(); document.body.appendChild(this.menu); } loadOption() { let HUD = this.addSection("HUD"); this.addOption(HUD, { label: "Use Legacy Menu", value: this.kxsClient.isLegaySecondaryMenu, type: "toggle", onChange: (value) => { this.kxsClient.isLegaySecondaryMenu = !this.kxsClient.isLegaySecondaryMenu; this.kxsClient.updateLocalStorage(); this.kxsClient.secondaryMenu = new KxsClientSecondaryMenu(this.kxsClient); this.destroy(); }, }); this.addOption(HUD, { label: "Clean Main Menu", value: this.kxsClient.isMainMenuCleaned, type: "toggle", onChange: (value) => { this.kxsClient.isMainMenuCleaned = !this.kxsClient.isMainMenuCleaned; this.kxsClient.MainMenuCleaning(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Show Ping", value: this.kxsClient.isPingVisible, type: "toggle", onChange: (value) => { this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible; this.kxsClient.updatePingVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Show FPS", value: this.kxsClient.isFpsVisible, type: "toggle", onChange: (value) => { this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible; this.kxsClient.updateFpsVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Show Kills", value: this.kxsClient.isKillsVisible, type: "toggle", onChange: (value) => { this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible; this.kxsClient.updateKillsVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Kill Feed Blint Text", value: this.kxsClient.isKillFeedBlint, type: "toggle", onChange: (value) => { this.kxsClient.isKillFeedBlint = !this.kxsClient.isKillFeedBlint; this.kxsClient.updateLocalStorage(); }, }); let musicSection = this.addSection("Music"); this.addOption(musicSection, { label: "Death sound", value: this.kxsClient.isDeathSoundEnabled, type: "toggle", onChange: (value) => { this.kxsClient.isDeathSoundEnabled = !this.kxsClient.isDeathSoundEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(musicSection, { label: "Win sound", value: this.kxsClient.isWinSoundEnabled, type: "toggle", onChange: (value) => { this.kxsClient.isWinSoundEnabled = !this.kxsClient.isWinSoundEnabled; this.kxsClient.updateLocalStorage(); }, }); let pluginsSection = this.addSection("Plugins"); this.addOption(pluginsSection, { label: "Webhook URL", value: this.kxsClient.discordWebhookUrl || "", type: "input", onChange: (value) => { value = value.toString().trim(); this.kxsClient.discordWebhookUrl = value; this.kxsClient.discordTracker.setWebhookUrl(value); this.kxsClient.updateLocalStorage(); }, }); this.addOption(pluginsSection, { label: "Heal Warning", value: this.kxsClient.isHealthWarningEnabled, type: "toggle", onChange: (value) => { this.kxsClient.isHealthWarningEnabled = !this.kxsClient.isHealthWarningEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(pluginsSection, { label: "Update Checker", value: this.kxsClient.isAutoUpdateEnabled, type: "toggle", onChange: (value) => { this.kxsClient.isAutoUpdateEnabled = !this.kxsClient.isAutoUpdateEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Spotify Player`, value: this.kxsClient.isSpotifyPlayerEnabled, type: "toggle", onChange: () => { this.kxsClient.isSpotifyPlayerEnabled = !this.kxsClient.isSpotifyPlayerEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.toggleSpotifyMenu(); }, }); this.addOption(pluginsSection, { label: `Uncap FPS`, value: this.kxsClient.isFpsUncapped, type: "toggle", onChange: () => { this.kxsClient.isFpsUncapped = !this.kxsClient.isFpsUncapped; this.kxsClient.toggleFpsUncap(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(pluginsSection, { label: `Winning Animation`, value: this.kxsClient.isWinningAnimationEnabled, type: "toggle", onChange: () => { this.kxsClient.isWinningAnimationEnabled = !this.kxsClient.isWinningAnimationEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(pluginsSection, { label: `Rich Presence (Account token required)`, value: this.kxsClient.discordToken || "", type: "input", onChange: (value) => { value = value.toString().trim(); this.kxsClient.discordToken = this.kxsClient.parseToken(value); this.kxsClient.discordRPC.disconnect(); this.kxsClient.discordRPC.connect(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(pluginsSection, { label: `Kill Leader Tracking`, value: this.kxsClient.isKillLeaderTrackerEnabled, type: "toggle", onChange: (value) => { this.kxsClient.isKillLeaderTrackerEnabled = !this.kxsClient.isKillLeaderTrackerEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(pluginsSection, { label: `Friends Detector (separe with ',')`, value: this.kxsClient.all_friends, type: "input", onChange: (value) => { this.kxsClient.all_friends = value; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Change Background`, value: true, type: "click", onChange: () => { const backgroundElement = document.getElementById("background"); if (!backgroundElement) { alert("Element with id 'background' not found."); return; } const choice = prompt("Enter '0' to default Kxs background, '1' to provide a URL or '2' to upload a local image:"); if (choice === "0") { localStorage.removeItem("lastBackgroundUrl"); localStorage.removeItem("lastBackgroundFile"); localStorage.removeItem("lastBackgroundType"); localStorage.removeItem("lastBackgroundValue"); } 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(); } }, }); } clearMenu() { this.sections.forEach((section) => { if (section.element) { section.element.remove(); } }); this.sections = []; } applyMenuStyles() { Object.assign(this.menu.style, { backgroundColor: "rgba(30, 30, 30, 0.95)", padding: "15px", borderRadius: "10px", boxShadow: "0 4px 15px rgba(0, 0, 0, 0.7)", zIndex: "10001", width: "300px", fontFamily: "Arial, sans-serif", color: "#fff", maxHeight: "500px", overflowY: "auto", position: "fixed", top: "15%", left: "10%", cursor: "move", display: "none", }); } createHeader() { const title = document.createElement("h2"); title.textContent = "KxsClient alpha"; Object.assign(title.style, { margin: "0 0 10px", textAlign: "center", fontSize: "18px", color: "#FFAE00", }); const subtitle = document.createElement("p"); subtitle.textContent = "reset with tab"; Object.assign(subtitle.style, { margin: "0 0 10px", textAlign: "center", fontSize: "12px", color: "#ccc", }); this.menu.appendChild(title); this.menu.appendChild(subtitle); } addSection(title) { const section = { title, options: [], }; const sectionElement = document.createElement("div"); sectionElement.className = "menu-section"; const sectionTitle = document.createElement("h3"); sectionTitle.textContent = title; Object.assign(sectionTitle.style, { margin: "15px 0 10px", fontSize: "16px", color: "#4CAF50", }); sectionElement.appendChild(sectionTitle); this.menu.appendChild(sectionElement); // Stocker la référence à l'élément DOM section.element = sectionElement; this.sections.push(section); return section; } addOption(section, option) { section.options.push(option); const optionDiv = document.createElement("div"); Object.assign(optionDiv.style, { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "8px", padding: "4px", borderRadius: "4px", backgroundColor: "rgba(255, 255, 255, 0.1)", }); const label = document.createElement("span"); label.textContent = option.label; label.style.color = "#fff"; let valueElement = null; switch (option.type) { case "toggle": valueElement = this.createToggleElement(option); break; case "input": valueElement = this.createInputElement(option); break; case "click": valueElement = this.createClickElement(option); break; } optionDiv.appendChild(label); optionDiv.appendChild(valueElement); // Utiliser la référence stockée à l'élément de section if (section.element) { section.element.appendChild(optionDiv); } } createToggleElement(option) { const toggle = document.createElement("div"); toggle.style.cursor = "pointer"; toggle.style.color = option.value ? "#4CAF50" : "#ff4444"; toggle.textContent = String(option.value); toggle.addEventListener("click", () => { var _a; const newValue = !option.value; option.value = newValue; toggle.textContent = String(newValue); toggle.style.color = newValue ? "#4CAF50" : "#ff4444"; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, newValue); }); return toggle; } createClickElement(option) { const button = document.createElement("button"); button.textContent = option.label; button.style.backgroundColor = "rgba(255, 255, 255, 0.1)"; button.style.border = "none"; button.style.borderRadius = "3px"; button.style.color = "#FFAE00"; button.style.padding = "2px 5px"; button.style.cursor = "pointer"; button.style.fontSize = "12px"; button.addEventListener("click", () => { var _a; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, true); }); return button; } createInputElement(option) { const input = document.createElement("input"); input.type = "text"; input.value = String(option.value); Object.assign(input.style, { backgroundColor: "rgba(255, 255, 255, 0.1)", border: "none", borderRadius: "3px", color: "#FFAE00", padding: "2px 5px", width: "60px", textAlign: "right", }); input.addEventListener("change", () => { var _a; option.value = input.value; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, input.value); }); return input; } addShiftListener() { window.addEventListener("keydown", this.boundShiftListener); } addDragListeners() { this.menu.addEventListener("mousedown", this.boundMouseDownListener); window.addEventListener("mousemove", this.boundMouseMoveListener); window.addEventListener("mouseup", this.boundMouseUpListener); } toggleMenuVisibility() { this.isClientMenuVisible = !this.isClientMenuVisible; this.kxsClient.nm.showNotification(this.isClientMenuVisible ? "Opening menu..." : "Closing menu...", "info", 1400); this.menu.style.display = this.isClientMenuVisible ? "block" : "none"; } destroy() { // Remove event listeners window.removeEventListener("keydown", this.boundShiftListener); this.menu.removeEventListener("mousedown", this.boundMouseDownListener); window.removeEventListener("mousemove", this.boundMouseMoveListener); window.removeEventListener("mouseup", this.boundMouseUpListener); // Remove all section elements and clear sections array this.sections.forEach(section => { if (section.element) { // Remove all option elements within the section const optionElements = section.element.querySelectorAll("div"); optionElements.forEach(element => { // Remove event listeners from toggle and input elements const interactive = element.querySelector("div, input"); if (interactive) { interactive.replaceWith(interactive.cloneNode(true)); } element.remove(); }); section.element.remove(); } }); this.sections = []; // Remove the menu from DOM this.menu.remove(); // Reset instance variables this.isClientMenuVisible = false; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.menu = null; // Clear references this.kxsClient = null; this.boundShiftListener = null; this.boundMouseDownListener = null; this.boundMouseMoveListener = null; this.boundMouseUpListener = null; } getMenuVisibility() { return this.isClientMenuVisible; } } ;// ./src/ClientSecondaryMenuRework.ts class KxsClientSecondaryMenu { constructor(kxsClient) { this.searchTerm = ''; this.shiftListener = (event) => { if (event.key === "Shift" && event.location == 2) { this.clearMenu(); this.toggleMenuVisibility(); this.loadOption(); // Ensure options are displayed after loading this.filterOptions(); } }; this.mouseMoveListener = (e) => { if (this.isDragging) { const x = e.clientX - this.dragOffset.x; const y = e.clientY - this.dragOffset.y; this.menu.style.transform = 'none'; this.menu.style.left = `${x}px`; this.menu.style.top = `${y}px`; } }; this.mouseUpListener = () => { this.isDragging = false; this.menu.style.cursor = "grab"; }; this.kxsClient = kxsClient; this.isClientMenuVisible = false; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.sections = []; this.allOptions = []; this.activeCategory = "ALL"; this.menu = document.createElement("div"); this.initMenu(); this.addShiftListener(); this.addDragListeners(); } initMenu() { this.menu.id = "kxsMenuIG"; this.applyMenuStyles(); this.createHeader(); this.createGridContainer(); document.body.appendChild(this.menu); } applyMenuStyles() { Object.assign(this.menu.style, { backgroundColor: "rgba(17, 24, 39, 0.95)", padding: "20px", borderRadius: "12px", boxShadow: "0 4px 20px rgba(0, 0, 0, 0.8)", zIndex: "10001", width: "800px", fontFamily: "'Segoe UI', Arial, sans-serif", color: "#fff", maxHeight: "80vh", overflowY: "auto", overflowX: "hidden", // Prevent horizontal scrolling position: "fixed", top: "10%", left: "50%", transform: "translateX(-50%)", display: "none", boxSizing: "border-box", // Include padding in width calculation }); } createHeader() { const header = document.createElement("div"); header.style.marginBottom = "20px"; header.innerHTML = `
Logo KXS CLIENT
${["ALL", "HUD", "SERVER", "MECHANIC"].map(cat => ` `).join('')}
`; header.querySelectorAll('.category-btn').forEach(btn => { btn.addEventListener('click', (e) => { const category = e.target.dataset.category; if (category) { this.setActiveCategory(category); } }); }); const closeButton = header.querySelector('button'); closeButton === null || closeButton === void 0 ? void 0 : closeButton.addEventListener('click', () => { this.toggleMenuVisibility(); }); const searchInput = header.querySelector('#kxsSearchInput'); if (searchInput) { // Gestionnaire pour mettre à jour la recherche searchInput.addEventListener('input', (e) => { this.searchTerm = e.target.value.toLowerCase(); this.filterOptions(); }); // Prevent keys from being interpreted by the game // We only block the propagation of keyboard events, except for special keys ['keydown', 'keyup', 'keypress'].forEach(eventType => { searchInput.addEventListener(eventType, (e) => { const keyEvent = e; // Don't block special keys (Escape, Shift) if (keyEvent.key === 'Escape' || (keyEvent.key === 'Shift' && keyEvent.location === 2)) { return; // Let the event propagate normally } // Block propagation for all other keys e.stopPropagation(); }); }); // Assurer que le champ garde le focus searchInput.addEventListener('blur', () => { // Retarder légèrement pour permettre d'autres interactions setTimeout(() => { if (this.isClientMenuVisible) { searchInput.focus(); } }, 100); }); } this.menu.appendChild(header); } clearMenu() { const gridContainer = document.getElementById('kxsMenuGrid'); if (gridContainer) { gridContainer.innerHTML = ''; } // Reset search term when clearing menu this.searchTerm = ''; const searchInput = document.getElementById('kxsSearchInput'); if (searchInput) { searchInput.value = ''; } } loadOption() { // Clear existing options to avoid duplicates this.allOptions = []; let HUD = this.addSection("HUD", 'HUD'); let MECHANIC = this.addSection("MECHANIC", 'MECHANIC'); let SERVER = this.addSection("SERVER", 'SERVER'); this.addOption(HUD, { label: "Clean Main Menu", value: this.kxsClient.isMainMenuCleaned, category: "HUD", icon: ' clean ', type: "toggle", onChange: (value) => { this.kxsClient.isMainMenuCleaned = !this.kxsClient.isMainMenuCleaned; this.kxsClient.MainMenuCleaning(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Show Ping", value: this.kxsClient.isPingVisible, category: "HUD", icon: '', type: "toggle", onChange: (value) => { this.kxsClient.isPingVisible = !this.kxsClient.isPingVisible; this.kxsClient.updatePingVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Show FPS", value: this.kxsClient.isFpsVisible, category: "HUD", type: "toggle", icon: '', onChange: (value) => { this.kxsClient.isFpsVisible = !this.kxsClient.isFpsVisible; this.kxsClient.updateFpsVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Show Kills", value: this.kxsClient.isKillsVisible, type: "toggle", category: "HUD", icon: ' ', onChange: (value) => { this.kxsClient.isKillsVisible = !this.kxsClient.isKillsVisible; this.kxsClient.updateKillsVisibility(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Use Legacy Menu", value: this.kxsClient.isLegaySecondaryMenu, type: "toggle", category: 'HUD', icon: ' ', onChange: (value) => { this.kxsClient.isLegaySecondaryMenu = !this.kxsClient.isLegaySecondaryMenu; this.kxsClient.updateLocalStorage(); this.kxsClient.secondaryMenu = new KxsLegacyClientSecondaryMenu(this.kxsClient); this.destroy(); }, }); this.addOption(MECHANIC, { label: "Death sound", value: this.kxsClient.isDeathSoundEnabled, type: "toggle", icon: ' ', category: "MECHANIC", onChange: (value) => { this.kxsClient.isDeathSoundEnabled = !this.kxsClient.isDeathSoundEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: "Win sound", value: this.kxsClient.isWinSoundEnabled, type: "toggle", icon: ' ', category: "HUD", onChange: (value) => { this.kxsClient.isWinSoundEnabled = !this.kxsClient.isWinSoundEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(SERVER, { label: "Webhook URL", value: this.kxsClient.discordWebhookUrl || "", icon: ' ', category: "SERVER", type: "input", onChange: (value) => { value = value.toString().trim(); this.kxsClient.discordWebhookUrl = value; this.kxsClient.discordTracker.setWebhookUrl(value); this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: "Heal Warning", value: this.kxsClient.isHealthWarningEnabled, type: "toggle", category: "MECHANIC", icon: ' health ', onChange: (value) => { this.kxsClient.isHealthWarningEnabled = !this.kxsClient.isHealthWarningEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(SERVER, { label: "Update Checker", value: this.kxsClient.isAutoUpdateEnabled, type: "toggle", icon: ' ', category: "SERVER", onChange: (value) => { this.kxsClient.isAutoUpdateEnabled = !this.kxsClient.isAutoUpdateEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: `Uncap FPS`, value: this.kxsClient.isFpsUncapped, type: "toggle", icon: ' ic_fluent_fps_960_24_filled Created with Sketch. ', category: 'MECHANIC', onChange: () => { this.kxsClient.isFpsUncapped = !this.kxsClient.isFpsUncapped; this.kxsClient.toggleFpsUncap(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Winning Animation`, value: this.kxsClient.isWinningAnimationEnabled, icon: ' ', category: "HUD", type: "toggle", onChange: () => { this.kxsClient.isWinningAnimationEnabled = !this.kxsClient.isWinningAnimationEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Spotify Player`, value: this.kxsClient.isSpotifyPlayerEnabled, icon: ' spotify ', category: "HUD", type: "toggle", onChange: () => { this.kxsClient.isSpotifyPlayerEnabled = !this.kxsClient.isSpotifyPlayerEnabled; this.kxsClient.updateLocalStorage(); this.kxsClient.toggleSpotifyMenu(); }, }); this.addOption(HUD, { label: "Kill Feed Blint Text", value: this.kxsClient.isKillFeedBlint, icon: ` `, category: "HUD", type: "toggle", onChange: () => { this.kxsClient.isKillFeedBlint = !this.kxsClient.isKillFeedBlint; this.kxsClient.updateLocalStorage(); }, }); this.addOption(SERVER, { label: `Rich Presence (Account token required)`, value: this.kxsClient.discordToken || "", icon: ' ', category: "SERVER", type: "input", onChange: (value) => { value = value.toString().trim(); this.kxsClient.discordToken = this.kxsClient.parseToken(value); this.kxsClient.discordRPC.disconnect(); this.kxsClient.discordRPC.connect(); this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: `Kill Leader Tracking`, icon: ' crown ', category: "MECHANIC", value: this.kxsClient.isKillLeaderTrackerEnabled, type: "toggle", onChange: (value) => { this.kxsClient.isKillLeaderTrackerEnabled = !this.kxsClient.isKillLeaderTrackerEnabled; this.kxsClient.updateLocalStorage(); }, }); this.addOption(MECHANIC, { label: `Friends Detector (separe with ',')`, icon: ' ', category: "MECHANIC", value: this.kxsClient.all_friends, type: "input", onChange: (value) => { this.kxsClient.all_friends = value; this.kxsClient.updateLocalStorage(); }, }); this.addOption(HUD, { label: `Change Background`, icon: ' ', category: "HUD", value: true, type: "click", onChange: () => { const backgroundElement = document.getElementById("background"); if (!backgroundElement) { alert("Element with id 'background' not found."); return; } const choice = prompt("Enter '0' to default Kxs background, '1' to provide a URL or '2' to upload a local image:"); if (choice === "0") { localStorage.removeItem("lastBackgroundUrl"); localStorage.removeItem("lastBackgroundFile"); localStorage.removeItem("lastBackgroundType"); localStorage.removeItem("lastBackgroundValue"); backgroundElement.style.backgroundImage = `url(${background_image})`; } else if (choice === "1") { const newBackgroundUrl = prompt("Enter the URL of the new background image:"); if (newBackgroundUrl) { backgroundElement.style.backgroundImage = `url(${newBackgroundUrl})`; this.kxsClient.saveBackgroundToLocalStorage(newBackgroundUrl); alert("Background updated successfully!"); } } else if (choice === "2") { const fileInput = document.createElement("input"); fileInput.type = "file"; fileInput.accept = "image/*"; fileInput.onchange = (event) => { var _a, _b; const file = (_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]; if (file) { const reader = new FileReader(); reader.onload = () => { backgroundElement.style.backgroundImage = `url(${reader.result})`; this.kxsClient.saveBackgroundToLocalStorage(file); alert("Background updated successfully!"); }; reader.readAsDataURL(file); } }; fileInput.click(); } }, }); } createOptionCard(option, container) { const optionCard = document.createElement("div"); Object.assign(optionCard.style, { background: "rgba(31, 41, 55, 0.8)", borderRadius: "10px", padding: "16px", display: "flex", flexDirection: "column", alignItems: "center", gap: "12px", minHeight: "150px", }); const iconContainer = document.createElement("div"); Object.assign(iconContainer.style, { width: "48px", height: "48px", borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: "8px" }); iconContainer.innerHTML = option.icon || ''; const title = document.createElement("div"); title.textContent = option.label; title.style.fontSize = "16px"; title.style.textAlign = "center"; let control = null; switch (option.type) { case "input": control = this.createInputElement(option); break; case "toggle": control = this.createToggleButton(option); break; case "click": control = this.createClickButton(option); } optionCard.appendChild(iconContainer); optionCard.appendChild(title); optionCard.appendChild(control); container.appendChild(optionCard); } setActiveCategory(category) { this.activeCategory = category; this.filterOptions(); // Update button styles this.menu.querySelectorAll('.category-btn').forEach(btn => { const btnCategory = btn.dataset.category; btn.style.background = btnCategory === category ? '#3B82F6' : 'rgba(55, 65, 81, 0.8)'; }); } filterOptions() { const gridContainer = document.getElementById('kxsMenuGrid'); if (gridContainer) { // Clear existing content gridContainer.innerHTML = ''; // Get unique options based on category and search term const displayedOptions = new Set(); this.sections.forEach(section => { if (this.activeCategory === 'ALL' || section.category === this.activeCategory) { section.options.forEach(option => { // Create a unique key for each option const optionKey = `${option.label}-${option.category}`; // Check if option matches search term const matchesSearch = this.searchTerm === '' || option.label.toLowerCase().includes(this.searchTerm) || option.category.toLowerCase().includes(this.searchTerm); if (!displayedOptions.has(optionKey) && matchesSearch) { displayedOptions.add(optionKey); this.createOptionCard(option, gridContainer); } }); } }); // Show a message if no options match the search if (displayedOptions.size === 0 && this.searchTerm !== '') { const noResultsMsg = document.createElement('div'); noResultsMsg.textContent = `No results found for "${this.searchTerm}"`; noResultsMsg.style.gridColumn = '1 / -1'; noResultsMsg.style.textAlign = 'center'; noResultsMsg.style.padding = '20px'; noResultsMsg.style.color = '#9CA3AF'; gridContainer.appendChild(noResultsMsg); } } } createGridContainer() { const gridContainer = document.createElement("div"); Object.assign(gridContainer.style, { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "16px", padding: "16px", gridAutoRows: "minmax(150px, auto)", overflowY: "auto", overflowX: "hidden", // Prevent horizontal scrolling maxHeight: "calc(3 * 150px + 2 * 16px)", width: "100%", boxSizing: "border-box" // Include padding in width calculation }); gridContainer.id = "kxsMenuGrid"; this.menu.appendChild(gridContainer); } addOption(section, option) { section.options.push(option); // Store all options for searching this.allOptions.push(option); } addSection(title, category = "ALL") { const section = { title, 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) { const btn = document.createElement("button"); Object.assign(btn.style, { width: "100%", padding: "8px", background: option.value ? "#059669" : "#DC2626", border: "none", borderRadius: "6px", color: "white", cursor: "pointer", transition: "background 0.2s", fontSize: "14px", fontWeight: "bold" }); btn.textContent = option.value ? "ENABLED" : "DISABLED"; btn.addEventListener("click", () => { var _a; const newValue = !option.value; option.value = newValue; btn.textContent = newValue ? "ENABLED" : "DISABLED"; btn.style.background = newValue ? "#059669" : "#DC2626"; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, newValue); }); return btn; } createClickButton(option) { const btn = document.createElement("button"); Object.assign(btn.style, { width: "100%", padding: "8px", background: "#3B82F6", border: "none", borderRadius: "6px", color: "white", cursor: "pointer", transition: "background 0.2s", fontSize: "14px", fontWeight: "bold" }); btn.textContent = option.label; btn.addEventListener("click", () => { var _a; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, true); }); return btn; } addShiftListener() { window.addEventListener("keydown", (event) => { if (event.key === "Shift" && event.location == 2) { this.clearMenu(); this.toggleMenuVisibility(); this.loadOption(); // Ensure options are displayed after loading this.filterOptions(); } }); } createInputElement(option) { const input = document.createElement("input"); input.type = "text"; input.value = String(option.value); Object.assign(input.style, { width: "100%", padding: "8px", background: "rgba(55, 65, 81, 0.8)", border: "none", borderRadius: "6px", color: "#FFAE00", fontSize: "14px" }); input.addEventListener("change", () => { var _a; option.value = input.value; (_a = option.onChange) === null || _a === void 0 ? void 0 : _a.call(option, input.value); }); return input; } addDragListeners() { this.menu.addEventListener('mousedown', (e) => { 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"; } }); document.addEventListener('mousemove', (e) => { if (this.isDragging) { 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`; } }); document.addEventListener('mouseup', () => { this.isDragging = false; this.menu.style.cursor = "grab"; }); } toggleMenuVisibility() { this.isClientMenuVisible = !this.isClientMenuVisible; this.kxsClient.nm.showNotification(this.isClientMenuVisible ? "Opening menu..." : "Closing menu...", "info", 1400); this.menu.style.display = this.isClientMenuVisible ? "block" : "none"; // If opening the menu, make sure to display options if (this.isClientMenuVisible) { this.filterOptions(); } } destroy() { // Remove global event listeners window.removeEventListener("keydown", this.shiftListener); document.removeEventListener('mousemove', this.mouseMoveListener); document.removeEventListener('mouseup', this.mouseUpListener); // 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/KxsClient.ts var KxsClient_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 KxsClient { constructor() { this.deathObserver = null; this.adBlockObserver = null; this.config = __webpack_require__(891); this.menu = document.createElement("div"); this.lastFrameTime = performance.now(); this.isFpsUncapped = this.getFpsUncappedFromLocalStorage(); this.isFpsVisible = true; this.isPingVisible = true; this.isKillsVisible = true; this.isDeathSoundEnabled = true; this.isWinSoundEnabled = true; this.isHealthWarningEnabled = true; this.isAutoUpdateEnabled = true; this.isWinningAnimationEnabled = true; this.isKillLeaderTrackerEnabled = true; this.isLegaySecondaryMenu = false; this.isKillFeedBlint = false; this.isSpotifyPlayerEnabled = false; this.discordToken = null; this.counters = {}; this.all_friends = ''; this.isMainMenuCleaned = false; this.defaultPositions = { fps: { left: 20, top: 160 }, ping: { left: 20, top: 220 }, kills: { left: 20, top: 280 }, }; this.defaultSizes = { fps: { width: 100, height: 30 }, ping: { width: 100, height: 30 }, kills: { width: 100, height: 30 }, }; // Before all, load local storage this.loadLocalStorage(); this.changeSurvevLogo(); this.nm = NotificationManager.getInstance(); this.discordRPC = new DiscordWebSocket(this, this.parseToken(this.discordToken)); this.updater = new UpdateChecker(this); this.kill_leader = new KillLeaderTracker(this); this.healWarning = new HealthWarning(this); this.setAnimationFrameCallback(); this.loadBackgroundFromLocalStorage(); this.initDeathDetection(); this.discordRPC.connect(); if (this.isLegaySecondaryMenu) { this.secondaryMenu = new KxsLegacyClientSecondaryMenu(this); } else { this.secondaryMenu = new KxsClientSecondaryMenu(this); } this.discordTracker = new DiscordTracking(this, this.discordWebhookUrl); if (this.isSpotifyPlayerEnabled) { this.createSimpleSpotifyPlayer(); } this.MainMenuCleaning(); } parseToken(token) { if (token) { return token.replace(/^(["'`])(.+)\1$/, '$2'); } return null; } getPlayerName() { let config = localStorage.getItem("surviv_config"); if (config) { let configObject = JSON.parse(config); return configObject.playerName; } } changeSurvevLogo() { var startRowHeader = document.querySelector("#start-row-header"); if (startRowHeader) { startRowHeader.style.backgroundImage = `url("${this.config.base_url}/assets/KysClient.gif")`; } } updateLocalStorage() { localStorage.setItem("userSettings", JSON.stringify({ isFpsVisible: this.isFpsVisible, isPingVisible: this.isPingVisible, isFpsUncapped: this.isFpsUncapped, isKillsVisible: this.isKillsVisible, discordWebhookUrl: this.discordWebhookUrl, isDeathSoundEnabled: this.isDeathSoundEnabled, isWinSoundEnabled: this.isWinSoundEnabled, isHealthWarningEnabled: this.isHealthWarningEnabled, isAutoUpdateEnabled: this.isAutoUpdateEnabled, isWinningAnimationEnabled: this.isWinningAnimationEnabled, discordToken: this.discordToken, isKillLeaderTrackerEnabled: this.isKillLeaderTrackerEnabled, isLegaySecondaryMenu: this.isLegaySecondaryMenu, isKillFeedBlint: this.isKillFeedBlint, all_friends: this.all_friends, isSpotifyPlayerEnabled: this.isSpotifyPlayerEnabled, isMainMenuCleaned: this.isMainMenuCleaned })); } ; initDeathDetection() { const config = { childList: true, subtree: true, attributes: false, characterData: false, }; this.deathObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length) { this.checkForDeathScreen(mutation.addedNodes); } } }); this.deathObserver.observe(document.body, config); } checkForDeathScreen(nodes) { let loseArray = [ "died", "eliminated", "was" ]; let winArray = [ "Winner", "Victory", "dinner", ]; nodes.forEach((node) => { if (node instanceof HTMLElement) { const deathTitle = node.querySelector(".ui-stats-header-title"); if (loseArray.some((word) => { var _a; return (_a = deathTitle === null || deathTitle === void 0 ? void 0 : deathTitle.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(word); })) { this.handlePlayerDeath(); } else if (winArray.some((word) => { var _a; return (_a = deathTitle === null || deathTitle === void 0 ? void 0 : deathTitle.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(word); })) { this.handlePlayerWin(); } } }); } handlePlayerDeath() { return KxsClient_awaiter(this, void 0, void 0, function* () { try { if (this.isDeathSoundEnabled) { const audio = new Audio(this.config.base_url + "/assets/dead.m4a"); audio.volume = 0.3; audio.play().catch((err) => false); } } catch (error) { console.error("Reading error:", error); } const stats = this.getPlayerStats(false); yield this.discordTracker.trackGameEnd({ username: stats.username, kills: stats.kills, damageDealt: stats.damageDealt, damageTaken: stats.damageTaken, duration: stats.duration, position: stats.position, isWin: false, }); }); } handlePlayerWin() { return KxsClient_awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; if (this.isWinningAnimationEnabled) { this.felicitation(); } const stats = this.getPlayerStats(true); yield this.discordTracker.trackGameEnd({ username: stats.username, kills: stats.kills, damageDealt: stats.damageDealt, damageTaken: stats.damageTaken, duration: stats.duration, position: stats.position, isWin: true, stuff: { main_weapon: (_a = document.querySelector('#ui-weapon-id-1 .ui-weapon-name')) === null || _a === void 0 ? void 0 : _a.textContent, secondary_weapon: (_b = document.querySelector('#ui-weapon-id-2 .ui-weapon-name')) === null || _b === void 0 ? void 0 : _b.textContent, soda: (_c = document.querySelector("#ui-loot-soda .ui-loot-count")) === null || _c === void 0 ? void 0 : _c.textContent, melees: (_d = document.querySelector('#ui-weapon-id-3 .ui-weapon-name')) === null || _d === void 0 ? void 0 : _d.textContent, grenades: (_e = document.querySelector(`#ui-weapon-id-4 .ui-weapon-name`)) === null || _e === void 0 ? void 0 : _e.textContent, medkit: (_f = document.querySelector("#ui-loot-healthkit .ui-loot-count")) === null || _f === void 0 ? void 0 : _f.textContent, bandage: (_g = document.querySelector("#ui-loot-bandage .ui-loot-count")) === null || _g === void 0 ? void 0 : _g.textContent, pills: (_h = document.querySelector("#ui-loot-painkiller .ui-loot-count")) === null || _h === void 0 ? void 0 : _h.textContent, backpack: (_j = document.querySelector("#ui-armor-backpack .ui-armor-level")) === null || _j === void 0 ? void 0 : _j.textContent, chest: (_k = document.querySelector("#ui-armor-chest .ui-armor-level")) === null || _k === void 0 ? void 0 : _k.textContent, helmet: (_l = document.querySelector("#ui-armor-helmet .ui-armor-level")) === null || _l === void 0 ? void 0 : _l.textContent, } }); }); } felicitation() { const goldText = document.createElement("div"); goldText.textContent = "#1"; 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 (this.isWinSoundEnabled) { const audio = new Audio(this.config.base_url + "/assets/win.m4a"); 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); } cleanup() { if (this.deathObserver) { this.deathObserver.disconnect(); this.deathObserver = null; } } getUsername() { const configKey = "surviv_config"; const savedConfig = localStorage.getItem(configKey); const config = JSON.parse(savedConfig); if (config.playerName) { return config.playerName; } else { return "Player"; } } getPlayerStats(win) { const statsInfo = win ? document.querySelector(".ui-stats-info-player") : document.querySelector(".ui-stats-info-player.ui-stats-info-status"); const rank = document.querySelector(".ui-stats-header-value"); if (!(statsInfo === null || statsInfo === void 0 ? void 0 : statsInfo.textContent) || !(rank === null || rank === void 0 ? void 0 : rank.textContent)) { return { username: this.getUsername(), kills: 0, damageDealt: 0, damageTaken: 0, duration: "0s", position: "#unknown", }; } const parsedStats = StatsParser.parse(statsInfo.textContent, rank === null || rank === void 0 ? void 0 : rank.textContent); parsedStats.username = this.getUsername(); return parsedStats; } setAnimationFrameCallback() { this.animationFrameCallback = this.isFpsUncapped ? (callback) => setTimeout(callback, 1) : window.requestAnimationFrame.bind(window); } makeResizable(element, storageKey) { let isResizing = false; let startX, startY, startWidth, startHeight; // Add a resize area in the bottom right const resizer = document.createElement("div"); Object.assign(resizer.style, { width: "10px", height: "10px", backgroundColor: "white", position: "absolute", right: "0", bottom: "0", cursor: "nwse-resize", zIndex: "10001", }); element.appendChild(resizer); resizer.addEventListener("mousedown", (event) => { isResizing = true; startX = event.clientX; startY = event.clientY; startWidth = element.offsetWidth; startHeight = element.offsetHeight; event.stopPropagation(); // Empêche l'activation du déplacement }); window.addEventListener("mousemove", (event) => { if (isResizing) { const newWidth = startWidth + (event.clientX - startX); const newHeight = startHeight + (event.clientY - startY); element.style.width = `${newWidth}px`; element.style.height = `${newHeight}px`; // Sauvegarde de la taille localStorage.setItem(storageKey, JSON.stringify({ width: newWidth, height: newHeight, })); } }); window.addEventListener("mouseup", () => { isResizing = false; }); const savedSize = localStorage.getItem(storageKey); if (savedSize) { const { width, height } = JSON.parse(savedSize); element.style.width = `${width}px`; element.style.height = `${height}px`; } else { element.style.width = "150px"; // Taille par défaut element.style.height = "50px"; } } makeDraggable(element, storageKey) { const gridSystem = new GridSystem(); let isDragging = false; let dragOffset = { x: 0, y: 0 }; element.addEventListener("mousedown", (event) => { if (event.button === 0) { // Left click only isDragging = true; gridSystem.toggleGrid(); // Afficher la grille quand on commence à déplacer dragOffset = { x: event.clientX - element.offsetLeft, y: event.clientY - element.offsetTop, }; element.style.cursor = "grabbing"; } }); window.addEventListener("mousemove", (event) => { if (isDragging) { const rawX = event.clientX - dragOffset.x; const rawY = event.clientY - dragOffset.y; // Get snapped coordinates from grid system const snapped = gridSystem.snapToGrid(element, rawX, rawY); // Prevent moving off screen const maxX = window.innerWidth - element.offsetWidth; const maxY = window.innerHeight - element.offsetHeight; element.style.left = `${Math.max(0, Math.min(snapped.x, maxX))}px`; element.style.top = `${Math.max(0, Math.min(snapped.y, maxY))}px`; // Highlight nearest grid lines while dragging gridSystem.highlightNearestGridLine(rawX, rawY); // Save position localStorage.setItem(storageKey, JSON.stringify({ x: parseInt(element.style.left), y: parseInt(element.style.top), })); } }); window.addEventListener("mouseup", () => { if (isDragging) { isDragging = false; gridSystem.toggleGrid(); // Masquer la grille quand on arrête de déplacer element.style.cursor = "move"; } }); // Load saved position const savedPosition = localStorage.getItem(storageKey); if (savedPosition) { const { x, y } = JSON.parse(savedPosition); const snapped = gridSystem.snapToGrid(element, x, y); element.style.left = `${snapped.x}px`; element.style.top = `${snapped.y}px`; } } getKills() { const killElement = document.querySelector(".ui-player-kills.js-ui-player-kills"); if (killElement) { const kills = parseInt(killElement.textContent || "", 10); return isNaN(kills) ? 0 : kills; } return 0; } getRegionFromLocalStorage() { let config = localStorage.getItem("surviv_config"); if (config) { let configObject = JSON.parse(config); return configObject.region; } return null; } getFpsUncappedFromLocalStorage() { const savedConfig = localStorage.getItem("userSettings"); if (savedConfig) { const configObject = JSON.parse(savedConfig); return configObject.isFpsUncapped || false; } return false; } saveFpsUncappedToLocalStorage() { let config = JSON.parse(localStorage.getItem("userSettings")) || {}; config.isFpsUncapped = this.isFpsUncapped; localStorage.setItem("userSettings", JSON.stringify(config)); } saveBackgroundToLocalStorage(image) { if (typeof image === "string") { localStorage.setItem("lastBackgroundUrl", image); } if (typeof image === "string") { localStorage.setItem("lastBackgroundType", "url"); localStorage.setItem("lastBackgroundValue", image); } else { localStorage.setItem("lastBackgroundType", "local"); const reader = new FileReader(); reader.onload = () => { localStorage.setItem("lastBackgroundValue", reader.result); }; reader.readAsDataURL(image); } } loadBackgroundFromLocalStorage() { const backgroundType = localStorage.getItem("lastBackgroundType"); const backgroundValue = localStorage.getItem("lastBackgroundValue"); const backgroundElement = document.getElementById("background"); if (backgroundElement && backgroundType && backgroundValue) { if (backgroundType === "url") { backgroundElement.style.backgroundImage = `url(${backgroundValue})`; } else if (backgroundType === "local") { backgroundElement.style.backgroundImage = `url(${backgroundValue})`; } } } loadLocalStorage() { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; const savedSettings = localStorage.getItem("userSettings") ? JSON.parse(localStorage.getItem("userSettings")) : null; if (savedSettings) { this.isFpsVisible = (_a = savedSettings.isFpsVisible) !== null && _a !== void 0 ? _a : this.isFpsVisible; this.isPingVisible = (_b = savedSettings.isPingVisible) !== null && _b !== void 0 ? _b : this.isPingVisible; this.isFpsUncapped = (_c = savedSettings.isFpsUncapped) !== null && _c !== void 0 ? _c : this.isFpsUncapped; this.isKillsVisible = (_d = savedSettings.isKillsVisible) !== null && _d !== void 0 ? _d : this.isKillsVisible; this.discordWebhookUrl = (_e = savedSettings.discordWebhookUrl) !== null && _e !== void 0 ? _e : this.discordWebhookUrl; this.isHealthWarningEnabled = (_f = savedSettings.isHealthWarningEnabled) !== null && _f !== void 0 ? _f : this.isHealthWarningEnabled; this.isAutoUpdateEnabled = (_g = savedSettings.isAutoUpdateEnabled) !== null && _g !== void 0 ? _g : this.isAutoUpdateEnabled; this.isWinningAnimationEnabled = (_h = savedSettings.isWinningAnimationEnabled) !== null && _h !== void 0 ? _h : this.isWinningAnimationEnabled; this.discordToken = (_j = savedSettings.discordToken) !== null && _j !== void 0 ? _j : this.discordToken; this.isKillLeaderTrackerEnabled = (_k = savedSettings.isKillLeaderTrackerEnabled) !== null && _k !== void 0 ? _k : this.isKillLeaderTrackerEnabled; this.isLegaySecondaryMenu = (_l = savedSettings.isLegaySecondaryMenu) !== null && _l !== void 0 ? _l : this.isLegaySecondaryMenu; this.isKillFeedBlint = (_m = savedSettings.isKillFeedBlint) !== null && _m !== void 0 ? _m : this.isKillFeedBlint; this.all_friends = (_o = savedSettings.all_friends) !== null && _o !== void 0 ? _o : this.all_friends; this.isSpotifyPlayerEnabled = (_p = savedSettings.isSpotifyPlayerEnabled) !== null && _p !== void 0 ? _p : this.isSpotifyPlayerEnabled; this.isMainMenuCleaned = (_q = savedSettings.isMainMenuCleaned) !== null && _q !== void 0 ? _q : this.isMainMenuCleaned; } this.updateKillsVisibility(); this.updateFpsVisibility(); this.updatePingVisibility(); } updateFpsVisibility() { if (this.counters.fps) { this.counters.fps.style.display = this.isFpsVisible ? "block" : "none"; this.counters.fps.style.backgroundColor = this.isFpsVisible ? "rgba(0, 0, 0, 0.2)" : "transparent"; } } updatePingVisibility() { if (this.counters.ping) { this.counters.ping.style.display = this.isPingVisible ? "block" : "none"; } } updateKillsVisibility() { if (this.counters.kills) { this.counters.kills.style.display = this.isKillsVisible ? "block" : "none"; this.counters.kills.style.backgroundColor = this.isKillsVisible ? "rgba(0, 0, 0, 0.2)" : "transparent"; } } toggleFpsUncap() { this.isFpsUncapped = !this.isFpsUncapped; this.setAnimationFrameCallback(); this.saveFpsUncappedToLocalStorage(); } createSimpleSpotifyPlayer() { // Main container const container = document.createElement('div'); container.id = 'spotify-player-container'; Object.assign(container.style, { position: 'fixed', bottom: '20px', right: '20px', width: '320px', backgroundColor: '#121212', borderRadius: '12px', boxShadow: '0 8px 24px rgba(0, 0, 0, 0.5)', overflow: 'hidden', zIndex: '10000', fontFamily: 'Montserrat, Arial, sans-serif', transition: 'transform 0.3s ease, opacity 0.3s ease', transform: 'translateY(0)', opacity: '1' }); // Player header const header = document.createElement('div'); Object.assign(header.style, { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 16px', backgroundColor: '#070707', color: 'white', borderBottom: '1px solid #282828', position: 'relative' // For absolute positioning of the button }); // Spotify logo const logo = document.createElement('div'); logo.innerHTML = ``; const title = document.createElement('span'); title.textContent = 'Spotify Player'; title.style.marginLeft = '8px'; title.style.fontWeight = 'bold'; const logoContainer = document.createElement('div'); logoContainer.style.display = 'flex'; logoContainer.style.alignItems = 'center'; logoContainer.appendChild(logo); logoContainer.appendChild(title); // Control buttons const controls = document.createElement('div'); controls.style.display = 'flex'; controls.style.alignItems = 'center'; // Minimize button const minimizeBtn = document.createElement('button'); Object.assign(minimizeBtn.style, { background: 'none', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: '18px', padding: '0', marginLeft: '10px', width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center' }); minimizeBtn.innerHTML = '−'; minimizeBtn.title = 'Minimize'; // Close button const closeBtn = document.createElement('button'); Object.assign(closeBtn.style, { background: 'none', border: 'none', color: '#aaa', cursor: 'pointer', fontSize: '18px', padding: '0', marginLeft: '10px', width: '24px', height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center' }); closeBtn.innerHTML = '×'; closeBtn.title = 'Close'; controls.appendChild(minimizeBtn); controls.appendChild(closeBtn); header.appendChild(logoContainer); header.appendChild(controls); // Album cover image const albumArt = document.createElement('div'); Object.assign(albumArt.style, { width: '50px', height: '50px', backgroundColor: '#333', backgroundSize: 'cover', backgroundPosition: 'center', borderRadius: '4px', flexShrink: '0' }); albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67616d00001e02fe24b9ffeb3c3fdb4f9abbe9')`; // Track information const trackInfo = document.createElement('div'); Object.assign(trackInfo.style, { flex: '1', overflow: 'hidden' }); // Player content const content = document.createElement('div'); content.style.padding = '0'; // Spotify iframe const iframe = document.createElement('iframe'); iframe.id = 'spotify-player-iframe'; iframe.src = 'https://open.spotify.com/embed/playlist/37i9dQZEVXcJZyENOWUFo7'; iframe.width = '100%'; iframe.height = '80px'; iframe.frameBorder = '0'; iframe.allow = 'encrypted-media'; iframe.style.border = 'none'; content.appendChild(iframe); // Playlist change button integrated in the header const changePlaylistContainer = document.createElement('div'); Object.assign(changePlaylistContainer.style, { display: 'flex', alignItems: 'center', marginRight: '10px' }); // Square button to enter a playlist ID const changePlaylistBtn = document.createElement('button'); Object.assign(changePlaylistBtn.style, { width: '24px', height: '24px', backgroundColor: '#1DB954', color: 'white', border: 'none', borderRadius: '4px', fontSize: '14px', fontWeight: 'bold', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 8px 0 0' }); changePlaylistBtn.innerHTML = ` `; changePlaylistBtn.addEventListener('click', () => { const id = prompt('Enter the Spotify playlist ID:', '37i9dQZEVXcJZyENOWUFo7'); if (id) { iframe.src = `https://open.spotify.com/embed/playlist/${id}`; localStorage.setItem('kxsSpotifyPlaylist', id); // Simulate an album cover based on the playlist ID albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67706f00000002${id.substring(0, 16)}')`; } }); changePlaylistContainer.appendChild(changePlaylistBtn); // Load saved playlist const savedPlaylist = localStorage.getItem('kxsSpotifyPlaylist'); if (savedPlaylist) { iframe.src = `https://open.spotify.com/embed/playlist/${savedPlaylist}`; // Simulate an album cover based on the playlist ID albumArt.style.backgroundImage = `url('https://i.scdn.co/image/ab67706f00000002${savedPlaylist.substring(0, 16)}')`; } // Integrate the playlist change button into the controls controls.insertBefore(changePlaylistContainer, minimizeBtn); // Assemble the elements container.appendChild(header); container.appendChild(content); // Add a title to the button for accessibility changePlaylistBtn.title = "Change playlist"; // Add to document document.body.appendChild(container); // Player states let isMinimized = false; // Events minimizeBtn.addEventListener('click', () => { if (isMinimized) { content.style.display = 'block'; changePlaylistContainer.style.display = 'block'; container.style.transform = 'translateY(0)'; minimizeBtn.innerHTML = '−'; } else { content.style.display = 'none'; changePlaylistContainer.style.display = 'none'; container.style.transform = 'translateY(0)'; minimizeBtn.innerHTML = '+'; } isMinimized = !isMinimized; }); closeBtn.addEventListener('click', () => { container.style.transform = 'translateY(150%)'; container.style.opacity = '0'; setTimeout(() => { container.style.display = 'none'; showButton.style.display = 'flex'; showButton.style.alignItems = 'center'; showButton.style.justifyContent = 'center'; }, 300); }); // Make the player draggable let isDragging = false; let offsetX = 0; let offsetY = 0; header.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - container.getBoundingClientRect().left; offsetY = e.clientY - container.getBoundingClientRect().top; container.style.transition = 'none'; }); document.addEventListener('mousemove', (e) => { if (isDragging) { container.style.right = 'auto'; container.style.bottom = 'auto'; container.style.left = (e.clientX - offsetX) + 'px'; container.style.top = (e.clientY - offsetY) + 'px'; } }); document.addEventListener('mouseup', () => { isDragging = false; container.style.transition = 'transform 0.3s ease, opacity 0.3s ease'; }); // Button to show the player again const showButton = document.createElement('button'); showButton.id = 'spotify-float-button'; Object.assign(showButton.style, { position: 'fixed', bottom: '20px', right: '20px', width: '50px', height: '50px', borderRadius: '50%', backgroundColor: '#1DB954', color: 'white', border: 'none', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)', cursor: 'pointer', zIndex: '9999', fontSize: '24px', transition: 'transform 0.2s ease', display: 'flex', alignItems: 'center', justifyContent: 'center' }); showButton.innerHTML = ``; document.body.appendChild(showButton); showButton.addEventListener('mouseenter', () => { showButton.style.transform = 'scale(1.1)'; }); showButton.addEventListener('mouseleave', () => { showButton.style.transform = 'scale(1)'; }); showButton.addEventListener('click', () => { container.style.display = 'block'; container.style.transform = 'translateY(0)'; container.style.opacity = '1'; showButton.style.display = 'none'; }); return container; } toggleSpotifyMenu() { if (this.isSpotifyPlayerEnabled) { this.createSimpleSpotifyPlayer(); } else { this.removeSimpleSpotifyPlayer(); } } applyCustomMainMenuStyle() { // Sélectionner le menu principal const startMenu = document.getElementById('start-menu'); const playButtons = document.querySelectorAll('.btn-green, #btn-help, .btn-team-option'); const playerOptions = document.getElementById('player-options'); const serverSelect = document.getElementById('server-select-main'); const nameInput = document.getElementById('player-name-input-solo'); const helpSection = document.getElementById('start-help'); if (startMenu) { // Apply styles to the main container Object.assign(startMenu.style, { background: 'linear-gradient(135deg, rgba(25, 25, 35, 0.95) 0%, rgba(15, 15, 25, 0.98) 100%)', border: '1px solid rgba(255, 255, 255, 0.1)', borderRadius: '12px', boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)', padding: '15px', backdropFilter: 'blur(10px)', margin: '0 auto' }); } // Style the buttons playButtons.forEach(button => { if (button instanceof HTMLElement) { if (button.classList.contains('btn-green')) { // Boutons Play Object.assign(button.style, { background: 'linear-gradient(135deg, #4287f5 0%, #3b76d9 100%)', borderRadius: '8px', border: '1px solid rgba(255, 255, 255, 0.2)', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)', transition: 'all 0.2s ease', color: 'white', fontWeight: 'bold' }); } else { // Autres boutons Object.assign(button.style, { background: 'rgba(40, 45, 60, 0.7)', borderRadius: '8px', border: '1px solid rgba(255, 255, 255, 0.1)', transition: 'all 0.2s ease', color: 'white' }); } // Hover effect for all buttons button.addEventListener('mouseover', () => { button.style.transform = 'translateY(-2px)'; button.style.boxShadow = '0 6px 16px rgba(0, 0, 0, 0.3)'; button.style.filter = 'brightness(1.1)'; }); button.addEventListener('mouseout', () => { button.style.transform = 'translateY(0)'; button.style.boxShadow = button.classList.contains('btn-green') ? '0 4px 12px rgba(0, 0, 0, 0.2)' : 'none'; button.style.filter = 'brightness(1)'; }); } }); // Styliser le sélecteur de serveur if (serverSelect instanceof HTMLSelectElement) { Object.assign(serverSelect.style, { background: 'rgba(30, 35, 50, 0.8)', borderRadius: '8px', border: '1px solid rgba(255, 255, 255, 0.1)', color: 'white', padding: '8px 12px', outline: 'none' }); } // Styliser l'input du nom if (nameInput instanceof HTMLInputElement) { Object.assign(nameInput.style, { background: 'rgba(30, 35, 50, 0.8)', borderRadius: '8px', border: '1px solid rgba(255, 255, 255, 0.1)', color: 'white', padding: '8px 12px', outline: 'none' }); // Focus style nameInput.addEventListener('focus', () => { nameInput.style.border = '1px solid #4287f5'; nameInput.style.boxShadow = '0 0 8px rgba(66, 135, 245, 0.5)'; }); nameInput.addEventListener('blur', () => { nameInput.style.border = '1px solid rgba(255, 255, 255, 0.1)'; nameInput.style.boxShadow = 'none'; }); } // Styliser la section d'aide if (helpSection) { Object.assign(helpSection.style, { background: 'rgba(20, 25, 40, 0.7)', borderRadius: '8px', padding: '15px', margin: '15px 0', maxHeight: '300px', overflowY: 'auto', scrollbarWidth: 'thin', scrollbarColor: '#4287f5 rgba(25, 25, 35, 0.5)' }); // Style the help section titles const helpTitles = helpSection.querySelectorAll('h1'); helpTitles.forEach(title => { if (title instanceof HTMLElement) { Object.assign(title.style, { color: '#4287f5', fontSize: '18px', marginTop: '15px', marginBottom: '8px' }); } }); // Style the paragraphs const helpParagraphs = helpSection.querySelectorAll('p'); helpParagraphs.forEach(p => { if (p instanceof HTMLElement) { p.style.color = 'rgba(255, 255, 255, 0.8)'; p.style.fontSize = '14px'; p.style.marginBottom = '8px'; } }); // Style the action terms and controls const actionTerms = helpSection.querySelectorAll('.help-action'); actionTerms.forEach(term => { if (term instanceof HTMLElement) { term.style.color = '#ffc107'; // Yellow term.style.fontWeight = 'bold'; } }); const controlTerms = helpSection.querySelectorAll('.help-control'); controlTerms.forEach(term => { if (term instanceof HTMLElement) { term.style.color = '#4287f5'; // Bleu term.style.fontWeight = 'bold'; } }); } // Apply specific style to double buttons const btnsDoubleRow = document.querySelector('.btns-double-row'); if (btnsDoubleRow instanceof HTMLElement) { btnsDoubleRow.style.display = 'flex'; btnsDoubleRow.style.gap = '10px'; btnsDoubleRow.style.marginTop = '10px'; } } MainMenuCleaning() { // Déconnecter l'observateur précédent s'il existe if (this.adBlockObserver) { this.adBlockObserver.disconnect(); this.adBlockObserver = null; } // Select elements to hide/show const newsWrapper = document.getElementById('news-wrapper'); const adBlockLeft = document.getElementById('ad-block-left'); const socialLeft = document.getElementById('social-share-block-wrapper'); const leftCollun = document.getElementById('left-column'); const elementsToMonitor = [ { element: newsWrapper, id: 'news-wrapper' }, { element: adBlockLeft, id: 'ad-block-left' }, { element: socialLeft, id: 'social-share-block-wrapper' }, { element: leftCollun, id: 'left-column' } ]; // Appliquer le style personnalisé au menu principal this.applyCustomMainMenuStyle(); if (this.isMainMenuCleaned) { // Clean mode: hide elements elementsToMonitor.forEach(item => { if (item.element) item.element.style.display = 'none'; }); // Create an observer to prevent the site from redisplaying elements this.adBlockObserver = new MutationObserver((mutations) => { let needsUpdate = false; mutations.forEach(mutation => { if (mutation.type === 'attributes' && mutation.attributeName === 'style') { const target = mutation.target; // Check if the element is one of those we are monitoring if (elementsToMonitor.some(item => item.id === target.id && target.style.display !== 'none')) { target.style.display = 'none'; needsUpdate = true; } } }); // If the site tries to redisplay an advertising element, we prevent it if (needsUpdate) { console.log('[KxsClient] Detection of attempt to redisplay ads - Forced hiding'); } }); // Observe style changes on elements elementsToMonitor.forEach(item => { if (item.element && this.adBlockObserver) { this.adBlockObserver.observe(item.element, { attributes: true, attributeFilter: ['style'] }); } }); // Vérifier également le document body pour de nouveaux éléments ajoutés const bodyObserver = new MutationObserver(() => { // Réappliquer notre nettoyage après un court délai setTimeout(() => { if (this.isMainMenuCleaned) { elementsToMonitor.forEach(item => { const element = document.getElementById(item.id); if (element && element.style.display !== 'none') { element.style.display = 'none'; } }); } }, 100); }); // Observe changes in the DOM bodyObserver.observe(document.body, { childList: true, subtree: true }); } else { // Mode normal: rétablir l'affichage elementsToMonitor.forEach(item => { if (item.element) item.element.style.display = 'block'; }); } } removeSimpleSpotifyPlayer() { // Supprimer le conteneur principal du lecteur const container = document.getElementById('spotify-player-container'); if (container) { container.remove(); } // Supprimer aussi le bouton flottant grâce à son ID const floatButton = document.getElementById('spotify-float-button'); if (floatButton) { floatButton.remove(); } } } ;// ./src/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() { // Styles for the main container Object.assign(this.container.style, { position: 'fixed', top: '0', left: '0', width: '100%', height: '100%', backgroundColor: 'rgba(0, 0, 0, 0.9)', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', zIndex: '9999', transition: 'opacity 0.5s ease-in-out', animation: 'fadeIn 0.5s ease-in-out', backdropFilter: 'blur(5px)' }); } /** * 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'; setTimeout(() => { if (this.container.parentNode) { document.body.removeChild(this.container); } }, 500); // Wait for the transition to finish before removing the element } } ;// ./src/index.ts const src_packageInfo = __webpack_require__(330); const src_config = __webpack_require__(891); const background_song = src_config.base_url + "/assets/Stranger_Things_Theme_Song_C418_REMIX.mp3"; const kxs_logo = src_config.base_url + "/assets/KysClientLogo.png"; const background_image = src_config.base_url + "/assets/background.jpg"; const loadingScreen = new LoadingScreen(kxs_logo); loadingScreen.show(); const backgroundElement = document.getElementById("background"); if (backgroundElement) backgroundElement.style.backgroundImage = `url("${background_image}")`; const favicon = document.createElement('link'); favicon.rel = 'icon'; favicon.type = 'image/png'; favicon.href = kxs_logo; document.head.appendChild(favicon); document.title = "KxsClient"; intercept("audio/ambient/menu_music_01.mp3", background_song); intercept('img/survev_logo_full.png', kxs_logo); const uiStatsLogo = document.querySelector('#ui-stats-logo'); if (uiStatsLogo) { uiStatsLogo.style.backgroundImage = `url('${kxs_logo}')`; } const newChangelogUrl = src_config.base_url; const startBottomMiddle = document.getElementById("start-bottom-middle"); if (startBottomMiddle) { const links = startBottomMiddle.getElementsByTagName("a"); for (let i = 0; i < links.length; i++) { const link = links[i]; if (link.href.includes("changelogRec.html") || link.href.includes("changelog.html")) { link.href = newChangelogUrl; link.textContent = src_packageInfo.version; } if (i === 1) { link.remove(); } } } const kxsClient = new KxsClient(); const kxsClientHUD = new KxsClientHUD(kxsClient); const mainMenu = new KxsMainClientMenu(kxsClient); setInterval(() => { loadingScreen.hide(); }, 1400); })(); /******/ })() ;