🎃 Кликер конфет
by PrismViper36638 lines30.0 KB
<!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}
/* Убираем transition, анимацию делает JS */
.score #score{color:#ffe066;font-size:48px;text-shadow:0 0 20px #ffb347, 0 0 40px #ff8c00;display:inline-block;font-variant-numeric:tabular-nums;min-width:80px;text-align:center;will-change:transform}
.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:200px;height:200px;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;
mix-blend-mode: screen;
will-change: transform; /* Ускоряем анимацию */
}
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:8px;width:100%;overflow-x:auto;padding:6px 2px 12px;align-items:center;}
.skins::-webkit-scrollbar{display:none}
.sk{
flex:0 0 64px; width:64px; height:80px;
border-radius:14px;background:rgba(0,0,0,0.4);border:2px solid rgba(255,180,71,.5);
cursor:pointer;padding:3px;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:56px;object-fit:cover;border-radius:10px;display:block;background:transparent;pointer-events:none;mix-blend-mode:screen}
.sk .mult{position:absolute;bottom:2px;left:2px;right:2px;text-align:center;font-size:10px;font-weight:900;color:#ffe066;background:rgba(0,0,0,.8);border-radius:6px;padding:1px 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:10px;font-weight:700;border-radius:11px;padding:2px;text-align:center;line-height:1.2}
.sk.active::before{content:'✓';position:absolute;top:-3px;right:-3px;width:18px;height:18px;border-radius:50%;background:#ffb347;color:#2a0e3f;display:flex;align-items:center;justify-content:center;font-size:11px;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:26px;width:40px;height:40px;flex:0 0 40px;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)}
.info-item{display:flex;gap:10px;background:rgba(0,0,0,0.2);border:1px solid rgba(255,255,255,0.1);border-radius:14px;padding:8px 10px;margin-bottom:6px;align-items:center;overflow:hidden}
.info-icon{width:40px;height:40px;flex:0 0 40px;display:flex;align-items:center;justify-content:center;background:rgba(255,180,71,0.2);border-radius:10px;overflow:hidden;position:relative}
.info-icon img{width:40px;height:40px;object-fit:cover;border-radius:10px;display:block;flex-shrink:0}
.info-txt{flex:1;min-width:0}
.info-name{color:#ffb347;font-size:12px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.info-stat{color:#ffe066;font-size:10px;margin-top:2px}
.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;will-change:transform, opacity}
.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:170px;height:170px}.score #score{font-size:40px}.sk{flex:0 0 56px;width:56px;height:72px}.sk img{height:48px}}
</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 style="color:#ffc896;text-align:center;margin:14px 0 8px;font-size:11px;font-weight:700;letter-spacing:2px;">🎨 ТВОИ СКИНЫ</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 = 'ccV57_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);
}
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;
// Анимация через Web Animations (без тормозов)
cat.animate([{transform:'scale(1)'}, {transform:'scale(1.1)'}, {transform:'scale(1)'}], {duration: 150});
scoreEl.animate([{transform:'scale(1)'}, {transform:'scale(1.15)'}, {transform:'scale(1)'}], {duration: 150});
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();
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(); rScore();
});
})(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 10px #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: PrismViper36
Libraries: none
Complexity: complex (638 lines, 30.0 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-prismviper36-mvdwf2mn" to link back to the original. Then publish at arcadelab.ai/publish.