fix(studio): NPC-киты (robot→character-a), дверь по коду плавно распахивается

1) NPC-преследователь/торговец/волна: spawnNpc('robot') → 'character-a'
   (модели 'robot' не существует в ModelTypes → spawnNpc возвращал null → ошибка).
2) Дверь по коду: верный код → ПЛАВНОЕ открытие вокруг петли (как дверь по E),
   декор-панели вращаются вместе с полотном. Раньше полотно уезжало вниз,
   декор оставался.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
min 2026-06-05 19:36:18 +03:00
parent 3bf1e77230
commit fe8b6b5b38

View File

@ -591,38 +591,49 @@ game.hud.setHpVisible(false);` }],
{ type: 'cube', x: 0, y: 4.1, z: 0, sx: 0.4, sy: 0.4, sz: 3.5, color: '#3a4250', material: 'metal', name: 'Перемычка' },
],
scripts: [{ attachTo: 'on-target', code:
`// Дверь по коду 1234. Поле ввода появляется ТОЛЬКО когда игрок рядом.
`// Дверь по коду 1234. Поле ввода появляется ТОЛЬКО рядом. Верный код →
// дверь ПЛАВНО распахивается вокруг петли (вместе с панелями).
const CODE = '1234';
const p0 = game.self.position;
const halfW = 1.3; // полуширина полотна (sz=2.6)
const hingeX = p0.x, hingeZ = p0.z - halfW;
const RADIUS = 6;
let opened = false, near = false, inp = null;
game.ui.set('codehint', '', {});
game.onTick(() => {
if (opened) return;
const pl = game.player.position;
const dx = pl.x - p0.x, dz = pl.z - p0.z;
const d = Math.sqrt(dx*dx + dz*dz);
if (d < RADIUS && !near) {
near = true;
inp = game.gui.create('textbox', { id:'codein', x:50, y:86, w:24, h:8, anchor:'center', placeholder:'Код...', textSize:20 });
game.ui.set('codehint', '🔢 Введи код двери и нажми Enter', {x:50,y:78,anchor:'bottom',color:'#fff',size:16});
} else if (d >= RADIUS && near) {
near = false;
if (inp) game.gui.remove('codein');
game.ui.set('codehint', '', {});
let opened = false, near = false, cur = 0, target = 0;
const SPEED = Math.PI; // ~0.5с на 90°
// Декор полотна — двигается вместе с дверью.
const decorNames = ['Панель двери', 'Панель двери низ', 'Кодовая панель'];
const decor = [];
for (const nm of decorNames) {
const o = game.scene.findOne(nm);
if (o && o.position) decor.push({ obj:o, dx:o.position.x-p0.x, dy:o.position.y-p0.y, dz:o.position.z-p0.z });
}
function rotY(lx, lz, a){ const s=Math.sin(a),c=Math.cos(a); return { x: lx*c+lz*s, z: -lx*s+lz*c }; }
function place(a){
const pc = rotY(0, halfW, a);
const cx = hingeX + pc.x, cz = hingeZ + pc.z;
game.self.move(cx, p0.y, cz); game.self.rotate(a);
for (const d of decor){ const r = rotY(d.dx, d.dz, a); d.obj.move(cx+r.x, p0.y+d.dy, cz+r.z); if (d.obj.rotate) d.obj.rotate(a); }
}
game.onTick((dt) => {
if (cur !== target){ const st=SPEED*dt; cur = Math.abs(target-cur)<=st ? target : cur+Math.sign(target-cur)*st; place(cur); }
if (opened) return;
// Поле ввода появляется/прячется по дистанции.
const pl = game.player.position;
const d = Math.sqrt((pl.x-p0.x)**2 + (pl.z-p0.z)**2);
if (d < RADIUS && !near){ near = true;
game.gui.create('textbox', { id:'codein', x:50, y:86, w:24, h:8, anchor:'center', placeholder:'Код...', textSize:20 });
game.ui.set('codehint', '🔢 Введи код двери (1234) и нажми Enter', {x:50,y:78,anchor:'bottom',color:'#fff',size:16}); }
else if (d >= RADIUS && near){ near = false; game.gui.remove('codein'); game.ui.set('codehint','',{}); }
});
game.gui.onSubmit('codein', (text) => {
if (opened) return;
if (String(text).trim() === CODE){
opened = true;
game.self.move(p0.x, p0.y - 4.2, p0.z); // дверь уезжает вниз (открыта)
opened = true; target = Math.PI/2; // плавно распахнуть
game.gui.remove('codein');
game.ui.set('codehint','✓ Открыто!', {x:50,y:78,anchor:'bottom',color:'#36d57a',size:18});
game.after(2, () => game.ui.set('codehint','',{}));
} else {
game.ui.set('codehint', '✗ Неверный код', {x:50,y:78,anchor:'bottom',color:'#ff5555',size:18});
}
} else game.ui.set('codehint','✗ Неверный код', {x:50,y:78,anchor:'bottom',color:'#ff5555',size:18});
});` }],
},
{
@ -690,7 +701,7 @@ game.onTick(() => {
icon: 'chase', category: 'npc',
scripts: [{ attachTo: 'global', code:
`// Спавним NPC, который преследует игрока.
const enemy = game.scene.spawnNpc('robot', { x: 8, z: 8, name: 'Охотник', speed: 4 });
const enemy = game.scene.spawnNpc('character-a', { x: 8, z: 8, name: 'Охотник', speed: 4 });
if (enemy && enemy.follow) enemy.follow('player');` }],
},
{
@ -760,7 +771,7 @@ game.self.onClick(() => {
`// Каждые 5с спавнит 2 врагов из точки портала, они идут к игроку.
const p = game.self.position;
function wave(){
for (let i=0;i<2;i++){ const e = game.scene.spawnNpc('robot', { x:p.x+(Math.random()-0.5)*2, z:p.z+(Math.random()-0.5)*2, name:'Враг', speed:3 });
for (let i=0;i<2;i++){ const e = game.scene.spawnNpc('character-a', { x:p.x+(Math.random()-0.5)*2, z:p.z+(Math.random()-0.5)*2, name:'Враг', speed:3 });
if (e && e.follow) e.follow('player'); }
}
game.after(2, wave); game.every(5, wave);` }],