Files
L2-SPP/nickgen.html

246 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>NickGen — генератор ников</title>
<style>
body { background:#16181d; color:#d6d8de; font:14px/1.5 Consolas, monospace; margin:0; padding:24px; }
h1 { font-size:18px; margin:0 0 16px; color:#e8c65a; }
.controls { display:flex; flex-wrap:wrap; gap:16px; align-items:center; margin-bottom:16px;
background:#1e2128; border:1px solid #2c3038; border-radius:6px; padding:12px 16px; }
.controls label { display:flex; align-items:center; gap:6px; cursor:pointer; white-space:nowrap; }
.controls input[type=number] { width:56px; background:#111318; color:#d6d8de; border:1px solid #3a3f48; border-radius:4px; padding:3px 6px; font:inherit; }
button { background:#e8c65a; color:#16181d; border:0; border-radius:4px; padding:6px 18px; font:bold 14px Consolas, monospace; cursor:pointer; }
button:hover { background:#f5d878; }
button.sec { background:#2c3038; color:#d6d8de; }
button.sec:hover { background:#3a3f48; }
#grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(180px,1fr)); gap:6px; }
.nick { background:#1e2128; border:1px solid #2c3038; border-radius:4px; padding:6px 10px; cursor:pointer; }
.nick:hover { border-color:#e8c65a; }
.nick small { color:#6b7280; float:right; }
#status { color:#6b7280; margin:10px 0; min-height:1.5em; }
.weights { display:flex; gap:8px; align-items:center; }
</style>
</head>
<body>
<h1>NickGen — генератор ников для ботов</h1>
<div class="controls">
<label>Кол-во <input type="number" id="count" value="60" min="1" max="5000"></label>
<div class="weights">Части 1/2/3:
<input type="number" id="w1" value="25" min="0"><input type="number" id="w2" value="55" min="0"><input type="number" id="w3" value="20" min="0">
</div>
<label><input type="checkbox" id="optTranslit" checked> транслит</label>
<label><input type="checkbox" id="optFakeCyr" checked> псевдокириллица</label>
<label><input type="checkbox" id="optLeet" checked> l33t-цифры</label>
<label><input type="checkbox" id="optDigits" checked> цифровой суффикс</label>
<label><input type="checkbox" id="optWrap" checked> xX…Xx</label>
<button id="go">Генерировать</button>
<button id="copyAll" class="sec">Скопировать все</button>
</div>
<div id="status"></div>
<div id="grid"></div>
<script>
'use strict';
// ============================================================================
// Вся логика ниже — самодостаточная и переносимая в Java как есть:
// пулы (массивы строк), карты (switch/Map), генерация (Random).
// ============================================================================
// --- Пулы слов (русские — в кириллице, рендер решает транслит/псевдокириллицу) ---
const RU_ADJ = ['злой','добрый','старый','дикий','мелкий','жирный','хитрый','бешеный','мамкин','просто','лысый','грозный','тихий','сонный','голодный','кривой','ржавый','бодрый','унылый','модный','четкий','дерзкий','борзый','лютый','шустрый','реальный','угарный','наглый'];
const RU_NOUN = ['гном','пес','кот','волк','сухарь','травка','пельмень','колбаса','мармелад','чума','ведьмак','сквозняк','холодок','бомж','топор','кабан','хомяк','огурец','батон','чеснок','валенок','тапок','кефир','компот','барсук','ежик','мухомор','лопата','кирпич','шашлык','беляш','танкист','дед','сосед','медоед','каратель','монах','гопник','чебурек','шаурма','семки','пацан','браток','кент','батя','гараж','подъезд','чушпан','задрот','нуб','читер','красавчик','нагибатор','убиватор','терминатор'];
const RU_NAME = ['олег','вася','дашка','ленка','федор','санек','толик','серега','витек','петруха','колян','димон','настя','машка','гриша','степан','юрец','макс','жека','вован','толян','саня','диман','леха','андрюха','кирюха','паша','стас','руслан','тимур','натаха','светка','танюха','ирка','вовчик','петрович','палыч'];
const RU_SURNAME = ['пупкин','сидоров','петров','иванов','смирнов','кузнецов','козлов','волков','зайцев','медведев'];
const PREFIXES = ['MC','Dj','Mr'];
const EN_ADJ = ['Dark','Shadow','Storm','Night','Blood','Ghost','Crazy','Old','Swift','Silent','Grim','Mad','Evil','Frost','Iron','Black','Wild','Lone','Sweet','True'];
const EN_NOUN = ['Slayer','Reaper','Hunter','Killer','Knight','Rogue','Monk','Rider','Arrow','Blade','Master','Lord','King','Sniper','Mage','Angel','Demon','Raven','Wolf','Dragon','Fire','Ice','Death','Star','Dream','Soul','Fang','Claw','Bishop','Titan'];
// --- Транслит: кириллица -> латиница ---
const TRANSLIT = { 'а':'a','б':'b','в':'v','г':'g','д':'d','е':'e','ё':'e','ж':'zh','з':'z','и':'i','й':'j','к':'k','л':'l','м':'m','н':'n','о':'o','п':'p','р':'r','с':'s','т':'t','у':'u','ф':'f','х':'h','ц':'c','ч':'ch','ш':'sh','щ':'sch','ъ':'','ы':'y','ь':'','э':'e','ю':'yu','я':'ya' };
// --- Псевдокириллица: латинские символы, похожие на кириллицу (KpucuK, TpaBka, IIeJIbMeHb) ---
// null = нет похожего, берём транслит этой буквы.
const FAKE_CYR = { 'а':'a','б':'6','в':'B','г':'r','д':null,'е':'e','ё':'e','ж':null,'з':'3','и':'u','й':'u','к':'K','л':'JI','м':'M','н':'H','о':'o','п':'II','р':'p','с':'c','т':'T','у':'y','ф':null,'х':'x','ц':null,'ч':'4','ш':'LLI','щ':null,'ъ':'','ы':'bI','ь':'b','э':'3','ю':'IO','я':'9I' };
// --- l33t для латинских слов (Ki4iga, OlegBI4) ---
const LEET = { 'e':'3','o':'0','a':'4','i':'1','s':'5' };
const rnd = (n) => Math.floor(Math.random() * n);
const pick = (arr) => arr[rnd(arr.length)];
const chance = (pct) => rnd(100) < pct;
// Транслит слова, с вариативностью окончаний (zloj/zloy)
function translitWord(w) {
let out = '';
for (const ch of w) {
let t = TRANSLIT[ch] ?? ch;
if (ch === 'й' && chance(50)) t = 'y';
if (ch === 'в' && chance(15)) t = 'w'; // Wowan-стиль
if (ch === 'х') t = chance(40) ? 'x' : (chance(30) ? 'kh' : 'h'); // Xyligan-стиль
out += t;
}
return out.charAt(0).toUpperCase() + out.slice(1);
}
// Псевдокириллица слова
function fakeCyrWord(w) {
let out = '';
for (const ch of w) {
const f = FAKE_CYR[ch];
out += (f !== null && f !== undefined) ? f : (TRANSLIT[ch] ?? ch);
}
return out;
}
// l33t: до 2 замен на слово, каждая с шансом 45%
function leetWord(w) {
let out = '', used = 0;
for (const ch of w) {
const l = LEET[ch.toLowerCase()];
if (l && used < 2 && chance(45)) { out += l; used++; }
else out += ch;
}
return out;
}
// --- Сборка частей: язык + число частей -> массив пар [слово, isSurname] ---
function buildParts(lang, n) {
const w = (x) => [x, false], s = (x) => [x, true];
if (lang === 'ru') {
if (n === 1) {
const v = rnd(100);
if (v < 45) return [w(pick(RU_NOUN))];
if (v < 80) return [w(pick(RU_NAME))];
return [s(pick(RU_SURNAME))];
}
if (n === 2) {
const v = rnd(4);
if (v === 0) return [w(pick(RU_ADJ)), w(pick(RU_NOUN))];
if (v === 1) return [w(pick(RU_ADJ)), w(pick(RU_NAME))];
if (v === 2) return [w(pick(RU_NOUN)), w(pick(RU_NOUN))];
return [w(pick(RU_NAME)), s(pick(RU_SURNAME))]; // VasyaPupkin
}
if (chance(50)) return [w(pick(RU_ADJ)), w(pick(RU_NOUN)), w(pick(RU_NOUN))];
return [w(pick(RU_ADJ)), w(pick(RU_NAME)), s(pick(RU_SURNAME))];
}
if (n === 1) return [w(pick(EN_NOUN))];
if (n === 2) return [w(pick(EN_ADJ)), w(pick(EN_NOUN))];
const first = pick(EN_ADJ);
const second = chance(50) ? pick(EN_ADJ.filter(x => x !== first)) : pick(EN_NOUN);
return [w(first), w(second), w(pick(EN_NOUN))];
}
const isRu = (w) => /[а-яё]/.test(w);
// --- Один ник. opts: {translit, fakeCyr, leet, digits, wrap, weights:[w1,w2,w3]} ---
function generateOne(opts) {
// число частей по весам
const total = opts.weights[0] + opts.weights[1] + opts.weights[2];
let roll = rnd(Math.max(total, 1)), n = 3;
if (roll < opts.weights[0]) n = 1;
else if (roll < opts.weights[0] + opts.weights[1]) n = 2;
const lang = chance(70) ? 'ru' : 'en';
const parts = buildParts(lang, n);
// редкая смесь языков в 2-3-частных никах
if (n > 1 && chance(15)) parts[parts.length - 1] = (lang === 'ru') ? [pick(EN_NOUN), false] : [translitWord(pick(RU_NOUN)), false];
// режим рендера русских слов — один на весь ник
const modes = [];
if (opts.translit) modes.push('translit');
if (opts.fakeCyr) modes.push('fakeCyr');
const ruMode = modes.length ? pick(modes) : 'translit';
let nick = parts.map(([w, isSurname]) => {
if (isRu(w)) {
if (ruMode === 'fakeCyr') return fakeCyrWord(w);
let t = translitWord(w);
// фамилии на -ov: SmirnOFF / Smirnoff
if (isSurname && t.toLowerCase().endsWith('ov') && chance(50)) t = t.slice(0, -2) + (chance(40) ? 'OFF' : 'off');
return t;
}
return (opts.leet && chance(20)) ? leetWord(w) : w;
}).join('');
// декорации (стиль сида: dashka, ZloyPES, HolodOK, OldRogue38, MCVovan95, xXDarkSlayerXx)
if (ruMode === 'translit') {
if (chance(12)) nick = nick.toLowerCase(); // dashka
else if (chance(12) && nick.length > 4) { // ZloyPES / HolodOK
const k = 2 + rnd(2);
nick = nick.slice(0, -k) + nick.slice(-k).toUpperCase();
}
}
if (opts.digits && chance(28) && nick.length <= 14) {
const v = rnd(100);
let suf;
if (v < 45) suf = chance(50) ? String(1 + rnd(9)) : String(10 + rnd(90)); // OlegBI4-стиль
else if (v < 80) suf = String((85 + rnd(23)) % 100).padStart(2, '0'); // год рождения 85..07
else suf = String(1985 + rnd(23)); // 1985..2007
if (nick.length + suf.length <= 16) nick += suf;
}
if (chance(4) && nick.length <= 13) nick += 'RUS';
let prefixed = false;
if (chance(6) && nick.length <= 14) { nick = pick(PREFIXES) + nick; prefixed = true; } // MCVovan
if (opts.wrap && !prefixed && chance(4) && nick.length <= 12) nick = 'xX' + nick + 'Xx';
return nick;
}
// Валидный для клиента L2: [A-Za-z0-9], 3-16 символов
const VALID = /^[A-Za-z0-9]{3,16}$/;
function generateBatch(count, opts, taken) {
const out = [];
let guard = count * 50;
while (out.length < count && guard-- > 0) {
const nick = generateOne(opts);
if (VALID.test(nick) && !taken.has(nick.toLowerCase())) {
taken.add(nick.toLowerCase());
out.push(nick);
}
}
return out;
}
// ============================================================================
// UI
// ============================================================================
const $ = (id) => document.getElementById(id);
let current = [];
function run() {
const opts = {
translit: $('optTranslit').checked,
fakeCyr: $('optFakeCyr').checked,
leet: $('optLeet').checked,
digits: $('optDigits').checked,
wrap: $('optWrap').checked,
weights: [+$('w1').value || 0, +$('w2').value || 0, +$('w3').value || 0],
};
const count = Math.min(+$('count').value || 60, 5000);
current = generateBatch(count, opts, new Set());
const grid = $('grid');
grid.innerHTML = '';
for (const nick of current) {
const d = document.createElement('div');
d.className = 'nick';
d.innerHTML = nick + ' <small>' + nick.length + '</small>';
d.title = 'Кликни — скопировать';
d.onclick = () => { navigator.clipboard.writeText(nick); $('status').textContent = 'Скопировано: ' + nick; };
grid.appendChild(d);
}
$('status').textContent = 'Сгенерировано: ' + current.length + ' (уникальные, ≤16 символов, [A-Za-z0-9])';
}
$('go').onclick = run;
$('copyAll').onclick = () => {
navigator.clipboard.writeText(current.join('\n'));
$('status').textContent = 'Скопировано в буфер: ' + current.length + ' ников';
};
run();
</script>
</body>
</html>