feat(studio): +5 готовых механик (цветная плитка/лава/лифт/финиш/звук)

Партия 2 из TOOLBOX_KITS_FROM_WIKI.md:
- Цветная плитка — onTouch → смена цвета (self.setColor).
- Лава — onTouch/onUntouch → урон 15 HP/сек пока стоишь (player.damage).
- Лифт — onTick синусоида, ездит вверх-вниз 8 единиц.
- Финиш (победа) — onTouch → экран «ПОБЕДА!» + setInputBlocked.
- Звуковая плитка — onTouch → sound.play('coin') + подсветка.

game.self расширен: setColor(hex). Все 22 кита прошли синтаксис-проверку.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
min 2026-06-05 09:31:49 +03:00
parent 6938f83a3c
commit f270854795
2 changed files with 93 additions and 0 deletions

View File

@ -338,6 +338,92 @@ game.self.onInteract(() => {
target = open ? Math.PI / 2 : 0; // 90° открыта / 0° закрыта
}, { text: 'Открыть / закрыть', key: 'e', distance: 5 });` }],
},
// ===== Партия 2 из Вики (киты 18-22) =====
{
id: 'color-tiles',
name: 'Цветная плитка',
desc: 'Наступи на плитку — она меняет цвет на случайный. (Вики: «Цветные плитки»)',
icon: 'palette', category: 'world',
prims: [{ type: 'cube', x: 0, y: 0.1, z: 0, sx: 2.5, sy: 0.2, sz: 2.5, color: '#cfd8dc', material: 'matte', name: 'Цветная плитка' }],
scripts: [{ attachTo: 'on-target', code:
`// Плитка меняет цвет при касании.
const colors = ['#ff5ab0','#ffd23a','#4d6bff','#36d57a','#ff7a3a','#a05aff'];
let i = 0;
game.self.onTouch(() => {
i = (i + 1) % colors.length;
game.self.setColor(colors[i]);
});` }],
},
{
id: 'lava-floor',
name: 'Лава (урон по касанию)',
desc: 'Раскалённая плита: наступишь — теряешь здоровье каждую секунду, пока стоишь. (Вики: «Лава-пол»)',
icon: 'lava', category: 'world',
prims: [{ type: 'cube', x: 0, y: 0.1, z: 0, sx: 5, sy: 0.2, sz: 5, color: '#ff4422', material: 'neon', name: 'Лава' }],
scripts: [{ attachTo: 'on-target', code:
`// Лава: пока игрок на плите — урон каждую секунду.
let onLava = false, timer = null;
game.self.onTouch(() => {
if (onLava) return; onLava = true;
const tick = () => { if (!onLava) return; game.player.damage(15);
game.ui.set('lava', '🔥 Горячо! -15 HP', { x: 50, y: 80, anchor: 'bottom', color: '#ff6644', size: 18 });
timer = game.after(1, tick); };
tick();
});
game.self.onUntouch(() => { onLava = false; if (timer) game.cancel(timer); game.ui.set('lava', ''); });` }],
},
{
id: 'elevator',
name: 'Лифт',
desc: 'Платформа-лифт сама ездит вверх-вниз между двумя этажами. Встань на неё и катайся. (Вики: «Лифт»)',
icon: 'elevator', category: 'world',
prims: [{ type: 'cube', x: 0, y: 0.5, z: 0, sx: 4, sy: 0.5, sz: 4, color: '#7a8a9a', material: 'metal', name: 'Лифт' }],
scripts: [{ attachTo: 'on-target', code:
`// Лифт: плавно ездит между нижней и верхней высотой.
const p0 = game.self.position;
const lowY = p0.y, highY = p0.y + 8;
let t = 0;
game.onTick((dt) => {
t += dt;
// Синусоида 0..1 с паузами на концах (период ~8с).
const k = (Math.sin(t * 0.5 - Math.PI/2) + 1) / 2;
game.self.move(p0.x, lowY + (highY - lowY) * k, p0.z);
});` }],
},
{
id: 'finish-line',
name: 'Финиш (победа)',
desc: 'Финишная плита: дойди до неё — на экране «ПОБЕДА!» и управление блокируется. (Вики: «Беги к финишу»)',
icon: 'flag', category: 'ui',
prims: [{ type: 'cube', x: 0, y: 0.15, z: 0, sx: 4, sy: 0.3, sz: 2, color: '#ffd23a', material: 'neon', name: 'Финиш', canCollide: false }],
scripts: [{ attachTo: 'on-target', code:
`// Финиш: касание → экран победы.
let done = false;
game.self.onTouch(() => {
if (done) return; done = true;
game.ui.set('win', '🏆 ПОБЕДА!', { x: 50, y: 42, anchor: 'center', color: '#ffd23a', size: 48 });
game.ui.set('winsub', 'Ты дошёл до финиша!', { x: 50, y: 54, anchor: 'center', color: '#fff', size: 22 });
game.player.setInputBlocked(true);
});` }],
},
{
id: 'sound-tile',
name: 'Звуковая плитка',
desc: 'Наступи на плитку — играет звук. Из таких можно собрать мелодию. (Вики: «Эхо-комната»)',
icon: 'sound', category: 'fx',
prims: [{ type: 'cube', x: 0, y: 0.1, z: 0, sx: 2, sy: 0.2, sz: 2, color: '#6f8bff', material: 'neon', name: 'Звуковая плитка' }],
scripts: [{ attachTo: 'on-target', code:
`// Плитка играет звук при касании + подсвечивается.
let cd = false;
game.self.onTouch(() => {
if (cd) return; cd = true;
game.sound.play('coin');
game.self.setColor('#ffffff');
game.after(0.25, () => { game.self.setColor('#6f8bff'); cd = false; });
});` }],
},
];
/** Найти кит по id. */

View File

@ -766,6 +766,13 @@ function _buildSelfApi() {
const id = _target.id ?? _target.ref;
_send('scene.setCollide', { kind: k, id, ref: (k && id != null) ? (k + ':' + id) : undefined, canCollide: !!can });
},
/** Перекрасить объект-носитель (только примитив). */
setColor(hex) {
if (typeof hex !== 'string') return;
const k = _target.kind;
const id = _target.id ?? _target.ref;
_send('scene.setColor', { kind: k, id, ref: (k && id != null) ? (k + ':' + id) : undefined, color: hex });
},
delete() {
_send('self.delete', { target: _target });
},