🎮ArcadeLab

🎃 Кликер конфет

by SuperHero55
631 lines29.4 KB
▶ Play
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>🎃 Кликер конфет</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;user-select:none;-webkit-tap-highlight-color:transparent}

/* Плавный переливающийся фон */
body{
  min-height:100vh;
  background: linear-gradient(-45deg, #2a0e3f, #1a0628, #0d0416, #4a1e6b);
  background-size: 400% 400%;
  animation: gradientBG 15s ease infinite;
  font-family:system-ui,Arial,sans-serif;color:#fff;padding:10px;
  display:flex;justify-content:center;overflow-x:hidden;
}
@keyframes gradientBG {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.game{display:flex;flex-direction:column;align-items:center;gap:12px;width:100%;max-width:460px;padding:8px;position:relative;z-index:1}

/* Стеклянные кнопки и меню */
#settingsBtn{position:fixed;top:12px;right:12px;width:46px;height:46px;border-radius:50%;background:rgba(58,21,80,0.7);backdrop-filter:blur(10px);border:2px solid #ffb347;color:#ffe066;font-size:22px;cursor:pointer;z-index:100;display:flex;align-items:center;justify-content:center;box-shadow:0 0 15px rgba(255,179,71,0.4)}
.modal{position:fixed;inset:0;z-index:200;background:rgba(10,3,18,.8);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:16px;opacity:0;pointer-events:none;transition:opacity .3s}
.modal.show{opacity:1;pointer-events:auto}
.modal-box{background:rgba(58,21,80,0.8);backdrop-filter:blur(15px);border:2px solid rgba(255,179,71,0.6);border-radius:22px;padding:22px 20px;width:100%;max-width:440px;max-height:85vh;overflow-y:auto;position:relative;box-shadow:0 10px 40px rgba(0,0,0,0.8), inset 0 0 20px rgba(255,179,71,0.2)}
.modal-title{color:#ffe066;font-size:18px;font-weight:900;letter-spacing:3px;text-align:center;margin-bottom:16px;text-transform:uppercase;text-shadow:0 0 10px #ffb347}
.modal-close{position:absolute;top:12px;right:12px;width:34px;height:34px;border-radius:50%;background:#a02040;border:2px solid #ffb347;color:#fff;font-size:15px;font-weight:900;cursor:pointer;display:flex;align-items:center;justify-content:center}

/* Промокоды */
.promo-box{background:rgba(0,0,0,0.3);border:2px solid #ffe066;border-radius:16px;padding:16px;margin-bottom:14px;text-align:center;box-shadow:0 0 15px rgba(255,224,102,0.2)}
.promo-title{color:#ffe066;font-size:14px;font-weight:900;letter-spacing:2px;margin-bottom:10px;text-shadow:0 0 8px #ffe066}
.promo-input{width:100%;padding:12px 14px;background:rgba(0,0,0,.5);border:2px solid #ffb347;border-radius:12px;color:#fff;font-size:15px;font-family:inherit;text-align:center;letter-spacing:2px;outline:none;margin-bottom:10px}
.promo-input:focus{border-color:#ffe066;box-shadow:0 0 15px #ffb347}
.promo-btn{width:100%;padding:11px;background:linear-gradient(180deg,#ffe066,#ff8c00);border:2px solid #ffe066;color:#2a0e3f;border-radius:12px;font-size:13px;font-weight:900;letter-spacing:1px;cursor:pointer;font-family:inherit;box-shadow:0 0 10px #ffb347}
.promo-hint{color:#ffb347;font-size:10px;margin-top:8px}

/* Меню счёта */
.menu{background:rgba(58,21,80,0.6);backdrop-filter:blur(10px);border:2px solid rgba(255,179,71,0.5);border-radius:22px;padding:18px 22px;text-align:center;width:100%;box-shadow:0 10px 30px rgba(0,0,0,0.5), inset 0 0 15px rgba(255,179,71,0.1)}
.menu-t{color:#ffc896;font-size:10px;letter-spacing:4px;margin-bottom:8px;text-transform:uppercase;text-shadow:0 0 5px #ffb347}
.score{font-size:42px;font-weight:900;line-height:1;display:flex;justify-content:center;align-items:baseline;gap:8px}
.score #score{color:#ffe066;font-size:48px;text-shadow:0 0 20px #ffb347, 0 0 40px #ff8c00;display:inline-block;transition:transform .15s;font-variant-numeric:tabular-nums;min-width:80px;text-align:center}
.score #score.bump{transform:scale(1.15)}
.unit{font-size:26px;filter:drop-shadow(0 0 5px #ffb347)}
.per-sec{font-size:11px;color:#ffe066;margin-top:8px;background:rgba(255,235,59,.15);display:inline-block;padding:4px 14px;border-radius:10px;border:1px solid #ffeb3b80}
.bonus{font-size:10px;color:#ff6b8a;margin-top:6px;font-weight:700;min-height:14px}

/* Кот */
.cat-wrap{position:relative;padding:6px;cursor:pointer}
.cat-glow{position:absolute;inset:-10px;border-radius:50%;background:radial-gradient(circle,rgba(255,180,71,.5),transparent 70%);pointer-events:none;animation:pulseGlow 2s infinite}
@keyframes pulseGlow{0%,100%{transform:scale(1);opacity:0.6}50%{transform:scale(1.1);opacity:1}}

#cat{
  position:relative;z-index:2;width:220px;height:220px;object-fit:cover;border-radius:50%;
  cursor:pointer;box-shadow:0 15px 35px #000d, 0 0 40px #ff8c00;
  border:5px solid #ffb347;background:transparent;display:block;
  transition:transform .12s;
  /* Убираем черный фон у картинок */
  mix-blend-mode: screen; 
}
#cat.pop{animation:catPop .4s cubic-bezier(.34,1.56,.64,1)}
@keyframes catPop{0%{transform:scale(1)}30%{transform:scale(1.1)}60%{transform:scale(.97)}100%{transform:scale(1)}}

h3{color:#ffc896;font-size:11px;letter-spacing:4px;margin:8px 0 4px;font-weight:700;text-align:center;text-transform:uppercase;text-shadow:0 0 8px #ffb347}

/* Скины */
.skins{display:flex;gap:10px;width:100%;overflow-x:auto;padding:6px 2px 12px}
.skins::-webkit-scrollbar{display:none}
.sk{flex:0 0 auto;width:76px;height:92px;border-radius:18px;background:rgba(0,0,0,0.4);border:2px solid rgba(255,180,71,.5);cursor:pointer;padding:4px;position:relative;overflow:hidden;transition:transform .2s,border-color .2s;box-shadow:inset 0 0 10px #000}
.sk:active{transform:scale(.93)}
.sk img{width:100%;height:68px;object-fit:cover;border-radius:13px;display:block;background:transparent;pointer-events:none;mix-blend-mode:screen}
.sk .mult{position:absolute;bottom:3px;left:3px;right:3px;text-align:center;font-size:11px;font-weight:900;color:#ffe066;background:rgba(0,0,0,.8);border-radius:7px;padding:2px 0}
.sk.active{border-color:#ffe066;box-shadow:0 0 25px #ffb347;transform:translateY(-3px) scale(1.05)}
.sk.lock img{filter:grayscale(1) brightness(.3)}
.sk.lock::after{content:'🔒' attr(data-c);position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,.85);color:#ffb347;font-size:11px;font-weight:700;border-radius:15px;padding:4px;text-align:center;line-height:1.2}
.sk.active::before{content:'✓';position:absolute;top:-3px;right:-3px;width:22px;height:22px;border-radius:50%;background:#ffb347;color:#2a0e3f;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:900;z-index:2;box-shadow:0 0 10px #ffb347}

/* Прокачка */
.ups-panel{width:100%;background:rgba(58,21,80,0.6);backdrop-filter:blur(10px);border:2px solid rgba(255,179,71,0.5);border-radius:22px;padding:16px 14px 18px;box-shadow:0 10px 30px rgba(0,0,0,0.5)}
.ups{display:flex;flex-direction:column;gap:10px;margin-top:10px}
.up{display:flex;align-items:center;gap:12px;background:rgba(0,0,0,0.3);border:2px solid #ff8c0060;border-radius:16px;padding:12px 14px;cursor:pointer;color:#fff;text-align:left;font-family:inherit;width:100%;position:relative;transition:transform .2s,border-color .2s;box-shadow:inset 0 0 10px rgba(0,0,0,0.5)}
.up::before{content:'';position:absolute;top:0;left:0;width:5px;height:100%;background:#ff8c0080;border-radius:16px 0 0 16px;transition:background .3s}
.up:disabled{opacity:.5;cursor:not-allowed}
.up.aff{border-color:#ffb347;box-shadow:0 0 18px #ff8c00a0, inset 0 0 10px rgba(255,140,0,0.3)}
.up.aff::before{background:#ffb347;box-shadow:0 0 10px #ffb347}
.up:active:not(:disabled){transform:scale(.97)}
.up.maxed{border-color:#ff4466;background:rgba(90,20,40,0.8);opacity:1}
.up.maxed::before{background:#ff4466}
.up-i{font-size:28px;width:44px;height:44px;flex:0 0 44px;display:flex;align-items:center;justify-content:center;background:rgba(255,180,71,.15);border-radius:12px;box-shadow:inset 0 0 10px #000}
.up-t{flex:1;min-width:0}
.up-n{font-size:13px;font-weight:700;color:#ffb347;margin-bottom:3px;display:flex;justify-content:space-between;gap:6px}
.up-n .lvl{font-size:10px;color:#ffc896;background:rgba(255,180,71,.2);padding:3px 9px;border-radius:9px;border:1px solid #ff8c0060}
.up.aff .up-n .lvl{background:#ffb347;color:#2a0e3f}
.up.maxed .up-n{color:#ff8899}
.up.maxed .up-n .lvl{background:#ff4466;color:#fff}
.up-d{font-size:10px;color:#ffffffcc;line-height:1.4;margin-bottom:5px}
.up-progress{height:5px;background:rgba(0,0,0,0.6);border-radius:3px;overflow:hidden}
.up-progress-fill{height:100%;width:0;background:linear-gradient(180deg,#ffd080,#ff8c00);border-radius:3px;transition:width .4s ease-out;box-shadow:0 0 8px #ffb347}
.up.maxed .up-progress-fill{background:#ff4466}
.up-c{display:flex;flex-direction:column;align-items:flex-end;gap:4px;min-width:80px}
.up-c .cost{font-size:12px;font-weight:900;color:#ffe066;white-space:nowrap}
.up.aff .up-c .cost{color:#fff}
.up-c .buy{font-size:9px;color:#ffb347;text-transform:uppercase;letter-spacing:1.5px;font-weight:700}
.up-c .maxed-label{display:none;color:#ff8899;font-weight:900;font-size:13px}
.up.maxed .up-c .cost,.up.maxed .up-c .buy{display:none}
.up.maxed .up-c .maxed-label{display:block}

.btns-row{display:flex;gap:10px;width:100%}
.btns-row button{flex:1;background:rgba(58,21,80,0.7);border:2px solid #ff8c0070;color:#ffb347;padding:13px;border-radius:14px;font-size:13px;font-weight:700;cursor:pointer;font-family:inherit;box-shadow:0 0 10px #000}
.btns-row button:active{transform:scale(.96)}

.pool{position:fixed;inset:0;pointer-events:none;z-index:999;overflow:hidden}
.ft{position:absolute;color:#ffe066;font-weight:900;font-size:24px;text-shadow:0 0 10px #ff8c00, 0 0 20px #ffb347;transform:translate(-50%,-50%);pointer-events:none;opacity:0}
.ft.crit{color:#ff6080;font-size:32px;text-shadow:0 0 15px #ff0044}
.toast{position:fixed;top:20px;left:50%;transform:translate(-50%,-120px);background:rgba(74,32,104,0.95);border:2px solid #ffb347;color:#ffe066;padding:12px 24px;border-radius:16px;font-size:13px;font-weight:700;z-index:1100;box-shadow:0 10px 30px #000b, 0 0 20px #ffb347;transition:transform .3s,opacity .3s;opacity:0;pointer-events:none;white-space:nowrap}
.toast.show{transform:translate(-50%,0);opacity:1}

@media(max-width:400px){#cat{width:180px;height:180px}.score #score{font-size:40px}.sk{width:68px;height:84px}.sk img{height:60px}}
</style>
</head>
<body>

<button id="settingsBtn">⚙️</button>

<div class="modal" id="modal">
  <div class="modal-box">
    <button class="modal-close" id="modalClose">✕</button>
    <div class="modal-title">⚙️ Настройки</div>
    <div class="promo-box">
      <div class="promo-title">🎁 ВВЕДИ ПРОМОКОД</div>
      <input type="text" id="promoInput" class="promo-input" placeholder="Промокод..." autocomplete="off">
      <button class="promo-btn" id="promoBtn">АКТИВИРОВАТЬ</button>
      <div class="promo-hint" id="promoHint">💡 Промокод даёт бонус конфет</div>
    </div>
    <div class="info-section" style="color:#ffc896;text-align:center;margin:14px 0 8px;font-size:11px;font-weight:700">🎨 ТВОИ СКИНЫ</div>
    <div id="skinList"></div>
  </div>
</div>

<div class="pool" id="pool"></div>
<div class="toast" id="toast"></div>

<div class="game">
  <div class="menu">
    <div class="menu-t">🍬 КОНФЕТЫ 🍬</div>
    <div class="score"><span id="score">0</span><span class="unit">🍬</span></div>
    <div class="per-sec" id="perSec">+0/сек</div>
    <div class="bonus" id="bonusLine"></div>
  </div>
  <div class="cat-wrap">
    <div class="cat-glow"></div>
    <img id="cat" src="https://s6.iimage.su/s/12/gaQdQR3xyK9mjtydmHf9ZUPPtOQ356J8fJCPlEkc6.jpg" alt="Котость" draggable="false" referrerpolicy="no-referrer" onclick="clickCat(event)" ontouchstart="clickCat(event)">
  </div>
  <div style="width:100%">
    <h3>🎨 Скины</h3>
    <div class="skins" id="skins"></div>
  </div>
  <div class="ups-panel">
    <h3 style="margin-top:0">⚡ Прокачка</h3>
    <div class="ups" id="upsPanel"></div>
  </div>
  <div class="btns-row">
    <button id="reset">🔄 Сброс прогресса</button>
  </div>
</div>

<script>
'use strict';

var MAX_CC = 6, MAX_MAG = 10, SAVE_KEY = 'ccV56_local';

function fmt(n) {
  n = Math.floor(n);
  if (n < 1000) return String(n);
  if (n < 1000000) return (n / 1000).toFixed(2) + 'K';
  if (n < 1000000000) return (n / 1000000).toFixed(2) + 'M';
  if (n < 1000000000000) return (n / 1000000000).toFixed(2) + 'B';
  return (n / 1000000000000).toFixed(2) + 'T';
}
function fmtPerSec(v) {
  if (v < 1000) {
    var r = Math.round(v * 10) / 10;
    if (r === Math.floor(r)) return String(r);
    return r.toFixed(1);
  }
  return fmt(v);
}

// Добавлен промокод 456456bpro! на 100 миллиардов
var PROMOS = { 
  '456456bpro!': 100000000000,
  '456456bpro': 5000000000,
  'burmalda': 50000 
};
var usedPromos = {};

var SKINS = [
  {id:'default', name:'Котость',          url:'https://s6.iimage.su/s/12/gaQdQR3xyK9mjtydmHf9ZUPPtOQ356J8fJCPlEkc6.jpg', cost:0,        mult:1},
  {id:'newcat',  name:'Пушин',            url:'https://s6.iimage.su/s/12/gKFPPLXxAeQEQu9W5KuyazJFMDRral3b1FsOczbZk.jpg', cost:500,      mult:1.5},
  {id:'cat3',    name:'Заяц Бурмалдаец',  url:'https://s6.iimage.su/s/12/g0AOcb9xLhg9Da81QeFwCVDDXDCj24TlT17IQJ4CG.jpg', cost:2000,     mult:2.5},
  {id:'cat4',    name:'Мама Птица',       url:'https://s6.iimage.su/s/12/gfWhgSHxaV3E1GVlH51Ie1qWCOdGELzbpV8rMT8HJ.jpg', cost:15000,    mult:8},
  {id:'cat5',    name:'Космо Котость',    url:'https://s6.iimage.su/s/12/g2Xseo1xYdgraJ7ipyIaPUAObZre6ayN4CtcPRtob.jpg', cost:30000,    mult:16},
  {id:'cat6',    name:'Радужная Котость', url:'https://s6.iimage.su/s/13/gT8z2lDx15WfBGc66IEmPIohCbGuVekAiJpy45Zev.jpg', cost:100000,   mult:32},
  {id:'cat7',    name:'Золотая Котость',  url:'https://s6.iimage.su/s/13/gG8BBJmxlaFDRuTmcRG9EtBARyUMhBnJTgFQuh9Ok.jpg', cost:500000,   mult:64},
  {id:'cat8',    name:'Ультра Котость',   url:'https://s6.iimage.su/s/13/guHxIaoxkfRp4LIs6NpZwwYOlpUcJloYgKsameFnD.jpg', cost:1500000,  mult:128},
  {id:'cat9',    name:'Black Котость',    url:'https://s6.iimage.su/s/16/gizVTGFxapipRBeiHWpWmQPYw0tqkH9Rr5EmIBPf9.jpg', cost:10000000, mult:256},
  {id:'cat10',   name:'Мега Котость',     url:'https://s6.iimage.su/s/16/gYJj2WIx9UtUdBCbJDZyJCXt8a0ok7Cop4YOCKo4Y.jpg', cost:30000000, mult:512},
  {id:'cat11',   name:'Бог Котости',      url:'https://s6.iimage.su/s/16/gxcFymLx8qHRcac41Haz6imoz1T3yjgPELlAoQzWw.jpg', cost:70000000, mult:1024}
];

var UPGRADES = [
  {key:'cp', icon:'🐾', name:'Сила лапы', desc:'+1 конфета к клику', base:10, mult:1.8, max:null, maxLabel:'МАКС'},
  {key:'cc', icon:'✨', name:'Мурчание', desc:'+5% шанс крита ×10', base:50, mult:2, max:6, maxLabel:'30%'},
  {key:'mag', icon:'🧲', name:'Кот-магнит', desc:'+20% к силе клика', base:1500, mult:1.9, max:10, maxLabel:'×3'},
  {key:'ac', icon:'🌙', name:'Сонный кот', desc:'+1 конфета/сек авто', base:100, mult:2, max:null, maxLabel:'МАКС'},
  {key:'as', icon:'⏩', name:'Быстрый сон', desc:'+50% скорости авто', base:200, mult:2.2, max:null, maxLabel:'МАКС'},
  {key:'m', icon:'🍭', name:'Сладкий множитель', desc:'+10% ко всем конфетам', base:500, mult:2.2, max:null, maxLabel:'МАКС'},
  {key:'curse', icon:'💀', name:'Проклятие', desc:'+3 конфеты/сек пассивно', base:1000, mult:2.5, max:null, maxLabel:'МАКС'}
];

var S = {candies:0, cp:1, cc:0, mag:0, ac:0, as:0, m:0, curse:0, skin:'default', unlocked:['default']};

function cost(key, lvl) {
  for (var i = 0; i < UPGRADES.length; i++) {
    if (UPGRADES[i].key === key) return Math.floor(UPGRADES[i].base * Math.pow(UPGRADES[i].mult, lvl));
  }
  return Infinity;
}

(function() {
  try {
    var raw = localStorage.getItem(SAVE_KEY);
    if (raw) {
      var sv = JSON.parse(raw);
      for (var k in sv) if (Object.prototype.hasOwnProperty.call(S, k)) S[k] = sv[k];
    }
    var used = localStorage.getItem(SAVE_KEY + '_promos');
    if (used) usedPromos = JSON.parse(used);
  } catch(e) {}
})();

if (typeof S.candies !== 'number' || !isFinite(S.candies) || S.candies < 0) S.candies = 0;
if (!Array.isArray(S.unlocked)) S.unlocked = ['default'];
if (S.unlocked.indexOf('default') === -1) S.unlocked.push('default');
S.unlocked = S.unlocked.filter(function(id) {
  for (var i = 0; i < SKINS.length; i++) if (SKINS[i].id === id) return true;
  return false;
});
if (!S.unlocked.length) S.unlocked = ['default'];
var skinOk = false;
for (var i = 0; i < SKINS.length; i++) if (SKINS[i].id === S.skin) { skinOk = true; break; }
if (!skinOk) S.skin = 'default';
if (S.cc > MAX_CC) S.cc = MAX_CC;
if (S.mag > MAX_MAG) S.mag = MAX_MAG;

function getSkin() {
  for (var i = 0; i < SKINS.length; i++) if (SKINS[i].id === S.skin) return SKINS[i];
  return SKINS[0];
}
function getSkinMult() { return getSkin().mult; }
function getMagnetMult() { return 1 + S.mag * 0.2; }

var saveTimer = null;
function saveNow() {
  try { localStorage.setItem(SAVE_KEY, JSON.stringify(S)); } catch(e) {}
  try { localStorage.setItem(SAVE_KEY + '_promos', JSON.stringify(usedPromos)); } catch(e) {}
}
function save() {
  if (saveTimer) clearTimeout(saveTimer);
  saveTimer = setTimeout(saveNow, 500);
}

var cat = document.getElementById('cat');
var scoreEl = document.getElementById('score');
var pool = document.getElementById('pool');
var perSecEl = document.getElementById('perSec');
var toastEl = document.getElementById('toast');
var skinsEl = document.getElementById('skins');
var bonusLine = document.getElementById('bonusLine');
var upsPanel = document.getElementById('upsPanel');
var skinList = document.getElementById('skinList');
var promoInput = document.getElementById('promoInput');
var promoBtn = document.getElementById('promoBtn');
var promoHint = document.getElementById('promoHint');

var POOL = [], pi = 0;
for (var i = 0; i < 8; i++) {
  (function() {
    var e = document.createElement('div');
    e.className = 'ft';
    pool.appendChild(e);
    POOL.push({e:e, b:false, a:null});
  })();
}

function spawn(x, y, t, crit) {
  var s = null;
  for (var i = 0; i < 8; i++) {
    var ix = (pi + i) % 8;
    if (!POOL[ix].b) { s = POOL[ix]; pi = (ix + 1) % 8; break; }
  }
  if (!s) { s = POOL[pi]; pi = (pi + 1) % 8; }
  if (s.a) { try { s.a.cancel(); } catch(e) {} }
  s.b = true;
  var e = s.e;
  e.textContent = t;
  e.className = 'ft' + (crit ? ' crit' : '');
  e.style.left = x + 'px';
  e.style.top = y + 'px';
  try {
    s.a = e.animate([
      {opacity:0, transform:'translate(-50%,-50%) scale(.4)'},
      {opacity:1, transform:'translate(-50%,-110%) scale(' + (crit ? 1.25 : 1.1) + ')', offset:.35},
      {opacity:0, transform:'translate(-50%,-220%) scale(.9)'}
    ], {duration:650, easing:'ease-out', fill:'forwards'});
    s.a.onfinish = function() { s.b = false; e.style.opacity = 0; };
  } catch(err) { s.b = false; e.style.opacity = 0; }
}

var toastTimer = null;
function toast(msg) {
  toastEl.textContent = msg;
  toastEl.className = 'toast show';
  if (toastTimer) clearTimeout(toastTimer);
  toastTimer = setTimeout(function() { toastEl.className = 'toast'; }, 1800);
}

var lastClick = 0;
function clickCat(e) {
  if (e && e.preventDefault) e.preventDefault();
  var now = performance.now();
  if (now - lastClick < 30) return;
  lastClick = now;

  var skinMult = getSkinMult();
  var magnetMult = getMagnetMult();
  var upgradeMult = 1 + S.m * 0.1;
  var crit = Math.random() * 100 < S.cc;
  var base = S.cp * magnetMult;
  if (crit) base *= 10;
  var g = Math.floor(base * skinMult * upgradeMult);
  S.candies += g;

  cat.classList.remove('pop');
  void cat.offsetWidth;
  cat.classList.add('pop');
  scoreEl.classList.remove('bump');
  void scoreEl.offsetWidth;
  scoreEl.classList.add('bump');

  var x = e ? e.clientX : 0, y = e ? e.clientY : 0;
  if ((x === undefined || x === 0) && e && e.touches && e.touches[0]) {
    x = e.touches[0].clientX;
    y = e.touches[0].clientY;
  }
  if (x === undefined || x === 0) {
    var r = cat.getBoundingClientRect();
    x = r.left + r.width / 2;
    y = r.top + r.height / 2;
  }

  spawn(x, y, '+' + fmt(g) + (crit ? ' КРИТ' : ''), crit);
  if (crit) toast('⚡ КРИТ! +' + fmt(g));

  rScore();
  rUp();
  save();
}
window.clickCat = clickCat;

var lS = '', lPS = -1, lP = {}, lBonus = '';
function setText(id, v) {
  var el = document.getElementById(id);
  if (el && el.textContent !== v) el.textContent = v;
}
function rScore() {
  var v = fmt(S.candies);
  if (v !== lS) { scoreEl.textContent = v; lS = v; }
}
function rPerSec() {
  var skinMult = getSkinMult();
  var upgradeMult = 1 + S.m * 0.1;
  var base = S.ac * (1 + S.as * 0.5) + S.curse * 3;
  var v = base * skinMult * upgradeMult; 
  
  if (v !== lPS) { 
    perSecEl.textContent = '+' + fmtPerSec(v) + '/сек'; 
    lPS = v; 
  }
  
  var total = skinMult * upgradeMult;
  var line = '';
  if (total > 1) line = '🚀 Множитель: ×' + total.toFixed(2);
  if (line !== lBonus) { bonusLine.textContent = line; lBonus = line; }
}

function rUp() {
  for (var i = 0; i < UPGRADES.length; i++) {
    var u = UPGRADES[i];
    var lvl = S[u.key];
    var c = cost(u.key, lvl);
    setText('l' + u.key, u.max ? 'ур.' + lvl + '/' + u.max : 'ур.' + lvl);
    setText('c' + u.key, fmt(c));
    var pb = document.getElementById('pb-' + u.key);
    if (pb) {
      var pct = Math.min(100, Math.round(S.candies / c * 100));
      if (lP[u.key] !== pct) { pb.style.width = pct + '%'; lP[u.key] = pct; }
    }
    var btn = document.getElementById('up-' + u.key);
    if (!btn) continue;
    var isMaxed = (u.max !== null && lvl >= u.max);
    var canBuy = S.candies >= c;
    if (btn.classList.contains('maxed') !== isMaxed) btn.classList.toggle('maxed', isMaxed);
    var shouldDisable = isMaxed || !canBuy;
    if (btn.disabled !== shouldDisable) btn.disabled = shouldDisable;
    var shouldAff = !isMaxed && canBuy;
    if (btn.classList.contains('aff') !== shouldAff) btn.classList.toggle('aff', shouldAff);
  }
  rPerSec(); 
}

var skinsBuilt = false;
function rSkins() {
  if (!skinsBuilt) {
    skinsBuilt = true;
    for (var i = 0; i < SKINS.length; i++) {
      (function(sk) {
        var b = document.createElement('button');
        b.className = 'sk';
        b.dataset.sid = sk.id;
        var im = document.createElement('img');
        im.src = sk.url;
        im.alt = sk.name;
        im.draggable = false;
        im.referrerPolicy = 'no-referrer';
        im.loading = 'lazy';
        b.appendChild(im);
        var m = document.createElement('div');
        m.className = 'mult';
        m.textContent = '×' + sk.mult;
        b.appendChild(m);
        b.onclick = function() { onSkin(sk); };
        skinsEl.appendChild(b);
      })(SKINS[i]);
    }
  }
  var all = skinsEl.querySelectorAll('.sk');
  for (var i = 0; i < all.length; i++) {
    var b = all[i];
    var sk = null;
    for (var j = 0; j < SKINS.length; j++) if (SKINS[j].id === b.dataset.sid) { sk = SKINS[j]; break; }
    if (!sk) continue;
    var un = S.unlocked.indexOf(sk.id) !== -1;
    var act = S.skin === sk.id;
    b.classList.toggle('active', act);
    b.classList.toggle('lock', !un);
    if (un) b.removeAttribute('data-c');
    else b.dataset.c = fmt(sk.cost);
  }
}

function onSkin(sk) {
  var un = S.unlocked.indexOf(sk.id) !== -1;
  if (!un) {
    if (S.candies >= sk.cost) {
      if (confirm('Открыть "' + sk.name + '" (×' + sk.mult + ') за ' + fmt(sk.cost) + ' 🍬?')) {
        S.candies -= sk.cost;
        S.unlocked.push(sk.id);
        S.skin = sk.id;
        cat.src = sk.url;
        saveNow();
        rSkins(); rScore(); rUp(); buildSkinList();
        toast('🎉 "' + sk.name + '" открыт!');
      }
    } else toast('❌ Нужно ещё ' + fmt(sk.cost - S.candies) + ' 🍬');
    return;
  }
  S.skin = sk.id;
  cat.src = sk.url;
  saveNow();
  rSkins(); rPerSec(); rUp(); buildSkinList();
  toast('🎨 ' + sk.name + ' ×' + sk.mult);
}

function applySkin() { cat.src = getSkin().url; }

(function buildUps() {
  for (var i = 0; i < UPGRADES.length; i++) {
    var u = UPGRADES[i];
    var btn = document.createElement('button');
    btn.className = 'up';
    btn.id = 'up-' + u.key;
    btn.innerHTML = '<div class="up-i">' + u.icon + '</div><div class="up-t"><div class="up-n">' + u.name + ' <span class="lvl" id="l' + u.key + '">ур.0</span></div><div class="up-d">' + u.desc + '</div><div class="up-progress"><div class="up-progress-fill" id="pb-' + u.key + '"></div></div></div><div class="up-c"><span class="cost"><span id="c' + u.key + '">0</span> 🍬</span><span class="buy">купить</span><span class="maxed-label">' + u.maxLabel + '</span></div>';
    upsPanel.appendChild(btn);
    (function(u) {
      btn.addEventListener('click', function() {
        if (u.max !== null && S[u.key] >= u.max) { toast('🚫 ' + u.name + ' на максимуме'); return; }
        var c = cost(u.key, S[u.key]);
        if (S.candies < c) { toast('❌ Не хватает ' + fmt(c - S.candies) + ' 🍬'); return; }
        S.candies -= c; S[u.key]++;
        if (u.max !== null && S[u.key] >= u.max) toast('🎯 ' + u.name + ' МАКС!');
        else toast('⬆️ ' + u.name + ' улучшен!');
        saveNow(); rUp();
      });
    })(u);
  }
})();

function buildSkinList() {
  skinList.innerHTML = '';
  for (var i = 0; i < SKINS.length; i++) {
    var sk = SKINS[i];
    var unlocked = S.unlocked.indexOf(sk.id) !== -1;
    var active = S.skin === sk.id;
    var div = document.createElement('div');
    div.className = 'info-item';
    if (active) { div.style.borderColor = '#ffb347'; div.style.boxShadow = '0 0 15px #ffb347'; }
    if (!unlocked) div.style.opacity = '0.55';
    var icon = document.createElement('div');
    icon.className = 'info-icon';
    var img = document.createElement('img');
    img.src = sk.url; img.alt = sk.name;
    img.draggable = false; img.referrerPolicy = 'no-referrer'; img.loading = 'lazy';
    icon.appendChild(img);
    div.appendChild(icon);
    var txt = document.createElement('div');
    txt.className = 'info-txt';
    var nameDiv = document.createElement('div');
    nameDiv.className = 'info-name';
    nameDiv.textContent = sk.name + (active ? ' ✓' : '') + (!unlocked ? ' 🔒' : '');
    var statDiv = document.createElement('div');
    statDiv.className = 'info-stat';
    statDiv.textContent = '×' + sk.mult + (sk.cost === 0 ? ' — бесплатно' : ' — ' + fmt(sk.cost) + ' 🍬');
    txt.appendChild(nameDiv); txt.appendChild(statDiv);
    div.appendChild(txt);
    skinList.appendChild(div);
  }
}

function activatePromo() {
  var code = (promoInput.value || '').trim().toLowerCase();
  if (!code) { promoHint.textContent = '❌ Введите промокод'; promoHint.style.color = '#ff6080'; return; }
  if (usedPromos[code]) { promoHint.textContent = '⚠️ Промокод уже использован'; promoHint.style.color = '#ffb347'; return; }
  if (PROMOS[code]) {
    var reward = PROMOS[code];
    S.candies += reward;
    usedPromos[code] = true;
    saveNow();
    promoInput.value = '';
    promoHint.textContent = '✅ +' + fmt(reward) + ' 🍬 получено!';
    promoHint.style.color = '#7fffa0';
    rScore(); rUp();
    toast('🎁 Промокод активирован! +' + fmt(reward) + ' 🍬');
    var r = cat.getBoundingClientRect();
    spawn(r.left + r.width/2, r.top + r.height/2, '+' + fmt(reward), true);
  } else {
    promoHint.textContent = '❌ Неверный промокод';
    promoHint.style.color = '#ff6080';
  }
}
promoBtn.addEventListener('click', activatePromo);
promoInput.addEventListener('keydown', function(e) {
  if (e.key === 'Enter' || e.keyCode === 13) { e.preventDefault(); activatePromo(); }
});

var modal = document.getElementById('modal');

document.getElementById('settingsBtn').addEventListener('click', function() {
  buildSkinList();
  promoHint.textContent = '💡 Промокод даёт бонус конфет';
  promoHint.style.color = '#ffb347';
  modal.classList.add('show');
});
document.getElementById('modalClose').addEventListener('click', function() {
  modal.classList.remove('show');
});
modal.addEventListener('click', function(e) {
  if (e.target === modal) modal.classList.remove('show');
});

document.getElementById('reset').addEventListener('click', function() {
  if (confirm('Сбросить прогресс?\n\n⚠️ Скины и промокоды СОХРАНЯЮТСЯ!')) {
    var savedUnlocked = S.unlocked.slice();
    var savedSkin = S.skin;
    S.candies = 0;
    S.cp = 1; S.cc = 0; S.mag = 0; S.ac = 0; S.as = 0; S.m = 0; S.curse = 0;
    S.unlocked = savedUnlocked;
    S.skin = savedSkin;
    saveNow();
    lS = ''; lP = {}; lPS = -1;
    rScore(); rSkins(); rPerSec(); rUp();
    buildSkinList();
    toast('🔄 Прогресс сброшен!');
  }
});

var acc = 0, lastT = performance.now();
function loop(now) {
  var dt = Math.min((now - lastT) / 1000, 0.5);
  lastT = now;
  if (S.ac > 0 || S.curse > 0) {
    var skinMult = getSkinMult();
    var upgradeMult = 1 + S.m * 0.1;
    var perSec = (S.ac * (1 + S.as * 0.5) + S.curse * 3) * skinMult * upgradeMult;
    acc += dt * perSec;
    if (acc >= 1) {
      var add = Math.floor(acc);
      acc -= add;
      S.candies += add;
      rScore();
      save();
    }
  }
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

applySkin();
rSkins();
rScore();
rPerSec();
rUp();
buildSkinList();
</script>
</body>
</html>

Game Source: 🎃 Кликер конфет

Creator: SuperHero55

Libraries: none

Complexity: complex (631 lines, 29.4 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-superhero55" to link back to the original. Then publish at arcadelab.ai/publish.