From f2708547954143cb9ff5983ccbf5e30b64c9c0df Mon Sep 17 00:00:00 2001 From: min Date: Fri, 5 Jun 2026 09:31:49 +0300 Subject: [PATCH] =?UTF-8?q?feat(studio):=20+5=20=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D1=8B=D1=85=20=D0=BC=D0=B5=D1=85=D0=B0=D0=BD=D0=B8=D0=BA?= =?UTF-8?q?=20(=D1=86=D0=B2=D0=B5=D1=82=D0=BD=D0=B0=D1=8F=20=D0=BF=D0=BB?= =?UTF-8?q?=D0=B8=D1=82=D0=BA=D0=B0/=D0=BB=D0=B0=D0=B2=D0=B0/=D0=BB=D0=B8?= =?UTF-8?q?=D1=84=D1=82/=D1=84=D0=B8=D0=BD=D0=B8=D1=88/=D0=B7=D0=B2=D1=83?= =?UTF-8?q?=D0=BA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Партия 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 --- src/editor/engine/GameplayKits.js | 86 ++++++++++++++++++++++++ src/editor/engine/ScriptSandboxWorker.js | 7 ++ 2 files changed, 93 insertions(+) diff --git a/src/editor/engine/GameplayKits.js b/src/editor/engine/GameplayKits.js index 6c80889..fc4cd30 100644 --- a/src/editor/engine/GameplayKits.js +++ b/src/editor/engine/GameplayKits.js @@ -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. */ diff --git a/src/editor/engine/ScriptSandboxWorker.js b/src/editor/engine/ScriptSandboxWorker.js index 468051a..5bad044 100644 --- a/src/editor/engine/ScriptSandboxWorker.js +++ b/src/editor/engine/ScriptSandboxWorker.js @@ -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 }); },