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:
@@ -37,6 +37,9 @@ const BG_SHOP = [
|
||||
{ id:'forest', name:'Лес', price:50, desc:'Таинственный лес' },
|
||||
{ id:'aqua', name:'Океан', price:75, desc:'Морская глубина' },
|
||||
{ id:'sunset', name:'Закат', price:75, desc:'Пурпурный закат' },
|
||||
{ id:'aurora', name:'Сияние', price:100, desc:'Северное сияние' },
|
||||
{ id:'candy', name:'Леденец', price:100, desc:'Сладкие облака' },
|
||||
{ id:'sakura', name:'Сакура', price:125, desc:'Цветущая вишня' },
|
||||
];
|
||||
|
||||
function _parseOwned(raw) {
|
||||
@@ -67,16 +70,35 @@ function _mood(streak, daysSinceLogin) {
|
||||
/* ── Гардероб: каталог аксессуаров (разблокировка по порогам/бесплатно) ──
|
||||
slot — на каждый слот можно надеть только один предмет. */
|
||||
const ACCESSORY_CATALOG = [
|
||||
// голова
|
||||
{ id: 'grad', name: 'Шапочка выпускника', slot: 'head', unlock: { type: 'free' } },
|
||||
{ id: 'party', name: 'Праздничный колпак', slot: 'head', unlock: { type: 'free' } },
|
||||
{ id: 'hat', name: 'Цилиндр', slot: 'head', unlock: { type: 'streak', value: 7 } },
|
||||
{ id: 'crown', name: 'Корона', slot: 'head', unlock: { type: 'xp', value: 5000 } },
|
||||
// лицо
|
||||
{ id: 'glasses', name: 'Очки', slot: 'face', unlock: { type: 'level', value: 5 } },
|
||||
{ id: 'sunglasses', name: 'Тёмные очки', slot: 'face', unlock: { type: 'free' } },
|
||||
// шея
|
||||
{ id: 'bowtie', name: 'Бабочка', slot: 'neck', unlock: { type: 'free' } },
|
||||
{ id: 'scarf', name: 'Шарф', slot: 'neck', unlock: { type: 'free' } },
|
||||
// уши
|
||||
{ id: 'headphones', name: 'Наушники', slot: 'ears', unlock: { type: 'free' } },
|
||||
// акцент
|
||||
{ id: 'flower', name: 'Цветок', slot: 'accent', unlock: { type: 'free' } },
|
||||
{ id: 'star', name: 'Звезда', slot: 'accent', unlock: { type: 'level', value: 10 } },
|
||||
];
|
||||
const ACC_BY_ID = new Map(ACCESSORY_CATALOG.map(a => [a.id, a]));
|
||||
|
||||
// Узоры тела (новая ось кастомизации). Все бесплатны.
|
||||
const PET_PATTERNS = [
|
||||
{ id: 'none', name: 'Без узора' },
|
||||
{ id: 'spots', name: 'Пятнышки' },
|
||||
{ id: 'stripes', name: 'Полоски' },
|
||||
{ id: 'gradient', name: 'Градиент' },
|
||||
{ id: 'galaxy', name: 'Галактика' },
|
||||
];
|
||||
const PATTERN_IDS = new Set(PET_PATTERNS.map(p => p.id));
|
||||
|
||||
function _accUnlocked(u, a) {
|
||||
const k = a.unlock;
|
||||
if (k.type === 'free') return true;
|
||||
@@ -130,7 +152,7 @@ function getPet(req, res) {
|
||||
const user = db.prepare(
|
||||
`SELECT xp, level, streak_current, streak_best, streak_date, coins,
|
||||
pet_name, last_login, pet_color, pet_last_petted, pet_petting_streak,
|
||||
pet_bg, pet_bg_owned, pet_last_fed, pet_equipped
|
||||
pet_bg, pet_bg_owned, pet_last_fed, pet_equipped, pet_pattern
|
||||
FROM users WHERE id = ?`
|
||||
).get(req.user.id);
|
||||
|
||||
@@ -205,6 +227,8 @@ function getPet(req, res) {
|
||||
petName: user.pet_name || 'Квантик',
|
||||
petLevel: petLvl,
|
||||
petColor: user.pet_color || 'purple',
|
||||
petPattern: user.pet_pattern || 'none',
|
||||
patterns: PET_PATTERNS,
|
||||
mood,
|
||||
daysSinceLogin: daysSince,
|
||||
accessories,
|
||||
@@ -278,6 +302,14 @@ function updateColor(req, res) {
|
||||
res.json({ ok: true, color });
|
||||
}
|
||||
|
||||
/* ── PATCH /api/pet/pattern — узор тела ───────────────────────────────── */
|
||||
function updatePattern(req, res) {
|
||||
const pattern = req.body && req.body.pattern;
|
||||
if (!PATTERN_IDS.has(pattern)) return res.status(400).json({ error: 'invalid pattern' });
|
||||
db.prepare('UPDATE users SET pet_pattern = ? WHERE id = ?').run(pattern, req.user.id);
|
||||
res.json({ ok: true, pattern });
|
||||
}
|
||||
|
||||
/* ── PATCH /api/pet/equip — надеть/снять аксессуары (гардероб) ─────────── */
|
||||
function equipAccessories(req, res) {
|
||||
const list = req.body && req.body.equipped;
|
||||
@@ -376,4 +408,4 @@ function feedPet(req, res) {
|
||||
res.json({ ok: true, xpAwarded: 15, xp: updated.xp, coins: updated.coins });
|
||||
}
|
||||
|
||||
module.exports = { getPet, renamePet, petAction, updateColor, starCatch, getShop, buyBg, setBg, feedPet, equipAccessories };
|
||||
module.exports = { getPet, renamePet, petAction, updateColor, starCatch, getShop, buyBg, setBg, feedPet, equipAccessories, updatePattern };
|
||||
|
||||
Reference in New Issue
Block a user