diff --git a/AGENTS.md b/AGENTS.md index 1723bcc..37c2667 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -228,6 +228,7 @@ Gemini rules: - Keep edits scoped to the requested behavior and related contracts. - Update `GDD.md` if gameplay/design behavior changes. - Use structured APIs/parsers where available instead of ad hoc string manipulation. +- For parser command/debugging issues, use the running app when practical: enable `#PEEK-ON`, reproduce the command, and inspect `CONTEXT`, `SCOPE`, `ENVELOPE`, `CORE`, and `RESULT` before guessing. - For runtime/scene/gameplay bugs, prefer diagnostic helpers or temporary probes that explain engine decisions, such as why `isWalkable` returned false, which object blocked a path, or which semantic rule selected a parser target. - Do not revert user changes. Work with dirty files unless the user explicitly asks to revert them. diff --git a/GDD.md b/GDD.md index 464cda1..817cd38 100644 --- a/GDD.md +++ b/GDD.md @@ -267,6 +267,7 @@ Parser обрабатывает пользовательский ввод кас ### Свойства сцены Сцена может поддерживать _Depth-scaling_ -- масштабирование объектов, имитирующее 3d перспективу, когда объекты, находящиеся "дальше от камеры" (то есть, выше по оси Y), становятся меньше. Настройки масштабирования для каждой сцены свои. +Также у сцены есть _Correctional scale_ -- editor-level инструмент нормализации масштаба самой сцены. При изменении этого значения редактор масштабирует объекты сцены и их абсолютные координаты вокруг общего центра, чтобы относительное расположение объектов сохранялось. У Entity обычное поле _Scale_ является референсным размером объекта/префаба, не зависящим от сцены; при переносе объекта в сцену оно не умножается на Correctional scale. Поверх Scale всё ещё может применяться Depth-scaling и временные runtime-множители вроде `Subscene.itemScale`. Кроме того, объекты типа Static и Actor имеют свойство, запрещающее их Depth-scaling. Если Depth-scaling объекта запрещен, то он не изменяет свой размер при изменении Y, даже если Depth-scaling включен для сцены. Это полезно, например, для сцен, где персонаж лезет вертикально вверх по пожарной лестнице, и не должен уменьшаться по мере подъёма, поскольку не удаляется от камеры. Сцена имеет свойство, определяющее _положение "камеры"_ (viewport), т.е. задаёт какая область сцены будет отображаться на экране и с каким зумом. Например, при приближении персонажа игрока к краю экрана сцена скроллится. По умолчанию камера позиционируется на персонаже игрока, но позиционированием можно управлять и динамически, например если игрок выходит из дома на улицу, то масштаб изображения может уменьшиться кастомной логикой (скриптом) этой сцены, отдалив камеру чтобы передать ощущение большого открытого пространства. @@ -387,7 +388,7 @@ Parser видит это так, как будто: Из этого следует правило: `on`/`under`/`behind` между вложенными значимыми объектами не отменяет их `in`-отношение к более внешнему контейнеру. Одно и то же дерево spatial-узлов может давать разные корректные текстовые отношения в зависимости от якоря запроса. -Это правило используется не только для описаний, но и для relation-aware parser-действий. `TAKE Book B FROM Cabinet` может найти `Book B`, потому что относительно `Cabinet` она находится внутри шкафа; `TAKE Book B FROM Book A` тоже может быть допустимой естественной формулировкой, потому что `from` для `TAKE` трактуется как общий источник, а ближайшее конкретное отношение между `Book A` и `Book B` остаётся `on`. После semantic resolution все действия всё равно проходят обычные runtime-проверки расстояния, блокеров, closed/transparent Switch и возможности взять предмет. +Это правило используется не только для описаний, но и для relation-aware parser-действий. `LOOK Cabinet` и `EXAMINE Cabinet` после основного текста перечисляют видимые titled-потомки по всем relations (`in`, `on`, `under`, `behind`) относительно `Cabinet`; `LOOK IN Cabinet` перечисляет только потомков в выбранном relation. `TAKE Book B FROM Cabinet` может найти `Book B`, потому что относительно `Cabinet` она находится внутри шкафа; `TAKE Book B FROM Book A` тоже может быть допустимой естественной формулировкой, потому что `from` для `TAKE` трактуется как общий источник, а ближайшее конкретное отношение между `Book A` и `Book B` остаётся `on`. После semantic resolution все действия всё равно проходят обычные runtime-проверки расстояния, блокеров, closed/transparent Switch и возможности взять предмет. Таким образом, технические spatial-узлы можно использовать для внутренней структуры сцены, не засоряя ими текстовый слой и не ломая parser-команды. @@ -499,7 +500,7 @@ _Sort Mode_ (v0, v1, v2, v3, ignore) - _Exit_ (id сцены, id TriggerBox) : попадание в такой триггер вызывает перенос персонажа игрока в сцену с указанным id и в TriggerBox с id, указанном в компоненте. id сцены может указывать и на текущею сцену, тогда персонаж игрока будет телепортирован в TriggerBox текущей сцены. -- _Entry_ (направление): Ответный триггер по отношению к Exit, он обозначает ту точку в сцене, куда перемещается Actor при переходе, а также направление, в котором он будет находиться при появлении. +- _Entry_ (направление): Ответный триггер по отношению к Exit, он обозначает ту точку в сцене, куда перемещается Actor при переходе, а также направление, в котором он будет находиться при появлении. При входе через Entry Actor также получает `Layer` и `Parallax` этого Entry, чтобы портал контролировал глубинный слой входа. Если player Actor переходит в другую сцену, zoom камеры целевой сцены сбрасывается на её default camera zoom. - _Subscene_ (targetID: identifies object(s) to enable, [Name]: optional EXAMINE command keyword, [itemScale]: optional runtime scale multiplier for Items, see below) : Триггер, который при клике мышью или по команде `EXAMINE Name` открывает крупный план чего-то поверх затемнённой/заблюренной текущей сцены. Например, игрок кликает по столу и ему открывается крупный план стола как "картинка в картинке". Технически это просто включение (Enable) указанных целей. Для выхода обратно в главную сцену игрок должен кликнуть за пределами объектов этой группы. @@ -611,10 +612,10 @@ Parser-команды `OPEN` и `CLOSE` используют тот же runtime - `hidden` (`false | lookable | examinable`) : семантическое поле titled-объекта. - `false`: обычное поведение; - - `lookable`: объект отсутствует в semantic world model, пока не будет раскрыт через `LOOK`-контекст, включая relation-look и mouse title reveal; + - `lookable`: объект отсутствует в semantic world model, пока не будет раскрыт через осмотр видимого anchor: `LOOK `, relation-look (`LOOK UNDER `), `EXAMINE ` или mouse title reveal; - `examinable`: объект отсутствует, пока не будет раскрыт успешным `EXAMINE`. - После раскрытия объект становится обычной семантической сущностью до конца текущей runtime-сессии сцены. + При первом раскрытии `lookable`-объекта через spatial-осмотр player-facing текст использует discovery-формулировку (`you discover`) вместо обычной visibility-формулировки (`you see`). После раскрытия объект становится обычной семантической сущностью до конца текущей runtime-сессии сцены, и последующие описания используют обычный `you see`. - _State_ () : Состояние объекта, например открыт/закрыт, включено/выключено, etc. Состояния это по сути переменные или свойства объекта, которые могут быть изменены скриптом. Они имеют поля: @@ -766,6 +767,7 @@ Hero.walkTo(100, 100); - `game.playSound(filename: string)`: проигрывает звук из `public/sounds`; - `game.sceneManager.currentScene`: ссылка на текущую сцену; - `game.sceneManager.switchTo(sceneId: string)`: переключает игру на другую сцену; +- `game.sceneManager.transferActorToScene(actor, targetSceneId, options?)`: переносит Actor-а в другую сцену централизованным путём вместе с его inventory/spatial-owned Entity descendants; - `game.inventory`: массив предметов в инвентаре игрока. Пример: @@ -860,6 +862,7 @@ if (outcome.status === 'needs_clarification' && outcome.message) { - `api.getEntity(name: string)`: возвращает объект сцены по имени; - `api.getActor(name: string)`: возвращает `Actor` по имени; - `api.getQuad(name: string)`: возвращает `QuadObject` по имени; +- `api.transferActor(actorName: string, targetSceneId: string, targetEntryId?: string)`: переносит Actor-а через `SceneManager.transferActorToScene`; - `api.setTimeout(...)`, `api.clearTimeout(...)`: таймеры; - `api.setInterval(...)`, `api.clearInterval(...)`: интервалы; - `api.saveCheckpoint()`: сохраняет текущее состояние сцены в undo history редактора. @@ -1067,7 +1070,9 @@ F1 Game F2 Save F3 Load F4 New F5 Sprite Edit F9 Settings - _New_: Создаёт новую сцену из файла шаблона (default). Если текущая сцена содержит несохранённые изменения, перед созданием или загрузкой новой сцены открывается системный поп-ап подтверждения. - _Sprite_: Переход в редактор спрайтов. -- _Settings_: Открывает в правой панели глобальные настройки игры. Сейчас они ограничены только настройками шейдера CRT: +- _Settings_: Открывает в правой панели глобальные настройки игры: + - _Attached Volume_: глобальная коррекция громкости только для звуков, присоединённых к объектам сцены. Значение 1.0 оставляет авторскую громкость без изменений. + - _CRT settings_: настройки шейдера CRT: - _CRT MODE on/off_: включает/выключает все эффекты CRT (серый фон, искажения, scanlines, abberations) - _CRT geometry_: задаёт степень "выпуклости" экрана - _CRT scanlines_: размер scanlines @@ -1103,6 +1108,7 @@ Prefab можно загрузить в текущую сцену из файл - _ID cцены_ (оно же имя файла при сохранении) - _Переключатель Depth scaling_ (on/off) - _Настройки Depth scaling_ (мин и макс. умножители масштаба, уровень горизонта и уровень переднего плана) +- _Correctional scale_ в подсекции Correction: общий scene-level поправочный множитель для `Scale` объектов. При изменении этого значения редактор масштабирует все объекты сцены, включая locked, и их абсолютные координаты вокруг общего центра, чтобы относительное расположение объектов сохранялось. - _Настройки камеры_: текущие X/Y-координаты и Zoom, дефолтные координаты/Zoom, переключатель автоматического центрирования камеры на игрока, настройки автоцентрирования, настройки Depth scaling. Настройки камеры могут меняться как через редактор, так и динамически, прямо в игре, через игровую логику. Например, в кат-сцене камера может сделать zoom-out, переместиться с игрока на другой объект, а затем вернуться обратно и восстановить zoom. Если включено автоматическое центрирование, камера перемещается когда игрок подходим к краю экрана, что для пользователя выглядит как скроллинг. diff --git a/InventorySys.md b/InventorySys.md index ba24d83..56b882f 100644 --- a/InventorySys.md +++ b/InventorySys.md @@ -60,6 +60,8 @@ Spatial relation внешнего контейнера/поверхности с Мы меняем реализацию компонента Inventory так, что находящиеся в них предметы теперь участвуют в иерархии сцены и видны там как дочерние объекты своих контейнеров (это относится и к предметам в инвентаре у игрока). Они имеют spatial relation "IN". Предметы, находящиемя в каком-то инвентаре, исключены из рендеринга и игре не отображаются. В панели иерархии в редакторе они видны, но имеют другой цвет, похожий на тот, что у disabled объектов, однако без перечёркнутой иконки как у disabled. Таким образом, если надо включить предмет в инвентарь или исключить его оттуда -- это можно сделать прямо в редакторе. +При `TAKE` движок переносит в Inventory тот же самый scene object. Он не создаёт копию предмета для инвентаря и не оставляет отдельный оригинал/дубликат в сцене. Меняется только storage ownership: предмет остаётся частью сцены, становится hidden для render layer и получает spatial placement `IN` относительно владельца Inventory. + При этом для parser/text layer важно не отношение предмета к самому inventory-узлу, а spatial relation inventory-слота относительно его значимого владельца. Например, если у шкафа есть built-in Inventory с relation `BEHIND`, а книга технически находится как `IN`-ребёнок шкафа, то в текстовом слое книга считается находящейся `BEHIND the Cabinet`. Команды парсера типа TAKE FROM или PUT INTO работают с внешним (по отношению к персонажу игрока) инвентарём как с IN контейнером, если Inventory не имеет свойства Protected. В противном случае он недоступен в текстовом слое игры (за исключением случая, когда это Inventory самого игрока). diff --git a/Parser.md b/Parser.md index 03f5f29..c1f3357 100644 --- a/Parser.md +++ b/Parser.md @@ -225,6 +225,8 @@ Parser не должен самостоятельно обходить raw `.spa - `EXAMINE` использует инвентарь, объекты активной subscene и объекты в пределах допустимой дистанции; - `GO TO` использует сценовые цели и достижимые сценовые объекты. +Для direct `LOOK ` и `EXAMINE ` parser после основного описания добавляет spatial-summary видимых titled-потомков target по relations `in`, `on`, `under`, `behind`. Сводка строится через runtime text layer, поэтому безымянные технические узлы схлопываются, hidden unrevealed объекты не попадают в обычную видимость, а relation определяется относительно выбранного semantic anchor. + Текущая модель scope: ```ts @@ -873,6 +875,8 @@ Plural matching в v1 намеренно простой: - `LOOK` использует обычное краткое описание (`description`); - `EXAMINE` использует расширенное описание (`details`). +- После основного описания `EXAMINE ` добавляет ту же spatial-summary видимых дочерних объектов, что и `LOOK `. +- `hidden: lookable` spatial-дочерние объекты могут быть раскрыты через `EXAMINE ` так же, как через `LOOK ` или relation LOOK. Если `details` отсутствует: @@ -890,6 +894,15 @@ Plural matching в v1 намеренно простой: Это правило относится к игровому миру, а не к языку, поэтому применяется на стороне `Game.examineEntity()`. +### Spatial discovery text + +Relation-aware visibility messages use two service text keys: + +- `parser.relation_contents`: ordinary visible contents, default `"{Relation} the {target} you see: {items}."`; +- `parser.relation_discovered_contents`: first-time discovery of `hidden: lookable` contents, default `"{Relation} the {target} you discover: {items}."`. + +The discovery text is used only when the command actually reveals at least one previously hidden lookable object. After reveal, the same object is part of normal semantic visibility for the current scene runtime session, so later `LOOK` / `EXAMINE` output uses `parser.relation_contents`. + --- ## Pending Clarification diff --git a/Sessions.md b/Sessions.md index 64b849d..cd78d7d 100644 --- a/Sessions.md +++ b/Sessions.md @@ -1981,3 +1981,219 @@ Refining 3D Spatial Audio for the engine, ensuring that sound triggering, pannin - Convolution reverb loudness remains inherently IR-dependent; the current output trim is calibrated empirically for the tested IRs. - `Reverb Min % = 0` only guarantees no wet at true zero total distance. `test_3d_sound2` often still has nonzero X/Z distance, so some reverb can remain by design. - NotebookLM source upload for this wrap-up required CLI re-auth because `python -m notebooklm source list` reported expired authentication. + +## Session Entry - 2026-05-15 12:33 +02:00 + +### Session Goals +- Diagnose corrupted `test_room` scene/inventory state where a held cassette had lost its usable connection to the scene object. +- Fix editor/runtime cleanup so deleted scene entities cannot remain as phantom Inventory/Surface entries. +- Correct parser/runtime visibility behavior around `LOOK` / `EXAMINE` nested spatial contents. +- Commit the complete current working tree as a single `Fixes` commit and leave a durable handoff. + +### What Was Implemented +- Fixed the broken cassette state in `public/scenes/test_room.json`: + - The held `Compact cassette` now points at the real scene entity `test`. + - The stale phantom `test_` entry was removed from the player inventory data. +- Fixed editor deletion cleanup in `src/scene/Scene.ts`: + - `Scene.removeEntity()` now removes the entity from the active inventory/storage manager before deleting it from the scene graph. + - This prevents the editor from leaving inventory references to non-existing entities. +- Preserved the important runtime distinction between inventory ownership and generic spatial containment: + - Objects should be hidden from scene rendering when they are actually stored in Inventory. + - Objects with spatial relation `in` are not automatically inventory items; this avoids hiding legitimate world-contained objects such as `CityView` inside `Window`. +- Added direct semantic scene-text helpers in `src/scene/SceneTextLayer.ts`: + - Direct semantic descendants are immediate titled children after collapsing untitled technical intermediates. + - Traversal stops at titled children, so grandchildren under another titled object are not reported as direct contents. +- Updated `LOOK` / `EXAMINE` handling: + - `src/systems/GameSemanticAPI.ts` now reveals and describes only first-level titled semantic children for examine/look relation descriptions. + - `src/mechanics/Parser.ts` now uses the same first-level direct semantics for entity content text. + - Hidden `lookable` / `examinable` descendants are revealed only when they are first-level children of the inspected target. +- Preserved recursive relation behavior for mechanics that intentionally need it: + - `TAKE ... FROM ...` and related relation-scoped candidate discovery still use recursive descendant search. + - This keeps nested container interactions working while narrowing only the descriptive/reveal behavior. +- Included the current workspace's scene, prompt, LLM cascade, and kitchen asset changes in the all-in `Fixes` commit as requested. + +### Important Architecture / Runtime Decisions +- Inventory state must be derived from Inventory/Surface component storage, not from spatial `relation: "in"`. +- Spatial `IN` means world containment; it does not imply the object is carried by the player. +- `LOOK` and `EXAMINE` are descriptive/reveal commands and should expose only the first semantic level below the target. +- First semantic level means titled children directly below the target, with untitled technical nodes collapsed. +- Hidden objects under a titled child remain hidden until that titled child is inspected. +- Recursive spatial traversal remains valid for targeted gameplay mechanics where the command explicitly scopes through a container. + +### Parser / Mechanics / Scene / Inventory Changes +- `Scene.removeEntity()` now clears current inventory/storage ownership before scene deletion. +- `SceneTextLayer` now exposes direct relation helpers alongside existing recursive helpers. +- `GameSemanticAPI` uses direct helpers for `describeSpatialRelation()` and hidden descendant reveal during examine/look flows. +- `Parser.getEntitySpatialContentsText()` uses direct helpers so `LOOK SOFA` reports pillows but not a remote hidden under a pillow. +- Parser fixture semantic API mirrors the runtime helper split. +- Tests now cover: + - editor deletion of held entities clearing inventory storage; + - `LOOK` / `EXAMINE` first-level-only reporting; + - hidden lookable grandchildren staying hidden from ancestor inspection; + - nested `TAKE FROM` still reaching deeper candidates where intended. + +### Tests Run And Outcomes +- `npm test -- tests/game/semantic-api.test.ts -- --runInBand` + - Passed. +- `npm test -- tests/integration/parser-game.test.ts -- --runInBand` + - Passed, 77 tests. +- `npm run typecheck` + - Passed. +- `npm test -- tests/game/navigation-and-spatial.test.ts tests/game/semantic-api.test.ts tests/integration/parser-game.test.ts -- --runInBand` + - Passed, 172 tests. +- Full `npm test` + - Passed, 28 files / 344 tests. +- `codex-doctor -Fast` + - Passed, 17/17. +- Pre-commit hook during commit: + - Ran prettier and eslint through lint-staged. + - First attempt caught one unused fixture import; it was removed and the second commit attempt passed. + +### Commits Created +- `6102beb` - `Fixes` + - Includes inventory/entity deletion cleanup, `LOOK` / `EXAMINE` direct semantic reveal behavior, parser/game regression tests, current scene/prompt/LLM-cascade updates, and kitchen assets. + +### Remaining Work / Next Recommended Steps +- Manually verify in the running editor/game that: + - `LOOK SOFA` reports only the sofa's first-level pillows. + - `LOOK RIGHT PILLOW` reveals the `TV remote`. + - `take rc` remains unavailable until the remote is revealed. + - Deleting a held item in the editor removes it cleanly from inventory. +- If scene data continues to drift through manual editor saves, consider a small scene-integrity diagnostic that reports inventory references to missing entity IDs. +- If UX needs it, add an editor validation warning for Inventory/Surface references that point to deleted scene entities. + +### Risks / Caveats / Open Questions +- The `Fixes` commit intentionally includes all current workspace changes, including scene data, prompt/LLM cascade files, and kitchen assets, per user request. +- The parser's lower regex cascade correctly does not resolve `rc` while `tv_rc` is hidden and unrevealed; this was confirmed as intended behavior during the session. +- Direct semantic content behavior is now narrower by design; any previous tests expecting recursive `LOOK` disclosure were updated to the new contract. + +## Session Entry - 2026-05-17 21:09 +02:00 + +### Session Goals +- Continue from the previous wrap-up without repeating the `Fixes` work. +- Introduce a centralized Actor scene-transfer path that moves a live Actor together with inventory/spatial-owned entities. +- Fix scene travel through `GO`, `Exit`/`Entry`, and script API so player/NPC transfers preserve live objects and inventory state. +- Add controlled Entry placement behavior: default Entry fallback, target camera zoom reset, Entry layer/parallax application. +- Rework scene/object scaling so `Correctional Scale` is an editor scene-normalization tool, while object `Scale` remains portable across scenes. +- Improve text-console cursor/focus behavior in game mode. + +### What Was Implemented +- Added `SceneManager.transferActorToScene(actor, targetSceneId, options?)` as the central transfer API. + - Collects the Actor itself. + - Collects Entity descendants spatially owned by the Actor. + - Collects items stored in the Actor's Inventory components. + - Recursively collects nested descendants of those carried items. + - Moves live object instances between scenes without cloning and without using normal `removeEntity()` cleanup that would clear inventory storage. +- Updated `SceneManager.switchTo(sceneId, activator?)` to delegate Actor movement to the transfer API when an activator is supplied. +- Updated `ComponentSystem.handleExit()` to call `transferActorToScene()` directly with `targetEntryId`. +- Added `ScriptAPI.transferActor(actorName, targetSceneId, targetEntryId?)` for script-side actor movement. +- Fixed semantic `GO ` travel: + - `Game.goToScene()` now passes the current player Actor into `switchTo()`. + - If `currentScene.player` is missing, it falls back to a player Actor in the current scene entities. + - Parser/game integration remains routed through this semantic path. +- Added Entry fallback for scene transfer: + - If cross-scene transfer has no explicit `targetEntryId`, the first `Entry` object in the target scene is used. + - The lookup uses `scene.getAllSceneObjects()`, so it sees `Triggerbox` Entries such as the one in `quad4`. +- Entry placement now applies only to the Actor: + - Actor coordinates/direction are set from Entry. + - Actor `layer` and `parallax` are copied from the Entry. + - Carried inventory items keep inventory ownership and do not receive Entry coordinates/layer/parallax directly. +- Player cross-scene transfer now resets `targetScene.camera.zoom` to `targetScene.defaultCamera.zoom` before camera snap. +- Target-scene pre-authored player placeholders are removed/replaced by the live transferred player Actor. +- NPC Actor transfers move the NPC and its inventory contents without making the NPC `scene.player`. +- Same-scene teleport uses the same transfer API but skips detach/add and only applies Entry placement. + +### Scaling And Editor Changes +- Added `Scene.scaling.correctionalScale` with default `1`. +- Added internal `Entity.refScale` serialization as the stored reference/prefab scale. + - The editor-facing field remains the normal `Scale` field. + - Legacy objects without `refScale` recover it from `modelScale` or `scale`. +- Rejected the intermediate idea of applying target-scene `Correctional Scale` to incoming Actors/items. + - Incoming objects now keep their portable object `Scale`. + - `Correctional Scale` is editor-only scene normalization, not transfer-time object scaling. +- Added `Scene.applyCorrectionalScaleChange(nextScale)`: + - Computes a correction ratio from old to new scale. + - Scales all scene objects around a shared scene center. + - Updates absolute coordinates for entities. + - Updates polygons for Walkboxes/Triggerboxes. + - Updates Quad vertices. + - Updates existing Entity stored scale values so the authored scene itself is normalized. + - Explicitly includes locked objects; locked entities/triggers must not remain behind when the scene is normalized. +- Updated Scene Properties UI: + - Section `2. Scaling` is split into `Depth Scaling` and `Correction`. + - Added `Correctional Scale` field under `Correction`. + - Tooltip explains that it scales all scene objects, including locked ones, around the shared scene center. +- Updated Entity Properties UI: + - Returned to one editor-visible `Scale` field. + - The field edits `refScale` internally while preserving the old UI concept. + +### Text Console / Input Changes +- Improved text console cursor behavior. +- Added Ctrl+Left / Ctrl+Right command-line navigation. +- Added protection against losing command-line focus in game mode. +- The latest related commits are separate from the scene-transfer commit. + +### Important Architecture / Runtime Decisions +- Actor scene movement must use `SceneManager.transferActorToScene()` rather than raw `oldScene.removeEntity(actor)` / `targetScene.addEntity(actor)`. +- Direct scene removal is unsafe for carried objects because normal entity removal clears inventory/storage ownership. +- Inventory contents are live scene entities and should travel with their owning Actor. +- Entry is the authoritative authored portal for Actor coordinates, direction, layer, and parallax. +- Target-scene camera zoom should come from target scene defaults when the player enters a different scene. +- `Correctional Scale` is not a runtime per-object multiplier for incoming objects. +- Object `Scale` remains portable; scene normalization should mutate the authored scene layout, not objects entering that scene. + +### Parser / Mechanics / Scene / Inventory Changes +- Parser `GO` scene changes now preserve the live player Actor and its inventory. +- `Exit`/`Entry`, semantic `GO`, and script transfer all share the same central Actor-transfer path. +- Inventory-owned items remain hidden and spatially owned by the Actor after transfer. +- Nested carried descendants transfer with their carried parent. +- `InventoryManager.handleSceneChange()` runs after final scene state is established. +- Parser static prompt preparation, scene exposure, and scene-change hooks remain part of scene activation. + +### Tests Run And Outcomes +- Focused scene transfer and scale tests: + - `npm test -- tests/entities/entity-ref-scale.test.ts tests/game/navigation-and-spatial.test.ts tests/scene/scene-transition.test.ts -- --runInBand` + - Passed. +- Parser/game integration checks: + - `npm test -- tests/integration/parser-game.test.ts -- --runInBand` + - Passed. +- Semantic API checks: + - `npm test -- tests/game/semantic-api.test.ts -- --runInBand` + - Passed. +- Combined focused suites after scale/correction work: + - `npm test -- tests/scene/scene-correctional-scale.test.ts tests/entities/entity-ref-scale.test.ts tests/game/navigation-and-spatial.test.ts tests/scene/scene-transition.test.ts tests/game/semantic-api.test.ts tests/integration/parser-game.test.ts -- --runInBand` + - Passed, 188 tests. +- Full suite: + - `npm test` + - Passed, 29 files / 354 tests. +- TypeScript: + - `npm run typecheck` + - Passed. +- Whitespace/diff check: + - `git diff --check` + - Passed with only CRLF warnings. + +### Commits Created +- `758e5ce` - `Feature: Centralized Actor Scene Transfer API` + - Central Actor transfer API, GO/Exit/script transfer integration, Entry fallback, camera zoom reset, Entry parallax/layer, Scale/Correctional Scale model, scene correction tests, docs, and scene/text additions including `quad5`. +- `0e546e5` - `Fixed and improved cursor in text console. Added Ctrl+ left/right arrows for navigation` + - Console cursor improvements, Ctrl+arrow movement, related game/UI plumbing. +- `1443b87` - `Protection against losing command line focus in Game Mode` + - Focus protection around game canvas/UI overlay so the command line does not lose focus unexpectedly. + +### Remaining Work / Next Recommended Steps +- Manually verify in the editor: + - `GO quad4` places the transferred player on the target `Triggerbox` Entry. + - The transferred player keeps inventory contents. + - The transferred player inherits Entry `Layer` and `Parallax`. + - Target scene zoom resets to the default camera zoom. + - Changing `Correctional Scale` moves locked and unlocked entities/triggers together. + - Existing neighboring objects remain adjacent after scene correction. +- If scene scaling normalization is used heavily, consider adding an editor command name/history label for correction-scale changes so undo history reads more clearly. +- Consider a small UI hint that `Correctional Scale` is a destructive authored-layout normalization, not a temporary runtime multiplier. + +### Risks / Caveats / Open Questions +- The current working tree is clean at wrap-up time. +- The previous memory decision that described transfer-time object correction was superseded by the later decision: `Correctional Scale` is editor-only scene normalization. +- Scene correction intentionally affects locked objects. This differs from normal transform editing, where locked objects are protected from accidental manual manipulation. +- `Correctional Scale` mutates authored object positions/polygons and stored scale values; use editor undo or source control when experimenting. diff --git a/SoundSys.md b/SoundSys.md index 91e8a85..0d7b98f 100644 --- a/SoundSys.md +++ b/SoundSys.md @@ -48,6 +48,9 @@ The engine provides a dedicated **3D SOUND ENV.** section in the Scene Propertie ### Dynamic Hot-Swapping The system supports real-time switching of the **Default Reverb IR**. When changed in the editor, all active sounds using the scene default will immediately update their acoustics by recreating their internal `ConvolverNode`. Clearing the field will smoothly return sounds to a "dry" state. +### Global Attached Volume +The F9 Settings panel exposes **Attached Volume**, a global correction applied only to sounds attached to scene objects. `1.0` preserves authored playback volume, values above `1.0` boost attached 3D sounds, and lower values reduce them. It is applied before the dry/reverb split, so scene acoustics and dry/wet ratios remain unchanged. + ## Proximity Effect (EQ & Reverb Scaling) When `useProximityEQ: true` is enabled, the spatial relationship between the camera and the object drives a dynamic mixer: diff --git a/SpatialSys.md b/SpatialSys.md index 6819b75..4e3aade 100644 --- a/SpatialSys.md +++ b/SpatialSys.md @@ -411,6 +411,8 @@ Distance - runtime actionability check, а не visibility check. Для предметов на `Surface` distance считается по actual surface placement coordinates из `Surface.items`, если они есть. Нельзя полагаться только на `entity.x/y`, потому что item position может быть stored в surface placement. +Для polygon-объектов distance нельзя считать до среднего центра вершин. Большие или асимметричные полигоны, особенно `Walkbox`/floor surfaces, могут иметь centroid далеко от текущей позиции игрока, хотя игрок стоит внутри того же walkbox. Runtime должен считать distance до polygon как `0`, если player point внутри polygon, иначе как расстояние до ближайшего ребра polygon. + ## ParserWorldModel `ParserWorldModelBuilder` строит context/scope для parser-а. Public JSON shape сохраняется стабильным. @@ -475,6 +477,28 @@ Parser не должен вручную обходить storage через raw - сначала резолвит currently takable candidates; - если nothing found, fallback-ит в broader visible scope для diagnostics; - если visible candidate найден, вызывает runtime `takeEntity`, чтобы получить честную причину failure. +- при успешном взятии переносит **тот же scene entity** в main actor inventory; `TAKE` не создаёт inventory-копию и не удаляет original object из сцены. Inventory ownership прячет этот entity из render layer и задаёт raw placement `in` относительно inventory owner. + +### Actor Scene Transfer + +Перенос `Actor` между сценами должен идти через централизованный runtime API `SceneManager.transferActorToScene(actor, targetSceneId, options?)`. + +Этот путь используется для `Exit`/`Entry`, console/game teleport-команд и script API. Он переносит live object instances без clone/copy: + +- самого `Actor`; +- все `Entity`, spatial ancestry которых указывает на этого `Actor`; +- предметы из `Inventory` компонентов Actor-а; +- nested spatial descendants этих предметов. + +Для таких переносов нельзя напрямую делать пару `oldScene.removeEntity(actor)` / `targetScene.addEntity(actor)`: обычное удаление entity из сцены также чистит storage ownership и может разрушить inventory state. Transfer API сначала собирает замыкание владения, затем снимает entities из source scene и добавляет их в target scene как те же самые объекты. + +`Entry` placement применяется только к Actor-у. Предметы в inventory остаются hidden, spatially `in` владельце inventory и не получают координаты Entry напрямую. При same-scene teleport тот же API только применяет `Entry` placement к Actor-у и не detach-ит inventory children. + +Если перенос идёт в другую сцену и `targetEntryId` не задан, transfer API использует первый `Entry` target scene. При входе через `Entry` Actor получает координаты/направление Entry, а также его `Layer` и `Parallax`. При переносе player Actor-а camera zoom target scene устанавливается в `defaultCamera.zoom` перед camera snap. + +У `Entity` обычное editor-facing поле `Scale` хранится как reference size (`refScale`) - prefab/object size, не зависящий от сцены. При переносе live Entity в другую сцену runtime сохраняет этот размер и не умножает его на target scene `Correctional Scale`. Финальный runtime `scale` дальше может учитывать Depth Scaling и временные множители вроде `Subscene.itemScale`. `Correctional Scale` является editor-level инструментом: при его изменении редактор масштабирует все scene objects, включая locked objects, и их абсолютные координаты вокруг общего центра, чтобы соседние объекты оставались соседними. + +Если переносится player Actor, target scene получает именно этот live Actor как `scene.player`, а pre-authored placeholder player в target scene удаляется. После финального состояния сцены один раз запускается `InventoryManager.handleSceneChange()`, затем обычные scene-change hooks/parser exposure. Пример: @@ -606,6 +630,7 @@ Walkbox может выступать player-facing pseudo-floor/pseudo-ground t - auto-drop может использовать walkbox surface; - для explicit `PUT item ON FLOOR` или `PUT item IN FLOOR` user-facing сообщение должно нормализоваться к floor placement; - walkbox может иметь relation fallback для `on`, чтобы floor command работала естественно. +- distance до walkbox floor при `PUT`/`DROP` считается по polygon containment / nearest-edge distance, а не до центра walkbox; игрок, стоящий в любой части текущего walkbox, не должен получать ложное `too far from the floor`. - direct `LOOK floor` / `EXAMINE floor` сначала проверяют walkbox pseudo-floor, на котором стоит player. `LOOK` использует его `description`, `EXAMINE` использует его `details`. - Если текущий walkbox отсутствует или у него нет нужного поля (`description` для `LOOK`, `details` для `EXAMINE`), parser ищет обычный visible/held titled object с Title/Synonym `Floor`. - Если ни current pseudo-floor, ни real `Floor` object не дают текст, parser возвращает стандартное `parser.look_default_object` для floor. diff --git a/TextAssets.md b/TextAssets.md index 0158cb0..495dc39 100644 --- a/TextAssets.md +++ b/TextAssets.md @@ -111,6 +111,13 @@ Scripts do not generate text themselves. They only change which named text field - Parser and UI should read only the resolved standard fields, not custom variant names directly. - The LLM parser cascade receives resolved parser/world context plus the system prompt asset; it should not read arbitrary scene files directly. +Parser service text also owns relation-summary phrasing: + +- `parser.relation_contents` is used when `LOOK `, `EXAMINE `, or relation LOOK reports already visible spatial contents. +- `parser.relation_discovered_contents` is used only on the first reveal of `hidden: lookable` spatial contents; the default wording changes `you see` to `you discover`. + +After discovery, the object is no longer hidden for the current runtime scene session, so later summaries return to `parser.relation_contents`. + ## Object semantic fields for LLM context Object Text Assets can describe lightweight semantic knowledge for the Stage 2 LLM cascade. diff --git a/public/assets/kitchen.png b/public/assets/kitchen.png new file mode 100644 index 0000000..2262a31 Binary files /dev/null and b/public/assets/kitchen.png differ diff --git a/public/assets/kitchen_table.png b/public/assets/kitchen_table.png new file mode 100644 index 0000000..4e2dc29 Binary files /dev/null and b/public/assets/kitchen_table.png differ diff --git a/public/assets/tv_rc.png b/public/assets/tv_rc.png new file mode 100644 index 0000000..902fc56 Binary files /dev/null and b/public/assets/tv_rc.png differ diff --git a/public/scenes/quad5.json b/public/scenes/quad5.json new file mode 100644 index 0000000..3384a5e --- /dev/null +++ b/public/scenes/quad5.json @@ -0,0 +1,2538 @@ +{ + "id": "quad5", + "name": "Test Room", + "description": "You are in Test Room.", + "textRedirects": {}, + "filename": "quad5", + "walkbox": [], + "triggerboxes": [ + { + "name": "entry", + "type": "Triggerbox", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Entry", + "direction": "left" + } + ], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "poly": [ + { + "x": -1035.9671848653868, + "y": 183.39472576163055 + }, + { + "x": -1037.9671848653868, + "y": 144.39472576163055 + }, + { + "x": -952.9671848653869, + "y": 133.39472576163055 + }, + { + "x": -986.9671848653869, + "y": 187.39472576163055 + } + ], + "script": "" + } + ], + "scaling": { + "enabled": true, + "min": 0.7, + "max": 0.9, + "horizon": 65, + "front": 128, + "correctionalScale": 2.7 + }, + "entities": [ + { + "name": "bg", + "type": "Entity", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": -10, + "visible": true, + "hidden": false, + "parallax": 0.1, + "x": 131, + "y": 756, + "width": 2150.064, + "height": 1442.4479999999999, + "baseWidth": 1264, + "baseHeight": 848, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "window-view.json", + "color": "#AAAAAA", + "scale": 1.7009999999999998, + "refScale": 0.7, + "modelScale": 1.89, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "sunbeam1", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": -1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 38.75, + "y": -61.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 98, + "y": -223, + "p": 0.64 + }, + { + "x": 313, + "y": -221, + "p": 0.64 + }, + { + "x": 66, + "y": 97, + "p": 0.64 + }, + { + "x": -322, + "y": 101, + "p": 0.64 + } + ], + "color": "#c95618", + "sortMode": "ignore", + "opacity": 0.1, + "blendMode": "lighter", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Static_3", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": -1, + "visible": true, + "hidden": false, + "parallax": 0.65, + "x": 288, + "y": 97, + "width": 506.4428571428572, + "height": 364.40387755102046, + "baseWidth": 1170, + "baseHeight": 841.8571428571429, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "wall-window.json", + "color": "#AAAAAA", + "scale": 0.4328571428571429, + "refScale": 0.2, + "modelScale": 0.54, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "Static_4", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": -1, + "visible": true, + "hidden": false, + "parallax": 0.65, + "x": -114, + "y": 97, + "width": 506.4428571428572, + "height": 364.40387755102046, + "baseWidth": 1170, + "baseHeight": 841.8571428571429, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "wall-window.json", + "color": "#AAAAAA", + "scale": 0.4328571428571429, + "refScale": 0.2, + "modelScale": 0.54, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "glass", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 75.75, + "y": -63.5, + "ignoreScaling": false, + "vertices": [ + { + "x": -359, + "y": -225, + "p": 0.65 + }, + { + "x": 488, + "y": -223, + "p": 0.65 + }, + { + "x": 533, + "y": 97, + "p": 0.699, + "binding": { + "targetName": "exit1", + "type": "vertex", + "index": 1 + } + }, + { + "x": -359, + "y": 97, + "p": 0.65 + } + ], + "color": "#827c3a", + "sortMode": "ignore", + "opacity": 0.9, + "blendMode": "overlay", + "isGrid": false, + "gridLinesX": 2, + "gridLinesY": 2, + "lineWidth": 1.6, + "gridColor": "#1d1616", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_72", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -17, + "y": -287, + "ignoreScaling": false, + "vertices": [ + { + "x": -466, + "y": -219, + "p": 0.65 + }, + { + "x": 486, + "y": -223, + "p": 0.65 + }, + { + "x": 614, + "y": -353, + "p": 1 + }, + { + "x": -702, + "y": -353, + "p": 1 + } + ], + "color": "#947171", + "sortMode": "v2", + "opacity": 1, + "blendMode": "source-over", + "isGrid": true, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 0.5, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "wall_2", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 550.5, + "y": -52.75, + "ignoreScaling": false, + "vertices": [ + { + "x": 616, + "y": -353, + "p": 1 + }, + { + "x": 488, + "y": -223, + "p": 0.65 + }, + { + "x": 488, + "y": 97, + "p": 0.65, + "binding": { + "targetName": "exit1", + "type": "vertex", + "index": 0 + } + }, + { + "x": 610, + "y": 268, + "p": 1 + } + ], + "color": "#855d38", + "sortMode": "v1", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "sunbeam3", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 271.25, + "y": 37.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 340, + "y": -186, + "p": 0.64 + }, + { + "x": 480, + "y": -186, + "p": 0.64 + }, + { + "x": 330, + "y": 261, + "p": 1 + }, + { + "x": -65, + "y": 261, + "p": 1 + } + ], + "color": "#c95618", + "sortMode": "v0", + "opacity": 0.1, + "blendMode": "lighter", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Static_2", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 0.7, + "x": 232, + "y": 84, + "width": 162.6062298950786, + "height": 41.34751952439104, + "baseWidth": 79.20971532815241, + "baseHeight": 20.141450011881506, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#4a1215", + "scale": 2.052857142857143, + "refScale": 1, + "modelScale": 2.7, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "tabletop1_2", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 91.5, + "y": 40.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 70, + "y": 49, + "p": 0.75 + }, + { + "x": 112, + "y": -2, + "p": 0.651 + }, + { + "x": 114, + "y": 31, + "p": 0.651 + }, + { + "x": 70, + "y": 84, + "p": 0.75 + } + ], + "color": "#1c495e", + "sortMode": "v3", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_1", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": "<", + "targetId": "Quad_1", + "cullingType": "render" + } + ], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -558, + "y": 232.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 540, + "y": 163, + "p": 0.845, + "binding": { + "targetName": "exit1", + "type": "vertex", + "index": 3 + } + }, + { + "x": -823, + "y": 97, + "p": 0.65, + "binding": { + "targetName": "lightspot1_1", + "type": "vertex", + "index": 0 + } + }, + { + "x": -817, + "y": 245, + "p": 0.65 + }, + { + "x": -1132, + "y": 425, + "p": 1 + } + ], + "color": "#283743", + "sortMode": "v1", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "floor", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "3d-parallax" + }, + { + "type": "WalkBox", + "mode": "Invert" + } + ], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -213.75, + "y": 183.5, + "ignoreScaling": false, + "vertices": [ + { + "x": -823, + "y": 99, + "p": 0.65 + }, + { + "x": 488, + "y": 97, + "p": 0.65 + }, + { + "x": 610, + "y": 266, + "p": 1 + }, + { + "x": -1130, + "y": 272, + "p": 1 + } + ], + "color": "#5b3a1a", + "sortMode": "v0", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 9, + "gridLinesY": 9, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "shadow_3", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 230, + "y": 123.25, + "ignoreScaling": false, + "vertices": [ + { + "x": 216, + "y": 99, + "p": 0.645 + }, + { + "x": 278, + "y": 99, + "p": 0.645 + }, + { + "x": 268, + "y": 144, + "p": 0.77 + }, + { + "x": 158, + "y": 151, + "p": 0.77 + } + ], + "color": "#535246", + "sortMode": "v1", + "opacity": 1, + "blendMode": "multiply", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 5 + }, + { + "name": "lightspot1_1", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": "#lightSpots", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -777.25, + "y": 184, + "ignoreScaling": false, + "vertices": [ + { + "x": -823, + "y": 97, + "p": 0.65, + "binding": { + "targetName": "Quad_1", + "type": "vertex", + "index": 1 + } + }, + { + "x": -366, + "y": 101, + "p": 0.65 + }, + { + "x": -790, + "y": 266, + "p": 1 + }, + { + "x": -1130, + "y": 272, + "p": 1, + "binding": { + "targetName": "floor", + "type": "vertex", + "index": 3 + } + } + ], + "color": "#c95618", + "sortMode": "v1", + "opacity": 0.2, + "blendMode": "lighter", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 2 + }, + { + "name": "lightspot2", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": "#lightSpots", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 201.5, + "y": 179.75, + "ignoreScaling": false, + "vertices": [ + { + "x": 104, + "y": 97, + "p": 0.65 + }, + { + "x": 444, + "y": 101, + "p": 0.65 + }, + { + "x": 372, + "y": 255, + "p": 1 + }, + { + "x": -114, + "y": 266, + "p": 1 + } + ], + "color": "#c95618", + "sortMode": "v1", + "opacity": 0.2, + "blendMode": "lighter", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 2 + }, + { + "name": "lightspot1", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": "#lightSpots", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -246.75, + "y": 182, + "ignoreScaling": false, + "vertices": [ + { + "x": -304, + "y": 101, + "p": 0.64 + }, + { + "x": 51, + "y": 101, + "p": 0.64 + }, + { + "x": -208, + "y": 263, + "p": 1 + }, + { + "x": -526, + "y": 263, + "p": 1 + } + ], + "color": "#c95618", + "sortMode": "v0", + "opacity": 0.2, + "blendMode": "lighter", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 2 + }, + { + "name": "Quad_303", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 525, + "y": -2.25, + "ignoreScaling": false, + "vertices": [ + { + "x": 498, + "y": -134, + "p": 0.67 + }, + { + "x": 558, + "y": -171, + "p": 0.8 + }, + { + "x": 548, + "y": 189, + "p": 0.845 + }, + { + "x": 496, + "y": 107, + "p": 0.685 + } + ], + "color": "#1d2272", + "sortMode": "v3", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Static_231", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 0.748, + "x": 121, + "y": 144, + "width": 92.91056214742805, + "height": 81.21309764453962, + "baseWidth": 382.3479923762471, + "baseHeight": 334.210278372591, + "colliderWidth": 32, + "colliderHeight": 38, + "spriteName": "weird.json", + "color": "#381a1a", + "scale": 0.24300000000000002, + "refScale": 0.1, + "modelScale": 0.27, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "shadow_2", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -264.5, + "y": 120.25, + "ignoreScaling": false, + "vertices": [ + { + "x": -272, + "y": 97, + "p": 0.645 + }, + { + "x": -210, + "y": 97, + "p": 0.645 + }, + { + "x": -254, + "y": 138, + "p": 0.7 + }, + { + "x": -322, + "y": 149, + "p": 0.7 + } + ], + "color": "#535246", + "sortMode": "v2", + "opacity": 1, + "blendMode": "multiply", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 5 + }, + { + "name": "shadow_1", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 224.25, + "y": 114.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 168, + "y": 80, + "p": 0.645 + }, + { + "x": 332, + "y": 78, + "p": 0.645 + }, + { + "x": 274, + "y": 149, + "p": 0.77 + }, + { + "x": 123, + "y": 151, + "p": 0.77 + } + ], + "color": "#535246", + "sortMode": "v3", + "opacity": 1, + "blendMode": "multiply", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 5 + }, + { + "name": "Quad_2", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": ">", + "targetId": "Quad_2", + "cullingType": "render" + } + ], + "layer": 0, + "visible": false, + "hidden": false, + "parallax": 1, + "x": -339, + "y": -60.75, + "ignoreScaling": false, + "vertices": [ + { + "x": -366, + "y": -272, + "p": 0.75 + }, + { + "x": -314, + "y": -219, + "p": 0.65 + }, + { + "x": -312, + "y": 97, + "p": 0.65 + }, + { + "x": -364, + "y": 151, + "p": 0.75 + } + ], + "color": "#ba814b", + "sortMode": "v3", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_891", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -390.5, + "y": -59, + "ignoreScaling": false, + "vertices": [ + { + "x": -415, + "y": -268, + "p": 0.75 + }, + { + "x": -366, + "y": -270, + "p": 0.75 + }, + { + "x": -364, + "y": 151, + "p": 0.75 + }, + { + "x": -417, + "y": 151, + "p": 0.75 + } + ], + "color": "#474747", + "sortMode": "v2", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_204", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": "<", + "targetId": "Quad_204", + "cullingType": "render" + }, + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": ">", + "targetId": "plant", + "cullingType": "layer" + }, + { + "type": "WalkBox", + "mode": "Subtract" + } + ], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -387.5, + "y": -61.25, + "ignoreScaling": false, + "vertices": [ + { + "x": -415, + "y": -268, + "p": 0.75 + }, + { + "x": -359, + "y": -225, + "p": 0.65 + }, + { + "x": -359, + "y": 97, + "p": 0.65 + }, + { + "x": -417, + "y": 151, + "p": 0.75 + } + ], + "color": "#636363", + "sortMode": "v3", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_4", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": ">", + "targetId": "Quad_5", + "cullingType": "render" + } + ], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -453.5, + "y": -50.25, + "ignoreScaling": false, + "vertices": [ + { + "x": -489, + "y": -377, + "p": 1 + }, + { + "x": -417, + "y": -297, + "p": 0.85 + }, + { + "x": -419, + "y": 201, + "p": 0.85 + }, + { + "x": -489, + "y": 272, + "p": 1 + } + ], + "color": "#888888", + "sortMode": "v2", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "shadow_hero", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -324.75, + "y": 235, + "ignoreScaling": false, + "vertices": [ + { + "x": -172, + "y": 163, + "p": 0.7928818169518489 + }, + { + "x": -112, + "y": 163, + "p": 0.7946761995139993 + }, + { + "x": -495, + "y": 317, + "p": 0.9910943320051704 + }, + { + "x": -520, + "y": 297, + "p": 0.9971687824531933 + } + ], + "color": "#535246", + "sortMode": "v2", + "opacity": 0.5, + "blendMode": "multiply", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 5 + }, + { + "name": "Quad_8", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": ">", + "targetId": "Quad_8", + "cullingType": "render" + } + ], + "layer": 1, + "visible": false, + "hidden": false, + "parallax": 1, + "x": -391.5, + "y": -216, + "ignoreScaling": false, + "vertices": [ + { + "x": -417, + "y": -297, + "p": 0.85 + }, + { + "x": -366, + "y": -270, + "p": 0.7666666666666667 + }, + { + "x": -366, + "y": -132, + "p": 0.75 + }, + { + "x": -417, + "y": -165, + "p": 0.85 + } + ], + "color": "#ba814b", + "sortMode": "ignore", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_9", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": "<", + "targetId": "Quad_9", + "cullingType": "render" + } + ], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -455, + "y": -196.25, + "ignoreScaling": false, + "vertices": [ + { + "x": -480, + "y": -287, + "p": 0.85 + }, + { + "x": -430, + "y": -241, + "p": 0.75 + }, + { + "x": -428, + "y": -121, + "p": 0.75 + }, + { + "x": -482, + "y": -136, + "p": 0.85 + } + ], + "color": "#646464", + "sortMode": "ignore", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_225", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -426.5, + "y": -139.75, + "ignoreScaling": false, + "vertices": [ + { + "x": -487, + "y": -183, + "p": 1 + }, + { + "x": -366, + "y": -132, + "p": 0.75 + }, + { + "x": -366, + "y": -121, + "p": 0.75 + }, + { + "x": -487, + "y": -123, + "p": 1 + } + ], + "color": "#292828", + "sortMode": "ignore", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_6", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 525.5, + "y": -133.75, + "ignoreScaling": false, + "vertices": [ + { + "x": 496, + "y": -132, + "p": 0.67 + }, + { + "x": 556, + "y": -167, + "p": 0.8 + }, + { + "x": 552, + "y": -115, + "p": 0.8 + }, + { + "x": 498, + "y": -121, + "p": 0.67 + } + ], + "color": "#191d5d", + "sortMode": "v3", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "tabletop1", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 130.5, + "y": 25.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 112, + "y": -2, + "p": 0.651 + }, + { + "x": 174, + "y": -2, + "p": 0.651 + }, + { + "x": 166, + "y": 53, + "p": 0.75 + }, + { + "x": 70, + "y": 53, + "p": 0.75 + } + ], + "color": "#2c647d", + "sortMode": "v1", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "table-top-dice", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 0.7, + "x": 263, + "y": 41, + "width": 175.93542510029846, + "height": 13.5, + "baseWidth": 93.08752650809443, + "baseHeight": 7.142857142857143, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#1d4853", + "scale": 1.89, + "refScale": 1, + "modelScale": 2.7, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "tabletop1_1", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 251.5, + "y": 16.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 174, + "y": -2, + "p": 0.651 + }, + { + "x": 332, + "y": -2, + "p": 0.651 + }, + { + "x": 340, + "y": 35, + "p": 0.7 + }, + { + "x": 160, + "y": 35, + "p": 0.7 + } + ], + "color": "#2c647d", + "sortMode": "v3", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Static_386", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 0.75, + "x": 121, + "y": 84, + "width": 97.31154375082349, + "height": 19.26098062560257, + "baseWidth": 47.402978862614084, + "baseHeight": 9.382523617203757, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#1d4853", + "scale": 2.052857142857143, + "refScale": 1, + "modelScale": 2.7, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "sunbeam_fuse", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -147.5, + "y": -24, + "ignoreScaling": false, + "vertices": [ + { + "x": -283, + "y": -71, + "p": 0.65 + }, + { + "x": 8, + "y": -221, + "p": 0.65 + }, + { + "x": -5, + "y": 99, + "p": 0.7 + }, + { + "x": -310, + "y": 97, + "p": 0.7 + } + ], + "color": "#c95618", + "sortMode": "v3", + "opacity": 0.2, + "blendMode": "screen", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 5 + }, + { + "name": "shadow", + "type": "Quad", + "locked": true, + "disabled": true, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": false, + "hidden": false, + "parallax": 1, + "x": 121.75, + "y": 131, + "ignoreScaling": false, + "vertices": [ + { + "x": 112, + "y": 101, + "p": 0.645 + }, + { + "x": 226, + "y": 99, + "p": 0.645 + }, + { + "x": 131, + "y": 163, + "p": 0.77 + }, + { + "x": 18, + "y": 161, + "p": 0.77 + } + ], + "color": "#535246", + "sortMode": "v1", + "opacity": 1, + "blendMode": "multiply", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 5 + }, + { + "name": "plant", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 0.648, + "x": -252, + "y": 109, + "width": 195.88114285714286, + "height": 204.04285714285714, + "baseWidth": 432, + "baseHeight": 450, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "office-plant", + "color": "#AAAAAA", + "scale": 0.45342857142857146, + "refScale": 0.2, + "modelScale": 0.54, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "chair", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 0.69, + "x": 232, + "y": 142, + "width": 113.62680000000002, + "height": 193.23360000000002, + "baseWidth": 167, + "baseHeight": 284, + "colliderWidth": 33, + "colliderHeight": 9, + "spriteName": "chair", + "color": "#AAAAAA", + "scale": 0.6804000000000001, + "refScale": 0.28, + "modelScale": 0.7560000000000001, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "Quad_7", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": "<", + "targetId": "Quad_7", + "cullingType": "render" + } + ], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -523.5, + "y": -52.25, + "ignoreScaling": false, + "vertices": [ + { + "x": -553, + "y": -383, + "p": 1 + }, + { + "x": -487, + "y": -287, + "p": 0.85 + }, + { + "x": -480, + "y": 191, + "p": 0.85 + }, + { + "x": -574, + "y": 270, + "p": 1 + } + ], + "color": "#666666", + "sortMode": "v2", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_5", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": ">", + "targetId": "Quad_5", + "cullingType": "render" + } + ], + "layer": 1, + "visible": false, + "hidden": false, + "parallax": 1, + "x": -453.5, + "y": -50.25, + "ignoreScaling": false, + "vertices": [ + { + "x": -489, + "y": -377, + "p": 1 + }, + { + "x": -417, + "y": -297, + "p": 0.85 + }, + { + "x": -419, + "y": 201, + "p": 0.85 + }, + { + "x": -489, + "y": 272, + "p": 1 + } + ], + "color": "#b47e4b", + "sortMode": "v2", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_3", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -532.5, + "y": -52.5, + "ignoreScaling": false, + "vertices": [ + { + "x": -576, + "y": -377, + "p": 1 + }, + { + "x": -489, + "y": -377, + "p": 1 + }, + { + "x": -489, + "y": 272, + "p": 1 + }, + { + "x": -576, + "y": 272, + "p": 1 + } + ], + "color": "#474747", + "sortMode": "v2", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "w4_1", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 2, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 27.5, + "y": -399.5, + "ignoreScaling": false, + "vertices": [ + { + "x": -698, + "y": -351, + "p": 1 + }, + { + "x": -700, + "y": -443, + "p": 1 + }, + { + "x": 754, + "y": -451, + "p": 1 + }, + { + "x": 754, + "y": -353, + "p": 1 + } + ], + "color": "#0a0a0a", + "sortMode": "ignore", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#1c9245", + "filled": true, + "blur": 0 + }, + { + "name": "bas1", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 2, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -190.5, + "y": 344.25, + "ignoreScaling": false, + "vertices": [ + { + "x": -1130, + "y": 272, + "p": 1 + }, + { + "x": 750, + "y": 263, + "p": 1 + }, + { + "x": 750, + "y": 417, + "p": 1 + }, + { + "x": -1132, + "y": 425, + "p": 1 + } + ], + "color": "#0a0a0a", + "sortMode": "ignore", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "bas2", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 2, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 683, + "y": -42.5, + "ignoreScaling": false, + "vertices": [ + { + "x": 616, + "y": -353, + "p": 1 + }, + { + "x": 754, + "y": -353, + "p": 1 + }, + { + "x": 752, + "y": 268, + "p": 1 + }, + { + "x": 610, + "y": 268, + "p": 1 + } + ], + "color": "#0a0a0a", + "sortMode": "ignore", + "opacity": 1, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Quad_521", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Backface", + "vertexA": 0, + "vertexB": 1, + "axis": "x", + "op": ">", + "targetId": "Quad_521", + "cullingType": "render" + } + ], + "layer": 2, + "visible": false, + "hidden": false, + "parallax": 1, + "x": -398.5, + "y": -224, + "ignoreScaling": false, + "vertices": [ + { + "x": -485, + "y": -353, + "p": 1 + }, + { + "x": -314, + "y": -219, + "p": 0.65 + }, + { + "x": -310, + "y": -190, + "p": 0.65 + }, + { + "x": -485, + "y": -134, + "p": 1 + } + ], + "color": "#855d38", + "sortMode": "ignore", + "opacity": 0.75, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 2 + }, + { + "name": "sunbeam2", + "type": "Quad", + "locked": true, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 2, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 309.5, + "y": -24, + "ignoreScaling": false, + "vertices": [ + { + "x": 323, + "y": -188, + "p": 0.65 + }, + { + "x": 475, + "y": -188, + "p": 0.65 + }, + { + "x": 391, + "y": 142, + "p": 0.64 + }, + { + "x": 49, + "y": 138, + "p": 0.64 + } + ], + "color": "#c95618", + "sortMode": "v3", + "opacity": 0.5, + "blendMode": "screen", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 10 + }, + { + "name": "exit1", + "type": "Quad", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Exit", + "targetSceneId": "", + "targetEntryId": "entry" + }, + { + "type": "WalkBox", + "mode": "Add" + } + ], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 526.75, + "y": 121.75, + "ignoreScaling": false, + "vertices": [ + { + "x": 488, + "y": 97, + "p": 0.65, + "binding": { + "targetName": "wall_2", + "type": "vertex", + "index": 2 + } + }, + { + "x": 533, + "y": 97, + "p": 0.699 + }, + { + "x": 546, + "y": 130, + "p": 0.845 + }, + { + "x": 540, + "y": 163, + "p": 0.845, + "binding": { + "targetName": "Quad_1", + "type": "vertex", + "index": 0 + } + } + ], + "color": "#52779aff", + "sortMode": "ignore", + "opacity": 0, + "blendMode": "source-over", + "isGrid": false, + "gridLinesX": 5, + "gridLinesY": 5, + "lineWidth": 1, + "gridColor": "#ffffff", + "filled": true, + "blur": 0 + }, + { + "name": "Hero_1", + "type": "Actor", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Actor" + }, + { + "type": "Shadow", + "shadowQuadId": "shadow", + "offsetX": -70, + "offsetY": -25, + "triggerId": "#f" + }, + { + "type": "Inventory", + "capacity": 9007199254740991, + "groups": [], + "protected": false, + "items": ["test"], + "relation": "in" + } + ], + "layer": 1, + "visible": true, + "hidden": false, + "parallax": 0.7727213747436679, + "x": -928.8248173132807, + "y": 159.65664684322715, + "width": 71.03999999999999, + "height": 290.08, + "baseWidth": 96, + "baseHeight": 392, + "colliderWidth": 88, + "colliderHeight": 4, + "spriteName": "miles_ds-idle-right.json", + "color": "#00ffff", + "scale": 0.74, + "refScale": 0.74, + "modelScale": 0.74, + "ignoreScaling": true, + "animationSpeed": 30, + "opacity": 1, + "blendMode": "source-over", + "blur": 0, + "isPlayer": true, + "speed": 0.24, + "direction": "right", + "animSets": { + "idle": { + "id": "idle", + "up": "miles_ds-idle-up.json", + "down": "miles_ds-idle-down.json", + "left": null, + "right": "miles_ds-idle-right.json" + }, + "walk": { + "id": "walk", + "up": "miles_ds-walk-up.json", + "down": "miles_ds-walk-down.json", + "left": null, + "right": "miles_ds-walk-right.json" + } + } + }, + { + "name": "test", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": "#compact_cassete", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Item" + } + ], + "layer": 4, + "visible": false, + "hidden": false, + "spatial": { + "parentNodeId": "Hero_1", + "relation": "in" + }, + "parallax": 1, + "x": -928.8248173132807, + "y": 159.65664684322715, + "width": 14.183901773533426, + "height": 20.156070941336974, + "baseWidth": 19.69986357435198, + "baseHeight": 27.994542974079128, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#AAAAAA", + "scale": 0.7200000000000001, + "refScale": 0.8, + "modelScale": 0.8, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + } + ], + "folders": [], + "camera": { + "x": -927.0552275306312, + "y": 0, + "zoom": 0.33832234175059017 + }, + "autoCenter": true, + "cameraSpeed": 5, + "camDeadzoneX": 50, + "camDeadzoneY": 30, + "camMaxX": 14, + "camMaxY": 12, + "soundEnv": { + "audioMaxDistance": 10000, + "reverbMaxDist": 1750, + "reverbMinPercent": 0.2, + "zoomSensitivity": 0.7, + "pannerRefDistance": 100, + "pannerRolloffFactor": 0.7, + "panningModel": "HRTF", + "distanceModel": "linear" + } +} diff --git a/public/scenes/test_room.json b/public/scenes/test_room.json index cd0614f..0fe49a4 100644 --- a/public/scenes/test_room.json +++ b/public/scenes/test_room.json @@ -51,7 +51,7 @@ "components": [ { "type": "Surface", - "relation": "in", + "relation": "on", "capacity": 8, "groups": [], "items": [ @@ -93,12 +93,12 @@ "y": 211 }, { - "x": 680, - "y": 211 + "x": 1341, + "y": 210 }, { - "x": 680, - "y": 297 + "x": 1409, + "y": 264 }, { "x": -234, @@ -134,7 +134,7 @@ { "type": "Subscene", "targetGroupId": "#D", - "itemScale": 8 + "itemScale": 7 } ], "layer": 0, @@ -403,11 +403,17 @@ "type": "Surface", "capacity": 8, "groups": [], - "items": [], + "items": [ + { + "id": "miles_id", + "x": -62, + "y": -114 + } + ], "relation": "on" } ], - "layer": 5, + "layer": 6, "visible": true, "hidden": false, "spatial": { @@ -728,6 +734,58 @@ } ], "script": "" + }, + { + "name": "Trig_450", + "type": "Triggerbox", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "poly": [], + "script": "" + }, + { + "name": "Entry", + "type": "Triggerbox", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Entry", + "direction": "down" + } + ], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "poly": [ + { + "x": 1341, + "y": 220 + }, + { + "x": 1303, + "y": 243 + }, + { + "x": 1366, + "y": 253 + } + ], + "script": "" } ], "scaling": { @@ -735,13 +793,14 @@ "min": 0.91, "max": 1, "horizon": 193, - "front": 269 + "front": 269, + "correctionalScale": 1 }, "entities": [ { "name": "CityView", "type": "Entity", - "locked": true, + "locked": false, "disabled": false, "groupID": null, "customName": "", @@ -765,17 +824,18 @@ "relation": "in" }, "parallax": 0.4, - "x": 119.67896209456934, - "y": 234, - "width": 821.6, - "height": 551.2, - "baseWidth": 1264, - "baseHeight": 848, + "x": 103, + "y": 284, + "width": 1064, + "height": 657, + "baseWidth": 1636.923076923077, + "baseHeight": 1010.7692307692307, "colliderWidth": 0, "colliderHeight": 0, "spriteName": "window-view.json", "color": "#00ff00", "scale": 0.65, + "refScale": 0.65, "modelScale": 0.65, "ignoreScaling": true, "animationSpeed": 150, @@ -817,6 +877,7 @@ "spriteName": "room2", "color": "#888888", "scale": 0.7, + "refScale": 0.7, "modelScale": 0.7, "ignoreScaling": true, "animationSpeed": 150, @@ -857,6 +918,7 @@ "spriteName": "chair.json", "color": "#00ff00", "scale": 0.7, + "refScale": 0.7, "modelScale": 0.7, "ignoreScaling": true, "animationSpeed": 150, @@ -882,7 +944,7 @@ "shadowQuadId": "shadow", "offsetX": -70, "offsetY": -25, - "triggerId": "room" + "triggerId": "#f" }, { "type": "Inventory", @@ -896,18 +958,19 @@ "layer": 0, "visible": true, "hidden": false, - "parallax": 1.0470361218703443, - "x": 192.6578538406663, - "y": 257.9343259181667, - "width": 71.03999999999999, - "height": 290.08, - "baseWidth": 96, - "baseHeight": 392, + "parallax": 1.040913443385125, + "x": 653.2461823654976, + "y": 248.0313619847128, + "width": 119.88, + "height": 289.34, + "baseWidth": 162, + "baseHeight": 391, "colliderWidth": 88, "colliderHeight": 4, - "spriteName": "miles_ds-idle-right.json", + "spriteName": "miles_ds-idle-down.json", "color": "#00ffff", "scale": 0.74, + "refScale": 0.74, "modelScale": 0.74, "ignoreScaling": true, "animationSpeed": 30, @@ -916,7 +979,7 @@ "blur": 0, "isPlayer": true, "speed": 0.24, - "direction": "left", + "direction": "down", "animSets": { "idle": { "id": "idle", @@ -968,6 +1031,7 @@ "spriteName": null, "color": "#000000", "scale": 1.1, + "refScale": 1.1, "modelScale": 1.1, "ignoreScaling": false, "animationSpeed": 150, @@ -1004,6 +1068,7 @@ "spriteName": "sub_drawers_main", "color": "#00ff00", "scale": 0.6, + "refScale": 0.6, "modelScale": 0.6, "ignoreScaling": true, "animationSpeed": 150, @@ -1045,6 +1110,7 @@ "spriteName": "sub_drawers_d2.json", "color": "#00ff00", "scale": 0.6, + "refScale": 0.6, "modelScale": 0.6, "ignoreScaling": true, "animationSpeed": 150, @@ -1081,6 +1147,7 @@ "spriteName": "sub_drawers_d1_body.json", "color": "#00ff00", "scale": 0.6, + "refScale": 0.6, "modelScale": 0.6, "ignoreScaling": true, "animationSpeed": 150, @@ -1117,6 +1184,7 @@ "spriteName": "sub_drawers_d1_items.json", "color": "#00ff00", "scale": 1, + "refScale": 1, "modelScale": 1, "ignoreScaling": true, "animationSpeed": 150, @@ -1143,7 +1211,7 @@ "items": [] } ], - "layer": 6, + "layer": 8, "visible": true, "hidden": false, "spatial": { @@ -1162,6 +1230,7 @@ "spriteName": "sub_drawers_top.json", "color": "#00ff00", "scale": 0.6, + "refScale": 0.6, "modelScale": 0.6, "ignoreScaling": true, "animationSpeed": 150, @@ -1203,6 +1272,7 @@ "spriteName": "sub_drawers_d1_facade.json", "color": "#00ff00", "scale": 0.6, + "refScale": 0.6, "modelScale": 0.6, "ignoreScaling": true, "animationSpeed": 150, @@ -1215,7 +1285,7 @@ "type": "Quad", "locked": false, "disabled": false, - "groupID": null, + "groupID": "#f", "customName": "", "textRedirects": {}, "interactions": {}, @@ -1238,12 +1308,12 @@ "p": 1 }, { - "x": 708, - "y": 186, + "x": 1335, + "y": 185, "p": 1 }, { - "x": 766, + "x": 1579, "y": 339, "p": 1.1 }, @@ -1257,7 +1327,7 @@ "sortMode": "ignore", "opacity": 0, "blendMode": "source-over", - "isGrid": false, + "isGrid": true, "gridLinesX": 5, "gridLinesY": 5, "lineWidth": 1, @@ -1290,9 +1360,9 @@ "layer": 0, "visible": true, "hidden": false, - "parallax": 1.0790382036293829, - "x": 222.90433731748357, - "y": 306.92845155295566, + "parallax": 1.0791811402100033, + "x": 223.04286339314422, + "y": 306.938955923405, "width": 1008.8000000000001, "height": 90.39999999999999, "baseWidth": 1261, @@ -1302,6 +1372,7 @@ "spriteName": "sofa", "color": "#36d87fff", "scale": 0.8, + "refScale": 0.8, "modelScale": 0.8, "ignoreScaling": false, "animationSpeed": 150, @@ -1347,6 +1418,7 @@ "spriteName": null, "color": "#AAAAAA", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1383,6 +1455,7 @@ "spriteName": null, "color": "#AAAAAA", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1424,6 +1497,7 @@ "spriteName": null, "color": "#AAAAAA", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1440,48 +1514,39 @@ "customName": "", "textRedirects": {}, "interactions": {}, - "components": [ - { - "type": "Inventory", - "relation": "in", - "capacity": 9007199254740991, - "groups": [], - "protected": false, - "items": [] - } - ], + "components": [], "layer": 0, "visible": true, "hidden": false, "parallax": 1, - "x": 138.33846025303774, - "y": 238.8437936498268, + "x": 593.1052025036854, + "y": 228.9545808884336, "ignoreScaling": false, "vertices": [ { - "x": 138.33846025303774, - "y": 238.8437936498268, - "p": 1.0345663246696715 + "x": 593.1052025036854, + "y": 228.9545808884336, + "p": 1.0283679059375372 }, { - "x": 236.13581222784623, - "y": 237.6845657297887, - "p": 1.0337240504265903 + "x": 688.2347726051288, + "y": 227.8764937219134, + "p": 1.0277624950709003 }, { - "x": 166.80360248596494, - "y": 272.3086674406411, - "p": 1.0564313371083014 + "x": 703.4430391453096, + "y": 260.3080588874047, + "p": 1.0489484925749581 }, { - "x": 118.52658023996095, - "y": 270.76173138031174, - "p": 1.0555078997741538 + "x": 651.4624110015549, + "y": 258.8538504273255, + "p": 1.0480273728177847 } ], "color": "#2b019d", "sortMode": "ignore", - "opacity": 0.6, + "opacity": 1, "blendMode": "multiply", "isGrid": false, "gridLinesX": 5, @@ -1510,12 +1575,12 @@ "visible": true, "hidden": false, "spatial": { - "parentNodeId": "Drawer1", - "relation": "in" + "parentNodeId": "d1_surface", + "relation": "on" }, "parallax": 1, - "x": 82, - "y": -2, + "x": 79, + "y": -34, "width": 19.55767142352019, "height": 12.120460784313726, "baseWidth": 165.32266630194582, @@ -1525,6 +1590,7 @@ "spriteName": "sub_drawers_d1_id.json", "color": "#AAAAAA", "scale": 0.1183, + "refScale": 0.13, "modelScale": 0.13, "ignoreScaling": false, "animationSpeed": 150, @@ -1532,46 +1598,6 @@ "blendMode": "source-over", "blur": 0 }, - { - "name": "test", - "type": "Entity", - "locked": false, - "disabled": false, - "groupID": "#compact_cassete", - "customName": "", - "textRedirects": {}, - "interactions": {}, - "components": [ - { - "type": "Item" - } - ], - "layer": 4, - "visible": false, - "hidden": false, - "spatial": { - "parentNodeId": "Hero_1", - "relation": "in" - }, - "parallax": 1, - "x": 192.6578538406663, - "y": 257.9343259181667, - "width": 15.55337186706756, - "height": 22.10216002162232, - "baseWidth": 19.69986357435198, - "baseHeight": 27.994542974079128, - "colliderWidth": 0, - "colliderHeight": 0, - "spriteName": null, - "color": "#AAAAAA", - "scale": 0.7895167298172105, - "modelScale": 0.8, - "ignoreScaling": false, - "animationSpeed": 150, - "opacity": 1, - "blendMode": "source-over", - "blur": 0 - }, { "name": "test_1", "type": "Entity", @@ -1591,20 +1617,21 @@ "hidden": false, "spatial": { "parentNodeId": "Walk_main", - "relation": "in" + "relation": "on" }, "parallax": 1, "x": 555, "y": 214, - "width": 14.73342428376535, - "height": 20.936971350613916, - "baseWidth": 19.69986357435198, - "baseHeight": 27.994542974079128, + "width": 19.632236842105264, + "height": 14.023026315789474, + "baseWidth": 26.25, + "baseHeight": 18.75, "colliderWidth": 0, "colliderHeight": 0, "spriteName": null, - "color": "#86cece", + "color": "#5e184f", "scale": 0.7478947368421053, + "refScale": 0.8, "modelScale": 0.8, "ignoreScaling": false, "animationSpeed": 150, @@ -1653,6 +1680,7 @@ "spriteName": null, "color": "#d66e29", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1701,6 +1729,7 @@ "spriteName": null, "color": "#d0cb39", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1733,6 +1762,7 @@ "spriteName": null, "color": "#AAAAAA", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1765,6 +1795,7 @@ "spriteName": null, "color": "#AAAAAA", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1797,6 +1828,7 @@ "spriteName": null, "color": "#AAAAAA", "scale": 0.91, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, @@ -1814,27 +1846,250 @@ "textRedirects": {}, "interactions": {}, "components": [], - "layer": 0, + "layer": -2, "visible": true, "hidden": false, "parallax": 1, - "x": -137, - "y": -37, - "width": 27.3, - "height": 27.3, - "baseWidth": 30, - "baseHeight": 30, + "x": -106, + "y": -40, + "width": 37, + "height": 26, + "baseWidth": 40.65934065934066, + "baseHeight": 28.57142857142857, "colliderWidth": 0, "colliderHeight": 0, "spriteName": null, "color": "#AAAAAA", "scale": 0.91, + "refScale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "sofa_pillow1", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "spatial": { + "parentNodeId": "Sofa", + "relation": "on" + }, + "parallax": 1, + "x": 277, + "y": 357, + "width": 141, + "height": 56, + "baseWidth": 141, + "baseHeight": 56, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#4c2e94", + "scale": 1, + "refScale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "sofa_pillow2", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "spatial": { + "parentNodeId": "Sofa", + "relation": "on" + }, + "parallax": 1, + "x": 461, + "y": 357, + "width": 141, + "height": 56, + "baseWidth": 141, + "baseHeight": 56, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#4c2e94", + "scale": 1, + "refScale": 1, "modelScale": 1, "ignoreScaling": false, "animationSpeed": 150, "opacity": 1, "blendMode": "source-over", "blur": 0 + }, + { + "name": "tv_rc", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": "", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Item" + } + ], + "layer": 0, + "visible": true, + "hidden": "lookable", + "spatial": { + "parentNodeId": "sofa_pillow2", + "relation": "under" + }, + "parallax": 1, + "x": 544, + "y": 345, + "width": 25.999999999999996, + "height": 38.35, + "baseWidth": 199.99999999999997, + "baseHeight": 295, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "tv_rc", + "color": "#AAAAAA", + "scale": 0.13, + "refScale": 0.13, + "modelScale": 0.13, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "room_k", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": -1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 1028, + "y": 304, + "width": 788, + "height": 554, + "baseWidth": 788, + "baseHeight": 554, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "kitchen", + "color": "#AAAAAA", + "scale": 1, + "refScale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "Static_81", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1.1, + "x": 1052.1313725490197, + "y": 355.2450980392157, + "width": 466.4373193890188, + "height": 210.21054080088066, + "baseWidth": 466.4373193890188, + "baseHeight": 210.21054080088066, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "kitchen_table", + "color": "#AAAAAA", + "scale": 1, + "refScale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "test", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": "#compact_cassete", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Item" + } + ], + "layer": 4, + "visible": false, + "hidden": false, + "spatial": { + "parentNodeId": "Hero_1", + "relation": "in" + }, + "parallax": 1, + "x": 653.2461823654976, + "y": 248.0313619847128, + "width": 15.368552567463674, + "height": 21.83952206955364, + "baseWidth": 19.69986357435198, + "baseHeight": 27.994542974079128, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#AAAAAA", + "scale": 0.7801349745118332, + "refScale": 0.8, + "modelScale": 0.8, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 } ], "folders": [], diff --git a/public/scenes/test_room1.json b/public/scenes/test_room1.json index b4fccb0..06128a6 100644 --- a/public/scenes/test_room1.json +++ b/public/scenes/test_room1.json @@ -59,11 +59,6 @@ "id": "test_1", "x": 555, "y": 214 - }, - { - "id": "test", - "x": 244, - "y": 255 } ] } @@ -98,12 +93,12 @@ "y": 211 }, { - "x": 680, - "y": 211 + "x": 1341, + "y": 210 }, { - "x": 680, - "y": 297 + "x": 1409, + "y": 264 }, { "x": -234, @@ -139,7 +134,7 @@ { "type": "Subscene", "targetGroupId": "#D", - "itemScale": 8 + "itemScale": 7 } ], "layer": 0, @@ -408,11 +403,17 @@ "type": "Surface", "capacity": 8, "groups": [], - "items": [], + "items": [ + { + "id": "miles_id", + "x": -62, + "y": -114 + } + ], "relation": "on" } ], - "layer": 5, + "layer": 6, "visible": true, "hidden": false, "spatial": { @@ -570,7 +571,7 @@ "items": [] } ], - "layer": 4, + "layer": 3, "visible": true, "hidden": false, "spatial": { @@ -597,6 +598,159 @@ } ], "script": "" + }, + { + "name": "floor_lamp", + "type": "Triggerbox", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "poly": [ + { + "x": 401, + "y": 12 + }, + { + "x": 414, + "y": -35 + }, + { + "x": 444, + "y": -35 + }, + { + "x": 460, + "y": 13 + }, + { + "x": 438, + "y": 17 + }, + { + "x": 438, + "y": 17 + }, + { + "x": 436, + "y": 180 + }, + { + "x": 446, + "y": 191 + }, + { + "x": 446, + "y": 191 + }, + { + "x": 420, + "y": 192 + }, + { + "x": 424, + "y": 181 + }, + { + "x": 424, + "y": 14 + } + ], + "script": "" + }, + { + "name": "desk_lamp", + "type": "Triggerbox", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "poly": [ + { + "x": 3, + "y": -6 + }, + { + "x": 10, + "y": -20 + }, + { + "x": 19, + "y": -10 + }, + { + "x": 36, + "y": 6 + }, + { + "x": 11, + "y": 20 + }, + { + "x": 3, + "y": 2 + }, + { + "x": -24, + "y": 23 + }, + { + "x": -7, + "y": 73 + }, + { + "x": -2, + "y": 76 + }, + { + "x": -14, + "y": 78 + }, + { + "x": -31, + "y": 29 + }, + { + "x": -31, + "y": 29 + }, + { + "x": -29, + "y": 16 + } + ], + "script": "" + }, + { + "name": "Trig_450", + "type": "Triggerbox", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "poly": [], + "script": "" } ], "scaling": { @@ -610,7 +764,7 @@ { "name": "CityView", "type": "Entity", - "locked": true, + "locked": false, "disabled": false, "groupID": null, "customName": "", @@ -634,12 +788,12 @@ "relation": "in" }, "parallax": 0.4, - "x": 119.67896209456934, - "y": 234, - "width": 821.6, - "height": 551.2, - "baseWidth": 1264, - "baseHeight": 848, + "x": 103, + "y": 284, + "width": 1064, + "height": 657, + "baseWidth": 1636.923076923077, + "baseHeight": 1010.7692307692307, "colliderWidth": 0, "colliderHeight": 0, "spriteName": "window-view.json", @@ -751,30 +905,30 @@ "shadowQuadId": "shadow", "offsetX": -70, "offsetY": -25, - "triggerId": "room" + "triggerId": "#f" }, { "type": "Inventory", "capacity": 9007199254740991, "groups": [], "protected": false, - "items": ["miles_id"], + "items": [], "relation": "in" } ], "layer": 0, "visible": true, "hidden": false, - "parallax": 1.041603758200544, - "x": 199.4899156537383, - "y": 249.6082973905191, - "width": 71.03999999999999, - "height": 290.08, - "baseWidth": 96, - "baseHeight": 392, + "parallax": 1.0600999917398644, + "x": 653.9435807732922, + "y": 277.55398727939104, + "width": 119.88, + "height": 289.34, + "baseWidth": 162, + "baseHeight": 391, "colliderWidth": 88, "colliderHeight": 4, - "spriteName": "miles_ds-idle-right.json", + "spriteName": "miles_ds-idle-down.json", "color": "#00ffff", "scale": 0.74, "modelScale": 0.74, @@ -785,7 +939,7 @@ "blur": 0, "isPlayer": true, "speed": 0.24, - "direction": "left", + "direction": "down", "animSets": { "idle": { "id": "idle", @@ -895,7 +1049,7 @@ "target": "Drawer2" } ], - "layer": 3, + "layer": 2, "visible": true, "hidden": false, "spatial": { @@ -931,7 +1085,7 @@ "textRedirects": {}, "interactions": {}, "components": [], - "layer": 5, + "layer": 4, "visible": true, "hidden": false, "spatial": { @@ -1012,7 +1166,7 @@ "items": [] } ], - "layer": 6, + "layer": 8, "visible": true, "hidden": false, "spatial": { @@ -1084,7 +1238,7 @@ "type": "Quad", "locked": false, "disabled": false, - "groupID": null, + "groupID": "#f", "customName": "", "textRedirects": {}, "interactions": {}, @@ -1093,7 +1247,7 @@ "type": "3d-parallax" } ], - "layer": 0, + "layer": -1, "visible": true, "hidden": false, "parallax": 1, @@ -1107,12 +1261,12 @@ "p": 1 }, { - "x": 708, - "y": 186, + "x": 1335, + "y": 185, "p": 1 }, { - "x": 766, + "x": 1579, "y": 339, "p": 1.1 }, @@ -1126,7 +1280,7 @@ "sortMode": "ignore", "opacity": 0, "blendMode": "source-over", - "isGrid": false, + "isGrid": true, "gridLinesX": 5, "gridLinesY": 5, "lineWidth": 1, @@ -1159,9 +1313,9 @@ "layer": 0, "visible": true, "hidden": false, - "parallax": 1.0790382036293829, - "x": 222.90433731747987, - "y": 306.9284515529557, + "parallax": 1.0791811402100033, + "x": 223.04286339314416, + "y": 306.93895592340505, "width": 1008.8000000000001, "height": 90.39999999999999, "baseWidth": 1261, @@ -1197,7 +1351,7 @@ "capacity": 1, "groups": ["#compact_cassete"], "protected": false, - "items": [], + "items": ["test"], "relation": "in" } ], @@ -1309,48 +1463,39 @@ "customName": "", "textRedirects": {}, "interactions": {}, - "components": [ - { - "type": "Inventory", - "relation": "in", - "capacity": 9007199254740991, - "groups": [], - "protected": false, - "items": [] - } - ], + "components": [], "layer": 0, "visible": true, "hidden": false, "parallax": 1, - "x": 145.18694662091718, - "y": 230.54902619661678, + "x": 595.3371473161715, + "y": 258.497010067161, "ignoreScaling": false, "vertices": [ { - "x": 145.18694662091718, - "y": 230.54902619661678, - "p": 1.0291755094471586 + "x": 595.3371473161715, + "y": 258.497010067161, + "p": 1.0477227203569743 }, { - "x": 242.85278512344576, - "y": 229.35714201277415, - "p": 1.0283762522966693 + "x": 691.9753666746899, + "y": 257.3939667925368, + "p": 1.0470090693458032 }, { - "x": 177.78444354447626, - "y": 265.0256985553919, - "p": 1.051655163104805 + "x": 658.2815148069235, + "y": 290.460617761526, + "p": 1.0684809206243675 }, { - "x": 129.3058658734136, - "y": 263.4290846171659, - "p": 1.0506246467766456 + "x": 608.3944452508779, + "y": 288.9779797519234, + "p": 1.0675181686700803 } ], "color": "#2b019d", "sortMode": "ignore", - "opacity": 0.6, + "opacity": 1, "blendMode": "multiply", "isGrid": false, "gridLinesX": 5, @@ -1364,8 +1509,8 @@ "name": "miles_id", "type": "Entity", "locked": false, - "disabled": false, - "groupID": null, + "disabled": true, + "groupID": "#D1", "customName": "your ID card", "textRedirects": {}, "interactions": {}, @@ -1375,25 +1520,25 @@ "ignoreDistance": true } ], - "layer": 5, - "visible": false, + "layer": 6, + "visible": true, "hidden": false, "spatial": { - "parentNodeId": "Hero_1", - "relation": "in" + "parentNodeId": "d1_surface", + "relation": "on" }, "parallax": 1, - "x": 199.4899156537383, - "y": 249.6082973905191, - "width": 20.998408601351045, - "height": 13.013327735918267, + "x": -62, + "y": -114, + "width": 19.55767142352019, + "height": 12.120460784313726, "baseWidth": 165.32266630194582, "baseHeight": 102.45528980823099, "colliderWidth": 0, "colliderHeight": 0, "spriteName": "sub_drawers_d1_id.json", "color": "#AAAAAA", - "scale": 0.12701469841406676, + "scale": 0.1183, "modelScale": 0.13, "ignoreScaling": false, "animationSpeed": 150, @@ -1401,46 +1546,6 @@ "blendMode": "source-over", "blur": 0 }, - { - "name": "test", - "type": "Entity", - "locked": false, - "disabled": false, - "groupID": "#compact_cassete", - "customName": "", - "textRedirects": {}, - "interactions": {}, - "components": [ - { - "type": "Item" - } - ], - "layer": 0, - "visible": true, - "hidden": false, - "spatial": { - "parentNodeId": "Walk_main", - "relation": "in" - }, - "parallax": 1, - "x": 244, - "y": 255, - "width": 15.49860845839018, - "height": 22.024338335607094, - "baseWidth": 19.69986357435198, - "baseHeight": 27.994542974079128, - "colliderWidth": 0, - "colliderHeight": 0, - "spriteName": null, - "color": "#AAAAAA", - "scale": 0.7867368421052632, - "modelScale": 0.8, - "ignoreScaling": false, - "animationSpeed": 150, - "opacity": 1, - "blendMode": "source-over", - "blur": 0 - }, { "name": "test_1", "type": "Entity", @@ -1465,14 +1570,14 @@ "parallax": 1, "x": 555, "y": 214, - "width": 14.73342428376535, - "height": 20.936971350613916, - "baseWidth": 19.69986357435198, - "baseHeight": 27.994542974079128, + "width": 19.632236842105264, + "height": 14.023026315789474, + "baseWidth": 26.25, + "baseHeight": 18.75, "colliderWidth": 0, "colliderHeight": 0, "spriteName": null, - "color": "#86cece", + "color": "#5e184f", "scale": 0.7478947368421053, "modelScale": 0.8, "ignoreScaling": false, @@ -1640,6 +1745,286 @@ "opacity": 0, "blendMode": "source-over", "blur": 0 + }, + { + "name": "phone_modem", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -41, + "y": 91, + "width": 28, + "height": 40, + "baseWidth": 30.769230769230766, + "baseHeight": 43.956043956043956, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#AAAAAA", + "scale": 0.91, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 0, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "sound_test", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": -2, + "visible": true, + "hidden": false, + "parallax": 1, + "x": -106, + "y": -40, + "width": 37, + "height": 26, + "baseWidth": 40.65934065934066, + "baseHeight": 28.57142857142857, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#AAAAAA", + "scale": 0.91, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "sofa_pillow1", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "spatial": { + "parentNodeId": "Sofa", + "relation": "on" + }, + "parallax": 1, + "x": 277, + "y": 357, + "width": 141, + "height": 56, + "baseWidth": 141, + "baseHeight": 56, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#4c2e94", + "scale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "sofa_pillow2", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "spatial": { + "parentNodeId": "Sofa", + "relation": "on" + }, + "parallax": 1, + "x": 461, + "y": 357, + "width": 141, + "height": 56, + "baseWidth": 141, + "baseHeight": 56, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#4c2e94", + "scale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "tv_rc", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": "", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Item" + } + ], + "layer": 0, + "visible": true, + "hidden": "lookable", + "spatial": { + "parentNodeId": "sofa_pillow2", + "relation": "under" + }, + "parallax": 1, + "x": 544, + "y": 345, + "width": 25.999999999999996, + "height": 38.35, + "baseWidth": 199.99999999999997, + "baseHeight": 295, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "tv_rc", + "color": "#AAAAAA", + "scale": 0.13, + "modelScale": 0.13, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "room_k", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": -1, + "visible": true, + "hidden": false, + "parallax": 1, + "x": 1028, + "y": 304, + "width": 788, + "height": 554, + "baseWidth": 788, + "baseHeight": 554, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "kitchen", + "color": "#AAAAAA", + "scale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "Static_81", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": null, + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [], + "layer": 0, + "visible": true, + "hidden": false, + "parallax": 1.1, + "x": 1052.1313725490197, + "y": 355.2450980392157, + "width": 466.4373193890188, + "height": 210.21054080088066, + "baseWidth": 466.4373193890188, + "baseHeight": 210.21054080088066, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": "kitchen_table", + "color": "#AAAAAA", + "scale": 1, + "modelScale": 1, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 + }, + { + "name": "test", + "type": "Entity", + "locked": false, + "disabled": false, + "groupID": "#compact_cassete", + "customName": "", + "textRedirects": {}, + "interactions": {}, + "components": [ + { + "type": "Item" + } + ], + "layer": 4, + "visible": false, + "hidden": false, + "spatial": { + "parentNodeId": "boombox", + "relation": "in" + }, + "parallax": 1, + "x": -152, + "y": -11, + "width": 14.341500682128244, + "height": 20.380027285129607, + "baseWidth": 19.69986357435198, + "baseHeight": 27.994542974079128, + "colliderWidth": 0, + "colliderHeight": 0, + "spriteName": null, + "color": "#AAAAAA", + "scale": 0.7280000000000001, + "modelScale": 0.8, + "ignoreScaling": false, + "animationSpeed": 150, + "opacity": 1, + "blendMode": "source-over", + "blur": 0 } ], "folders": [], @@ -1653,5 +2038,16 @@ "camDeadzoneX": 200, "camDeadzoneY": -21, "camMinX": 143, - "camMaxY": 45 + "camMaxY": 45, + "soundEnv": { + "audioMaxDistance": 10000, + "reverbMaxDist": 6000, + "reverbMinPercent": 0.1, + "zoomSensitivity": 0.7, + "pannerRefDistance": 100, + "pannerRolloffFactor": 0.7, + "panningModel": "HRTF", + "distanceModel": "linear", + "defaultReverbIR": "/sounds/ir/room_drum_medium.wav" + } } diff --git a/public/sprites/kitchen.json b/public/sprites/kitchen.json new file mode 100644 index 0000000..f03e33f --- /dev/null +++ b/public/sprites/kitchen.json @@ -0,0 +1,9 @@ +{ + "id": "kitchen", + "imageFile": "public/assets/kitchen.png", + "x": 0, + "y": 0, + "width": 600, + "height": 401, + "frames": 1 +} diff --git a/public/sprites/kitchen_table.json b/public/sprites/kitchen_table.json new file mode 100644 index 0000000..7f0a590 --- /dev/null +++ b/public/sprites/kitchen_table.json @@ -0,0 +1,9 @@ +{ + "id": "kitchen_table", + "imageFile": "public/assets/kitchen_table.png", + "x": 0, + "y": 0, + "width": 285, + "height": 136, + "frames": 1 +} diff --git a/public/sprites/tv_rc.json b/public/sprites/tv_rc.json new file mode 100644 index 0000000..2732ad8 --- /dev/null +++ b/public/sprites/tv_rc.json @@ -0,0 +1,9 @@ +{ + "id": "tv_rc", + "imageFile": "public/assets/tv_rc.png", + "x": 0, + "y": 0, + "width": 200, + "height": 295, + "frames": 1 +} diff --git a/public/text/objects/Static_305.json b/public/text/objects/Static_305.json new file mode 100644 index 0000000..1b2422d --- /dev/null +++ b/public/text/objects/Static_305.json @@ -0,0 +1,8 @@ +{ + "title": "Static_305", + "description": "You see nothing special.", + "details": "", + "lore": "", + "takeFailure": "", + "synonyms": [] +} diff --git a/public/text/objects/left_dofa_pillow.json b/public/text/objects/left_dofa_pillow.json new file mode 100644 index 0000000..c6b5fda --- /dev/null +++ b/public/text/objects/left_dofa_pillow.json @@ -0,0 +1,8 @@ +{ + "title": "left_dofa_pillow", + "description": "You see nothing special.", + "details": "", + "lore": "", + "takeFailure": "", + "synonyms": [] +} diff --git a/public/text/objects/left_sofa_pillow.json b/public/text/objects/left_sofa_pillow.json new file mode 100644 index 0000000..bfd47d1 --- /dev/null +++ b/public/text/objects/left_sofa_pillow.json @@ -0,0 +1,8 @@ +{ + "title": "left_sofa_pillow", + "description": "You see nothing special.", + "details": "", + "lore": "", + "takeFailure": "", + "synonyms": [] +} diff --git a/public/text/objects/rc_tv.json b/public/text/objects/rc_tv.json new file mode 100644 index 0000000..43ec573 --- /dev/null +++ b/public/text/objects/rc_tv.json @@ -0,0 +1,8 @@ +{ + "title": "rc_tv", + "description": "You see nothing special.", + "details": "", + "lore": "", + "takeFailure": "", + "synonyms": [] +} diff --git a/public/text/objects/sofa_pillow1.json b/public/text/objects/sofa_pillow1.json new file mode 100644 index 0000000..2f24285 --- /dev/null +++ b/public/text/objects/sofa_pillow1.json @@ -0,0 +1,7 @@ +{ + "title": "left sofa pillow", + "description": "A slightly worn pillow rests on the left side of the couch.", + "details": "The pillow has the permanently compressed look of something that has survived countless late-night movies, accidental naps, and long stretches of staring silently at the city through the blinds.", + "takeFailure": "You leave the pillow where it is. Some small remaining part of the apartment still vaguely resembles civilization thanks to things like this.", + "synonyms": ["pillow", "cushion", "sofa pillow", "left pillow"] +} diff --git a/public/text/objects/sofa_pillow1_1.json b/public/text/objects/sofa_pillow1_1.json new file mode 100644 index 0000000..cdd9c65 --- /dev/null +++ b/public/text/objects/sofa_pillow1_1.json @@ -0,0 +1,7 @@ +{ + "title": "left sofa pillow", + "description": "A slightly worn sofa pillow rests on the left side of the couch.", + "details": "The pillow has the permanently compressed look of something that has survived countless late-night movies, accidental naps, and long stretches of staring silently at the city through the blinds. \n\nAt some point it probably matched the rest of the sofa properly. Now it mostly matches your lifestyle.", + "takeFailure": "You leave the pillow where it is. Some small remaining part of the apartment still vaguely resembles civilization thanks to things like this.", + "synonyms": ["pillow", "cushion", "sofa pillow", "left pillow"] +} diff --git a/public/text/objects/sofa_pillow2.json b/public/text/objects/sofa_pillow2.json new file mode 100644 index 0000000..6741ad1 --- /dev/null +++ b/public/text/objects/sofa_pillow2.json @@ -0,0 +1,8 @@ +{ + "title": "right sofa pillow", + "description": "A pillow leans against the right side of sofa.", + "details": "The pillow has survived at least one previous tenant, several years of cheap detergent, and countless evenings of background television..", + "lore": "", + "takeFailure": "You leave the pillow alone. Some small remaining part of the apartment still vaguely resembles civilization thanks to things like this.", + "synonyms": ["pillow", "cushion", "sofa pillow", "right pillow"] +} diff --git a/public/text/objects/sofa_pillow_left.json b/public/text/objects/sofa_pillow_left.json new file mode 100644 index 0000000..2e68226 --- /dev/null +++ b/public/text/objects/sofa_pillow_left.json @@ -0,0 +1,8 @@ +{ + "title": "sofa_pillow_left", + "description": "You see nothing special.", + "details": "", + "lore": "", + "takeFailure": "", + "synonyms": [] +} diff --git a/public/text/objects/tc_tv.json b/public/text/objects/tc_tv.json new file mode 100644 index 0000000..333d15f --- /dev/null +++ b/public/text/objects/tc_tv.json @@ -0,0 +1,8 @@ +{ + "title": "tc_tv", + "description": "You see nothing special.", + "details": "", + "lore": "", + "takeFailure": "", + "synonyms": [] +} diff --git a/public/text/objects/tv_rc.json b/public/text/objects/tv_rc.json new file mode 100644 index 0000000..b4b4d25 --- /dev/null +++ b/public/text/objects/tv_rc.json @@ -0,0 +1,6 @@ +{ + "title": "TV remote", + "description": "A rectangular Sony remote control.", + "details": "Solid in the hand. You have to admit it — the Japanese make pleasant things.\nIt mostly controls the Trinitron, though a few buttons can also operate basic functions on the VHS deck if the alignment gods happen to be smiling that day.", + "synonyms": ["remote", "remote control", "controller", "tv remote", "sony remote", "rc"] +} diff --git a/public/text/scenes/quad5.json b/public/text/scenes/quad5.json new file mode 100644 index 0000000..0343556 --- /dev/null +++ b/public/text/scenes/quad5.json @@ -0,0 +1,4 @@ +{ + "title": "Test Room", + "description": "You are in Test Room." +} diff --git a/public/text/scenes/test_room.json b/public/text/scenes/test_room.json index 6896ada..68bd781 100644 --- a/public/text/scenes/test_room.json +++ b/public/text/scenes/test_room.json @@ -1,6 +1,9 @@ { "title": "Mile's Home", - "description": "You look toward the working corner of the apartment. The desk stands against the wall perpendicular to the window, with a shelf of books above it that makes the space feel close, personal, and lived in. It is cozy, but not tidy. There is the usual kind of programmer's clutter everywhere: cables coiled and tangled across the floor, Post-it notes and scraps of paper with old reminders, crumpled printouts of BASIC and assembler code, five-inch floppy disks, and the small debris of long hours spent thinking at a machine. Across the room, against the wall opposite the window, sits the sofa, set so you could watch the television or turn and look out at the city. The whole room feels warm, familiar, and comfortably disordered, like a place you genuinely likes to work in.", + "description": [ + "You look toward the working corner of the apartment. It is cozy, but not tidy. There is the usual kind of programmer's clutter everywhere: cables coiled and tangled across the floor, Post-it notes and scraps of paper with old reminders, crumpled printouts of BASIC and assembler code, five-inch floppy disks, and the small debris of long hours spent thinking at a machine.", + "Across the room, against the wall opposite the window, sits the sofa, set so you could watch the television or turn and look out at the city. The whole room feels warm, familiar, and comfortably disordered, like a place you genuinely likes to work in." + ], "lore": [ "Now is year 1987. Monday, 23rd of February. Morning, dawn. Somewhere in West Coast, USA. City of San Vesper.", "Miles lives alone in a compact studio apartment inside a once-upscale early-1950s mixed-use building on the edge of old industrial district and downtown core. By 1987 the neighborhood has declined: office workers, aging tenants, cheap sublets, distant sirens, and neon reflecting off rain-stained concrete.", diff --git a/public/text/system/commands/teleport_with.json b/public/text/system/commands/teleport_with.json index 2aa69a5..726ba11 100644 --- a/public/text/system/commands/teleport_with.json +++ b/public/text/system/commands/teleport_with.json @@ -8,7 +8,7 @@ "required": true, "scopes": ["held", "takable"], "validation": { - "allowedTitles": ["your ID card"] + "allowedEntityIds": ["miles_id"] }, "messages": { "missing": "Teleport with what?", diff --git a/public/text/system/parser-llm-system.md b/public/text/system/parser-llm-system.md index a4c02ea..61dbff2 100644 --- a/public/text/system/parser-llm-system.md +++ b/public/text/system/parser-llm-system.md @@ -125,6 +125,8 @@ If a harmless player action leaves a persistent mark on an object or area, store Do not overuse notes. Do not store passing jokes, generic mood, or obvious facts already present in the game context. +If your response invents or changes a persistent small in-world fact about an object or scene, such as a radio being left on, a device producing static, a cushion staying creased, or a room now smelling faintly of smoke, return a `plan` with `showText` plus the appropriate Parser Note action. Do not return that kind of persistent change as `final_response`, because `final_response` cannot carry Parser Notes. + Parser Notes must be paired with a player-facing `showText` action in the same plan. A plan that writes Parser Notes must not also return ordinary world actions such as `lookTarget`, `examineTarget`, `openTarget`, or `takeTarget`. Parser Notes must contain only in-world facts. Never write parser reasoning, player attempts, command mapping, missing capability, available actions, mechanics, JSON, APIs, implementation limits, or instructions to treat one action as another. @@ -161,7 +163,7 @@ For a game command: { "kind": "plan", "actions": [ { "type": "..." } ] } ``` -For conversation, atmosphere, reactions, or when no safe action fits: +For conversation, atmosphere, reactions, or when no safe action fits and you are not creating or updating a persistent Parser Note: ```json { "kind": "final_response", "message": "Short in-world response." } diff --git a/public/text/system/parser-llm.json b/public/text/system/parser-llm.json index a5fe029..5a919cf 100644 --- a/public/text/system/parser-llm.json +++ b/public/text/system/parser-llm.json @@ -4,11 +4,11 @@ "forced_handoff_instructions": [ "Cascade 1 test mode asks you to handle this command yourself.", "Use the lower cascade interpretation as a hint for what the dry machine parser would do.", - "If you can give a richer, more atmospheric, and still grounded response, prefer final_response or showText.", + "If you can give a richer, more atmospheric, and still grounded response, prefer final_response or a plan with showText.", "If the lower cascade action is genuinely the best answer, you may return that action plan.", "Do not force the player's intent into a merely related plan just because the target object exists.", "If the player's intent is recognized but no exact standard action fits it, invent a short atmospheric and logical Game Master response instead of calling a merely adjacent standard action.", - "If the lower cascade maps the request to a plan that misses the player's real intent, prefer final_response or showText as a Game Master.", + "If the lower cascade maps the request to a plan that misses the player's real intent, prefer final_response or a plan with showText as a Game Master.", "If you cannot improve the lower cascade result safely, return fallback." ], "post_api_escalation_instructions": [ @@ -16,7 +16,7 @@ "First try to correct the player intent, target, relation, or action plan if the lower parser likely misunderstood the command.", "If context.focusedTarget is present and the player omitted an explicit object, use focusedTarget.title as the default target or item.", "Do not repeat the same failing action unless you intentionally corrected the target, relation, or intent.", - "If the requested action is impossible in the current world, return final_response or a showText action with a short in-world reason.", + "If the requested action is impossible in the current world, return final_response or a plan containing showText with a short in-world reason.", "If the player's intent is recognized but no exact standard action fits it, invent a short atmospheric and logical Game Master response instead of calling a merely adjacent standard action.", "If the request is plausible but no grounded plan fits it, you may narrate a harmless no-result attempt instead of choosing an unrelated plan.", "If you cannot improve the previous attempt safely, return fallback." @@ -34,12 +34,12 @@ "The previous parser/game attempt recognized a command but ended in a recoverable failed outcome.", "First decide whether the lower parser likely misunderstood the player intent, target, relation, or action. If so, return a corrected safe action plan using only real context titles.", "If context.focusedTarget is present and the player omitted an explicit object, use focusedTarget.title as the default target or item.", - "If the intent and target are correct but the game outcome says the action is impossible, do not override game state. Return final_response or showText with a short atmospheric in-world reason.", + "If the intent and target are correct but the game outcome says the action is impossible, do not override game state. Return final_response or a plan containing showText with a short atmospheric in-world reason.", "When rejecting unsupported player intent, prefer the protagonist's judgment, reluctance, dignity, caution, fatigue, disgust, or lack of desire over inventing a physical obstacle.", "Do not claim that an action succeeded unless the returned plan can actually execute it.", "If you cannot improve the previous attempt safely or interestingly, return fallback.", "If the player's intent is recognized but no exact standard action fits it, invent a short atmospheric and logical Game Master response instead of calling a merely adjacent standard action.", - "If the requested action is impossible in the current world, return final_response or showText with a short noir narrator response that explains the failure through tone, implication, sarcasm, or cynical observation rather than plain technical description.", + "If the requested action is impossible in the current world, return final_response or a plan containing showText with a short noir narrator response that explains the failure through tone, implication, sarcasm, or cynical observation rather than plain technical description.", "For unsupported but plausible minor actions, prefer: the player character has no time, desire, or reason to do it; the action happens but produces no meaningful result; or, more rarely, the object or mechanism does not work.", "Do not say a prop is nailed down, bolted, glued, locked, or too heavy unless current worldFacts, contents/location, spatial relations, or object text support that." ], @@ -68,6 +68,7 @@ "Example: if a note says a cassette inside a device is playing, but current world facts or contents show that no cassette is inside that device, clear or replace that note before responding to the player.", "Do not store temporary player character actions, poses, intentions, emotions, or current activity in Parser Notes. Narrate those moments in showText or final_response.", "If a harmless player action leaves a persistent mark, store only the persistent in-world result on the affected object or scene, not that the player character is doing the action now.", + "If your response invents or changes a persistent small in-world fact about an object or scene, such as a radio being left on, a device producing static, a cushion staying creased, or a room now smelling faintly of smoke, return a plan with showText plus the appropriate Parser Note action. Do not return that kind of persistent change as final_response, because final_response cannot carry Parser Notes.", "A Parser Note action must appear with showText in the same plan.", "A plan that writes Parser Notes must not include ordinary world actions such as lookTarget, examineTarget, openTarget, or takeTarget.", "Parser Notes are not player-facing prose. Keep them short, factual, in-world, and story-neutral.", diff --git a/public/text/system/parser.json b/public/text/system/parser.json index c9e6a35..8fdf5a4 100644 --- a/public/text/system/parser.json +++ b/public/text/system/parser.json @@ -10,6 +10,7 @@ "examine_relation_prompt": "Examine what area?", "relation_empty": "You see nothing {relation} the {target}.", "relation_contents": "{Relation} the {target} you see: {items}.", + "relation_discovered_contents": "{Relation} the {target} you discover: {items}.", "relation_location": "It is {relation} the {target}.", "relation_not_supported": "You can't determine what is {relation} the {target} from here.", "take_prompt": "Take what?", diff --git a/src/components/ConsoleOverlay.tsx b/src/components/ConsoleOverlay.tsx index 0a002da..d507db9 100644 --- a/src/components/ConsoleOverlay.tsx +++ b/src/components/ConsoleOverlay.tsx @@ -100,12 +100,24 @@ export const ConsoleOverlay: React.FC = ({ game }) => { boxSizing: 'border-box', overflow: 'hidden', pointerEvents: 'auto', // Allow scrolling + userSelect: 'text', + WebkitUserSelect: 'text', }} >
{ + event.stopPropagation(); + }} > {lines.map((line, i) => (
= ({ game }) => { color: line.type === 'command' ? '#aaa' : line.type === 'error' ? '#f55' : '#fff', whiteSpace: 'pre-wrap', overflowWrap: 'break-word', + userSelect: 'text', + WebkitUserSelect: 'text', }} > {line.text} @@ -131,25 +145,56 @@ export const ConsoleOverlay: React.FC = ({ game }) => { }; const InputMirror: React.FC<{ game: Game }> = ({ game }) => { - const [val, setVal] = useState(''); + const [inputState, setInputState] = useState({ value: '', caret: 0, cursorVisible: false }); useEffect(() => { const input = game.getCommandInput(); if (!input) return; + let frame = 0; const update = () => { - if (input && input.value !== val) { - setVal(input.value); - } - requestAnimationFrame(update); + const value = input.value; + const caret = Math.max(0, Math.min(input.selectionStart ?? value.length, value.length)); + const cursorVisible = + document.activeElement === input && Math.floor(game.cursorBlink / 500) % 2 === 0; + + setInputState((current) => { + if ( + current.value === value && + current.caret === caret && + current.cursorVisible === cursorVisible + ) { + return current; + } + return { value, caret, cursorVisible }; + }); + frame = requestAnimationFrame(update); }; - const rAF = requestAnimationFrame(update); + frame = requestAnimationFrame(update); return () => { - cancelAnimationFrame(rAF); + cancelAnimationFrame(frame); }; - }, [game, val]); + }, [game]); + + const { value, caret, cursorVisible } = inputState; + const beforeCaret = value.slice(0, caret); + const cursorChar = value[caret] || '\u00a0'; + const afterCaret = value.slice(caret + (value[caret] ? 1 : 0)); - return {`> ${val}_`}; + if (!cursorVisible) { + return {`> ${value}`}; + } + + return ( + + {'> '} + {beforeCaret} + + {cursorChar} + + {afterCaret} + + ); }; diff --git a/src/components/GameCanvas.tsx b/src/components/GameCanvas.tsx index febff61..8c54d79 100644 --- a/src/components/GameCanvas.tsx +++ b/src/components/GameCanvas.tsx @@ -156,6 +156,7 @@ export const GameCanvas: React.FC = ({ onGameInit }) => { const active = document.activeElement as HTMLElement; if ( active && + active.id !== 'parser-input' && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT') diff --git a/src/components/UIOverlay.tsx b/src/components/UIOverlay.tsx index 29f575b..6727646 100644 --- a/src/components/UIOverlay.tsx +++ b/src/components/UIOverlay.tsx @@ -30,6 +30,7 @@ export const UIOverlay: React.FC = ({ game }) => { // Console History State const [historyIndex, setHistoryIndex] = useState(-1); const [, forceInventoryRefresh] = useState(0); + const suppressCommandFocusUntilRef = React.useRef(0); // Editor Store State const { enabled: editorEnabled } = useEditorStore(); @@ -98,6 +99,54 @@ export const UIOverlay: React.FC = ({ game }) => { return () => window.clearTimeout(timer); }, [game, editorEnabled, isConsoleOpen, isConsoleModal, previewEntity?.name]); + useEffect(() => { + if (!game) return; + const input = parserInputRef.current; + if (!input) return; + + const shouldLockCommandFocus = () => { + return ( + !input.disabled && + !fileBrowser && + !choiceDialog && + !isConsoleModal && + (!editorEnabled || isConsoleOpen) + ); + }; + + const isConsoleLogSelectionTarget = (target: EventTarget | null) => { + return target instanceof Element && !!target.closest('.console-scroll'); + }; + + const restoreCommandFocus = () => { + if (Date.now() < suppressCommandFocusUntilRef.current) return; + if (!shouldLockCommandFocus()) return; + const caret = input.selectionStart ?? input.value.length; + input.focus({ preventScroll: true }); + input.setSelectionRange(caret, caret); + }; + + const scheduleRestoreCommandFocus = (event?: Event) => { + if (isConsoleLogSelectionTarget(event?.target || null)) { + suppressCommandFocusUntilRef.current = Date.now() + 1000; + return; + } + window.setTimeout(restoreCommandFocus, 0); + }; + + window.addEventListener('pointerdown', scheduleRestoreCommandFocus, true); + window.addEventListener('focusin', scheduleRestoreCommandFocus, true); + input.addEventListener('blur', scheduleRestoreCommandFocus); + + restoreCommandFocus(); + + return () => { + window.removeEventListener('pointerdown', scheduleRestoreCommandFocus, true); + window.removeEventListener('focusin', scheduleRestoreCommandFocus, true); + input.removeEventListener('blur', scheduleRestoreCommandFocus); + }; + }, [game, editorEnabled, isConsoleOpen, isConsoleModal, fileBrowser, choiceDialog]); + useEffect(() => { if (message) { const timer = setTimeout(() => { @@ -148,6 +197,17 @@ export const UIOverlay: React.FC = ({ game }) => { return game?.console.continueClosedModal() || false; }, [game]); + const moveCommandInputCaret = React.useCallback( + (input: HTMLInputElement, delta: -1 | 1) => { + const caret = input.selectionStart ?? input.value.length; + const nextCaret = Math.max(0, Math.min(input.value.length, caret + delta)); + input.setSelectionRange(nextCaret, nextCaret); + game?.revealCommandCursor(); + setHistoryIndex(-1); + }, + [game] + ); + const keepCommandInputFocused = React.useCallback( (e: React.MouseEvent) => { e.preventDefault(); @@ -226,12 +286,26 @@ export const UIOverlay: React.FC = ({ game }) => { } } + if (!(e.ctrlKey || e.metaKey) && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) { + e.preventDefault(); + return; + } + + if (e.key === 'Home' || e.key === 'End') { + game?.revealCommandCursor(); + } + // History Navigation: Ctrl + Up/Down if (game && (e.ctrlKey || e.metaKey)) { - const history = game.console.history; - if (history.length === 0) return; + if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { + e.preventDefault(); + moveCommandInputCaret(e.currentTarget, e.key === 'ArrowLeft' ? -1 : 1); + return; + } if (e.key === 'ArrowUp') { + const history = game.console.history; + if (history.length === 0) return; e.preventDefault(); // Go back/older // If we are at -1 (new/empty), go to last item (length-1) @@ -246,9 +320,16 @@ export const UIOverlay: React.FC = ({ game }) => { } setHistoryIndex(newIndex); e.currentTarget.value = history[newIndex]; + e.currentTarget.setSelectionRange( + history[newIndex].length, + history[newIndex].length + ); + game.revealCommandCursor(); } if (e.key === 'ArrowDown') { + const history = game.console.history; + if (history.length === 0) return; e.preventDefault(); // Go forward/newer // If we are at length-1 (newest), go to -1 (empty) @@ -260,10 +341,16 @@ export const UIOverlay: React.FC = ({ game }) => { if (newIndex === history.length - 1) { newIndex = -1; e.currentTarget.value = ''; + e.currentTarget.setSelectionRange(0, 0); } else { newIndex = Math.min(history.length - 1, newIndex + 1); e.currentTarget.value = history[newIndex]; + e.currentTarget.setSelectionRange( + history[newIndex].length, + history[newIndex].length + ); } + game.revealCommandCursor(); } setHistoryIndex(newIndex); } diff --git a/src/components/editor/properties/EntityProperties.tsx b/src/components/editor/properties/EntityProperties.tsx index 3b1df20..5d78e95 100644 --- a/src/components/editor/properties/EntityProperties.tsx +++ b/src/components/editor/properties/EntityProperties.tsx @@ -8,6 +8,7 @@ interface EntityObject { y: number; width: number; height: number; + refScale: number; modelScale: number; layer: number; parallax: number; @@ -85,8 +86,8 @@ export const EntityProperties: React.FC = () => { type="number" step="0.1" className="e-input" - value={formatPanelNumber(entity.modelScale || 1)} - onChange={(e) => handleChange('modelScale', e.target.value, true)} + value={formatPanelNumber(entity.refScale || entity.modelScale || 1)} + onChange={(e) => handleChange('refScale', e.target.value, true)} />
diff --git a/src/components/editor/properties/PropertiesPanel.tsx b/src/components/editor/properties/PropertiesPanel.tsx index 85574ed..2d899fd 100644 --- a/src/components/editor/properties/PropertiesPanel.tsx +++ b/src/components/editor/properties/PropertiesPanel.tsx @@ -558,6 +558,9 @@ export const PropertiesPanel: React.FC = () => { if (field === 'spriteName') { if (obj.setSprite) obj.setSprite(finalVal); } + if (field === 'refScale') { + obj.applySceneCorrectionalScale?.(game?.sceneManager?.currentScene); + } if (field === 'ignoreScaling') { const isIgnored = finalVal; const scene = game?.sceneManager?.currentScene; diff --git a/src/components/editor/properties/SceneProperties.tsx b/src/components/editor/properties/SceneProperties.tsx index 44cef3e..e981a7c 100644 --- a/src/components/editor/properties/SceneProperties.tsx +++ b/src/components/editor/properties/SceneProperties.tsx @@ -1,7 +1,68 @@ import React from 'react'; import { usePropertiesContext } from './PropertiesContext'; import { Scene } from '../../../scene/Scene'; -import { SoundManager } from '../../../systems/SoundManager'; +import { + SoundManager, + type DistanceModelType, + type PanningModelType, +} from '../../../systems/SoundManager'; + +type NumberDraftInputProps = { + value: number; + step?: string; + min?: string; + max?: string; + className?: string; + formatPanelNumber: (value: unknown) => number | string; + onCommit: (value: number) => void; +}; + +const NumberDraftInput: React.FC = ({ + value, + step, + min, + max, + className, + formatPanelNumber, + onCommit, +}) => { + const [draft, setDraft] = React.useState(String(formatPanelNumber(value))); + const [focused, setFocused] = React.useState(false); + + React.useEffect(() => { + if (!focused) { + setDraft(String(formatPanelNumber(value))); + } + }, [focused, formatPanelNumber, value]); + + return ( + { + setFocused(true); + setDraft(String(formatPanelNumber(value))); + }} + onChange={(e) => { + const raw = e.target.value; + setDraft(raw); + if (raw === '' || raw === '-' || raw === '.' || raw === '-.') return; + const next = Number(raw); + if (Number.isFinite(next)) { + onCommit(next); + } + }} + onBlur={() => { + setFocused(false); + setDraft(String(formatPanelNumber(value))); + }} + /> + ); +}; export const SceneProperties: React.FC = () => { const { game, obj, formatPanelNumber, setSectionRef, incrementObjectVersion, handleChange } = @@ -217,13 +278,13 @@ export const SceneProperties: React.FC = () => {
- { - scene.defaultCamera.zoom = parseFloat(e.target.value); + value={scene.defaultCamera.zoom} + formatPanelNumber={formatPanelNumber} + onCommit={(value) => { + scene.defaultCamera.zoom = value; incrementObjectVersion(); }} /> @@ -262,6 +323,9 @@ export const SceneProperties: React.FC = () => { const s = game.sceneManager.currentScene.scaling; return ( <> +
+
Depth Scaling
+
)} +
+
Correction
+
+
+ + { + const val = parseFloat(e.target.value); + scene.applyCorrectionalScaleChange(Number.isFinite(val) && val > 0 ? val : 1); + incrementObjectVersion(); + }} + /> +
); })()} diff --git a/src/components/editor/properties/SettingsProperties.tsx b/src/components/editor/properties/SettingsProperties.tsx index 6e4df02..4ca37f6 100644 --- a/src/components/editor/properties/SettingsProperties.tsx +++ b/src/components/editor/properties/SettingsProperties.tsx @@ -3,12 +3,16 @@ import { usePropertiesContext } from './PropertiesContext'; import { Select } from '../../common/Select'; import { isTauriRuntime } from '../../../platform/fileApi'; import { useEditorStore } from '../../../store/editorStore'; +import { SoundManager } from '../../../systems/SoundManager'; interface GameSettings { editor?: { uiScale?: number; viewportZoom?: 'fit' | '1' | '1.5' | '2'; }; + audio?: { + attachedVolume?: number; + }; crt?: { enabled: boolean; curvature: number; @@ -82,6 +86,38 @@ export const SettingsProperties: React.FC = () => {
)} +
+ +
+ +
+ + { + if (!settings.audio) settings.audio = { attachedVolume: 1.0 }; + const val = parseFloat(e.target.value); + if (Number.isFinite(val)) { + settings.audio.attachedVolume = Math.max(0, Math.min(10, val)); + SoundManager.getInstance().setAttachedVolume(settings.audio.attachedVolume); + incrementObjectVersion(); + } + }} + /> +
+