// ==UserScript==
// @name Kxs Client - Survev.io Client
// @namespace https://github.com/Kisakay/KxsClient
// @version 2.0.6
// @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 none
// @downloadURL https://update.greasyfork.icu/scripts/531396/Kxs%20Client%20-%20Survevio%20Client.user.js
// @updateURL https://update.greasyfork.icu/scripts/531396/Kxs%20Client%20-%20Survevio%20Client.meta.js
// ==/UserScript==
;
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 123:
/***/ ((module) => {
const numeric = /^[0-9]+$/
const compareIdentifiers = (a, b) => {
const anum = numeric.test(a)
const bnum = numeric.test(b)
if (anum && bnum) {
a = +a
b = +b
}
return a === b ? 0
: (anum && !bnum) ? -1
: (bnum && !anum) ? 1
: a < b ? -1
: 1
}
const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a)
module.exports = {
compareIdentifiers,
rcompareIdentifiers,
}
/***/ }),
/***/ 272:
/***/ ((module) => {
const debug = (
typeof process === 'object' &&
process.env &&
process.env.NODE_DEBUG &&
/\bsemver\b/i.test(process.env.NODE_DEBUG)
) ? (...args) => console.error('SEMVER', ...args)
: () => {}
module.exports = debug
/***/ }),
/***/ 560:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const SemVer = __webpack_require__(908)
const compare = (a, b, loose) =>
new SemVer(a, loose).compare(new SemVer(b, loose))
module.exports = compare
/***/ }),
/***/ 580:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const compare = __webpack_require__(560)
const gt = (a, b, loose) => compare(a, b, loose) > 0
module.exports = gt
/***/ }),
/***/ 587:
/***/ ((module) => {
// parse out just the options we care about
const looseOption = Object.freeze({ loose: true })
const emptyOpts = Object.freeze({ })
const parseOptions = options => {
if (!options) {
return emptyOpts
}
if (typeof options !== 'object') {
return looseOption
}
return options
}
module.exports = parseOptions
/***/ }),
/***/ 718:
/***/ ((module, exports, __webpack_require__) => {
const {
MAX_SAFE_COMPONENT_LENGTH,
MAX_SAFE_BUILD_LENGTH,
MAX_LENGTH,
} = __webpack_require__(874)
const debug = __webpack_require__(272)
exports = module.exports = {}
// The actual regexps go on exports.re
const re = exports.re = []
const safeRe = exports.safeRe = []
const src = exports.src = []
const safeSrc = exports.safeSrc = []
const t = exports.t = {}
let R = 0
const LETTERDASHNUMBER = '[a-zA-Z0-9-]'
// Replace some greedy regex tokens to prevent regex dos issues. These regex are
// used internally via the safeRe object since all inputs in this library get
// normalized first to trim and collapse all extra whitespace. The original
// regexes are exported for userland consumption and lower level usage. A
// future breaking change could export the safer regex only with a note that
// all input should have extra whitespace removed.
const safeRegexReplacements = [
['\\s', 1],
['\\d', MAX_LENGTH],
[LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
]
const makeSafeRegex = (value) => {
for (const [token, max] of safeRegexReplacements) {
value = value
.split(`${token}*`).join(`${token}{0,${max}}`)
.split(`${token}+`).join(`${token}{1,${max}}`)
}
return value
}
const createToken = (name, value, isGlobal) => {
const safe = makeSafeRegex(value)
const index = R++
debug(name, index, value)
t[name] = index
src[index] = value
safeSrc[index] = safe
re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)
}
// The following Regular Expressions can be used for tokenizing,
// validating, and parsing SemVer version strings.
// ## Numeric Identifier
// A single `0`, or a non-zero digit followed by zero or more digits.
createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')
createToken('NUMERICIDENTIFIERLOOSE', '\\d+')
// ## Non-numeric Identifier
// Zero or more digits, followed by a letter or hyphen, and then zero or
// more letters, digits, or hyphens.
createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`)
// ## Main Version
// Three dot-separated numeric identifiers.
createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
`(${src[t.NUMERICIDENTIFIER]})\\.` +
`(${src[t.NUMERICIDENTIFIER]})`)
createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
`(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
`(${src[t.NUMERICIDENTIFIERLOOSE]})`)
// ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier.
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*$')
/***/ }),
/***/ 746:
/***/ (() => {
"use strict";
// --- HOOK GLOBAL WEBSOCKET POUR INTERCEPTION gameId & PTC monitoring ---
(function () {
const OriginalWebSocket = window.WebSocket;
function HookedWebSocket(url, protocols) {
const ws = protocols !== undefined
? new OriginalWebSocket(url, protocols)
: new OriginalWebSocket(url);
if (typeof url === "string" && url.includes("gameId=")) {
const gameId = url.split("gameId=")[1];
globalThis.kxsClient.kxsNetwork.sendGameInfoToWebSocket(gameId);
}
return ws;
}
// Copie le prototype
HookedWebSocket.prototype = OriginalWebSocket.prototype;
// Copie les propriétés statiques (CONNECTING, OPEN, etc.)
Object.defineProperties(HookedWebSocket, {
CONNECTING: { value: OriginalWebSocket.CONNECTING, writable: false },
OPEN: { value: OriginalWebSocket.OPEN, writable: false },
CLOSING: { value: OriginalWebSocket.CLOSING, writable: false },
CLOSED: { value: OriginalWebSocket.CLOSED, writable: false },
});
// Remplace le constructeur global
window.WebSocket = HookedWebSocket;
})();
/***/ }),
/***/ 814:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
var __webpack_unused_export__;
__webpack_unused_export__ = ({ value: true });
exports.w = void 0;
;
class SteganoDB {
data;
currentTable;
options;
database;
constructor(options) {
this.currentTable = options?.tableName || "json";
this.database = options.database || "stegano.db";
this.data = {
[this.currentTable]: []
};
this.fetchDataFromFile();
}
read() { return localStorage.getItem(this.database) || this.data; }
write() { return localStorage.setItem(this.database, JSON.stringify(this.data)); }
setNestedProperty = (object, key, value) => {
const properties = key.split('.');
let currentObject = object;
for (let i = 0; i < properties.length - 1; i++) {
const property = properties[i];
if (typeof currentObject[property] !== 'object' || currentObject[property] === null) {
currentObject[property] = {};
}
currentObject = currentObject[property];
}
currentObject[properties[properties.length - 1]] = value;
};
getNestedProperty = (object, key) => {
const properties = key.split('.');
let index = 0;
for (; index < properties.length; ++index) {
object = object && object[properties[index]];
}
return object;
};
fetchDataFromFile() {
try {
const content = this.read();
this.data = JSON.parse(content);
}
catch (error) {
this.data = { [this.currentTable]: [] };
}
}
updateNestedProperty(key, operation, value) {
const [id, ...rest] = key.split('.');
const nestedPath = rest.join('.');
let currentValue = this.data[this.currentTable].find((entry) => entry.id === id);
if (!currentValue && operation !== 'get') {
currentValue = { id, value: {} };
this.data[this.currentTable].push(currentValue);
}
if (!currentValue && operation === 'get') {
return undefined;
}
switch (operation) {
case 'get':
return nestedPath ? this.getNestedProperty(currentValue.value, nestedPath) : currentValue.value;
case 'set':
if (nestedPath) {
this.setNestedProperty(currentValue.value, nestedPath, value);
}
else {
currentValue.value = value;
}
this.write();
break;
case 'add':
if (!nestedPath) {
currentValue.value = (typeof currentValue.value === 'number' ? currentValue.value : 0) + value;
}
else {
const existingValue = this.getNestedProperty(currentValue.value, nestedPath);
if (typeof existingValue !== 'number' && existingValue !== undefined) {
throw new TypeError('The existing value is not a number.');
}
this.setNestedProperty(currentValue.value, nestedPath, (typeof existingValue === 'number' ? existingValue : 0) + value);
}
this.write();
break;
case 'sub':
if (!nestedPath) {
currentValue.value = (typeof currentValue.value === 'number' ? currentValue.value : 0) - value;
}
else {
const existingValue = this.getNestedProperty(currentValue.value, nestedPath);
if (typeof existingValue !== 'number' && existingValue !== undefined && existingValue !== null) {
throw new TypeError('The existing value is not a number.');
}
this.setNestedProperty(currentValue.value, nestedPath, (typeof existingValue === 'number' ? existingValue : 0) - value);
}
this.write();
break;
case 'delete':
if (nestedPath) {
const properties = nestedPath.split('.');
let currentObject = currentValue.value;
for (let i = 0; i < properties.length - 1; i++) {
const property = properties[i];
if (!currentObject[property]) {
return;
}
currentObject = currentObject[property];
}
delete currentObject[properties[properties.length - 1]];
}
else {
const index = this.data[this.currentTable].findIndex((entry) => entry.id === id);
if (index !== -1) {
this.data[this.currentTable].splice(index, 1);
}
}
this.write();
break;
case 'pull':
const existingArray = nestedPath ? this.getNestedProperty(currentValue.value, nestedPath) : currentValue.value;
if (!Array.isArray(existingArray)) {
throw new Error('The stored value is not an array');
}
const newArray = existingArray.filter((item) => item !== value);
if (nestedPath) {
this.setNestedProperty(currentValue.value, nestedPath, newArray);
}
else {
currentValue.value = newArray;
}
this.write();
break;
}
}
table(tableName) {
if (tableName.includes(" ") || !tableName || tableName === "") {
throw new SyntaxError("Key can't be null or contain a space.");
}
if (!this.data[tableName]) {
this.data[tableName] = [];
}
return new SteganoDB(this.options);
}
get(key) {
return this.updateNestedProperty(key, 'get');
}
set(key, value) {
if (key.includes(" ") || !key || key === "") {
throw new SyntaxError("Key can't be null or contain a space.");
}
this.updateNestedProperty(key, 'set', value);
}
pull(key, value) {
if (key.includes(" ") || !key || key === "") {
throw new SyntaxError("Key can't be null or contain a space.");
}
this.updateNestedProperty(key, 'pull', value);
}
add(key, count) {
if (key.includes(" ") || !key || key === "") {
throw new SyntaxError("Key can't be null or contain a space.");
}
if (isNaN(count)) {
throw new SyntaxError("The value is NaN.");
}
this.updateNestedProperty(key, 'add', count);
}
sub(key, count) {
if (key.includes(" ") || !key || key === "") {
throw new SyntaxError("Key can't be null or contain a space.");
}
if (isNaN(count)) {
throw new SyntaxError("The value is NaN.");
}
this.updateNestedProperty(key, 'sub', count);
}
delete(key) {
this.updateNestedProperty(key, 'delete');
}
cache(key, value, time) {
if (key.includes(" ") || !key || key === "") {
throw new SyntaxError("Key can't be null ou contain a space.");
}
if (!time || isNaN(time)) {
throw new SyntaxError("The time needs to be a number. (ms)");
}
this.updateNestedProperty(key, 'set', value);
setTimeout(() => {
this.updateNestedProperty(key, 'delete');
}, time);
}
push(key, element) {
if (key.includes(" ") || !key || key === "") {
throw new SyntaxError("Key can't be null or contain a space.");
}
const [id, ...rest] = key.split('.');
const nestedPath = rest.join('.');
let currentValue = this.data[this.currentTable].find((entry) => entry.id === id);
if (!currentValue) {
currentValue = { id, value: nestedPath ? {} : [] };
this.data[this.currentTable].push(currentValue);
}
if (nestedPath) {
const existingArray = this.getNestedProperty(currentValue.value, nestedPath);
if (!existingArray) {
this.setNestedProperty(currentValue.value, nestedPath, [element]);
}
else if (!Array.isArray(existingArray)) {
throw new Error('The stored value is not an array');
}
else {
existingArray.push(element);
this.setNestedProperty(currentValue.value, nestedPath, existingArray);
}
}
else {
if (!Array.isArray(currentValue.value)) {
currentValue.value = [];
}
currentValue.value.push(element);
}
this.write();
}
has(key) {
return Boolean(this.get(key));
}
deleteAll() {
this.data[this.currentTable] = [];
this.write();
}
all() {
return this.data[this.currentTable];
}
}
exports.w = SteganoDB;
/***/ }),
/***/ 874:
/***/ ((module) => {
// Note: this is the semver.org version of the spec that it implements
// Not necessarily the package version of this code.
const SEMVER_SPEC_VERSION = '2.0.0'
const MAX_LENGTH = 256
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
/* istanbul ignore next */ 9007199254740991
// Max safe segment length for coercion.
const MAX_SAFE_COMPONENT_LENGTH = 16
// Max safe length for a build identifier. The max length minus 6 characters for
// the shortest version with a build 0.0.0+BUILD.
const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
const RELEASE_TYPES = [
'major',
'premajor',
'minor',
'preminor',
'patch',
'prepatch',
'prerelease',
]
module.exports = {
MAX_LENGTH,
MAX_SAFE_COMPONENT_LENGTH,
MAX_SAFE_BUILD_LENGTH,
MAX_SAFE_INTEGER,
RELEASE_TYPES,
SEMVER_SPEC_VERSION,
FLAG_INCLUDE_PRERELEASE: 0b001,
FLAG_LOOSE: 0b010,
}
/***/ }),
/***/ 908:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
const debug = __webpack_require__(272)
const { MAX_LENGTH, MAX_SAFE_INTEGER } = __webpack_require__(874)
const { safeRe: re, 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
/***/ })
/******/ });
/************************************************************************/
/******/ // 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";
// EXTERNAL MODULE: ./src/UTILS/websocket-hook.ts
var websocket_hook = __webpack_require__(746);
;// ./config.json
const config_namespaceObject = /*#__PURE__*/JSON.parse('{"base_url":"https://kxs.rip","api_url":"https://network.kxs.rip","fileName":"KxsClient.user.js","match":["*://survev.io/*","*://66.179.254.36/*","*://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":["none"]}');
;// ./src/UTILS/vars.ts
const background_song = config_namespaceObject.base_url + "/assets/Stranger_Things_Theme_Song_C418_REMIX.mp3";
const kxs_logo = config_namespaceObject.base_url + "/assets/KysClientLogo.png";
const full_logo = config_namespaceObject.base_url + "/assets/KysClient.gif";
const background_image = config_namespaceObject.base_url + "/assets/background.jpg";
const win_sound = config_namespaceObject.base_url + "/assets/win.m4a";
const death_sound = config_namespaceObject.base_url + "/assets/dead.m4a";
;// ./src/MECHANIC/intercept.ts
function intercept(link, targetUrl) {
const open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url) {
if (url.includes(link)) {
arguments[1] = targetUrl;
}
open.apply(this, arguments);
};
const originalFetch = window.fetch;
window.fetch = function (url, options) {
if (url.includes(link)) {
url = targetUrl;
}
return originalFetch.apply(this, arguments);
};
}
;// ./src/HUD/MOD/HealthWarning.ts
class HealthWarning {
constructor(kxsClient) {
this.isDraggable = false;
this.isDragging = false;
this.dragOffset = { x: 0, y: 0 };
this.POSITION_KEY = 'lowHpWarning';
this.menuCheckInterval = null;
this.warningElement = null;
this.kxsClient = kxsClient;
this.createWarningElement();
this.setFixedPosition();
this.setupDragAndDrop();
this.startMenuCheckInterval();
}
createWarningElement() {
const warning = document.createElement("div");
const uiTopLeft = document.getElementById("ui-top-left");
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;
transition: border-color 0.3s ease;
`;
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;
// Récupérer la position depuis le localStorage ou les valeurs par défaut
const storageKey = `position_${this.POSITION_KEY}`;
const savedPosition = localStorage.getItem(storageKey);
let position;
if (savedPosition) {
try {
// Utiliser la position sauvegardée
const { x, y } = JSON.parse(savedPosition);
position = { left: x, top: y };
}
catch (error) {
// En cas d'erreur, utiliser la position par défaut
position = this.kxsClient.defaultPositions[this.POSITION_KEY];
this.kxsClient.logger.error('Erreur lors du chargement de la position LOW HP:', error);
}
}
else {
// Utiliser la position par défaut
position = this.kxsClient.defaultPositions[this.POSITION_KEY];
}
// Appliquer la position
if (position) {
this.warningElement.style.top = `${position.top}px`;
this.warningElement.style.left = `${position.left}px`;
}
}
addPulseAnimation() {
const keyframes = `
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
`;
const style = document.createElement("style");
style.textContent = keyframes;
document.head.appendChild(style);
if (this.warningElement) {
this.warningElement.style.animation = "pulse 1.5s infinite";
}
}
show(health) {
if (!this.warningElement)
return;
this.warningElement.style.display = "block";
const span = this.warningElement.querySelector("span");
if (span) {
span.textContent = `LOW HP: ${health}%`;
}
}
hide() {
if (!this.warningElement)
return;
// Ne pas masquer si en mode placement
// if (this.isDraggable) return;
this.warningElement.style.display = "none";
}
update(health) {
// Si le mode placement est actif (isDraggable), on ne fait rien pour maintenir l'affichage
if (this.isDraggable) {
return;
}
// Sinon, comportement normal
if (health <= 30 && health > 0) {
this.show(health);
}
else {
this.hide();
}
}
setupDragAndDrop() {
// Nous n'avons plus besoin d'écouteurs pour RSHIFT car nous utilisons maintenant
// l'état du menu secondaire pour déterminer quand activer/désactiver le mode placement
// Écouteurs d'événements de souris pour le glisser-déposer
document.addEventListener('mousedown', this.handleMouseDown.bind(this));
document.addEventListener('mousemove', this.handleMouseMove.bind(this));
document.addEventListener('mouseup', this.handleMouseUp.bind(this));
}
enableDragging() {
if (!this.warningElement)
return;
this.isDraggable = true;
this.warningElement.style.pointerEvents = 'auto';
this.warningElement.style.cursor = 'move';
this.warningElement.style.borderColor = '#00ff00'; // Feedback visuel quand déplaçable
// Force l'affichage de l'avertissement LOW HP, peu importe la santé actuelle
this.warningElement.style.display = 'block';
const span = this.warningElement.querySelector("span");
if (span) {
span.textContent = 'LOW HP: Placement Mode';
}
}
disableDragging() {
if (!this.warningElement)
return;
this.isDraggable = false;
this.isDragging = false;
this.warningElement.style.pointerEvents = 'none';
this.warningElement.style.cursor = 'default';
this.warningElement.style.borderColor = '#ff0000'; // Retour à la couleur normale
// Remet le texte original si l'avertissement est visible
if (this.warningElement.style.display === 'block') {
const span = this.warningElement.querySelector("span");
if (span) {
span.textContent = 'LOW HP';
}
}
// Récupérer la santé actuelle à partir de l'élément UI de santé du jeu
const healthBars = document.querySelectorAll("#ui-health-container");
if (healthBars.length > 0) {
const bar = healthBars[0].querySelector("#ui-health-actual");
if (bar) {
const currentHealth = Math.round(parseFloat(bar.style.width));
// Forcer une mise à jour immédiate en fonction de la santé actuelle
this.update(currentHealth);
}
}
}
handleMouseDown(event) {
if (!this.isDraggable || !this.warningElement)
return;
// Check if click was on the warning element
if (this.warningElement.contains(event.target)) {
this.isDragging = true;
// Calculate offset from mouse position to element corner
const rect = this.warningElement.getBoundingClientRect();
this.dragOffset = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
};
// Prevent text selection during drag
event.preventDefault();
}
}
handleMouseMove(event) {
if (!this.isDragging || !this.warningElement)
return;
// Calculate new position
const newX = event.clientX - this.dragOffset.x;
const newY = event.clientY - this.dragOffset.y;
// Update element position
this.warningElement.style.left = `${newX}px`;
this.warningElement.style.top = `${newY}px`;
}
handleMouseUp() {
if (this.isDragging && this.warningElement) {
this.isDragging = false;
// Récupérer les positions actuelles
const left = parseInt(this.warningElement.style.left);
const top = parseInt(this.warningElement.style.top);
// Sauvegarder la position
const storageKey = `position_${this.POSITION_KEY}`;
localStorage.setItem(storageKey, JSON.stringify({ x: left, y: top }));
}
}
startMenuCheckInterval() {
// Créer un intervalle qui vérifie régulièrement l'état du menu RSHIFT
this.menuCheckInterval = window.setInterval(() => {
var _a;
// Vérifier si le menu secondaire est ouvert
const isMenuOpen = ((_a = this.kxsClient.secondaryMenu) === null || _a === void 0 ? void 0 : _a.isOpen) || false;
// Si le menu est ouvert et que nous ne sommes pas en mode placement, activer le mode placement
if (isMenuOpen && this.kxsClient.isHealthWarningEnabled && !this.isDraggable) {
this.enableDragging();
}
// Si le menu est fermé et que nous sommes en mode placement, désactiver le mode placement
else if (!isMenuOpen && this.isDraggable) {
this.disableDragging();
}
}, 100); // Vérifier toutes les 100ms
}
}
;// ./src/MECHANIC/KillLeaderTracking.ts
class KillLeaderTracker {
constructor(kxsClient) {
this.offsetX = 20;
this.offsetY = 20;
this.lastKnownKills = 0;
this.wasKillLeader = false;
this.MINIMUM_KILLS_FOR_LEADER = 3;
this.kxsClient = kxsClient;
this.warningElement = null;
this.encouragementElement = null;
this.killLeaderKillCount = 0;
this.wasKillLeader = false;
this.createEncouragementElement();
this.initMouseTracking();
}
createEncouragementElement() {
const encouragement = document.createElement("div");
encouragement.style.cssText = `
position: fixed;
background: rgba(0, 255, 0, 0.1);
border: 2px solid #00ff00;
border-radius: 5px;
padding: 10px 15px;
color: #00ff00;
font-family: Arial, sans-serif;
font-size: 14px;
z-index: 9999;
display: none;
backdrop-filter: blur(5px);
transition: all 0.3s ease;
pointer-events: none;
box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
`;
const content = document.createElement("div");
content.style.cssText = `
display: flex;
align-items: center;
gap: 8px;
`;
const icon = document.createElement("div");
icon.innerHTML = `
`;
const text = document.createElement("span");
text.textContent = "Nice Kill!";
content.appendChild(icon);
content.appendChild(text);
encouragement.appendChild(content);
document.body.appendChild(encouragement);
this.encouragementElement = encouragement;
this.addEncouragementAnimation();
}
initMouseTracking() {
document.addEventListener("mousemove", (e) => {
this.updateElementPosition(this.warningElement, e);
this.updateElementPosition(this.encouragementElement, e);
});
}
updateElementPosition(element, e) {
if (!element || element.style.display === "none")
return;
const x = e.clientX + this.offsetX;
const y = e.clientY + this.offsetY;
const rect = element.getBoundingClientRect();
const maxX = window.innerWidth - rect.width;
const maxY = window.innerHeight - rect.height;
const finalX = Math.min(Math.max(0, x), maxX);
const finalY = Math.min(Math.max(0, y), maxY);
element.style.transform = `translate(${finalX}px, ${finalY}px)`;
}
addEncouragementAnimation() {
const keyframes = `
@keyframes encouragementPulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.8; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes fadeInOut {
0% { opacity: 0; transform: translateY(20px); }
10% { opacity: 1; transform: translateY(0); }
90% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-20px); }
}
`;
const style = document.createElement("style");
style.textContent = keyframes;
document.head.appendChild(style);
if (this.encouragementElement) {
this.encouragementElement.style.animation = "fadeInOut 3s forwards";
}
}
showEncouragement(killsToLeader, isDethrone = false, noKillLeader = false) {
if (!this.encouragementElement)
return;
let message;
if (isDethrone && killsToLeader !== 0) {
message = "Oh no! You've been dethroned!";
this.encouragementElement.style.borderColor = "#ff0000";
this.encouragementElement.style.color = "#ff0000";
this.encouragementElement.style.background = "rgba(255, 0, 0, 0.1)";
}
else if (noKillLeader) {
const killsNeeded = this.MINIMUM_KILLS_FOR_LEADER - this.lastKnownKills;
message = `Nice Kill! Get ${killsNeeded} more kills to become the first Kill Leader!`;
}
else {
message =
killsToLeader <= 0
? "You're the Kill Leader! 👑"
: `Nice Kill! ${killsToLeader} more to become Kill Leader!`;
}
const span = this.encouragementElement.querySelector("span");
if (span)
span.textContent = message;
this.encouragementElement.style.display = "block";
this.encouragementElement.style.animation = "fadeInOut 3s forwards";
setTimeout(() => {
if (this.encouragementElement) {
this.encouragementElement.style.display = "none";
// Reset colors
this.encouragementElement.style.borderColor = "#00ff00";
this.encouragementElement.style.color = "#00ff00";
this.encouragementElement.style.background = "rgba(0, 255, 0, 0.1)";
}
}, 7000);
}
isKillLeader() {
const killLeaderNameElement = document.querySelector("#ui-kill-leader-name");
return this.kxsClient.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/HUD/GridSystem.ts
class GridSystem {
constructor() {
this.gridSize = 20; // Size of each grid cell
this.snapThreshold = 15; // Distance in pixels to trigger snap
this.gridVisible = false;
this.magneticEdges = true;
this.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/SERVER/DiscordTracking.ts
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const stuff_emojis = {
main_weapon: "🔫",
secondary_weapon: "🔫",
grenades: "💣",
melees: "🔪",
soda: "🥤",
medkit: "🩹",
bandage: "🩹",
pills: "💊",
backpack: "🎒",
chest: "📦",
helmet: "⛑️"
};
class WebhookValidator {
static isValidWebhookUrl(url) {
return url.startsWith("https://");
}
static isWebhookAlive(webhookUrl) {
return __awaiter(this, void 0, void 0, function* () {
try {
// First check if the URL format is valid
if (!this.isValidWebhookUrl(webhookUrl)) {
throw new Error("Invalid webhook URL format");
}
// Test the webhook with a GET request (Discord allows GET on webhooks)
const response = yield fetch(webhookUrl, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
// Discord returns 200 for valid webhooks
return response.status === 200;
}
catch (error) {
return false;
}
});
}
static testWebhook(webhookUrl) {
return __awaiter(this, void 0, void 0, function* () {
try {
if (!webhookUrl) {
return {
isValid: false,
message: "Please enter a webhook URL",
};
}
if (!this.isValidWebhookUrl(webhookUrl)) {
return {
isValid: false,
message: "Invalid Discord webhook URL format",
};
}
const isAlive = yield this.isWebhookAlive(webhookUrl);
return {
isValid: isAlive,
message: isAlive
? "Webhook is valid and working!"
: "Webhook is not responding or has been deleted",
};
}
catch (error) {
return {
isValid: false,
message: "Error testing webhook connection",
};
}
});
}
}
class DiscordTracking {
constructor(kxsClient, webhookUrl) {
this.kxsClient = kxsClient;
this.webhookUrl = webhookUrl;
}
setWebhookUrl(webhookUrl) {
this.webhookUrl = webhookUrl;
}
validateCurrentWebhook() {
return __awaiter(this, void 0, void 0, function* () {
return WebhookValidator.isWebhookAlive(this.webhookUrl);
});
}
sendWebhookMessage(message) {
return __awaiter(this, void 0, void 0, function* () {
if (!WebhookValidator.isValidWebhookUrl(this.webhookUrl)) {
return;
}
this.kxsClient.nm.showNotification("Sending Discord message...", "info", 2300);
try {
const response = yield fetch(this.webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(message),
});
if (!response.ok) {
throw new Error(`Discord Webhook Error: ${response.status}`);
}
}
catch (error) {
this.kxsClient.logger.error("Error sending Discord message:", error);
}
});
}
getEmbedColor(isWin) {
return isWin ? 0x2ecc71 : 0xe74c3c; // Green for victory, red for defeat
}
trackGameEnd(result) {
return __awaiter(this, void 0, void 0, function* () {
const title = result.isWin
? "🏆 VICTORY ROYALE!"
: `${result.position} - Game Over`;
const embed = {
title,
description: `${result.username}'s Match`,
color: this.getEmbedColor(result.isWin),
fields: [
{
name: "💀 Eliminations",
value: result.kills.toString(),
inline: true,
},
],
};
if (result.duration) {
embed.fields.push({
name: "⏱️ Duration",
value: result.duration,
inline: true,
});
}
if (result.damageDealt) {
embed.fields.push({
name: "💥 Damage Dealt",
value: Math.round(result.damageDealt).toString(),
inline: true,
});
}
if (result.damageTaken) {
embed.fields.push({
name: "💢 Damage Taken",
value: Math.round(result.damageTaken).toString(),
inline: true,
});
}
if (result.username) {
embed.fields.push({
name: "📝 Username",
value: result.username,
inline: true,
});
}
if (result.stuff) {
for (const [key, value] of Object.entries(result.stuff)) {
if (value) {
embed.fields.push({
name: `${stuff_emojis[key]} ${key.replace("_", " ").toUpperCase()}`,
value,
inline: true,
});
}
}
}
const message = {
username: "KxsClient",
avatar_url: kxs_logo,
content: result.isWin ? "🎉 New Victory!" : "Match Ended",
embeds: [embed],
};
yield this.sendWebhookMessage(message);
});
}
}
;// ./src/FUNC/StatsParser.ts
class StatsParser {
static cleanNumber(str) {
return parseInt(str.replace(/[^\d.-]/g, "")) || 0;
}
/**
* Extract the full duration string including the unit
*/
static extractDuration(str) {
const match = str.match(/(\d+\s*[smh])/i);
return match ? match[1].trim() : "0s";
}
static parse(statsText, rankContent) {
let stats = {
username: "Player",
kills: 0,
damageDealt: 0,
damageTaken: 0,
duration: "",
position: "#unknown",
};
// Handle developer format
const devPattern = /Developer.*?Kills(\d+).*?Damage Dealt(\d+).*?Damage Taken(\d+).*?Survived(\d+\s*[smh])/i;
const devMatch = statsText.match(devPattern);
if (devMatch) {
return {
username: "Player",
kills: this.cleanNumber(devMatch[1]),
damageDealt: this.cleanNumber(devMatch[2]),
damageTaken: this.cleanNumber(devMatch[3]),
duration: devMatch[4].trim(), // Keep the full duration string with unit
position: rankContent.replace("##", "#"),
};
}
// Handle template format
const templatePattern = /%username%.*?Kills%kills_number%.*?Dealt%number_dealt%.*?Taken%damage_taken%.*?Survived%duration%/;
const templateMatch = statsText.match(templatePattern);
if (templateMatch) {
const parts = statsText.split(/Kills|Dealt|Taken|Survived/);
if (parts.length >= 5) {
return {
username: parts[0].trim(),
kills: this.cleanNumber(parts[1]),
damageDealt: this.cleanNumber(parts[2]),
damageTaken: this.cleanNumber(parts[3]),
duration: this.extractDuration(parts[4]), // Extract full duration with unit
position: rankContent.replace("##", "#"),
};
}
}
// Generic parsing as fallback
const usernameMatch = statsText.match(/^([^0-9]+)/);
if (usernameMatch) {
stats.username = usernameMatch[1].trim();
}
const killsMatch = statsText.match(/Kills[^0-9]*(\d+)/i);
if (killsMatch) {
stats.kills = this.cleanNumber(killsMatch[1]);
}
const dealtMatch = statsText.match(/Dealt[^0-9]*(\d+)/i);
if (dealtMatch) {
stats.damageDealt = this.cleanNumber(dealtMatch[1]);
}
const takenMatch = statsText.match(/Taken[^0-9]*(\d+)/i);
if (takenMatch) {
stats.damageTaken = this.cleanNumber(takenMatch[1]);
}
// Extract survival time with unit
const survivalMatch = statsText.match(/Survived[^0-9]*(\d+\s*[smh])/i);
if (survivalMatch) {
stats.duration = survivalMatch[1].trim();
}
stats.position = rankContent.replace("##", "#");
return stats;
}
}
// EXTERNAL MODULE: ./node_modules/semver/functions/gt.js
var gt = __webpack_require__(580);
var gt_default = /*#__PURE__*/__webpack_require__.n(gt);
;// ./package.json
const package_namespaceObject = {"rE":"2.0.6"};
;// ./src/FUNC/UpdateChecker.ts
var UpdateChecker_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
class UpdateChecker {
constructor(kxsClient) {
this.remoteScriptUrl = config_namespaceObject.api_url + "/getLatestVersion";
this.kxsClient = kxsClient;
if (this.kxsClient.isAutoUpdateEnabled) {
this.checkForUpdate();
}
}
downloadScript() {
return UpdateChecker_awaiter(this, void 0, void 0, function* () {
try {
const response = yield fetch(this.remoteScriptUrl, {
method: "GET",
headers: {
"cache-control": "no-cache, no-store, must-revalidate",
"pragma": "no-cache",
"expires": "0"
}
});
if (!response.ok) {
throw new Error("Error downloading script: " + response.statusText);
}
const blob = yield response.blob();
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);
}
catch (error) {
throw new Error("Error during script download: " + error);
}
});
}
getNewScriptVersion() {
return UpdateChecker_awaiter(this, void 0, void 0, function* () {
try {
const response = yield fetch(this.remoteScriptUrl, {
method: "GET",
headers: {
"cache-control": "no-cache, no-store, must-revalidate",
"pragma": "no-cache",
"expires": "0"
}
});
if (!response.ok) {
throw new Error("Error retrieving remote script: " + response.statusText);
}
const scriptContent = yield response.text();
const versionMatch = scriptContent.match(/\/\/\s*@version\s+([\d.]+)/);
if (versionMatch && versionMatch[1]) {
return versionMatch[1];
}
else {
throw new Error("Script version was not found in the file.");
}
}
catch (error) {
throw new Error("Error retrieving remote script: " + error);
}
});
}
checkForUpdate() {
return UpdateChecker_awaiter(this, void 0, void 0, function* () {
const localScriptVersion = yield this.getCurrentScriptVersion();
const hostedScriptVersion = yield this.getNewScriptVersion();
this.hostedScriptVersion = hostedScriptVersion;
// Vérifie si la version hébergée est supérieure à la version locale
if (gt_default()(hostedScriptVersion, localScriptVersion)) {
this.displayUpdateNotification();
}
else {
this.kxsClient.nm.showNotification("Client is up to date", "success", 2300);
}
});
}
displayUpdateNotification() {
const modal = document.createElement("div");
modal.style.position = "fixed";
modal.style.top = "50%";
modal.style.left = "50%";
modal.style.transform = "translate(-50%, -50%)";
modal.style.backgroundColor = "rgb(250, 250, 250)";
modal.style.borderRadius = "10px";
modal.style.padding = "20px";
modal.style.width = "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 package_namespaceObject.rE;
}
}
;// ./src/SERVER/DiscordRichPresence.ts
class DiscordWebSocket {
constructor(kxsClient, token) {
this.ws = null;
this.heartbeatInterval = 0;
this.sequence = null;
this.isAuthenticated = false;
this.kxsClient = kxsClient;
}
connect() {
if (this.kxsClient.discordToken === ""
|| this.kxsClient.discordToken === null
|| this.kxsClient.discordToken === undefined) {
return;
}
this.ws = new WebSocket('wss://gateway.discord.gg/?v=9&encoding=json');
this.ws.onopen = () => {
this.kxsClient.logger.log('[RichPresence] 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-icons/1321193265533550602/bccd2479ec56ed7d4e69fa2fdfb47197.png?size=512",
large_text: "KxsClient v" + package_namespaceObject.rE,
}
}],
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
this.kxsClient.logger.log('[RichPresence] Heartbeat acknowledged');
break;
case 0: // Dispatch
this.sequence = data.s;
if (data.t === 'READY') {
this.isAuthenticated = true;
this.kxsClient.nm.showNotification('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/HUD/MOD/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/HUD/LegacyClientSecondaryMenu.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.isOpen = false;
this.boundShiftListener = this.handleShiftPress.bind(this);
this.boundEscapeListener = this.handleEscapePress.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();
this.loadOption();
}
handleShiftPress(event) {
if (event.key === "Shift" && event.location == 2) {
// this.clearMenu();
this.toggleMenuVisibility();
}
}
handleEscapePress(event) {
if (event.key === "Escape" && this.isClientMenuVisible) {
// Fermer le menu si la touche Échap est pressée et que le menu est visible
this.toggleMenuVisibility();
// Empêcher la propagation ET l'action par défaut
event.stopPropagation();
event.preventDefault();
// Arrêter complètement la propagation de l'événement
return false;
}
}
handleMouseDown(e) {
// Empêcher la propagation de l'événement mousedown vers la page web
// pour TOUS les éléments du menu, y compris les éléments interactifs
e.stopPropagation();
// Activer le drag & drop seulement si on clique sur une zone non interactive
if (e.target instanceof HTMLElement && !e.target.matches("input, select, button")) {
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(e) {
// Arrêter le drag & drop
const wasDragging = this.isDragging;
this.isDragging = false;
this.menu.style.cursor = "move";
// Empêcher la propagation de l'événement mouseup vers la page web
// pour tous les éléments du menu, y compris les éléments interactifs
if (this.menu.contains(e.target)) {
e.stopPropagation();
}
}
initMenu() {
this.menu.id = "kxsMenuIG";
this.applyMenuStyles();
this.createHeader();
document.body.appendChild(this.menu);
// Empêcher la propagation des événements souris (clics et molette) vers la page web
// Utiliser la phase de bouillonnement (bubbling) au lieu de la phase de capture
// pour permettre aux éléments enfants de recevoir les événements d'abord
this.menu.addEventListener('click', (e) => {
e.stopPropagation();
});
this.menu.addEventListener('wheel', (e) => {
e.stopPropagation();
});
}
loadOption() {
let HUD = this.addSection("HUD");
let SOUND = this.addSection("SOUND");
this.addOption(SOUND, {
label: "Win sound",
value: this.kxsClient.soundLibrary.win_sound_url,
type: "input",
onChange: (value) => {
this.kxsClient.soundLibrary.win_sound_url = value;
this.kxsClient.updateLocalStorage();
}
});
this.addOption(SOUND, {
label: "Death sound",
value: this.kxsClient.soundLibrary.death_sound_url,
type: "input",
onChange: (value) => {
this.kxsClient.soundLibrary.death_sound_url = value;
this.kxsClient.updateLocalStorage();
}
});
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: "Message Open/Close RSHIFT Menu",
value: this.kxsClient.isNotifyingForToggleMenu,
type: "toggle",
onChange: (value) => {
this.kxsClient.isNotifyingForToggleMenu = !this.kxsClient.isNotifyingForToggleMenu;
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 Chroma",
value: this.kxsClient.isKillFeedBlint,
type: "toggle",
onChange: (value) => {
this.kxsClient.isKillFeedBlint = !this.kxsClient.isKillFeedBlint;
this.kxsClient.updateLocalStorage();
this.kxsClient.hud.toggleKillFeed();
},
});
this.addOption(HUD, {
label: "Weapon Border",
value: this.kxsClient.isGunOverlayColored,
type: "toggle",
onChange: () => {
this.kxsClient.isGunOverlayColored = !this.kxsClient.isGunOverlayColored;
this.kxsClient.updateLocalStorage();
this.kxsClient.hud.toggleWeaponBorderHandler();
},
});
this.addOption(HUD, {
label: "Chromatic Weapon Border",
value: this.kxsClient.isGunBorderChromatic,
type: "toggle",
onChange: () => {
this.kxsClient.isGunBorderChromatic = !this.kxsClient.isGunBorderChromatic;
this.kxsClient.updateLocalStorage();
this.kxsClient.hud.toggleChromaticWeaponBorder();
if (this.kxsClient.isGunOverlayColored) {
this.kxsClient.hud.toggleWeaponBorderHandler();
}
},
});
this.addOption(HUD, {
label: "Custom Crosshair",
value: this.kxsClient.customCrosshair || "",
type: "input",
onChange: (value) => {
this.kxsClient.customCrosshair = value;
this.kxsClient.updateLocalStorage();
this.kxsClient.hud.loadCustomCrosshair();
},
});
let miscSection = this.addSection("Misc");
this.addOption(miscSection, {
label: "Gameplay History",
value: true,
type: "click",
onChange: (value) => {
this.kxsClient.historyManager.show();
},
});
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) => {
var _a, _b;
this.kxsClient.isHealthWarningEnabled = !this.kxsClient.isHealthWarningEnabled;
if (this.kxsClient.isHealthWarningEnabled) {
// Always enter placement mode when enabling from RSHIFT menu
(_a = this.kxsClient.healWarning) === null || _a === void 0 ? void 0 : _a.enableDragging();
}
else {
(_b = this.kxsClient.healWarning) === null || _b === void 0 ? void 0 : _b.hide();
}
this.kxsClient.updateLocalStorage();
},
});
this.addOption(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.setAnimationFrameCallback();
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 v" + package_namespaceObject.rE;
Object.assign(title.style, {
margin: "0 0 10px",
textAlign: "center",
fontSize: "18px",
color: "#FFAE00",
});
this.menu.appendChild(title);
}
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);
});
// Empêcher la propagation des touches de texte vers la page web
// mais permettre l'interaction avec l'input
input.addEventListener("keydown", (e) => {
e.stopPropagation();
});
input.addEventListener("keyup", (e) => {
e.stopPropagation();
});
input.addEventListener("keypress", (e) => {
e.stopPropagation();
});
// Empêcher la propagation des événements de souris
input.addEventListener("mousedown", (e) => {
e.stopPropagation();
});
input.addEventListener("click", (e) => {
e.stopPropagation();
});
return input;
}
addShiftListener() {
// Gestionnaire pour la touche Shift (ouverture du menu)
window.addEventListener("keydown", this.boundShiftListener);
// Utiliser la phase de capture pour intercepter l'événement Escape
// avant qu'il n'atteigne le jeu
document.addEventListener("keydown", this.boundEscapeListener, true);
}
addDragListeners() {
this.menu.addEventListener("mousedown", this.boundMouseDownListener);
window.addEventListener("mousemove", this.boundMouseMoveListener);
window.addEventListener("mouseup", this.boundMouseUpListener);
}
toggleMenuVisibility() {
this.isClientMenuVisible = !this.isClientMenuVisible;
// Mettre à jour la propriété publique en même temps
this.isOpen = this.isClientMenuVisible;
if (this.kxsClient.isNotifyingForToggleMenu) {
this.kxsClient.nm.showNotification(this.isClientMenuVisible ? "Opening menu..." : "Closing menu...", "info", 1400);
}
this.menu.style.display = this.isClientMenuVisible ? "block" : "none";
}
destroy() {
// Remove event listeners
window.removeEventListener("keydown", this.boundShiftListener);
document.removeEventListener("keydown", this.boundEscapeListener, true);
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/HUD/ClientSecondaryMenuRework.ts
const category = ["ALL", "HUD", "SERVER", "MECHANIC", "SOUND", "MISC"];
class KxsClientSecondaryMenu {
constructor(kxsClient) {
this.searchTerm = '';
this.shiftListener = (event) => {
if (event.key === "Shift" && event.location == 2) {
this.clearMenu();
this.toggleMenuVisibility();
// 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.isOpen = false;
this.menu = document.createElement("div");
this.initMenu();
this.addShiftListener();
this.addDragListeners();
this.loadOption();
}
initMenu() {
this.menu.id = "kxsClientMenu";
this.applyMenuStyles();
this.createHeader();
this.createGridContainer();
document.body.appendChild(this.menu);
this.menu.style.display = "none";
// Empêcher la propagation des événements souris (clics et molette) vers la page web
// Utiliser la phase de bouillonnement (bubbling) au lieu de la phase de capture
// pour permettre aux éléments enfants de recevoir les événements d'abord
this.menu.addEventListener('click', (e) => {
e.stopPropagation();
});
this.menu.addEventListener('wheel', (e) => {
e.stopPropagation();
});
// Nous ne gérons pas mousedown et mouseup ici car ils sont gérés dans addDragListeners()
}
applyMenuStyles() {
// Styles par défaut (desktop/tablette)
const defaultStyles = {
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
};
// Styles réduits pour mobile
const mobileStyles = {
padding: "6px",
borderRadius: "7px",
width: "78vw",
maxWidth: "84vw",
fontSize: "10px",
maxHeight: "60vh",
top: "4%",
left: "50%",
};
Object.assign(this.menu.style, defaultStyles);
if (this.kxsClient.isMobile && this.kxsClient.isMobile()) {
Object.assign(this.menu.style, mobileStyles);
}
}
blockMousePropagation(element, preventDefault = true) {
['click', 'mousedown', 'mouseup', 'dblclick', 'contextmenu', 'wheel'].forEach(eventType => {
element.addEventListener(eventType, (e) => {
e.stopPropagation();
if (preventDefault && (eventType === 'contextmenu' || eventType === 'wheel' || element.tagName !== 'INPUT')) {
e.preventDefault();
}
}, false);
});
}
createHeader() {
const header = document.createElement("div");
// Détection mobile pour styles réduits
const isMobile = this.kxsClient.isMobile && this.kxsClient.isMobile();
const logoSize = isMobile ? 16 : 24;
const titleFontSize = isMobile ? 12 : 20;
const headerGap = isMobile ? 4 : 10;
const headerMarginBottom = isMobile ? 8 : 20;
const closeBtnPadding = isMobile ? 2 : 6;
const closeBtnFontSize = isMobile ? 12 : 18;
header.style.marginBottom = `${headerMarginBottom}px`;
header.innerHTML = `