player/src/engine/BlockTypes.js
МИН d5968f7cb8 feat(09): сочные круглые studs (v4) — паритет со студией
- Текстура studs v4 (круглые, объём+тени, сочный цвет), URL studs_v4_*.
- PrimitiveManager: emissive 45% цвета + новые константы (GRID 4, UNIT 1).
- BlockManager/BlockTypes: studs-block на v4-текстуре, specular убран.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 12:22:03 +03:00

140 lines
8.6 KiB
JavaScript
Raw 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.

/**
* BlockTypes — реестр voxel-блоков для Рублокс-редактора.
*
* Используем Kenney Voxel Pack (CC0) — текстуры 32×32, лежат в
* /public/kubikon-assets/textures/.
*
* Каждый блок может иметь:
* texture — одна текстура на все 6 граней (простые кубы)
* ИЛИ
* top/side/bottom — три текстуры для верха, боков, низа (трава, тыква, бревно)
*
* BlockManager автоматически выберет правильный материал.
*/
const TEX = '/kubikon-assets/textures';
/**
* Создать описание простого куба с одной текстурой.
*/
const cube = (id, name, category, texture, opts = {}) => ({
id, name, category, texture, ...opts,
});
/**
* Создать описание мульти-материал куба (разные текстуры на гранях).
* top/side/bottom могут быть отдельными или одинаковыми (если совпадают — куб с side="top").
*/
const multiCube = (id, name, category, top, side, bottom, opts = {}) => ({
id, name, category, top, side, bottom, ...opts,
});
/**
* Полный список блоков.
* Цвет fallback в swatch — берётся из категории.
*/
export const BLOCK_TYPES = [
// === ЗЕМЛЯ ===
multiCube('grass', 'Трава', 'Земля', `${TEX}/grass_top.png`, `${TEX}/dirt_grass.png`, `${TEX}/dirt.png`),
cube('dirt', 'Земля', 'Земля', `${TEX}/dirt.png`),
cube('sand', 'Песок', 'Земля', `${TEX}/sand.png`),
cube('redsand', 'Красный песок', 'Земля', `${TEX}/redsand.png`),
cube('greysand', 'Серый песок', 'Земля', `${TEX}/greysand.png`),
cube('snow', 'Снег', 'Земля', `${TEX}/snow.png`),
multiCube('grass-snow', 'Снежная трава', 'Земля', `${TEX}/snow.png`, `${TEX}/dirt_snow.png`, `${TEX}/dirt.png`),
multiCube('grass-sand', 'Песочная трава','Земля', `${TEX}/sand.png`, `${TEX}/dirt_sand.png`, `${TEX}/dirt.png`),
cube('gravel-stone', 'Гравий', 'Земля', `${TEX}/gravel_stone.png`),
cube('gravel-dirt', 'Земляной гравий','Земля', `${TEX}/gravel_dirt.png`),
cube('ice', 'Лёд', 'Земля', `${TEX}/ice.png`, { alpha: 0.85 }),
// === КАМЕНЬ ===
// ВАЖНО: rock.png / rock_moss.png / stone_sand.png / greystone_sand.png — это
// спрайты-полублоки (камень в центре или песок сверху), не tile-able.
// На обычном кубе они выглядят как чёрный фон + камешек. Поэтому:
// - 'rock' → используем плиточную greystone.png
// - 'rock-moss' → greystone.png + лёгкий зелёный tint через emissive
// - 'stone-sand' / 'greystone-sand' оставляем без halfblock-текстур,
// используем обычные tile + цветовая схема
cube('rock', 'Камень', 'Камень', `${TEX}/greystone.png`),
cube('rock-moss', 'Замшелый камень', 'Камень', `${TEX}/greystone.png`, { emissive: [0.05, 0.18, 0.05] }),
cube('greystone', 'Серый камень', 'Камень', `${TEX}/greystone.png`),
cube('stone', 'Тёмный камень', 'Камень', `${TEX}/stone.png`),
cube('redstone', 'Редстоун', 'Камень', `${TEX}/redstone.png`),
// === РУДЫ (тёмный камень с вкраплениями) ===
cube('ore-coal', 'Уголь', 'Руда', `${TEX}/stone_coal.png`),
cube('ore-iron', 'Железная руда', 'Руда', `${TEX}/stone_iron.png`),
cube('ore-gold', 'Золотая руда', 'Руда', `${TEX}/stone_gold.png`),
cube('ore-diamond', 'Алмазная руда', 'Руда', `${TEX}/stone_diamond.png`),
cube('ore-silver', 'Серебряная руда', 'Руда', `${TEX}/stone_silver.png`),
cube('ore-browniron', 'Медная руда', 'Руда', `${TEX}/stone_browniron.png`),
cube('ore-ruby', 'Рубиновая руда', 'Руда', `${TEX}/greystone_ruby.png`),
cube('ore-emerald', 'Изумрудная руда', 'Руда', `${TEX}/redstone_emerald.png`),
// === ДЕРЕВО ===
cube('wood', 'Бревно', 'Дерево', `${TEX}/wood.png`),
cube('wood-red', 'Красное бревно', 'Дерево', `${TEX}/wood_red.png`),
// === ХЛОПОК / ШЕРСТЬ ===
cube('cotton-blue', 'Хлопок синий', 'Шерсть', `${TEX}/cotton_blue.png`),
cube('cotton-green', 'Хлопок зелёный', 'Шерсть', `${TEX}/cotton_green.png`),
cube('cotton-red', 'Хлопок красный', 'Шерсть', `${TEX}/cotton_red.png`),
cube('cotton-tan', 'Хлопок бежевый', 'Шерсть', `${TEX}/cotton_tan.png`),
// === КИРПИЧИ ===
cube('brick-red', 'Кирпич красный', 'Кирпич', `${TEX}/brick_red.png`),
cube('brick-grey', 'Кирпич серый', 'Кирпич', `${TEX}/brick_grey.png`),
// === СТЕКЛО ===
cube('glass', 'Стекло', 'Особые', `${TEX}/glass.png`, { alpha: 0.6 }),
cube('glass-frame', 'Стекло в раме', 'Особые', `${TEX}/glass_frame.png`, { alpha: 0.7 }),
// === ЛИСТВА ===
cube('leaves', 'Листва', 'Природа', `${TEX}/leaves.png`),
cube('leaves-orange', 'Жёлтая листва', 'Природа', `${TEX}/leaves_orange.png`),
cube('leaves-trans', 'Прозр. листва', 'Природа', `${TEX}/leaves_transparent.png`, { alpha: 0.9 }),
cube('leaves-orange-trans', 'Прозр.жёлт.листва', 'Природа', `${TEX}/leaves_orange_transparent.png`, { alpha: 0.9 }),
// === ЖИДКОСТИ И СПЕЦБЛОКИ ===
cube('water', 'Вода', 'Особые', `${TEX}/water.png`, { alpha: 0.75, isWater: true, animate: 'water' }),
cube('lava', 'Лава', 'Особые', `${TEX}/lava.png`, { emissive: [0.6, 0.2, 0], isLava: true, animate: 'lava' }),
// === КАКТУС / ПЕЧЬ (мульти-материал) ===
multiCube('cactus', 'Кактус', 'Природа', `${TEX}/cactus_top.png`, `${TEX}/cactus_side.png`, `${TEX}/cactus_inside.png`),
// Печь: только oven.png это «фасад» (дверца), остальное — серый камень.
// top = stone, side = oven (4 стороны с дверцей — лучше чем раньше),
// bottom = stone. В будущем для одной двери понадобится 6-face формат.
multiCube('oven', 'Печь', 'Особые', `${TEX}/stone.png`, `${TEX}/oven.png`, `${TEX}/stone.png`),
// === ОКРАШИВАЕМЫЕ (задача 09) — паритет со студией ===
cube('studs-block', 'Лего-кирпич', 'Окрашиваемые',
'/kubikon-assets/materials/studs_v4_diffuse.png',
{ colorable: true, normal: '/kubikon-assets/materials/studs_v4_normal.png', defaultColor: '#3a7aff' }),
];
/** Все доступные категории в порядке появления. */
export const BLOCK_CATEGORIES = Array.from(new Set(BLOCK_TYPES.map(b => b.category)));
/** Цвет для swatch'а в палитре, fallback по категории. */
export const CATEGORY_COLORS = {
'Земля': '#8b6f4e',
'Камень': '#7a7a7a',
'Руда': '#c9a04e',
'Дерево': '#a8743f',
'Шерсть': '#e8dcc4',
'Кирпич': '#9d4a3a',
'Особые': '#9966ff',
'Природа': '#5a8c3e',
'Окрашиваемые': '#3a7aff',
};
/** Найти описание блока по id. */
export function getBlockType(id) {
return BLOCK_TYPES.find(b => b.id === id) || BLOCK_TYPES[0];
}
/** Картинка-превью для палитры — для мульти-куба показываем верхнюю грань. */
export function blockPreview(b) {
return b.top || b.texture;
}