@dcl-regenesislabs/web-editor 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/editor.css +1 -0
- package/editor.js +33891 -0
- package/package.json +8 -4
- package/prefabs/admin-tools/composite.json +77 -0
- package/prefabs/admin-tools/data.json +28 -0
- package/prefabs/admin-tools/icons/admin-panel-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-moderation-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-rewards-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-smart-item-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-text-announcement-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-panel-video-control-button.png +0 -0
- package/prefabs/admin-tools/icons/admin-tool-background.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-chat-message.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-check.png +0 -0
- package/prefabs/admin-tools/icons/announcements/text-announcement-close-button.png +0 -0
- package/prefabs/admin-tools/icons/moderation/admin-panel-verified-user.png +0 -0
- package/prefabs/admin-tools/icons/moderation/ban.png +0 -0
- package/prefabs/admin-tools/icons/moderation/chevron-back.png +0 -0
- package/prefabs/admin-tools/icons/moderation/chevron-forward.png +0 -0
- package/prefabs/admin-tools/icons/moderation/close.png +0 -0
- package/prefabs/admin-tools/icons/moderation/error.png +0 -0
- package/prefabs/admin-tools/icons/moderation/person-outline.png +0 -0
- package/prefabs/admin-tools/icons/moderation-control-icon.png +0 -0
- package/prefabs/admin-tools/icons/rewards/close.png +0 -0
- package/prefabs/admin-tools/icons/rewards/send.png +0 -0
- package/prefabs/admin-tools/icons/rewards-control.png +0 -0
- package/prefabs/admin-tools/icons/smart-item-control.png +0 -0
- package/prefabs/admin-tools/icons/text-announcement-control.png +0 -0
- package/prefabs/admin-tools/icons/video/chevron-down.png +0 -0
- package/prefabs/admin-tools/icons/video/chevron-up.png +0 -0
- package/prefabs/admin-tools/icons/video/copy-to-clipboard.png +0 -0
- package/prefabs/admin-tools/icons/video/error.png +0 -0
- package/prefabs/admin-tools/icons/video/eye-off.png +0 -0
- package/prefabs/admin-tools/icons/video/eye.png +0 -0
- package/prefabs/admin-tools/icons/video/help.png +0 -0
- package/prefabs/admin-tools/icons/video/info.png +0 -0
- package/prefabs/admin-tools/icons/video/person-outline.png +0 -0
- package/prefabs/admin-tools/icons/video/star.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-dcl-cast.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-live.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-loop.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-mute.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-pause-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-play-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-video-icon.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-volume-minus-button.png +0 -0
- package/prefabs/admin-tools/icons/video/video-control-volume-plus-button.png +0 -0
- package/prefabs/admin-tools/icons/video-control.png +0 -0
- package/prefabs/admin-tools/scripts/actions.ts +206 -0
- package/prefabs/admin-tools/scripts/admin.tsx +253 -0
- package/prefabs/admin-tools/scripts/api.ts +125 -0
- package/prefabs/admin-tools/scripts/components.ts +190 -0
- package/prefabs/admin-tools/scripts/icons.ts +53 -0
- package/prefabs/admin-tools/scripts/message-bus.ts +297 -0
- package/prefabs/admin-tools/scripts/state.ts +69 -0
- package/prefabs/admin-tools/scripts/tabs/Moderation.tsx +155 -0
- package/prefabs/admin-tools/scripts/tabs/Rewards.tsx +136 -0
- package/prefabs/admin-tools/scripts/tabs/SmartItemActions.tsx +167 -0
- package/prefabs/admin-tools/scripts/tabs/TextAnnouncements.tsx +195 -0
- package/prefabs/admin-tools/scripts/tabs/VideoControl.tsx +156 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/Overlay.tsx +94 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/icons.ts +15 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/state.ts +71 -0
- package/prefabs/admin-tools/scripts/tabs/announcements/ui.tsx +37 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/AddUserInput.tsx +184 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/RemoveAdminConfirmation.tsx +93 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/UsersList.tsx +228 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/api.ts +35 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/icons.ts +23 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/kick.ts +95 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/state.ts +69 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/ui.tsx +172 -0
- package/prefabs/admin-tools/scripts/tabs/moderation/utils.ts +88 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/Button.tsx +52 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/Captcha.tsx +88 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/api.ts +138 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/claim.ts +99 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/controller.ts +103 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/icons.ts +13 -0
- package/prefabs/admin-tools/scripts/tabs/rewards/state.ts +32 -0
- package/prefabs/admin-tools/scripts/tabs/smart-items/selection.ts +90 -0
- package/prefabs/admin-tools/scripts/tabs/video/DclCast.tsx +226 -0
- package/prefabs/admin-tools/scripts/tabs/video/LiveStream.tsx +284 -0
- package/prefabs/admin-tools/scripts/tabs/video/SpeakerShowcase.tsx +112 -0
- package/prefabs/admin-tools/scripts/tabs/video/VideoUrl.tsx +107 -0
- package/prefabs/admin-tools/scripts/tabs/video/Volume.tsx +69 -0
- package/prefabs/admin-tools/scripts/tabs/video/api.ts +172 -0
- package/prefabs/admin-tools/scripts/tabs/video/controls.ts +108 -0
- package/prefabs/admin-tools/scripts/tabs/video/icons.ts +59 -0
- package/prefabs/admin-tools/scripts/tabs/video/speakers.ts +43 -0
- package/prefabs/admin-tools/scripts/tabs/video/state.ts +82 -0
- package/prefabs/admin-tools/scripts/tabs/video/ui.tsx +243 -0
- package/prefabs/admin-tools/scripts/types.ts +46 -0
- package/prefabs/admin-tools/scripts/ui.tsx +122 -0
- package/prefabs/admin-tools/thumbnail.png +0 -0
- package/prefabs/announcer/ai.md +76 -0
- package/prefabs/announcer/composite.json +31 -0
- package/prefabs/announcer/data.json +35 -0
- package/prefabs/announcer/scripts/announcer.tsx +79 -0
- package/prefabs/announcer/scripts/pure/toast.ts +30 -0
- package/prefabs/announcer/thumbnail.png +0 -0
- package/prefabs/black-chair/HighBackChair.glb +0 -0
- package/prefabs/black-chair/composite.json +74 -0
- package/prefabs/black-chair/data.json +30 -0
- package/prefabs/black-chair/scripts/seat.ts +191 -0
- package/prefabs/black-chair/thumbnail.png +0 -0
- package/prefabs/catalog.json +1 -0
- package/prefabs/classic-bench/Bench_01.glb +0 -0
- package/prefabs/classic-bench/composite.json +100 -0
- package/prefabs/classic-bench/data.json +34 -0
- package/prefabs/classic-bench/file1.png +0 -0
- package/prefabs/classic-bench/scripts/seat.ts +191 -0
- package/prefabs/classic-bench/thumbnail.png +0 -0
- package/prefabs/classic-bench-armrests/Bench 1.glb +0 -0
- package/prefabs/classic-bench-armrests/composite.json +100 -0
- package/prefabs/classic-bench-armrests/data.json +30 -0
- package/prefabs/classic-bench-armrests/scripts/seat.ts +191 -0
- package/prefabs/classic-bench-armrests/thumbnail.png +0 -0
- package/prefabs/classroom-chair/Chair_02.glb +0 -0
- package/prefabs/classroom-chair/composite.json +74 -0
- package/prefabs/classroom-chair/data.json +34 -0
- package/prefabs/classroom-chair/file1.png +0 -0
- package/prefabs/classroom-chair/scripts/seat.ts +191 -0
- package/prefabs/classroom-chair/thumbnail.png +0 -0
- package/prefabs/court-chair/Chair_Wood_01.glb +0 -0
- package/prefabs/court-chair/FanstasyPack_TX.png +0 -0
- package/prefabs/court-chair/composite.json +74 -0
- package/prefabs/court-chair/data.json +34 -0
- package/prefabs/court-chair/scripts/seat.ts +191 -0
- package/prefabs/court-chair/thumbnail.png +0 -0
- package/prefabs/curved-couch/CurvedCouch.glb +0 -0
- package/prefabs/curved-couch/composite.json +100 -0
- package/prefabs/curved-couch/data.json +30 -0
- package/prefabs/curved-couch/scripts/seat.ts +191 -0
- package/prefabs/curved-couch/thumbnail.png +0 -0
- package/prefabs/game-flow/ai.md +94 -0
- package/prefabs/game-flow/composite.json +45 -0
- package/prefabs/game-flow/data.json +27 -0
- package/prefabs/game-flow/scripts/game-flow.ts +168 -0
- package/prefabs/game-flow/scripts/pure/flow.ts +185 -0
- package/prefabs/game-flow/thumbnail.png +0 -0
- package/prefabs/health-respawn/ai.md +76 -0
- package/prefabs/health-respawn/composite.json +31 -0
- package/prefabs/health-respawn/data.json +38 -0
- package/prefabs/health-respawn/scripts/health-respawn.ts +98 -0
- package/prefabs/health-respawn/scripts/health.ts +22 -0
- package/prefabs/health-respawn/scripts/pure/health.ts +60 -0
- package/prefabs/health-respawn/thumbnail.png +0 -0
- package/prefabs/high-stool/HighStool.glb +0 -0
- package/prefabs/high-stool/composite.json +74 -0
- package/prefabs/high-stool/data.json +30 -0
- package/prefabs/high-stool/scripts/seat.ts +191 -0
- package/prefabs/high-stool/thumbnail.png +0 -0
- package/prefabs/large-couch/ThreeSeater_Couch.glb +0 -0
- package/prefabs/large-couch/composite.json +100 -0
- package/prefabs/large-couch/data.json +30 -0
- package/prefabs/large-couch/scripts/seat.ts +191 -0
- package/prefabs/large-couch/thumbnail.png +0 -0
- package/prefabs/leaderboard/ai.md +75 -0
- package/prefabs/leaderboard/board-panel.glb +0 -0
- package/prefabs/leaderboard/composite.json +82 -0
- package/prefabs/leaderboard/data.json +35 -0
- package/prefabs/leaderboard/scripts/leaderboard.ts +66 -0
- package/prefabs/leaderboard/scripts/pure/board.ts +71 -0
- package/prefabs/leaderboard/thumbnail.png +0 -0
- package/prefabs/loveseat/Couch_01.glb +0 -0
- package/prefabs/loveseat/composite.json +100 -0
- package/prefabs/loveseat/data.json +34 -0
- package/prefabs/loveseat/file1.png +0 -0
- package/prefabs/loveseat/scripts/seat.ts +191 -0
- package/prefabs/loveseat/thumbnail.png +0 -0
- package/prefabs/modern-sofa/ModernSofa.glb +0 -0
- package/prefabs/modern-sofa/composite.json +100 -0
- package/prefabs/modern-sofa/data.json +30 -0
- package/prefabs/modern-sofa/scripts/seat.ts +191 -0
- package/prefabs/modern-sofa/thumbnail.png +0 -0
- package/prefabs/outdoor-chair/Chair_07.glb +0 -0
- package/prefabs/outdoor-chair/composite.json +74 -0
- package/prefabs/outdoor-chair/data.json +34 -0
- package/prefabs/outdoor-chair/file1.png +0 -0
- package/prefabs/outdoor-chair/scripts/seat.ts +191 -0
- package/prefabs/outdoor-chair/thumbnail.png +0 -0
- package/prefabs/rustic-bench/Bench_01.glb +0 -0
- package/prefabs/rustic-bench/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-bench/composite.json +100 -0
- package/prefabs/rustic-bench/data.json +34 -0
- package/prefabs/rustic-bench/scripts/seat.ts +191 -0
- package/prefabs/rustic-bench/thumbnail.png +0 -0
- package/prefabs/rustic-round-stool/Chairwood_01.glb +0 -0
- package/prefabs/rustic-round-stool/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-round-stool/composite.json +74 -0
- package/prefabs/rustic-round-stool/data.json +34 -0
- package/prefabs/rustic-round-stool/scripts/seat.ts +191 -0
- package/prefabs/rustic-round-stool/thumbnail.png +0 -0
- package/prefabs/rustic-square-stool/Chairwood_02.glb +0 -0
- package/prefabs/rustic-square-stool/PiratesPack_TX.png.png +0 -0
- package/prefabs/rustic-square-stool/composite.json +74 -0
- package/prefabs/rustic-square-stool/data.json +34 -0
- package/prefabs/rustic-square-stool/scripts/seat.ts +191 -0
- package/prefabs/rustic-square-stool/thumbnail.png +0 -0
- package/prefabs/server-clock/ai.md +67 -0
- package/prefabs/server-clock/composite.json +45 -0
- package/prefabs/server-clock/data.json +24 -0
- package/prefabs/server-clock/scripts/clock-hud.tsx +55 -0
- package/prefabs/server-clock/scripts/server-clock.ts +71 -0
- package/prefabs/server-clock/thumbnail.png +0 -0
- package/prefabs/simple-chair/Chair_03.glb +0 -0
- package/prefabs/simple-chair/composite.json +74 -0
- package/prefabs/simple-chair/data.json +34 -0
- package/prefabs/simple-chair/file1.png +0 -0
- package/prefabs/simple-chair/scripts/seat.ts +191 -0
- package/prefabs/simple-chair/thumbnail.png +0 -0
- package/prefabs/sit-spot/composite.json +45 -0
- package/prefabs/sit-spot/data.json +30 -0
- package/prefabs/sit-spot/scripts/seat.ts +191 -0
- package/prefabs/sit-spot/thumbnail.png +0 -0
- package/prefabs/sit-spot-edge/composite.json +42 -0
- package/prefabs/sit-spot-edge/data.json +32 -0
- package/prefabs/sit-spot-edge/emotes/Sit_Edge1_emote.glb +0 -0
- package/prefabs/sit-spot-edge/emotes/Sit_Edge2_emote.glb +0 -0
- package/prefabs/sit-spot-edge/scripts/seat.ts +191 -0
- package/prefabs/sit-spot-edge/thumbnail.png +0 -0
- package/prefabs/small-couch/TwoSeater_Couch.glb +0 -0
- package/prefabs/small-couch/composite.json +100 -0
- package/prefabs/small-couch/data.json +30 -0
- package/prefabs/small-couch/scripts/seat.ts +191 -0
- package/prefabs/small-couch/thumbnail.png +0 -0
- package/prefabs/spawner/ai.md +109 -0
- package/prefabs/spawner/composite.json +73 -0
- package/prefabs/spawner/data.json +22 -0
- package/prefabs/spawner/scripts/spawner.ts +380 -0
- package/prefabs/spawner/thumbnail.png +0 -0
- package/prefabs/steampunk-bench/Steampunk Bench.glb +0 -0
- package/prefabs/steampunk-bench/composite.json +100 -0
- package/prefabs/steampunk-bench/data.json +30 -0
- package/prefabs/steampunk-bench/scripts/seat.ts +191 -0
- package/prefabs/steampunk-bench/thumbnail.png +0 -0
- package/prefabs/steampunk-chair/Steampunk Chair.glb +0 -0
- package/prefabs/steampunk-chair/composite.json +74 -0
- package/prefabs/steampunk-chair/data.json +30 -0
- package/prefabs/steampunk-chair/scripts/seat.ts +191 -0
- package/prefabs/steampunk-chair/thumbnail.png +0 -0
- package/prefabs/tall-stool/Stool_D.glb +0 -0
- package/prefabs/tall-stool/composite.json +74 -0
- package/prefabs/tall-stool/data.json +30 -0
- package/prefabs/tall-stool/scripts/seat.ts +191 -0
- package/prefabs/tall-stool/thumbnail.png +0 -0
- package/prefabs/trigger-zone/ai.md +115 -0
- package/prefabs/trigger-zone/composite.json +52 -0
- package/prefabs/trigger-zone/data.json +31 -0
- package/prefabs/trigger-zone/scripts/trigger-zone.ts +89 -0
- package/prefabs/trigger-zone/thumbnail.png +0 -0
- package/prefabs/video-screen/composite.json +98 -0
- package/prefabs/video-screen/data.json +21 -0
- package/prefabs/video-screen/thumbnail.png +0 -0
- package/prefabs/video-screen/video_player.glb +0 -0
- package/prefabs/wooden-chair/Chair 1.glb +0 -0
- package/prefabs/wooden-chair/composite.json +74 -0
- package/prefabs/wooden-chair/data.json +30 -0
- package/prefabs/wooden-chair/scripts/seat.ts +191 -0
- package/prefabs/wooden-chair/thumbnail.png +0 -0
- package/prefabs/wooden-chair-armrests/Chair 3.glb +0 -0
- package/prefabs/wooden-chair-armrests/composite.json +74 -0
- package/prefabs/wooden-chair-armrests/data.json +30 -0
- package/prefabs/wooden-chair-armrests/scripts/seat.ts +191 -0
- package/prefabs/wooden-chair-armrests/thumbnail.png +0 -0
- package/scene/about +17 -0
- package/scene/bafkreias2w4xuqaic6sdcvblrisayi6xg7drfowzpt5ubjkmrk3mx6czc4 +1 -0
- package/scene/bafkreiby2gmthtauf7cckttphmhpecsueyypi2hzlke4lcodg7fm4cm2sq +23 -0
- package/scene/bafkreidhwqwajqk4nsuffef5fkdrgmp5ql6vnn2knpqsuqalyosv5o6lk4 +1 -0
- package/scene/bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku +0 -0
- package/scene/bafybeihw3xlbff4sf5sc3kvmlmlwf2visnty5lfymawlunzt5vrkp5fd7u +90 -0
- package/web-build/esbuild.wasm +0 -0
- package/web-build/index.js +3 -0
- package/web-build/sdk-snapshot.json +1 -0
- package/web-build/worker.js +701 -0
- package/README.md +0 -3
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"components": [
|
|
4
|
+
{
|
|
5
|
+
"name": "core-schema::Name",
|
|
6
|
+
"data": {
|
|
7
|
+
"0": {
|
|
8
|
+
"json": {
|
|
9
|
+
"value": "Spawner"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "core::MeshRenderer",
|
|
16
|
+
"data": {
|
|
17
|
+
"0": {
|
|
18
|
+
"json": {
|
|
19
|
+
"mesh": {
|
|
20
|
+
"$case": "cylinder",
|
|
21
|
+
"cylinder": {
|
|
22
|
+
"radiusTop": 0.5,
|
|
23
|
+
"radiusBottom": 0.5
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "core::Material",
|
|
32
|
+
"data": {
|
|
33
|
+
"0": {
|
|
34
|
+
"json": {
|
|
35
|
+
"material": {
|
|
36
|
+
"$case": "unlit",
|
|
37
|
+
"unlit": {
|
|
38
|
+
"diffuseColor": { "r": 0.25, "g": 0.8, "b": 0.69, "a": 0.6 },
|
|
39
|
+
"castShadows": false
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "core::VisibilityComponent",
|
|
48
|
+
"data": {
|
|
49
|
+
"0": {
|
|
50
|
+
"json": {
|
|
51
|
+
"visible": true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "asset-packs::Script",
|
|
58
|
+
"data": {
|
|
59
|
+
"0": {
|
|
60
|
+
"json": {
|
|
61
|
+
"value": [
|
|
62
|
+
{
|
|
63
|
+
"path": "{assetPath}/scripts/spawner.ts",
|
|
64
|
+
"priority": 0,
|
|
65
|
+
"layout": "{\"params\":{},\"actions\":[]}"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "fc759fc0-57a1-461c-aa06-22c0d3d4c3e0",
|
|
3
|
+
"name": "Spawner",
|
|
4
|
+
"description": "Makes copies of a prefab appear while playing — on a click, when a player walks in, on a timer, or when another script asks. Copies appear at the Spawner's own spot, only on this player's client.",
|
|
5
|
+
"category": "custom",
|
|
6
|
+
"tags": [
|
|
7
|
+
"spawner",
|
|
8
|
+
"spawn",
|
|
9
|
+
"pickup",
|
|
10
|
+
"enemy"
|
|
11
|
+
],
|
|
12
|
+
"version": "1.0.0",
|
|
13
|
+
"changelog": [
|
|
14
|
+
{
|
|
15
|
+
"version": "1.0.0",
|
|
16
|
+
"notes": "Initial release: copies of a prefab appear while playing — on a click, when a player walks in, on a timer, or when another Script asks — made on that player's own client, with a per-spot cap, a lifetime and scatter."
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"origin": {
|
|
20
|
+
"source": "builtin"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// A spawn point. Pick the prefab it copies and what sets it off — a click, a
|
|
2
|
+
// player walking in, a timer, or another script asking — and a copy appears at
|
|
3
|
+
// the Spawner's own spot. Copies are made right here on this player's game:
|
|
4
|
+
// no server round-trip, nothing stored, nothing to wait for.
|
|
5
|
+
//
|
|
6
|
+
// The spot's id is the entity's NAME, the handle the creator, the inspector and
|
|
7
|
+
// another script already share, so a lever somewhere else sets this one off with
|
|
8
|
+
// requestSpawn('Crate Spawner') and nothing has to be wired. Placement makes
|
|
9
|
+
// names unique, so a second "Crate Spawner" is "Crate Spawner 2" and is its own
|
|
10
|
+
// spot.
|
|
11
|
+
//
|
|
12
|
+
// The pool is opened HERE rather than inside a runtime module: the guarantee
|
|
13
|
+
// scan skips every path under runtime/, so a pool opened there would leave the
|
|
14
|
+
// prefab this spot copies every ten seconds reading "Not used yet" forever.
|
|
15
|
+
import {
|
|
16
|
+
InputAction,
|
|
17
|
+
MeshCollider,
|
|
18
|
+
PointerEventType,
|
|
19
|
+
PointerEvents,
|
|
20
|
+
Transform,
|
|
21
|
+
TriggerArea,
|
|
22
|
+
VisibilityComponent,
|
|
23
|
+
engine,
|
|
24
|
+
inputSystem,
|
|
25
|
+
triggerAreaEventsSystem,
|
|
26
|
+
type Entity
|
|
27
|
+
} from '@dcl/sdk/ecs'
|
|
28
|
+
import { isServer } from '@dcl/sdk/network'
|
|
29
|
+
import { localPlayerPosition } from '~runtime/playerPositions'
|
|
30
|
+
import { pool as openPool, snapshotRootComponent, type Pool } from '~runtime/spawner'
|
|
31
|
+
import { registerSpawnPoint } from '~runtime/spawnPoints'
|
|
32
|
+
import { onZone, zoneOf } from '~runtime/zoneBus'
|
|
33
|
+
import { effectiveScatter, scatterOffset } from '~runtime/pure/spawnScatter'
|
|
34
|
+
import { composeWorld, type LocalTransform, type WorldTransform } from '~runtime/pure/worldTransform'
|
|
35
|
+
|
|
36
|
+
/** A prefab id picked in the inspector's prefab dropdown. */
|
|
37
|
+
type PrefabRef = string
|
|
38
|
+
|
|
39
|
+
// Collision layers by value, not by name: the SDK renamed CL_RESERVED2 to
|
|
40
|
+
// CL_MAIN_PLAYER, and this script compiles against whatever pin the creator's
|
|
41
|
+
// project happens to carry.
|
|
42
|
+
const CL_POINTER = 1
|
|
43
|
+
// The local avatar's collider carries this layer and remote avatars' do not, so
|
|
44
|
+
// the walk-in area fires for the player standing at THIS game, exactly once.
|
|
45
|
+
const CL_MAIN_PLAYER = 8
|
|
46
|
+
const CLICK_RANGE_M = 8
|
|
47
|
+
const TRANSFORM = 'core::Transform'
|
|
48
|
+
const PARENT_DEPTH_MAX = 32
|
|
49
|
+
// The editor materializes this child when `where` is 'custom spot' — a marker
|
|
50
|
+
// showing the prefab's model, positioned with the gizmos, hidden while playing.
|
|
51
|
+
// The prefix stops at a word boundary, so "Spawn Spot 2" and "spawn spot-a" are
|
|
52
|
+
// markers and the creator's own "Spawn Spotlight" is not — the same rule the
|
|
53
|
+
// editor's actions/spawn-spot.ts matches on, and the two must agree.
|
|
54
|
+
const SPAWN_SPOT_MATCH = /^spawn spot(?![a-z])/
|
|
55
|
+
|
|
56
|
+
export class Spawner {
|
|
57
|
+
private pool: Pool | null = null
|
|
58
|
+
private name = ''
|
|
59
|
+
/** copy → seconds left; Infinity when disappearsAfter is 0 */
|
|
60
|
+
private live = new Map<Entity, number>()
|
|
61
|
+
private spawnedCount = 0
|
|
62
|
+
private countdownS = 0
|
|
63
|
+
private armed = false
|
|
64
|
+
private subscribing = false
|
|
65
|
+
private customSpot: Entity | null = null
|
|
66
|
+
|
|
67
|
+
constructor(
|
|
68
|
+
public src: string,
|
|
69
|
+
public entity: Entity,
|
|
70
|
+
/** What appears here. Pick any prefab in this project. */
|
|
71
|
+
public spawn: PrefabRef = '',
|
|
72
|
+
/** What makes a copy appear */
|
|
73
|
+
public when:
|
|
74
|
+
| 'when clicked'
|
|
75
|
+
| 'when a player enters'
|
|
76
|
+
| 'every few seconds'
|
|
77
|
+
| 'when a script asks' = 'when clicked',
|
|
78
|
+
/** For "every few seconds": how many seconds between copies. */
|
|
79
|
+
public everySeconds: number = 10,
|
|
80
|
+
/** For "when clicked": the words a player sees before they click. */
|
|
81
|
+
public hoverLabel: string = 'Use',
|
|
82
|
+
/** How many copies can be out at once. At the limit, nothing more appears until one goes. */
|
|
83
|
+
public atMostAtOnce: number = 1,
|
|
84
|
+
/** Seconds a copy sticks around. 0 keeps it until something removes it. */
|
|
85
|
+
public disappearsAfter: number = 0,
|
|
86
|
+
/** Where a copy appears. 'custom spot' uses the "Spawn Spot" marker you position with the gizmos. */
|
|
87
|
+
public where: 'at this spawner' | 'at the player' | 'custom spot' = 'at this spawner'
|
|
88
|
+
) {}
|
|
89
|
+
|
|
90
|
+
// What sets this spot off is derived from WHERE IT SITS, never picked from a
|
|
91
|
+
// list: parented to something, that something is the button or the zone; on
|
|
92
|
+
// its own, the spawner itself is — its disc becomes the button, its spot
|
|
93
|
+
// becomes the walk-in area (scale is metres, the gizmo is the resize tool).
|
|
94
|
+
// Spread and marker visibility are automatic for the same reason: several
|
|
95
|
+
// copies spread just enough not to stack, and the disc shows itself in Play
|
|
96
|
+
// exactly when this spawner IS the button.
|
|
97
|
+
private parentEntity(): Entity | null {
|
|
98
|
+
return parentOf(Transform.getOrNull(this.entity))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private get isOwnButton(): boolean {
|
|
102
|
+
return this.when === 'when clicked' && this.parentEntity() === null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
start(): void {
|
|
106
|
+
// Every trigger is a player's gesture, so the copies are the player's too. In
|
|
107
|
+
// a scene that runs a Multiplayer Server this same script boots there — where
|
|
108
|
+
// the timer would drip copies nobody can see. That half stands down whole.
|
|
109
|
+
if (isServer()) return
|
|
110
|
+
VisibilityComponent.createOrReplace(this.entity, { visible: this.isOwnButton })
|
|
111
|
+
this.name = zoneOf(this.entity)
|
|
112
|
+
this.pool = this.spawnPool()
|
|
113
|
+
if (this.pool === null) return
|
|
114
|
+
// A cap above the prefab's Max alive can never fill; say the cure once, here.
|
|
115
|
+
if (this.atMostAtOnce > this.pool.max) {
|
|
116
|
+
console.log(
|
|
117
|
+
`[Spawner] '${this.name}' asks for ${this.atMostAtOnce} copies but the prefab's Max alive is ${this.pool.max} — raise the prefab's copy limit in the Prefabs tab`
|
|
118
|
+
)
|
|
119
|
+
this.atMostAtOnce = this.pool.max
|
|
120
|
+
}
|
|
121
|
+
this.countdownS = Math.max(1, this.everySeconds)
|
|
122
|
+
// Every mode registers, not just 'when a script asks': requestSpawn by NAME is
|
|
123
|
+
// the whole cross-script wiring, and a round script poking a click spawner for
|
|
124
|
+
// an extra crate must keep working the way it always has.
|
|
125
|
+
registerSpawnPoint(this.name, () => this.spawnOne())
|
|
126
|
+
this.hideSpawnSpots()
|
|
127
|
+
if (this.when === 'when a player enters') this.armWalkIn()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// The marker is an authoring surface: it shows the model in the editor so the
|
|
131
|
+
// creator can aim it, and disappears the moment the scene runs. Hiding runs in
|
|
132
|
+
// EVERY mode, not just 'custom spot' — a marker left behind by a changed
|
|
133
|
+
// dropdown must never ship as a ghost model players can see. Its GltfContainer
|
|
134
|
+
// ships with zeroed collision masks, so hiding the mesh is all that is left.
|
|
135
|
+
private hideSpawnSpots(): void {
|
|
136
|
+
for (const [child] of engine.getEntitiesWith(Transform)) {
|
|
137
|
+
if (parentOf(Transform.getOrNull(child)) !== this.entity) continue
|
|
138
|
+
if (!SPAWN_SPOT_MATCH.test(zoneOf(child).trim().toLowerCase())) continue
|
|
139
|
+
VisibilityComponent.createOrReplace(child, { visible: false })
|
|
140
|
+
if (this.customSpot === null) this.customSpot = child
|
|
141
|
+
}
|
|
142
|
+
if (this.where === 'custom spot' && this.customSpot === null) {
|
|
143
|
+
console.log(
|
|
144
|
+
`[Spawner] '${this.name}' is set to a custom spot but its "Spawn Spot" marker is gone — copies appear at the spawner instead`
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
update(dt: number): void {
|
|
150
|
+
if (this.pool === null) return
|
|
151
|
+
if (!this.armed) {
|
|
152
|
+
this.armed = true
|
|
153
|
+
this.subscribing = false
|
|
154
|
+
// The pointer hint is merged on the first TICK, not in start(): script start
|
|
155
|
+
// order is not authored, so another script's createOrReplace of PointerEvents
|
|
156
|
+
// during start() would silently erase it.
|
|
157
|
+
if (this.when === 'when clicked') this.armClick()
|
|
158
|
+
}
|
|
159
|
+
this.expire(dt)
|
|
160
|
+
if (this.when === 'every few seconds') {
|
|
161
|
+
this.countdownS -= dt
|
|
162
|
+
if (this.countdownS <= 0) {
|
|
163
|
+
this.countdownS = Math.max(1, this.everySeconds)
|
|
164
|
+
this.spawnOne()
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (this.when !== 'when clicked') return
|
|
168
|
+
if (inputSystem.isTriggered(InputAction.IA_POINTER, PointerEventType.PET_DOWN, this.clickTarget())) {
|
|
169
|
+
this.spawnOne()
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private spawnPool(): Pool | null {
|
|
174
|
+
if (this.spawn === '') {
|
|
175
|
+
console.log('[Spawner] no prefab picked — set "spawn" to the prefab this spot should copy')
|
|
176
|
+
return null
|
|
177
|
+
}
|
|
178
|
+
// A stale prefab ref (Spawnable turned off, prefab deleted) makes the pool
|
|
179
|
+
// throw, and a throw out of start() aborts every script the runner has not
|
|
180
|
+
// started yet plus the scene's own main(). One dropdown must not kill a scene.
|
|
181
|
+
try {
|
|
182
|
+
return openPool(this.spawn, 'seeded')
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.log('[Spawner] the "spawn" param is not a Spawnable prefab —', error)
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// --- the triggers ----------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
private armWalkIn(): void {
|
|
192
|
+
const parent = this.parentEntity()
|
|
193
|
+
const parentZone = parent !== null && TriggerArea.getOrNull(parent) !== null ? zoneOf(parent) : ''
|
|
194
|
+
if (parentZone !== '') {
|
|
195
|
+
this.subscribing = true
|
|
196
|
+
onZone(parentZone, 'enter', (event) => {
|
|
197
|
+
// Late subscribers get current occupancy replayed as enters, and those arrive
|
|
198
|
+
// synchronously inside this call: someone already standing here did not just
|
|
199
|
+
// walk in, and a copy must not appear because a script started.
|
|
200
|
+
if (this.subscribing || !event.local) return
|
|
201
|
+
this.spawnOne()
|
|
202
|
+
})
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
// Not sitting in a zone: this spot IS the walk-in area. The engine's enter
|
|
206
|
+
// event only fires on the transition, so someone already standing here when
|
|
207
|
+
// the scene starts does not set it off — same rule as the zone path.
|
|
208
|
+
TriggerArea.setBox(this.entity, CL_MAIN_PLAYER)
|
|
209
|
+
triggerAreaEventsSystem.onTriggerEnter(this.entity, () => this.spawnOne())
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private clickTarget(): Entity {
|
|
213
|
+
return this.parentEntity() ?? this.entity
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private armClick(): void {
|
|
217
|
+
const target = this.clickTarget()
|
|
218
|
+
// Only the marker gets a collider of its own. Another entity the creator
|
|
219
|
+
// pointed at is theirs — a cylinder stamped over it would be the wrong shape.
|
|
220
|
+
if (target === this.entity) {
|
|
221
|
+
MeshCollider.createOrReplace(target, {
|
|
222
|
+
mesh: { $case: 'cylinder', cylinder: { radiusTop: 0.5, radiusBottom: 0.5 } },
|
|
223
|
+
collisionMask: CL_POINTER
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
const existing = PointerEvents.getOrNull(target)?.pointerEvents ?? []
|
|
227
|
+
PointerEvents.createOrReplace(target, {
|
|
228
|
+
pointerEvents: [
|
|
229
|
+
...existing,
|
|
230
|
+
{
|
|
231
|
+
eventType: PointerEventType.PET_DOWN,
|
|
232
|
+
eventInfo: {
|
|
233
|
+
button: InputAction.IA_POINTER,
|
|
234
|
+
hoverText: this.hoverLabel,
|
|
235
|
+
maxDistance: CLICK_RANGE_M
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// --- the copies ------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
private spawnOne(): void {
|
|
245
|
+
if (this.pool === null) return
|
|
246
|
+
// Drop before the cap check, not only in expire(): retireSpawned frees the
|
|
247
|
+
// pool synchronously, and "retire then request" in one callback must spawn.
|
|
248
|
+
this.dropReleased()
|
|
249
|
+
if (this.live.size >= Math.max(1, this.atMostAtOnce)) return
|
|
250
|
+
const world = this.spawnBase()
|
|
251
|
+
const offset = scatterOffset(this.spawnedCount, effectiveScatter(0, this.atMostAtOnce))
|
|
252
|
+
// The init write replaces the clone's whole Transform, and the spot's world
|
|
253
|
+
// scale must not leak into it — a spawner shrunk to a marker would shrink its
|
|
254
|
+
// zombies. Position and rotation are the spot's; scale stays the prefab's own.
|
|
255
|
+
const authoredScale = localOf(snapshotRootComponent(this.pool.prefab, TRANSFORM)).scale
|
|
256
|
+
let spawned: Entity | null = null
|
|
257
|
+
try {
|
|
258
|
+
// A clone's own start() is creator code. Letting it throw out of here would
|
|
259
|
+
// abort the rest of the frame, so one bad script loses one copy, not the scene.
|
|
260
|
+
spawned = this.pool.acquire(undefined, {
|
|
261
|
+
[TRANSFORM]: {
|
|
262
|
+
position: {
|
|
263
|
+
x: world.position.x + offset.x,
|
|
264
|
+
y: world.position.y,
|
|
265
|
+
z: world.position.z + offset.z
|
|
266
|
+
},
|
|
267
|
+
rotation: world.rotation,
|
|
268
|
+
scale: authoredScale
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(`[Spawner] '${this.name}' could not make a copy of ${this.pool.prefab}:`, error)
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
if (spawned === null) {
|
|
276
|
+
console.log(`[Spawner] '${this.name}': the prefab's Max alive is reached — no copy until one goes`)
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
this.spawnedCount++
|
|
280
|
+
this.live.set(spawned, this.disappearsAfter > 0 ? this.disappearsAfter : Infinity)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Let go of copies something else already released (retire, round reset). */
|
|
284
|
+
private dropReleased(): void {
|
|
285
|
+
const pool = this.pool
|
|
286
|
+
if (pool === null) return
|
|
287
|
+
for (const copy of [...this.live.keys()]) {
|
|
288
|
+
if (pool.instanceIdOf(copy) === null) this.live.delete(copy)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private expire(dt: number): void {
|
|
293
|
+
if (this.pool === null || this.live.size === 0) return
|
|
294
|
+
this.dropReleased()
|
|
295
|
+
for (const [copy, left] of [...this.live]) {
|
|
296
|
+
if (left === Infinity) continue
|
|
297
|
+
const remaining = left - dt
|
|
298
|
+
if (remaining > 0) {
|
|
299
|
+
this.live.set(copy, remaining)
|
|
300
|
+
continue
|
|
301
|
+
}
|
|
302
|
+
this.live.delete(copy)
|
|
303
|
+
this.pool.release(copy)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Where a copy lands, by the `where` setting. Every branch that cannot answer
|
|
309
|
+
* (avatar not loaded yet, marker deleted) falls back to the spawner's own spot
|
|
310
|
+
* — a trigger that fired must always produce a copy somewhere sensible.
|
|
311
|
+
*/
|
|
312
|
+
private spawnBase(): WorldTransform {
|
|
313
|
+
if (this.where === 'at the player') {
|
|
314
|
+
// Both come off the avatar's own Transform — localPlayerPosition converts
|
|
315
|
+
// it to the scene's frame — so they are there together or not at all.
|
|
316
|
+
const avatar = Transform.getOrNull(engine.PlayerEntity)
|
|
317
|
+
const position = localPlayerPosition()
|
|
318
|
+
if (avatar !== null && position !== null) {
|
|
319
|
+
return { position, rotation: avatar.rotation, scale: { x: 1, y: 1, z: 1 } }
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// The marker may have been removed mid-play by a cleanup script — a spawn
|
|
323
|
+
// must land at the spawner then, never at whatever id 0 composes to.
|
|
324
|
+
if (this.where === 'custom spot' && this.customSpot !== null && Transform.getOrNull(this.customSpot) !== null) {
|
|
325
|
+
return worldOf(this.customSpot)
|
|
326
|
+
}
|
|
327
|
+
return worldOf(this.entity)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** An entity's composed world transform, walking parents to the scene root. */
|
|
332
|
+
function worldOf(entity: Entity): WorldTransform {
|
|
333
|
+
const chain: LocalTransform[] = []
|
|
334
|
+
const seen = new Set<number>()
|
|
335
|
+
let current: Entity | null = entity
|
|
336
|
+
for (let depth = 0; current !== null && depth < PARENT_DEPTH_MAX; depth++) {
|
|
337
|
+
const id = Number(current)
|
|
338
|
+
if (id === 0 || seen.has(id)) break
|
|
339
|
+
seen.add(id)
|
|
340
|
+
const local = Transform.getOrNull(current)
|
|
341
|
+
chain.push(localOf(local))
|
|
342
|
+
current = parentOf(local)
|
|
343
|
+
}
|
|
344
|
+
chain.reverse()
|
|
345
|
+
return composeWorld(chain)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** The parent to walk to next, or null at the scene root. */
|
|
349
|
+
function parentOf(local: { parent?: Entity } | null): Entity | null {
|
|
350
|
+
const parent = local?.parent
|
|
351
|
+
return parent === undefined || Number(parent) === 0 ? null : parent
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** Read a Transform — a live component or a snapshot's JSON — with identity for anything it does not say. */
|
|
355
|
+
function localOf(value: unknown): LocalTransform {
|
|
356
|
+
const source: Record<string, unknown> = isRecord(value) ? value : {}
|
|
357
|
+
const position = isRecord(source.position) ? source.position : {}
|
|
358
|
+
const rotation = isRecord(source.rotation) ? source.rotation : {}
|
|
359
|
+
const scale = isRecord(source.scale) ? source.scale : {}
|
|
360
|
+
return {
|
|
361
|
+
position: { x: numberOf(position.x, 0), y: numberOf(position.y, 0), z: numberOf(position.z, 0) },
|
|
362
|
+
rotation: {
|
|
363
|
+
x: numberOf(rotation.x, 0),
|
|
364
|
+
y: numberOf(rotation.y, 0),
|
|
365
|
+
z: numberOf(rotation.z, 0),
|
|
366
|
+
w: numberOf(rotation.w, 1)
|
|
367
|
+
},
|
|
368
|
+
scale: { x: numberOf(scale.x, 1), y: numberOf(scale.y, 1), z: numberOf(scale.z, 1) },
|
|
369
|
+
// worldOf walks the chain itself; composeWorld never reads this.
|
|
370
|
+
parent: 0
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
375
|
+
return typeof value === 'object' && value !== null
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function numberOf(value: unknown, fallback: number): number {
|
|
379
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback
|
|
380
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"components": [
|
|
4
|
+
{
|
|
5
|
+
"name": "core::GltfContainer",
|
|
6
|
+
"data": {
|
|
7
|
+
"0": {
|
|
8
|
+
"json": {
|
|
9
|
+
"src": "{assetPath}/Steampunk Bench.glb",
|
|
10
|
+
"visibleMeshesCollisionMask": 0,
|
|
11
|
+
"invisibleMeshesCollisionMask": 3
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "core-schema::Name",
|
|
18
|
+
"data": {
|
|
19
|
+
"0": {
|
|
20
|
+
"json": {
|
|
21
|
+
"value": "Steampunk Bench"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"513": {
|
|
25
|
+
"json": {
|
|
26
|
+
"value": "Sit Spot 1"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"514": {
|
|
30
|
+
"json": {
|
|
31
|
+
"value": "Sit Spot 2"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "core::Transform",
|
|
38
|
+
"data": {
|
|
39
|
+
"513": {
|
|
40
|
+
"json": {
|
|
41
|
+
"position": {
|
|
42
|
+
"x": 0.47,
|
|
43
|
+
"y": 0.12,
|
|
44
|
+
"z": 0.28
|
|
45
|
+
},
|
|
46
|
+
"rotation": {
|
|
47
|
+
"x": 0,
|
|
48
|
+
"y": 0.07845909893512726,
|
|
49
|
+
"z": 0,
|
|
50
|
+
"w": 0.9969173073768616
|
|
51
|
+
},
|
|
52
|
+
"scale": {
|
|
53
|
+
"x": 1,
|
|
54
|
+
"y": 1,
|
|
55
|
+
"z": 1
|
|
56
|
+
},
|
|
57
|
+
"parent": 0
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"514": {
|
|
61
|
+
"json": {
|
|
62
|
+
"position": {
|
|
63
|
+
"x": -0.4,
|
|
64
|
+
"y": 0.1182,
|
|
65
|
+
"z": 0.28
|
|
66
|
+
},
|
|
67
|
+
"rotation": {
|
|
68
|
+
"x": 0,
|
|
69
|
+
"y": 0.07845906913280487,
|
|
70
|
+
"z": 0,
|
|
71
|
+
"w": 0.9969173669815063
|
|
72
|
+
},
|
|
73
|
+
"scale": {
|
|
74
|
+
"x": 1,
|
|
75
|
+
"y": 1,
|
|
76
|
+
"z": 1
|
|
77
|
+
},
|
|
78
|
+
"parent": 0
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "asset-packs::Script",
|
|
85
|
+
"data": {
|
|
86
|
+
"0": {
|
|
87
|
+
"json": {
|
|
88
|
+
"value": [
|
|
89
|
+
{
|
|
90
|
+
"path": "{assetPath}/scripts/seat.ts",
|
|
91
|
+
"priority": 0,
|
|
92
|
+
"layout": "{\"params\":{},\"actions\":[]}"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "8a80b964-1f4d-4ea3-8f6b-078809891a17",
|
|
3
|
+
"name": "Steampunk Bench",
|
|
4
|
+
"description": "A riveted steampunk bench, seating two. Players can sit on it.",
|
|
5
|
+
"category": "custom",
|
|
6
|
+
"tags": [
|
|
7
|
+
"seat",
|
|
8
|
+
"furniture",
|
|
9
|
+
"sit"
|
|
10
|
+
],
|
|
11
|
+
"version": "1.1.0",
|
|
12
|
+
"changelog": [
|
|
13
|
+
{
|
|
14
|
+
"version": "1.1.0",
|
|
15
|
+
"notes": "The seat no longer runs on the Multiplayer Server, where it was walking every sit spot's transform chain each frame for nobody to see."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"version": "1.0.0",
|
|
19
|
+
"notes": "Initial release"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"origin": {
|
|
23
|
+
"source": "builtin"
|
|
24
|
+
},
|
|
25
|
+
"requiredPermissions": [
|
|
26
|
+
"ALLOW_TO_MOVE_PLAYER_INSIDE_SCENE",
|
|
27
|
+
"ALLOW_TO_TRIGGER_AVATAR_EMOTE"
|
|
28
|
+
],
|
|
29
|
+
"group": "Seats"
|
|
30
|
+
}
|