chore: initial commit — CS2 Simulator

This commit is contained in:
2026-07-05 10:40:00 +00:00
commit c268557997
40 changed files with 824236 additions and 0 deletions
+347
View File
@@ -0,0 +1,347 @@
// ─── Safe Mode (Recording Mode for Streamers/Bloggers) ─────────────────────
// Replaces gambling terms with YouTube-safe synonyms
const SafeMode = (() => {
const STORAGE_KEY = 'safe_mode';
const DICT = [
// Currency
[/₽(?=\s*\d)/g, 'тугриков'],
[/(\d+)\s*₽/g, (_, n) => {
const num = parseInt(n);
const last = num % 10;
const lastTwo = num % 100;
if (lastTwo >= 11 && lastTwo <= 19) return n + ' тугриков';
if (last === 1) return n + ' тугрик';
if (last >= 2 && last <= 4) return n + ' тугрика';
return n + ' тугриков';
}],
[/₽(?!\d)/g, 'тугриков'],
// Cases
[/\b(открыть|открывай|открыл)\s+кейс/gi, (m) => m.replace(/кейс/i, 'бокс')],
[/\bкейс[а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'кейс') return 'бокс';
if (lower === 'кейса') return 'бокса';
if (lower === 'кейсу') return 'боксу';
if (lower === 'кейсом') return 'боксом';
if (lower === 'кейсе') return 'боксе';
if (lower === 'кейсы') return 'боксы';
if (lower === 'кейсов') return 'боксов';
if (lower === 'кейсам') return 'боксам';
if (lower === 'кейсами') return 'боксами';
if (lower === 'кейсах') return 'боксах';
return m;
}],
// Case (English)
[/\bcase\b/gi, 'box'],
// Skins
[/\bскин[а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'скин') return 'облик';
if (lower === 'скина') return 'облика';
if (lower === 'скину') return 'облику';
if (lower === 'скином') return 'обликом';
if (lower === 'скине') return 'облике';
if (lower === 'скины' || lower === 'скин') return 'облики';
if (lower === 'скинов') return 'обликов';
if (lower === 'скинам') return 'обликам';
if (lower === 'скинами') return 'обликами';
if (lower === 'скинах') return 'обликах';
return m;
}],
// Contract
[/\bконтракт[а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'контракт') return 'сделка';
if (lower === 'контракта') return 'сделки';
if (lower === 'контракту') return 'сделке';
if (lower === 'контрактом') return 'сделкой';
if (lower === 'контракте') return 'сделке';
if (lower === 'контракты' || lower === 'контракт') return 'сделки';
if (lower === 'контрактов') return 'сделок';
if (lower === 'контрактам') return 'сделкам';
if (lower === 'контрактами') return 'сделками';
if (lower === 'контрактах') return 'сделках';
if (lower === 'контрактная' || lower === 'контрактный' || lower === 'контрактное') return 'договорная';
return m;
}],
// Upgrade
[/\bапгрейд[а-я]*\b/gi, (m) => {
if (m.toLowerCase() === 'апгрейд') return 'улучшение';
if (m.toLowerCase() === 'апгрейда') return 'улучшения';
if (m.toLowerCase() === 'апгрейду') return 'улучшению';
if (m.toLowerCase() === 'апгрейдом') return 'улучшением';
if (m.toLowerCase() === 'апгрейде') return 'улучшении';
if (m.toLowerCase() === 'апгрейды') return 'улучшения';
if (m.toLowerCase() === 'апгрейдов') return 'улучшений';
return m;
}],
[/\bupgrade\b/gi, 'upgrade'],
// Float
[/\bfloat\b/gi, 'состояние'],
// Rarity
[/\bредкост[ьи][а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'редкость') return 'класс';
if (lower === 'редкости') return 'классы';
if (lower === 'редкостью') return 'классом';
if (lower === 'редкостей') return 'классов';
if (lower === 'редкостям') return 'классам';
return m;
}],
// Chance
[/\bшанс[а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'шанс') return 'вероятность';
if (lower === 'шанса') return 'вероятности';
if (lower === 'шансу') return 'вероятности';
if (lower === 'шансом') return 'вероятностью';
if (lower === 'шансе') return 'вероятности';
if (lower === 'шансы') return 'вероятности';
if (lower === 'шансов') return 'вероятностей';
return m;
}],
// Bet
[/\bставк[а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'ставка') return 'вход';
if (lower === 'ставки') return 'входы';
if (lower === 'ставку') return 'вход';
if (lower === 'ставкой') return 'входом';
if (lower === 'ставок') return 'входов';
if (lower === 'ставкам') return 'входам';
return m;
}],
// Win / Lose
[/\bвыигрыш[а-я]*\b/gi, (m) => {
if (m.toLowerCase() === 'выигрыш' || m.toLowerCase() === 'выигрыша') return 'приз';
if (m.toLowerCase() === 'выигрышу') return 'призу';
if (m.toLowerCase() === 'выигрышем') return 'призом';
if (m.toLowerCase() === 'выигрыше') return 'призе';
return m;
}],
[/\b(выиграл|выиграла|выиграли)\b/gi, (m) => {
if (m.toLowerCase() === 'выиграл' || m.toLowerCase() === 'выиграла') return 'получил(a)';
if (m.toLowerCase() === 'выиграли') return 'получили';
return m;
}],
// Lose
[/\bпроиграл[а-я]*\b/gi, 'не получилось'],
// Crash game
[/\b(краш|crash)\b/gi, 'риск-игра'],
// Open case
[/\bоткры[тв][а-я]+\s+кейс/gi, (m) => m.replace(/кейс/i, 'бокс')],
// Wear
[/\bизнос\b/gi, 'состояние'],
// Probability display
[/\bвероятность\b/gi, 'шанс'],
// Quick mode tooltips
[/\bбыстрый режим\b/gi, 'турбо-режим'],
// Balance
[/\bбаланс\b/gi, 'счёт'],
// Sell
[/\bпрода[жв][а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'продажа' || lower === 'продать') return 'обменять';
if (lower === 'продал') return 'обменял';
if (lower === 'продайте') return 'обменяйте';
return m;
}],
// Inventory
[/\bинвентар[ьи][а-я]*\b/gi, (m) => {
const lower = m.toLowerCase();
if (lower === 'инвентарь') return 'коллекция';
if (lower === 'инвентаря') return 'коллекции';
if (lower === 'инвентаре') return 'коллекции';
if (lower === 'инвентарём') return 'коллекцией';
return m;
}],
// Trade up
[/\btrade.?up\b/gi, 'treyd-ap'],
// Premium
[/\bпремиум\b/gi, 'эксклюзив'],
// Rare names in English
[/\bcovert\b/gi, 'covert'],
[/\bclassified\b/gi, 'classified'],
[/\bconsumers?\b/gi, 'consumer'],
[/\bindustrials?\b/gi, 'industrial'],
[/\bmil-spec\b/gi, 'mil-spec'],
[/\brestricted\b/gi, 'restricted'],
];
let observer = null;
let active = false;
function isActive() {
return localStorage.getItem(STORAGE_KEY) === 'true';
}
function setActive(val) {
localStorage.setItem(STORAGE_KEY, val ? 'true' : 'false');
active = val;
}
function replaceText(node) {
if (node.nodeType === Node.TEXT_NODE) {
let text = node.textContent;
let changed = false;
for (const [pattern, replacement] of DICT) {
const newText = text.replace(pattern, replacement);
if (newText !== text) {
text = newText;
changed = true;
}
}
if (changed) {
node.textContent = text;
}
} else if (node.nodeType === Node.ELEMENT_NODE) {
// Don't process script, style, input, textarea, canvas elements
const tag = node.tagName.toLowerCase();
if (tag === 'script' || tag === 'style' || tag === 'input' ||
tag === 'textarea' || tag === 'canvas' || tag === 'svg' ||
tag === 'code' || tag === 'pre') {
return;
}
// Process title and alt attributes
if (node.hasAttribute('title')) {
let t = node.getAttribute('title');
for (const [pattern, replacement] of DICT) {
t = t.replace(pattern, replacement);
}
node.setAttribute('title', t);
}
if (node.hasAttribute('alt')) {
let a = node.getAttribute('alt');
for (const [pattern, replacement] of DICT) {
a = a.replace(pattern, replacement);
}
node.setAttribute('alt', a);
}
// Process children
for (let i = 0; i < node.childNodes.length; i++) {
replaceText(node.childNodes[i]);
}
}
}
function apply() {
replaceText(document.body);
// Update document title
if (document.title) {
let t = document.title;
for (const [pattern, replacement] of DICT) {
t = t.replace(pattern, replacement);
}
document.title = t;
}
// Update safe mode button appearance
const btn = document.getElementById('safeModeToggle');
if (btn) {
btn.textContent = active ? '🛡️' : '🎙️';
btn.title = active ? 'Режим записи активен' : 'Режим записи';
btn.style.borderColor = active ? 'var(--success-color)' : '';
}
}
function startObserver() {
if (observer) observer.disconnect();
observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
replaceText(node);
} else if (node.nodeType === Node.TEXT_NODE) {
replaceText(node);
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
characterData: true
});
}
function toggle() {
active = !isActive();
setActive(active);
if (active) {
apply();
startObserver();
} else {
if (observer) {
observer.disconnect();
observer = null;
}
// Reload to restore original text
location.reload();
}
return active;
}
function init() {
active = isActive();
if (active) {
// Wait for DOM to be ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
apply();
startObserver();
});
} else {
apply();
startObserver();
}
}
// Set initial button state
const btn = document.getElementById('safeModeToggle');
if (btn) {
btn.textContent = active ? '🛡️' : '🎙️';
btn.title = active ? 'Режим записи активен' : 'Режим записи';
if (active) btn.style.borderColor = 'var(--success-color)';
}
}
return {
init,
toggle,
isActive
};
})();
// Global toggle function for onclick in navbars
function toggleSafeMode() {
SafeMode.toggle();
}
// Auto-init
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => SafeMode.init());
} else {
SafeMode.init();
}