feat(pet): прокачанный блок кастомизации + много контента
Контент: - Узор тела (новая ось): пятнышки, полоски, градиент, галактика (клип по силуэту, рендер в обоих рендерерах) + миграция pet_pattern + /api/pet/pattern. - +4 аксессуара: колпак, тёмные очки, шарф, цветок (все бесплатные). - +3 фона: Сияние, Леденец, Сакура (CSS-градиенты + частицы: звёзды/пузыри/лепестки). UI кастомизации: - Вкладка «Узор» со свотчами-превью. - Гардероб по зонам (Голова/Лицо/Шея/Уши/Акцент) + счётчик надетого, кнопки «Случайный образ» и «Снять всё». - Фон — инлайн-сетка с превью/ценой/балансом (как раньше). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
|
||||
function renderPetSVG(level, mood, accessories = [], colorKey = 'purple', streak = 0) {
|
||||
function renderPetSVG(level, mood, accessories = [], colorKey = 'purple', streak = 0, pattern = 'none') {
|
||||
const col = PET_PALETTES[colorKey] || '#9B5DE5';
|
||||
const dark = shadeColor(col, -45);
|
||||
const light = shadeColor(col, 52);
|
||||
@@ -143,6 +143,26 @@
|
||||
|
||||
/* ── Accessories (гардероб: строго по equipped-списку, без авто по уровню) ── */
|
||||
let accSvg = '';
|
||||
if (accessories.includes('party')) {
|
||||
accSvg += `<path d="M55,1 L45,26 L65,26 Z" fill="${col}"/>
|
||||
<path d="M55,1 L49,16 L61,16 Z" fill="${light}" opacity=".55"/>
|
||||
<rect x="44" y="24" width="22" height="4" rx="2" fill="${dark}"/>
|
||||
<circle cx="55" cy="1.5" r="3.5" fill="#F9C74F"/>`;
|
||||
}
|
||||
if (accessories.includes('sunglasses')) {
|
||||
accSvg += `<rect x="28" y="46" width="22" height="13" rx="5" fill="#16181d"/>
|
||||
<rect x="60" y="46" width="22" height="13" rx="5" fill="#16181d"/>
|
||||
<rect x="49" y="50" width="12" height="3" rx="1.5" fill="#16181d"/>
|
||||
<rect x="31" y="48" width="8" height="3" rx="1.5" fill="rgba(255,255,255,.25)"/>
|
||||
<rect x="63" y="48" width="8" height="3" rx="1.5" fill="rgba(255,255,255,.25)"/>`;
|
||||
}
|
||||
if (accessories.includes('scarf')) {
|
||||
accSvg += `<path d="M36,80 Q55,90 74,80 L74,86 Q55,96 36,86 Z" fill="${col}" stroke="${dark}" stroke-width="1"/>
|
||||
<path d="M66,85 L74,99 L68,100 L62,87 Z" fill="${col}" stroke="${dark}" stroke-width="1"/>`;
|
||||
}
|
||||
if (accessories.includes('flower')) {
|
||||
accSvg += `<g transform="translate(82,30)"><circle cx="0" cy="-5" r="3.2" fill="#FF8FB1"/><circle cx="5" cy="-1" r="3.2" fill="#FF8FB1"/><circle cx="3" cy="5" r="3.2" fill="#FF8FB1"/><circle cx="-3" cy="5" r="3.2" fill="#FF8FB1"/><circle cx="-5" cy="-1" r="3.2" fill="#FF8FB1"/><circle cx="0" cy="0" r="2.6" fill="#F9C74F"/></g>`;
|
||||
}
|
||||
if (accessories.includes('headphones')) {
|
||||
accSvg += `<path d="M27,42 Q55,6 83,42" fill="none" stroke="#2a3340" stroke-width="4" stroke-linecap="round"/>
|
||||
<rect x="21" y="38" width="11" height="17" rx="4.5" fill="#2a3340"/>
|
||||
@@ -289,6 +309,17 @@
|
||||
shimmer = `<ellipse cx="55" cy="58" rx="30" ry="35" fill="url(#${uid}sh)" opacity="0.18"/>`;
|
||||
}
|
||||
|
||||
// Узор тела (клипуется по силуэту)
|
||||
let patternSvg = '';
|
||||
if (pattern && pattern !== 'none') {
|
||||
let pin = '';
|
||||
if (pattern === 'spots') pin = `<circle cx="38" cy="44" r="7" fill="${dark}" opacity=".18"/><circle cx="70" cy="40" r="6" fill="${dark}" opacity=".16"/><circle cx="60" cy="72" r="8" fill="${dark}" opacity=".18"/><circle cx="36" cy="74" r="6" fill="${dark}" opacity=".15"/><circle cx="76" cy="66" r="5" fill="${dark}" opacity=".15"/><circle cx="52" cy="34" r="5" fill="${light}" opacity=".3"/>`;
|
||||
else if (pattern === 'stripes') pin = `<g transform="rotate(20 55 60)">${[-10,8,26,44,62,80,98].map(x => `<rect x="${x}" y="-10" width="9" height="140" fill="${dark}" opacity=".16"/>`).join('')}</g>`;
|
||||
else if (pattern === 'galaxy') pin = `<ellipse cx="48" cy="52" rx="36" ry="42" fill="#1a0a3a" opacity=".42"/><circle cx="40" cy="44" r="1.4" fill="#fff"/><circle cx="66" cy="38" r="1.1" fill="#fff"/><circle cx="58" cy="64" r="1.5" fill="#fff"/><circle cx="36" cy="70" r="1" fill="#fff"/><circle cx="74" cy="60" r="1.2" fill="#fff"/><circle cx="52" cy="82" r="1" fill="#fff"/><circle cx="68" cy="74" r="1.3" fill="#cba6ff"/>`;
|
||||
else if (pattern === 'gradient') pin = `<rect x="16" y="18" width="78" height="82" fill="url(#${uid}pg)"/>`;
|
||||
patternSvg = `<clipPath id="${uid}clip"><path d="${bodyPath}"/></clipPath><g clip-path="url(#${uid}clip)">${pin}</g>`;
|
||||
}
|
||||
|
||||
return `<svg viewBox="0 0 110 115" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<radialGradient id="${uid}" cx="38%" cy="28%" r="70%">
|
||||
@@ -304,6 +335,10 @@
|
||||
<stop offset="0%" stop-color="#FFD700" stop-opacity="1"/>
|
||||
<stop offset="100%" stop-color="#FFD700" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="${uid}pg" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="${light}" stop-opacity="0"/>
|
||||
<stop offset="100%" stop-color="${dark}" stop-opacity="0.55"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<ellipse cx="55" cy="111" rx="28" ry="4" fill="rgba(0,0,0,.18)"/>
|
||||
${aura}
|
||||
@@ -314,6 +349,7 @@
|
||||
${antennae}
|
||||
${paws}
|
||||
<path d="${bodyPath}" fill="url(#${uid})"/>
|
||||
${patternSvg}
|
||||
${shimmer}
|
||||
<ellipse cx="55" cy="70" rx="18" ry="12" fill="url(#${uid}b)"/>
|
||||
${cheeks}${eyebrows}${eyeGroups}${nose}${mouth}${extras}
|
||||
|
||||
Reference in New Issue
Block a user