@zylem/game-lib 0.4.1 → 0.5.1
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/README.md +23 -41
- package/dist/actions.d.ts +25 -0
- package/dist/actions.js +287 -8
- package/dist/actions.js.map +1 -0
- package/dist/behaviors.d.ts +108 -0
- package/dist/behaviors.js +396 -6
- package/dist/behaviors.js.map +1 -0
- package/dist/{lib/camera/zylem-camera.d.ts → camera-Dk-fOVZE.d.ts} +55 -8
- package/dist/camera.d.ts +5 -0
- package/dist/camera.js +504 -4
- package/dist/camera.js.map +1 -0
- package/dist/core-C2mjetAd.d.ts +431 -0
- package/dist/core.d.ts +9 -0
- package/dist/core.js +4780 -9
- package/dist/core.js.map +1 -0
- package/dist/entities.d.ts +269 -0
- package/dist/entities.js +2161 -17
- package/dist/entities.js.map +1 -0
- package/dist/entity-bQElAdpo.d.ts +347 -0
- package/dist/entity-spawner-DNnLYnZq.d.ts +11 -0
- package/dist/main.d.ts +135 -0
- package/dist/main.js +6599 -56
- package/dist/main.js.map +1 -0
- package/dist/moveable-B_vyA6cw.d.ts +67 -0
- package/dist/stage-CrmY7V0i.d.ts +287 -0
- package/dist/stage.d.ts +26 -0
- package/dist/stage.js +2229 -15
- package/dist/stage.js.map +1 -0
- package/dist/transformable-CUhvyuYO.d.ts +67 -0
- package/package.json +12 -42
- package/LICENSE +0 -21
- package/dist/.vite/manifest.json +0 -676
- package/dist/api/actions.d.ts +0 -5
- package/dist/api/actions.d.ts.map +0 -1
- package/dist/api/behaviors.d.ts +0 -4
- package/dist/api/behaviors.d.ts.map +0 -1
- package/dist/api/camera.d.ts +0 -4
- package/dist/api/camera.d.ts.map +0 -1
- package/dist/api/core.d.ts +0 -6
- package/dist/api/core.d.ts.map +0 -1
- package/dist/api/entities.d.ts +0 -9
- package/dist/api/entities.d.ts.map +0 -1
- package/dist/api/main.d.ts +0 -32
- package/dist/api/main.d.ts.map +0 -1
- package/dist/api/stage.d.ts +0 -6
- package/dist/api/stage.d.ts.map +0 -1
- package/dist/assets/zylem-logo.png +0 -0
- package/dist/lib/actions/behaviors/actions.d.ts +0 -11
- package/dist/lib/actions/behaviors/actions.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/actions.js +0 -35
- package/dist/lib/actions/behaviors/behavior.d.ts +0 -6
- package/dist/lib/actions/behaviors/behavior.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/boundaries/boundary.d.ts +0 -37
- package/dist/lib/actions/behaviors/boundaries/boundary.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/boundaries/boundary.js +0 -40
- package/dist/lib/actions/behaviors/character-controller.d.ts +0 -6
- package/dist/lib/actions/behaviors/character-controller.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/debug/debug-collision.d.ts +0 -6
- package/dist/lib/actions/behaviors/debug/debug-collision.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/debug/debug-update.d.ts +0 -5
- package/dist/lib/actions/behaviors/debug/debug-update.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/debug/debug.d.ts +0 -10
- package/dist/lib/actions/behaviors/debug/debug.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/movement/movement-sequence-2d.d.ts +0 -24
- package/dist/lib/actions/behaviors/movement/movement-sequence-2d.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/ricochet/ricochet-2d-collision.d.ts +0 -11
- package/dist/lib/actions/behaviors/ricochet/ricochet-2d-collision.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/ricochet/ricochet-2d-collision.js +0 -100
- package/dist/lib/actions/behaviors/ricochet/ricochet-2d-in-bounds.d.ts +0 -12
- package/dist/lib/actions/behaviors/ricochet/ricochet-2d-in-bounds.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/ricochet/ricochet-2d-in-bounds.js +0 -37
- package/dist/lib/actions/behaviors/ricochet/ricochet.d.ts +0 -44
- package/dist/lib/actions/behaviors/ricochet/ricochet.d.ts.map +0 -1
- package/dist/lib/actions/behaviors/ricochet/ricochet.js +0 -6
- package/dist/lib/actions/behaviors/zylem-behavior.d.ts +0 -2
- package/dist/lib/actions/behaviors/zylem-behavior.d.ts.map +0 -1
- package/dist/lib/actions/capabilities/moveable.d.ts +0 -125
- package/dist/lib/actions/capabilities/moveable.d.ts.map +0 -1
- package/dist/lib/actions/capabilities/moveable.js +0 -108
- package/dist/lib/actions/capabilities/rotatable.d.ts +0 -111
- package/dist/lib/actions/capabilities/rotatable.d.ts.map +0 -1
- package/dist/lib/actions/capabilities/rotatable.js +0 -82
- package/dist/lib/actions/capabilities/transformable.d.ts +0 -7
- package/dist/lib/actions/capabilities/transformable.d.ts.map +0 -1
- package/dist/lib/actions/capabilities/transformable.js +0 -9
- package/dist/lib/actions/global-change.d.ts +0 -23
- package/dist/lib/actions/global-change.d.ts.map +0 -1
- package/dist/lib/actions/global-change.js +0 -40
- package/dist/lib/camera/camera.d.ts +0 -16
- package/dist/lib/camera/camera.d.ts.map +0 -1
- package/dist/lib/camera/camera.js +0 -19
- package/dist/lib/camera/fixed-2d.d.ts +0 -32
- package/dist/lib/camera/fixed-2d.d.ts.map +0 -1
- package/dist/lib/camera/fixed-2d.js +0 -30
- package/dist/lib/camera/perspective.d.ts +0 -9
- package/dist/lib/camera/perspective.d.ts.map +0 -1
- package/dist/lib/camera/perspective.js +0 -10
- package/dist/lib/camera/third-person.d.ts +0 -32
- package/dist/lib/camera/third-person.d.ts.map +0 -1
- package/dist/lib/camera/third-person.js +0 -42
- package/dist/lib/camera/zylem-camera.d.ts.map +0 -1
- package/dist/lib/camera/zylem-camera.js +0 -208
- package/dist/lib/collision/collision-builder.d.ts +0 -17
- package/dist/lib/collision/collision-builder.d.ts.map +0 -1
- package/dist/lib/collision/collision-builder.js +0 -46
- package/dist/lib/collision/collision-delegate.d.ts +0 -6
- package/dist/lib/collision/collision-delegate.d.ts.map +0 -1
- package/dist/lib/collision/collision-delegate.js +0 -6
- package/dist/lib/collision/collision-group.d.ts +0 -11
- package/dist/lib/collision/collision-group.d.ts.map +0 -1
- package/dist/lib/collision/collision-mask.d.ts +0 -18
- package/dist/lib/collision/collision-mask.d.ts.map +0 -1
- package/dist/lib/collision/collision.d.ts +0 -19
- package/dist/lib/collision/collision.d.ts.map +0 -1
- package/dist/lib/collision/utils.d.ts +0 -15
- package/dist/lib/collision/utils.d.ts.map +0 -1
- package/dist/lib/collision/utils.js +0 -24
- package/dist/lib/collision/world.d.ts +0 -23
- package/dist/lib/collision/world.d.ts.map +0 -1
- package/dist/lib/collision/world.js +0 -77
- package/dist/lib/core/asset-manager.d.ts +0 -2
- package/dist/lib/core/asset-manager.d.ts.map +0 -1
- package/dist/lib/core/base-node-life-cycle.d.ts +0 -68
- package/dist/lib/core/base-node-life-cycle.d.ts.map +0 -1
- package/dist/lib/core/base-node.d.ts +0 -37
- package/dist/lib/core/base-node.d.ts.map +0 -1
- package/dist/lib/core/base-node.js +0 -62
- package/dist/lib/core/entity-asset-loader.d.ts +0 -16
- package/dist/lib/core/entity-asset-loader.d.ts.map +0 -1
- package/dist/lib/core/entity-asset-loader.js +0 -54
- package/dist/lib/core/errors.d.ts +0 -5
- package/dist/lib/core/errors.d.ts.map +0 -1
- package/dist/lib/core/flags.d.ts +0 -3
- package/dist/lib/core/flags.d.ts.map +0 -1
- package/dist/lib/core/index.d.ts +0 -5
- package/dist/lib/core/index.d.ts.map +0 -1
- package/dist/lib/core/lazy-loader.d.ts +0 -80
- package/dist/lib/core/lazy-loader.d.ts.map +0 -1
- package/dist/lib/core/lifecycle-base.d.ts +0 -17
- package/dist/lib/core/lifecycle-base.d.ts.map +0 -1
- package/dist/lib/core/lifecycle-base.js +0 -20
- package/dist/lib/core/preset-shader.d.ts +0 -8
- package/dist/lib/core/preset-shader.d.ts.map +0 -1
- package/dist/lib/core/preset-shader.js +0 -26
- package/dist/lib/core/three-addons/Timer.d.ts +0 -92
- package/dist/lib/core/three-addons/Timer.d.ts.map +0 -1
- package/dist/lib/core/three-addons/Timer.js +0 -103
- package/dist/lib/core/utility/nodes.d.ts +0 -13
- package/dist/lib/core/utility/nodes.d.ts.map +0 -1
- package/dist/lib/core/utility/nodes.js +0 -31
- package/dist/lib/core/utility/strings.d.ts +0 -3
- package/dist/lib/core/utility/strings.d.ts.map +0 -1
- package/dist/lib/core/utility/strings.js +0 -14
- package/dist/lib/core/utility/vector.d.ts +0 -8
- package/dist/lib/core/utility/vector.d.ts.map +0 -1
- package/dist/lib/core/utility/vector.js +0 -8
- package/dist/lib/core/vector.d.ts +0 -4
- package/dist/lib/core/vector.d.ts.map +0 -1
- package/dist/lib/core/vessel.d.ts +0 -13
- package/dist/lib/core/vessel.d.ts.map +0 -1
- package/dist/lib/core/vessel.js +0 -26
- package/dist/lib/debug/console/console-state.d.ts +0 -20
- package/dist/lib/debug/console/console-state.d.ts.map +0 -1
- package/dist/lib/debug/console/console-state.js +0 -11
- package/dist/lib/debug/console/console-store.d.ts +0 -6
- package/dist/lib/debug/console/console-store.d.ts.map +0 -1
- package/dist/lib/debug/debug-state.d.ts +0 -38
- package/dist/lib/debug/debug-state.d.ts.map +0 -1
- package/dist/lib/debug/debug-state.js +0 -40
- package/dist/lib/debug/debug-store.d.ts +0 -15
- package/dist/lib/debug/debug-store.d.ts.map +0 -1
- package/dist/lib/debug/state-based-debug-loader.d.ts +0 -8
- package/dist/lib/debug/state-based-debug-loader.d.ts.map +0 -1
- package/dist/lib/device/aspect-ratio.d.ts +0 -38
- package/dist/lib/device/aspect-ratio.d.ts.map +0 -1
- package/dist/lib/device/aspect-ratio.js +0 -44
- package/dist/lib/device/desktop.d.ts +0 -2
- package/dist/lib/device/desktop.d.ts.map +0 -1
- package/dist/lib/device/mobile.d.ts +0 -2
- package/dist/lib/device/mobile.d.ts.map +0 -1
- package/dist/lib/device/tablet.d.ts +0 -2
- package/dist/lib/device/tablet.d.ts.map +0 -1
- package/dist/lib/entities/actor.d.ts +0 -44
- package/dist/lib/entities/actor.d.ts.map +0 -1
- package/dist/lib/entities/actor.js +0 -122
- package/dist/lib/entities/box.d.ts +0 -27
- package/dist/lib/entities/box.d.ts.map +0 -1
- package/dist/lib/entities/box.js +0 -68
- package/dist/lib/entities/builder.d.ts +0 -28
- package/dist/lib/entities/builder.d.ts.map +0 -1
- package/dist/lib/entities/builder.js +0 -79
- package/dist/lib/entities/create.d.ts +0 -15
- package/dist/lib/entities/create.d.ts.map +0 -1
- package/dist/lib/entities/create.js +0 -31
- package/dist/lib/entities/delegates/animation.d.ts +0 -33
- package/dist/lib/entities/delegates/animation.d.ts.map +0 -1
- package/dist/lib/entities/delegates/animation.js +0 -58
- package/dist/lib/entities/delegates/debug.d.ts +0 -29
- package/dist/lib/entities/delegates/debug.d.ts.map +0 -1
- package/dist/lib/entities/delegates/debug.js +0 -71
- package/dist/lib/entities/delegates/loader.d.ts +0 -12
- package/dist/lib/entities/delegates/loader.d.ts.map +0 -1
- package/dist/lib/entities/delegates/loader.js +0 -19
- package/dist/lib/entities/destroy.d.ts +0 -4
- package/dist/lib/entities/destroy.d.ts.map +0 -1
- package/dist/lib/entities/destroy.js +0 -15
- package/dist/lib/entities/entity.d.ts +0 -96
- package/dist/lib/entities/entity.d.ts.map +0 -1
- package/dist/lib/entities/entity.js +0 -120
- package/dist/lib/entities/index.d.ts +0 -12
- package/dist/lib/entities/index.d.ts.map +0 -1
- package/dist/lib/entities/plane.d.ts +0 -36
- package/dist/lib/entities/plane.d.ts.map +0 -1
- package/dist/lib/entities/plane.js +0 -81
- package/dist/lib/entities/rect.d.ts +0 -63
- package/dist/lib/entities/rect.d.ts.map +0 -1
- package/dist/lib/entities/rect.js +0 -160
- package/dist/lib/entities/sphere.d.ts +0 -29
- package/dist/lib/entities/sphere.d.ts.map +0 -1
- package/dist/lib/entities/sphere.js +0 -68
- package/dist/lib/entities/sprite.d.ts +0 -53
- package/dist/lib/entities/sprite.d.ts.map +0 -1
- package/dist/lib/entities/sprite.js +0 -118
- package/dist/lib/entities/text.d.ts +0 -49
- package/dist/lib/entities/text.d.ts.map +0 -1
- package/dist/lib/entities/text.js +0 -140
- package/dist/lib/entities/zone.d.ts +0 -54
- package/dist/lib/entities/zone.d.ts.map +0 -1
- package/dist/lib/entities/zone.js +0 -103
- package/dist/lib/game/game-blueprint.d.ts +0 -38
- package/dist/lib/game/game-blueprint.d.ts.map +0 -1
- package/dist/lib/game/game-canvas.d.ts +0 -35
- package/dist/lib/game/game-canvas.d.ts.map +0 -1
- package/dist/lib/game/game-canvas.js +0 -57
- package/dist/lib/game/game-config.d.ts +0 -59
- package/dist/lib/game/game-config.d.ts.map +0 -1
- package/dist/lib/game/game-config.js +0 -78
- package/dist/lib/game/game-default.d.ts +0 -14
- package/dist/lib/game/game-default.d.ts.map +0 -1
- package/dist/lib/game/game-default.js +0 -23
- package/dist/lib/game/game-interfaces.d.ts +0 -29
- package/dist/lib/game/game-interfaces.d.ts.map +0 -1
- package/dist/lib/game/game-retro-resolutions.d.ts +0 -25
- package/dist/lib/game/game-retro-resolutions.d.ts.map +0 -1
- package/dist/lib/game/game-retro-resolutions.js +0 -80
- package/dist/lib/game/game-state.d.ts +0 -11
- package/dist/lib/game/game-state.d.ts.map +0 -1
- package/dist/lib/game/game-state.js +0 -17
- package/dist/lib/game/game.d.ts +0 -37
- package/dist/lib/game/game.d.ts.map +0 -1
- package/dist/lib/game/game.js +0 -109
- package/dist/lib/game/zylem-game.d.ts +0 -58
- package/dist/lib/game/zylem-game.d.ts.map +0 -1
- package/dist/lib/game/zylem-game.js +0 -140
- package/dist/lib/graphics/geometries/XZPlaneGeometry.d.ts +0 -8
- package/dist/lib/graphics/geometries/XZPlaneGeometry.d.ts.map +0 -1
- package/dist/lib/graphics/geometries/XZPlaneGeometry.js +0 -34
- package/dist/lib/graphics/material.d.ts +0 -29
- package/dist/lib/graphics/material.d.ts.map +0 -1
- package/dist/lib/graphics/material.js +0 -64
- package/dist/lib/graphics/mesh.d.ts +0 -19
- package/dist/lib/graphics/mesh.d.ts.map +0 -1
- package/dist/lib/graphics/mesh.js +0 -14
- package/dist/lib/graphics/render-pass.d.ts +0 -17
- package/dist/lib/graphics/render-pass.d.ts.map +0 -1
- package/dist/lib/graphics/render-pass.js +0 -56
- package/dist/lib/graphics/shaders/fragment/debug.glsl.js +0 -23
- package/dist/lib/graphics/shaders/fragment/fire.glsl.js +0 -52
- package/dist/lib/graphics/shaders/fragment/standard.glsl.js +0 -11
- package/dist/lib/graphics/shaders/fragment/stars.glsl.js +0 -44
- package/dist/lib/graphics/shaders/vertex/debug.glsl.js +0 -15
- package/dist/lib/graphics/shaders/vertex/object-shader.glsl.js +0 -11
- package/dist/lib/graphics/shaders/vertex/standard.glsl.js +0 -9
- package/dist/lib/graphics/zylem-scene.d.ts +0 -50
- package/dist/lib/graphics/zylem-scene.d.ts.map +0 -1
- package/dist/lib/graphics/zylem-scene.js +0 -75
- package/dist/lib/input/gamepad-provider.d.ts +0 -14
- package/dist/lib/input/gamepad-provider.d.ts.map +0 -1
- package/dist/lib/input/gamepad-provider.js +0 -58
- package/dist/lib/input/input-manager.d.ts +0 -15
- package/dist/lib/input/input-manager.d.ts.map +0 -1
- package/dist/lib/input/input-manager.js +0 -70
- package/dist/lib/input/input-provider.d.ts +0 -7
- package/dist/lib/input/input-provider.d.ts.map +0 -1
- package/dist/lib/input/input.d.ts +0 -48
- package/dist/lib/input/input.d.ts.map +0 -1
- package/dist/lib/input/keyboard-provider.d.ts +0 -21
- package/dist/lib/input/keyboard-provider.d.ts.map +0 -1
- package/dist/lib/input/keyboard-provider.js +0 -120
- package/dist/lib/interfaces/entity.d.ts +0 -26
- package/dist/lib/interfaces/entity.d.ts.map +0 -1
- package/dist/lib/sounds/index.d.ts +0 -3
- package/dist/lib/sounds/index.d.ts.map +0 -1
- package/dist/lib/sounds/ping-pong-sound.d.ts +0 -5
- package/dist/lib/sounds/ping-pong-sound.d.ts.map +0 -1
- package/dist/lib/sounds/ricochet-sound.d.ts +0 -5
- package/dist/lib/sounds/ricochet-sound.d.ts.map +0 -1
- package/dist/lib/stage/debug-entity-cursor.d.ts +0 -24
- package/dist/lib/stage/debug-entity-cursor.d.ts.map +0 -1
- package/dist/lib/stage/debug-entity-cursor.js +0 -53
- package/dist/lib/stage/entity-spawner.d.ts +0 -9
- package/dist/lib/stage/entity-spawner.d.ts.map +0 -1
- package/dist/lib/stage/entity-spawner.js +0 -27
- package/dist/lib/stage/stage-blueprint.d.ts +0 -45
- package/dist/lib/stage/stage-blueprint.d.ts.map +0 -1
- package/dist/lib/stage/stage-blueprint.js +0 -56
- package/dist/lib/stage/stage-camera-debug-delegate.d.ts +0 -14
- package/dist/lib/stage/stage-camera-debug-delegate.d.ts.map +0 -1
- package/dist/lib/stage/stage-debug-delegate.d.ts +0 -26
- package/dist/lib/stage/stage-debug-delegate.d.ts.map +0 -1
- package/dist/lib/stage/stage-debug-delegate.js +0 -100
- package/dist/lib/stage/stage-default.d.ts +0 -3
- package/dist/lib/stage/stage-default.d.ts.map +0 -1
- package/dist/lib/stage/stage-default.js +0 -32
- package/dist/lib/stage/stage-state.d.ts +0 -14
- package/dist/lib/stage/stage-state.d.ts.map +0 -1
- package/dist/lib/stage/stage-state.js +0 -36
- package/dist/lib/stage/stage.d.ts +0 -33
- package/dist/lib/stage/stage.d.ts.map +0 -1
- package/dist/lib/stage/stage.js +0 -60
- package/dist/lib/stage/zylem-stage.d.ts +0 -128
- package/dist/lib/stage/zylem-stage.d.ts.map +0 -1
- package/dist/lib/stage/zylem-stage.js +0 -268
- package/dist/lib/systems/test-system.d.ts +0 -2
- package/dist/lib/systems/test-system.d.ts.map +0 -1
- package/dist/lib/systems/transformable.system.d.ts +0 -25
- package/dist/lib/systems/transformable.system.d.ts.map +0 -1
- package/dist/lib/systems/transformable.system.js +0 -43
- package/dist/lib/types/entity-types.d.ts +0 -24
- package/dist/lib/types/entity-types.d.ts.map +0 -1
- package/dist/lib/types/index.d.ts +0 -3
- package/dist/lib/types/index.d.ts.map +0 -1
- package/dist/lib/types/stage-types.d.ts +0 -26
- package/dist/lib/types/stage-types.d.ts.map +0 -1
- package/dist/lib/ui/Debug.d.ts +0 -6
- package/dist/lib/ui/Debug.d.ts.map +0 -1
- package/dist/lib/ui/console/Console.d.ts +0 -7
- package/dist/lib/ui/console/Console.d.ts.map +0 -1
- package/dist/lib/ui/debug-panel/AccordionMenu.d.ts +0 -7
- package/dist/lib/ui/debug-panel/AccordionMenu.d.ts.map +0 -1
- package/dist/lib/ui/debug-panel/Menu.d.ts +0 -6
- package/dist/lib/ui/debug-panel/Menu.d.ts.map +0 -1
- package/dist/lib/ui/debug-panel/sections/EntitiesSection.d.ts +0 -4
- package/dist/lib/ui/debug-panel/sections/EntitiesSection.d.ts.map +0 -1
- package/dist/lib/ui/debug-panel/sections/GameSection.d.ts +0 -3
- package/dist/lib/ui/debug-panel/sections/GameSection.d.ts.map +0 -1
- package/dist/lib/ui/debug-panel/sections/StagesSection.d.ts +0 -3
- package/dist/lib/ui/debug-panel/sections/StagesSection.d.ts.map +0 -1
- package/dist/lib/ui/debug-panel/sections/all.d.ts +0 -4
- package/dist/lib/ui/debug-panel/sections/all.d.ts.map +0 -1
- package/dist/lib/ui/toolbar/Toolbar.d.ts +0 -6
- package/dist/lib/ui/toolbar/Toolbar.d.ts.map +0 -1
- package/dist/tests/integration/debug.sim.spec.d.ts +0 -2
- package/dist/tests/integration/debug.sim.spec.d.ts.map +0 -1
- package/dist/tests/unit/collision/collision.spec.d.ts +0 -2
- package/dist/tests/unit/collision/collision.spec.d.ts.map +0 -1
- package/dist/tests/unit/core/game.spec.d.ts +0 -2
- package/dist/tests/unit/core/game.spec.d.ts.map +0 -1
- package/dist/tests/unit/core/stage.spec.d.ts +0 -2
- package/dist/tests/unit/core/stage.spec.d.ts.map +0 -1
- package/dist/tests/unit/core/vessel.spec.d.ts +0 -2
- package/dist/tests/unit/core/vessel.spec.d.ts.map +0 -1
package/dist/core.js
CHANGED
|
@@ -1,11 +1,4782 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/lib/core/utility/path-utils.ts
|
|
12
|
+
function setByPath(obj, path, value) {
|
|
13
|
+
if (!path) return;
|
|
14
|
+
const keys = path.split(".");
|
|
15
|
+
let current = obj;
|
|
16
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
17
|
+
const key = keys[i];
|
|
18
|
+
if (current[key] == null || typeof current[key] !== "object") {
|
|
19
|
+
current[key] = {};
|
|
20
|
+
}
|
|
21
|
+
current = current[key];
|
|
22
|
+
}
|
|
23
|
+
current[keys[keys.length - 1]] = value;
|
|
24
|
+
}
|
|
25
|
+
var init_path_utils = __esm({
|
|
26
|
+
"src/lib/core/utility/path-utils.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// src/lib/game/game-state.ts
|
|
32
|
+
import { proxy, subscribe } from "valtio/vanilla";
|
|
33
|
+
function setGlobal(path, value) {
|
|
34
|
+
setByPath(state.globals, path, value);
|
|
35
|
+
}
|
|
36
|
+
function getGlobals() {
|
|
37
|
+
return state.globals;
|
|
38
|
+
}
|
|
39
|
+
function initGlobals(globals) {
|
|
40
|
+
for (const [key, value] of Object.entries(globals)) {
|
|
41
|
+
setByPath(state.globals, key, value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function resetGlobals() {
|
|
45
|
+
state.globals = {};
|
|
46
|
+
}
|
|
47
|
+
var state;
|
|
48
|
+
var init_game_state = __esm({
|
|
49
|
+
"src/lib/game/game-state.ts"() {
|
|
50
|
+
"use strict";
|
|
51
|
+
init_path_utils();
|
|
52
|
+
state = proxy({
|
|
53
|
+
id: "",
|
|
54
|
+
globals: {},
|
|
55
|
+
time: 0
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// src/lib/debug/debug-state.ts
|
|
61
|
+
import { proxy as proxy2 } from "valtio";
|
|
62
|
+
function isPaused() {
|
|
63
|
+
return debugState.paused;
|
|
64
|
+
}
|
|
65
|
+
function setPaused(paused) {
|
|
66
|
+
debugState.paused = paused;
|
|
67
|
+
}
|
|
68
|
+
function getDebugTool() {
|
|
69
|
+
return debugState.tool;
|
|
70
|
+
}
|
|
71
|
+
function setSelectedEntity(entity) {
|
|
72
|
+
debugState.selectedEntity = entity;
|
|
73
|
+
}
|
|
74
|
+
function getHoveredEntity() {
|
|
75
|
+
return debugState.hoveredEntity;
|
|
76
|
+
}
|
|
77
|
+
function setHoveredEntity(entity) {
|
|
78
|
+
debugState.hoveredEntity = entity;
|
|
79
|
+
}
|
|
80
|
+
function resetHoveredEntity() {
|
|
81
|
+
debugState.hoveredEntity = null;
|
|
82
|
+
}
|
|
83
|
+
var debugState;
|
|
84
|
+
var init_debug_state = __esm({
|
|
85
|
+
"src/lib/debug/debug-state.ts"() {
|
|
86
|
+
"use strict";
|
|
87
|
+
debugState = proxy2({
|
|
88
|
+
enabled: false,
|
|
89
|
+
paused: false,
|
|
90
|
+
tool: "none",
|
|
91
|
+
selectedEntity: null,
|
|
92
|
+
hoveredEntity: null,
|
|
93
|
+
flags: /* @__PURE__ */ new Set()
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// src/lib/core/flags.ts
|
|
99
|
+
var DEBUG_FLAG;
|
|
100
|
+
var init_flags = __esm({
|
|
101
|
+
"src/lib/core/flags.ts"() {
|
|
102
|
+
"use strict";
|
|
103
|
+
DEBUG_FLAG = import.meta.env.VITE_DEBUG_FLAG === "true";
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// src/lib/core/base-node.ts
|
|
108
|
+
import { nanoid } from "nanoid";
|
|
109
|
+
var BaseNode;
|
|
110
|
+
var init_base_node = __esm({
|
|
111
|
+
"src/lib/core/base-node.ts"() {
|
|
112
|
+
"use strict";
|
|
113
|
+
init_flags();
|
|
114
|
+
BaseNode = class _BaseNode {
|
|
115
|
+
parent = null;
|
|
116
|
+
children = [];
|
|
117
|
+
options;
|
|
118
|
+
eid = 0;
|
|
119
|
+
uuid = "";
|
|
120
|
+
name = "";
|
|
121
|
+
markedForRemoval = false;
|
|
122
|
+
setup = () => {
|
|
123
|
+
};
|
|
124
|
+
loaded = () => {
|
|
125
|
+
};
|
|
126
|
+
update = () => {
|
|
127
|
+
};
|
|
128
|
+
destroy = () => {
|
|
129
|
+
};
|
|
130
|
+
cleanup = () => {
|
|
131
|
+
};
|
|
132
|
+
constructor(args = []) {
|
|
133
|
+
const options = args.filter((arg) => !(arg instanceof _BaseNode)).reduce((acc, opt) => ({ ...acc, ...opt }), {});
|
|
134
|
+
this.options = options;
|
|
135
|
+
this.uuid = nanoid();
|
|
136
|
+
}
|
|
137
|
+
setParent(parent) {
|
|
138
|
+
this.parent = parent;
|
|
139
|
+
}
|
|
140
|
+
getParent() {
|
|
141
|
+
return this.parent;
|
|
142
|
+
}
|
|
143
|
+
add(baseNode) {
|
|
144
|
+
this.children.push(baseNode);
|
|
145
|
+
baseNode.setParent(this);
|
|
146
|
+
}
|
|
147
|
+
remove(baseNode) {
|
|
148
|
+
const index = this.children.indexOf(baseNode);
|
|
149
|
+
if (index !== -1) {
|
|
150
|
+
this.children.splice(index, 1);
|
|
151
|
+
baseNode.setParent(null);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
getChildren() {
|
|
155
|
+
return this.children;
|
|
156
|
+
}
|
|
157
|
+
isComposite() {
|
|
158
|
+
return this.children.length > 0;
|
|
159
|
+
}
|
|
160
|
+
nodeSetup(params) {
|
|
161
|
+
if (DEBUG_FLAG) {
|
|
162
|
+
}
|
|
163
|
+
this.markedForRemoval = false;
|
|
164
|
+
if (typeof this._setup === "function") {
|
|
165
|
+
this._setup(params);
|
|
166
|
+
}
|
|
167
|
+
if (this.setup) {
|
|
168
|
+
this.setup(params);
|
|
169
|
+
}
|
|
170
|
+
this.children.forEach((child) => child.nodeSetup(params));
|
|
171
|
+
}
|
|
172
|
+
nodeUpdate(params) {
|
|
173
|
+
if (this.markedForRemoval) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (typeof this._update === "function") {
|
|
177
|
+
this._update(params);
|
|
178
|
+
}
|
|
179
|
+
if (this.update) {
|
|
180
|
+
this.update(params);
|
|
181
|
+
}
|
|
182
|
+
this.children.forEach((child) => child.nodeUpdate(params));
|
|
183
|
+
}
|
|
184
|
+
nodeDestroy(params) {
|
|
185
|
+
this.children.forEach((child) => child.nodeDestroy(params));
|
|
186
|
+
if (this.destroy) {
|
|
187
|
+
this.destroy(params);
|
|
188
|
+
}
|
|
189
|
+
if (typeof this._destroy === "function") {
|
|
190
|
+
this._destroy(params);
|
|
191
|
+
}
|
|
192
|
+
this.markedForRemoval = true;
|
|
193
|
+
}
|
|
194
|
+
getOptions() {
|
|
195
|
+
return this.options;
|
|
196
|
+
}
|
|
197
|
+
setOptions(options) {
|
|
198
|
+
this.options = { ...this.options, ...options };
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// src/lib/systems/transformable.system.ts
|
|
205
|
+
import {
|
|
206
|
+
defineSystem,
|
|
207
|
+
defineQuery,
|
|
208
|
+
defineComponent,
|
|
209
|
+
Types
|
|
210
|
+
} from "bitecs";
|
|
211
|
+
import { Quaternion } from "three";
|
|
212
|
+
function createTransformSystem(stage) {
|
|
213
|
+
const transformQuery = defineQuery([position, rotation]);
|
|
214
|
+
const stageEntities = stage._childrenMap;
|
|
215
|
+
return defineSystem((world) => {
|
|
216
|
+
const entities = transformQuery(world);
|
|
217
|
+
if (stageEntities === void 0) {
|
|
218
|
+
return world;
|
|
219
|
+
}
|
|
220
|
+
;
|
|
221
|
+
for (const [key, value] of stageEntities) {
|
|
222
|
+
const id = entities[key];
|
|
223
|
+
const stageEntity = value;
|
|
224
|
+
if (stageEntity === void 0 || !stageEntity?.body || stageEntity.markedForRemoval) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const { x, y, z } = stageEntity.body.translation();
|
|
228
|
+
position.x[id] = x;
|
|
229
|
+
position.y[id] = y;
|
|
230
|
+
position.z[id] = z;
|
|
231
|
+
if (stageEntity.group) {
|
|
232
|
+
stageEntity.group.position.set(position.x[id], position.y[id], position.z[id]);
|
|
233
|
+
} else if (stageEntity.mesh) {
|
|
234
|
+
stageEntity.mesh.position.set(position.x[id], position.y[id], position.z[id]);
|
|
235
|
+
}
|
|
236
|
+
if (stageEntity.controlledRotation) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const { x: rx, y: ry, z: rz, w: rw } = stageEntity.body.rotation();
|
|
240
|
+
rotation.x[id] = rx;
|
|
241
|
+
rotation.y[id] = ry;
|
|
242
|
+
rotation.z[id] = rz;
|
|
243
|
+
rotation.w[id] = rw;
|
|
244
|
+
const newRotation = new Quaternion(
|
|
245
|
+
rotation.x[id],
|
|
246
|
+
rotation.y[id],
|
|
247
|
+
rotation.z[id],
|
|
248
|
+
rotation.w[id]
|
|
249
|
+
);
|
|
250
|
+
if (stageEntity.group) {
|
|
251
|
+
stageEntity.group.setRotationFromQuaternion(newRotation);
|
|
252
|
+
} else if (stageEntity.mesh) {
|
|
253
|
+
stageEntity.mesh.setRotationFromQuaternion(newRotation);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return world;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
var position, rotation, scale;
|
|
260
|
+
var init_transformable_system = __esm({
|
|
261
|
+
"src/lib/systems/transformable.system.ts"() {
|
|
262
|
+
"use strict";
|
|
263
|
+
position = defineComponent({
|
|
264
|
+
x: Types.f32,
|
|
265
|
+
y: Types.f32,
|
|
266
|
+
z: Types.f32
|
|
267
|
+
});
|
|
268
|
+
rotation = defineComponent({
|
|
269
|
+
x: Types.f32,
|
|
270
|
+
y: Types.f32,
|
|
271
|
+
z: Types.f32,
|
|
272
|
+
w: Types.f32
|
|
273
|
+
});
|
|
274
|
+
scale = defineComponent({
|
|
275
|
+
x: Types.f32,
|
|
276
|
+
y: Types.f32,
|
|
277
|
+
z: Types.f32
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// src/lib/entities/entity.ts
|
|
283
|
+
import { ShaderMaterial } from "three";
|
|
284
|
+
var GameEntity;
|
|
285
|
+
var init_entity = __esm({
|
|
286
|
+
"src/lib/entities/entity.ts"() {
|
|
287
|
+
"use strict";
|
|
288
|
+
init_transformable_system();
|
|
289
|
+
init_base_node();
|
|
290
|
+
GameEntity = class extends BaseNode {
|
|
291
|
+
behaviors = [];
|
|
292
|
+
group;
|
|
293
|
+
mesh;
|
|
294
|
+
materials;
|
|
295
|
+
bodyDesc = null;
|
|
296
|
+
body = null;
|
|
297
|
+
colliderDesc;
|
|
298
|
+
collider;
|
|
299
|
+
custom = {};
|
|
300
|
+
debugInfo = {};
|
|
301
|
+
debugMaterial;
|
|
302
|
+
lifeCycleDelegate = {
|
|
303
|
+
setup: [],
|
|
304
|
+
update: [],
|
|
305
|
+
destroy: []
|
|
306
|
+
};
|
|
307
|
+
collisionDelegate = {
|
|
308
|
+
collision: []
|
|
309
|
+
};
|
|
310
|
+
collisionType;
|
|
311
|
+
behaviorCallbackMap = {
|
|
312
|
+
setup: [],
|
|
313
|
+
update: [],
|
|
314
|
+
destroy: [],
|
|
315
|
+
collision: []
|
|
316
|
+
};
|
|
317
|
+
constructor() {
|
|
318
|
+
super();
|
|
319
|
+
}
|
|
320
|
+
create() {
|
|
321
|
+
const { position: setupPosition } = this.options;
|
|
322
|
+
const { x, y, z } = setupPosition || { x: 0, y: 0, z: 0 };
|
|
323
|
+
this.behaviors = [
|
|
324
|
+
{ component: position, values: { x, y, z } },
|
|
325
|
+
{ component: scale, values: { x: 0, y: 0, z: 0 } },
|
|
326
|
+
{ component: rotation, values: { x: 0, y: 0, z: 0, w: 0 } }
|
|
327
|
+
];
|
|
328
|
+
this.name = this.options.name || "";
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
onSetup(...callbacks) {
|
|
332
|
+
const combineCallbacks = [...this.lifeCycleDelegate.setup ?? [], ...callbacks];
|
|
333
|
+
this.lifeCycleDelegate = {
|
|
334
|
+
...this.lifeCycleDelegate,
|
|
335
|
+
setup: combineCallbacks
|
|
336
|
+
};
|
|
337
|
+
return this;
|
|
338
|
+
}
|
|
339
|
+
onUpdate(...callbacks) {
|
|
340
|
+
const combineCallbacks = [...this.lifeCycleDelegate.update ?? [], ...callbacks];
|
|
341
|
+
this.lifeCycleDelegate = {
|
|
342
|
+
...this.lifeCycleDelegate,
|
|
343
|
+
update: combineCallbacks
|
|
344
|
+
};
|
|
345
|
+
return this;
|
|
346
|
+
}
|
|
347
|
+
onDestroy(...callbacks) {
|
|
348
|
+
this.lifeCycleDelegate = {
|
|
349
|
+
...this.lifeCycleDelegate,
|
|
350
|
+
destroy: callbacks.length > 0 ? callbacks : void 0
|
|
351
|
+
};
|
|
352
|
+
return this;
|
|
353
|
+
}
|
|
354
|
+
onCollision(...callbacks) {
|
|
355
|
+
this.collisionDelegate = {
|
|
356
|
+
collision: callbacks.length > 0 ? callbacks : void 0
|
|
357
|
+
};
|
|
358
|
+
return this;
|
|
359
|
+
}
|
|
360
|
+
_setup(params) {
|
|
361
|
+
this.behaviorCallbackMap.setup.forEach((callback) => {
|
|
362
|
+
callback({ ...params, me: this });
|
|
363
|
+
});
|
|
364
|
+
if (this.lifeCycleDelegate.setup?.length) {
|
|
365
|
+
const callbacks = this.lifeCycleDelegate.setup;
|
|
366
|
+
callbacks.forEach((callback) => {
|
|
367
|
+
callback({ ...params, me: this });
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async _loaded(_params) {
|
|
372
|
+
}
|
|
373
|
+
_update(params) {
|
|
374
|
+
this.updateMaterials(params);
|
|
375
|
+
if (this.lifeCycleDelegate.update?.length) {
|
|
376
|
+
const callbacks = this.lifeCycleDelegate.update;
|
|
377
|
+
callbacks.forEach((callback) => {
|
|
378
|
+
callback({ ...params, me: this });
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
this.behaviorCallbackMap.update.forEach((callback) => {
|
|
382
|
+
callback({ ...params, me: this });
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
_destroy(params) {
|
|
386
|
+
if (this.lifeCycleDelegate.destroy?.length) {
|
|
387
|
+
const callbacks = this.lifeCycleDelegate.destroy;
|
|
388
|
+
callbacks.forEach((callback) => {
|
|
389
|
+
callback({ ...params, me: this });
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
this.behaviorCallbackMap.destroy.forEach((callback) => {
|
|
393
|
+
callback({ ...params, me: this });
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
async _cleanup(_params) {
|
|
397
|
+
}
|
|
398
|
+
_collision(other, globals) {
|
|
399
|
+
if (this.collisionDelegate.collision?.length) {
|
|
400
|
+
const callbacks = this.collisionDelegate.collision;
|
|
401
|
+
callbacks.forEach((callback) => {
|
|
402
|
+
callback({ entity: this, other, globals });
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
this.behaviorCallbackMap.collision.forEach((callback) => {
|
|
406
|
+
callback({ entity: this, other, globals });
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
addBehavior(behaviorCallback) {
|
|
410
|
+
const handler = behaviorCallback.handler;
|
|
411
|
+
if (handler) {
|
|
412
|
+
this.behaviorCallbackMap[behaviorCallback.type].push(handler);
|
|
413
|
+
}
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
addBehaviors(behaviorCallbacks) {
|
|
417
|
+
behaviorCallbacks.forEach((callback) => {
|
|
418
|
+
const handler = callback.handler;
|
|
419
|
+
if (handler) {
|
|
420
|
+
this.behaviorCallbackMap[callback.type].push(handler);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
return this;
|
|
424
|
+
}
|
|
425
|
+
updateMaterials(params) {
|
|
426
|
+
if (!this.materials?.length) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
for (const material of this.materials) {
|
|
430
|
+
if (material instanceof ShaderMaterial) {
|
|
431
|
+
if (material.uniforms) {
|
|
432
|
+
material.uniforms.iTime && (material.uniforms.iTime.value += params.delta);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
buildInfo() {
|
|
438
|
+
const info = {};
|
|
439
|
+
info.name = this.name;
|
|
440
|
+
info.uuid = this.uuid;
|
|
441
|
+
info.eid = this.eid.toString();
|
|
442
|
+
return info;
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// src/lib/entities/delegates/loader.ts
|
|
449
|
+
function isLoadable(obj) {
|
|
450
|
+
return typeof obj?.load === "function" && typeof obj?.data === "function";
|
|
451
|
+
}
|
|
452
|
+
var EntityLoader;
|
|
453
|
+
var init_loader = __esm({
|
|
454
|
+
"src/lib/entities/delegates/loader.ts"() {
|
|
455
|
+
"use strict";
|
|
456
|
+
EntityLoader = class {
|
|
457
|
+
entityReference;
|
|
458
|
+
constructor(entity) {
|
|
459
|
+
this.entityReference = entity;
|
|
460
|
+
}
|
|
461
|
+
async load() {
|
|
462
|
+
if (this.entityReference.load) {
|
|
463
|
+
await this.entityReference.load();
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async data() {
|
|
467
|
+
if (this.entityReference.data) {
|
|
468
|
+
return this.entityReference.data();
|
|
469
|
+
}
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// src/lib/entities/create.ts
|
|
477
|
+
async function createEntity(params) {
|
|
478
|
+
const {
|
|
479
|
+
args,
|
|
480
|
+
defaultConfig,
|
|
481
|
+
EntityClass,
|
|
482
|
+
BuilderClass,
|
|
483
|
+
entityType,
|
|
484
|
+
MeshBuilderClass,
|
|
485
|
+
CollisionBuilderClass
|
|
486
|
+
} = params;
|
|
487
|
+
let builder = null;
|
|
488
|
+
let configuration;
|
|
489
|
+
const configurationIndex = args.findIndex((node) => !(node instanceof BaseNode));
|
|
490
|
+
if (configurationIndex !== -1) {
|
|
491
|
+
const subArgs = args.splice(configurationIndex, 1);
|
|
492
|
+
configuration = subArgs.find((node) => !(node instanceof BaseNode));
|
|
493
|
+
}
|
|
494
|
+
const mergedConfiguration = configuration ? { ...defaultConfig, ...configuration } : defaultConfig;
|
|
495
|
+
args.push(mergedConfiguration);
|
|
496
|
+
for (const arg of args) {
|
|
497
|
+
if (arg instanceof BaseNode) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
let entityData = null;
|
|
501
|
+
const entity = new EntityClass(arg);
|
|
502
|
+
try {
|
|
503
|
+
if (isLoadable(entity)) {
|
|
504
|
+
const loader = new EntityLoader(entity);
|
|
505
|
+
await loader.load();
|
|
506
|
+
entityData = await loader.data();
|
|
507
|
+
}
|
|
508
|
+
} catch (error) {
|
|
509
|
+
console.error("Error creating entity with loader:", error);
|
|
510
|
+
}
|
|
511
|
+
builder = new BuilderClass(
|
|
512
|
+
arg,
|
|
513
|
+
entity,
|
|
514
|
+
MeshBuilderClass ? new MeshBuilderClass(entityData) : null,
|
|
515
|
+
CollisionBuilderClass ? new CollisionBuilderClass(entityData) : null
|
|
516
|
+
);
|
|
517
|
+
if (arg.material) {
|
|
518
|
+
await builder.withMaterial(arg.material, entityType);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (!builder) {
|
|
522
|
+
throw new Error(`missing options for ${String(entityType)}, builder is not initialized.`);
|
|
523
|
+
}
|
|
524
|
+
return await builder.build();
|
|
525
|
+
}
|
|
526
|
+
var init_create = __esm({
|
|
527
|
+
"src/lib/entities/create.ts"() {
|
|
528
|
+
"use strict";
|
|
529
|
+
init_base_node();
|
|
530
|
+
init_loader();
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// src/lib/core/entity-asset-loader.ts
|
|
535
|
+
import { FBXLoader } from "three/addons/loaders/FBXLoader.js";
|
|
536
|
+
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
|
|
537
|
+
var FBXAssetLoader, GLTFAssetLoader, EntityAssetLoader;
|
|
538
|
+
var init_entity_asset_loader = __esm({
|
|
539
|
+
"src/lib/core/entity-asset-loader.ts"() {
|
|
540
|
+
"use strict";
|
|
541
|
+
FBXAssetLoader = class {
|
|
542
|
+
loader = new FBXLoader();
|
|
543
|
+
isSupported(file) {
|
|
544
|
+
return file.toLowerCase().endsWith("fbx" /* FBX */);
|
|
545
|
+
}
|
|
546
|
+
async load(file) {
|
|
547
|
+
return new Promise((resolve, reject) => {
|
|
548
|
+
this.loader.load(
|
|
549
|
+
file,
|
|
550
|
+
(object) => {
|
|
551
|
+
const animation = object.animations[0];
|
|
552
|
+
resolve({
|
|
553
|
+
object,
|
|
554
|
+
animation
|
|
555
|
+
});
|
|
556
|
+
},
|
|
557
|
+
void 0,
|
|
558
|
+
reject
|
|
559
|
+
);
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
GLTFAssetLoader = class {
|
|
564
|
+
loader = new GLTFLoader();
|
|
565
|
+
isSupported(file) {
|
|
566
|
+
return file.toLowerCase().endsWith("gltf" /* GLTF */);
|
|
567
|
+
}
|
|
568
|
+
async load(file) {
|
|
569
|
+
return new Promise((resolve, reject) => {
|
|
570
|
+
this.loader.load(
|
|
571
|
+
file,
|
|
572
|
+
(gltf) => {
|
|
573
|
+
resolve({
|
|
574
|
+
object: gltf.scene,
|
|
575
|
+
gltf
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
void 0,
|
|
579
|
+
reject
|
|
580
|
+
);
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
EntityAssetLoader = class {
|
|
585
|
+
loaders = [
|
|
586
|
+
new FBXAssetLoader(),
|
|
587
|
+
new GLTFAssetLoader()
|
|
588
|
+
];
|
|
589
|
+
async loadFile(file) {
|
|
590
|
+
const loader = this.loaders.find((l) => l.isSupported(file));
|
|
591
|
+
if (!loader) {
|
|
592
|
+
throw new Error(`Unsupported file type: ${file}`);
|
|
593
|
+
}
|
|
594
|
+
return loader.load(file);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// src/lib/entities/delegates/animation.ts
|
|
601
|
+
import {
|
|
602
|
+
AnimationMixer,
|
|
603
|
+
LoopOnce,
|
|
604
|
+
LoopRepeat
|
|
605
|
+
} from "three";
|
|
606
|
+
var AnimationDelegate;
|
|
607
|
+
var init_animation = __esm({
|
|
608
|
+
"src/lib/entities/delegates/animation.ts"() {
|
|
609
|
+
"use strict";
|
|
610
|
+
init_entity_asset_loader();
|
|
611
|
+
AnimationDelegate = class {
|
|
612
|
+
constructor(target) {
|
|
613
|
+
this.target = target;
|
|
614
|
+
}
|
|
615
|
+
_mixer = null;
|
|
616
|
+
_actions = {};
|
|
617
|
+
_animations = [];
|
|
618
|
+
_currentAction = null;
|
|
619
|
+
_pauseAtPercentage = 0;
|
|
620
|
+
_isPaused = false;
|
|
621
|
+
_queuedKey = null;
|
|
622
|
+
_fadeDuration = 0.5;
|
|
623
|
+
_currentKey = "";
|
|
624
|
+
_assetLoader = new EntityAssetLoader();
|
|
625
|
+
async loadAnimations(animations) {
|
|
626
|
+
if (!animations.length) return;
|
|
627
|
+
const results = await Promise.all(animations.map((a) => this._assetLoader.loadFile(a.path)));
|
|
628
|
+
this._animations = results.filter((r) => !!r.animation).map((r) => r.animation);
|
|
629
|
+
if (!this._animations.length) return;
|
|
630
|
+
this._mixer = new AnimationMixer(this.target);
|
|
631
|
+
this._animations.forEach((clip, i) => {
|
|
632
|
+
const key = animations[i].key || i.toString();
|
|
633
|
+
this._actions[key] = this._mixer.clipAction(clip);
|
|
634
|
+
});
|
|
635
|
+
this.playAnimation({ key: Object.keys(this._actions)[0] });
|
|
636
|
+
}
|
|
637
|
+
update(delta) {
|
|
638
|
+
if (!this._mixer || !this._currentAction) return;
|
|
639
|
+
this._mixer.update(delta);
|
|
640
|
+
const pauseAtTime = this._currentAction.getClip().duration * (this._pauseAtPercentage / 100);
|
|
641
|
+
if (!this._isPaused && this._pauseAtPercentage > 0 && this._currentAction.time >= pauseAtTime) {
|
|
642
|
+
this._currentAction.time = pauseAtTime;
|
|
643
|
+
this._currentAction.paused = true;
|
|
644
|
+
this._isPaused = true;
|
|
645
|
+
if (this._queuedKey !== null) {
|
|
646
|
+
const next = this._actions[this._queuedKey];
|
|
647
|
+
next.reset().play();
|
|
648
|
+
this._currentAction.crossFadeTo(next, this._fadeDuration, false);
|
|
649
|
+
this._currentAction = next;
|
|
650
|
+
this._currentKey = this._queuedKey;
|
|
651
|
+
this._queuedKey = null;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
playAnimation(opts) {
|
|
656
|
+
if (!this._mixer) return;
|
|
657
|
+
const { key, pauseAtPercentage = 0, pauseAtEnd = false, fadeToKey, fadeDuration = 0.5 } = opts;
|
|
658
|
+
if (key === this._currentKey) return;
|
|
659
|
+
this._queuedKey = fadeToKey || null;
|
|
660
|
+
this._fadeDuration = fadeDuration;
|
|
661
|
+
this._pauseAtPercentage = pauseAtEnd ? 100 : pauseAtPercentage;
|
|
662
|
+
this._isPaused = false;
|
|
663
|
+
const prev = this._currentAction;
|
|
664
|
+
if (prev) prev.stop();
|
|
665
|
+
const action = this._actions[key];
|
|
666
|
+
if (!action) return;
|
|
667
|
+
if (this._pauseAtPercentage > 0) {
|
|
668
|
+
action.setLoop(LoopOnce, Infinity);
|
|
669
|
+
action.clampWhenFinished = true;
|
|
670
|
+
} else {
|
|
671
|
+
action.setLoop(LoopRepeat, Infinity);
|
|
672
|
+
action.clampWhenFinished = false;
|
|
673
|
+
}
|
|
674
|
+
if (prev) {
|
|
675
|
+
prev.crossFadeTo(action, fadeDuration, false);
|
|
676
|
+
}
|
|
677
|
+
action.reset().play();
|
|
678
|
+
this._currentAction = action;
|
|
679
|
+
this._currentKey = key;
|
|
680
|
+
}
|
|
681
|
+
get currentAnimationKey() {
|
|
682
|
+
return this._currentKey;
|
|
683
|
+
}
|
|
684
|
+
get animations() {
|
|
685
|
+
return this._animations;
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// src/lib/collision/collision-builder.ts
|
|
692
|
+
import { ActiveCollisionTypes, ColliderDesc, RigidBodyDesc, RigidBodyType, Vector3 } from "@dimforge/rapier3d-compat";
|
|
693
|
+
function getOrCreateCollisionGroupId(type) {
|
|
694
|
+
let groupId = typeToGroup.get(type);
|
|
695
|
+
if (groupId === void 0) {
|
|
696
|
+
groupId = nextGroupId++ % 16;
|
|
697
|
+
typeToGroup.set(type, groupId);
|
|
698
|
+
}
|
|
699
|
+
return groupId;
|
|
700
|
+
}
|
|
701
|
+
function createCollisionFilter(allowedTypes) {
|
|
702
|
+
let filter = 0;
|
|
703
|
+
allowedTypes.forEach((type) => {
|
|
704
|
+
const groupId = getOrCreateCollisionGroupId(type);
|
|
705
|
+
filter |= 1 << groupId;
|
|
706
|
+
});
|
|
707
|
+
return filter;
|
|
708
|
+
}
|
|
709
|
+
var typeToGroup, nextGroupId, CollisionBuilder;
|
|
710
|
+
var init_collision_builder = __esm({
|
|
711
|
+
"src/lib/collision/collision-builder.ts"() {
|
|
712
|
+
"use strict";
|
|
713
|
+
typeToGroup = /* @__PURE__ */ new Map();
|
|
714
|
+
nextGroupId = 0;
|
|
715
|
+
CollisionBuilder = class {
|
|
716
|
+
static = false;
|
|
717
|
+
sensor = false;
|
|
718
|
+
gravity = new Vector3(0, 0, 0);
|
|
719
|
+
build(options) {
|
|
720
|
+
const bodyDesc = this.bodyDesc({
|
|
721
|
+
isDynamicBody: !this.static
|
|
722
|
+
});
|
|
723
|
+
const collider = this.collider(options);
|
|
724
|
+
const type = options.collisionType;
|
|
725
|
+
if (type) {
|
|
726
|
+
let groupId = getOrCreateCollisionGroupId(type);
|
|
727
|
+
let filter = 65535;
|
|
728
|
+
if (options.collisionFilter) {
|
|
729
|
+
filter = createCollisionFilter(options.collisionFilter);
|
|
730
|
+
}
|
|
731
|
+
collider.setCollisionGroups(groupId << 16 | filter);
|
|
732
|
+
}
|
|
733
|
+
const { KINEMATIC_FIXED, DEFAULT } = ActiveCollisionTypes;
|
|
734
|
+
collider.activeCollisionTypes = this.sensor ? KINEMATIC_FIXED : DEFAULT;
|
|
735
|
+
return [bodyDesc, collider];
|
|
736
|
+
}
|
|
737
|
+
withCollision(collisionOptions) {
|
|
738
|
+
this.sensor = collisionOptions?.sensor ?? this.sensor;
|
|
739
|
+
this.static = collisionOptions?.static ?? this.static;
|
|
740
|
+
return this;
|
|
741
|
+
}
|
|
742
|
+
collider(options) {
|
|
743
|
+
const size = options.size ?? new Vector3(1, 1, 1);
|
|
744
|
+
const half = { x: size.x / 2, y: size.y / 2, z: size.z / 2 };
|
|
745
|
+
let colliderDesc = ColliderDesc.cuboid(half.x, half.y, half.z);
|
|
746
|
+
return colliderDesc;
|
|
747
|
+
}
|
|
748
|
+
bodyDesc({ isDynamicBody = true }) {
|
|
749
|
+
const type = isDynamicBody ? RigidBodyType.Dynamic : RigidBodyType.Fixed;
|
|
750
|
+
const bodyDesc = new RigidBodyDesc(type).setTranslation(0, 0, 0).setGravityScale(1).setCanSleep(false).setCcdEnabled(true);
|
|
751
|
+
return bodyDesc;
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
// src/lib/core/utility/strings.ts
|
|
758
|
+
function sortedStringify(obj) {
|
|
759
|
+
const sortedObj = Object.keys(obj).sort().reduce((acc, key) => {
|
|
760
|
+
acc[key] = obj[key];
|
|
761
|
+
return acc;
|
|
762
|
+
}, {});
|
|
763
|
+
return JSON.stringify(sortedObj);
|
|
764
|
+
}
|
|
765
|
+
function shortHash(objString) {
|
|
766
|
+
let hash = 0;
|
|
767
|
+
for (let i = 0; i < objString.length; i++) {
|
|
768
|
+
hash = Math.imul(31, hash) + objString.charCodeAt(i) | 0;
|
|
769
|
+
}
|
|
770
|
+
return hash.toString(36);
|
|
771
|
+
}
|
|
772
|
+
var init_strings = __esm({
|
|
773
|
+
"src/lib/core/utility/strings.ts"() {
|
|
774
|
+
"use strict";
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
// src/lib/graphics/shaders/fragment/stars.glsl
|
|
779
|
+
var stars_default;
|
|
780
|
+
var init_stars = __esm({
|
|
781
|
+
"src/lib/graphics/shaders/fragment/stars.glsl"() {
|
|
782
|
+
"use strict";
|
|
783
|
+
stars_default = "#include <common>\n\nuniform vec3 iResolution;\nuniform float iTime;\nvarying vec2 vUv;\n\n// Credit goes to:\n// https://www.shadertoy.com/view/mtyGWy\n\nvec3 palette( float t ) {\n vec3 a = vec3(0.5, 0.5, 0.5);\n vec3 b = vec3(0.5, 0.5, 0.5);\n vec3 c = vec3(1.0, 1.0, 1.0);\n vec3 d = vec3(0.263,0.416,0.557);\n\n return a + b*cos( 6.28318*(c*t+d) );\n}\n\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord ) {\n vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y;\n vec2 uv0 = uv;\n vec3 finalColor = vec3(0.0);\n \n for (float i = 0.0; i < 4.0; i++) {\n uv = fract(uv * 1.5) - 0.5;\n\n float d = length(uv) * exp(-length(uv0));\n\n vec3 col = palette(length(uv0) + i*.4 + iTime*.4);\n\n d = sin(d*5. + iTime)/5.;\n d = abs(d);\n\n d = pow(0.01 / d, 1.2);\n\n finalColor += col * d;\n }\n \n fragColor = vec4(finalColor, 1.0);\n}\n \nvoid main() {\n mainImage(gl_FragColor, vUv);\n}";
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// src/lib/graphics/shaders/fragment/fire.glsl
|
|
788
|
+
var fire_default;
|
|
789
|
+
var init_fire = __esm({
|
|
790
|
+
"src/lib/graphics/shaders/fragment/fire.glsl"() {
|
|
791
|
+
"use strict";
|
|
792
|
+
fire_default = "#include <common>\n \nuniform vec3 iResolution;\nuniform float iTime;\nuniform vec2 iOffset;\nvarying vec2 vUv;\n\nfloat snoise(vec3 uv, float res)\n{\n const vec3 s = vec3(1e0, 1e2, 1e3);\n \n uv *= res;\n \n vec3 uv0 = floor(mod(uv, res))*s;\n vec3 uv1 = floor(mod(uv+vec3(1.), res))*s;\n \n vec3 f = fract(uv); f = f*f*(3.0-2.0*f);\n\n vec4 v = vec4(uv0.x+uv0.y+uv0.z, uv1.x+uv0.y+uv0.z,\n uv0.x+uv1.y+uv0.z, uv1.x+uv1.y+uv0.z);\n\n vec4 r = fract(sin(v*1e-1)*1e3);\n float r0 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);\n \n r = fract(sin((v + uv1.z - uv0.z)*1e-1)*1e3);\n float r1 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);\n \n return mix(r0, r1, f.z)*2.-1.;\n}\n\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord ) {\n vec2 p = -.5 + fragCoord.xy / iResolution.xy;\n p.x *= iResolution.x/iResolution.y;\n \n float color = 3.0 - (3.*length(2.*p));\n \n vec3 coord = vec3(atan(p.x,p.y)/6.2832+.5, length(p)*.4, .5);\n \n for(int i = 1; i <= 7; i++)\n {\n float power = pow(2.0, float(i));\n color += (1.5 / power) * snoise(coord + vec3(0.,-iTime*.05, iTime*.01), power*16.);\n }\n fragColor = vec4( color, pow(max(color,0.),2.)*0.4, pow(max(color,0.),3.)*0.15 , 1.0);\n}\n\nvoid main() {\n mainImage(gl_FragColor, vUv);\n}";
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// src/lib/graphics/shaders/fragment/standard.glsl
|
|
797
|
+
var standard_default;
|
|
798
|
+
var init_standard = __esm({
|
|
799
|
+
"src/lib/graphics/shaders/fragment/standard.glsl"() {
|
|
800
|
+
"use strict";
|
|
801
|
+
standard_default = "uniform sampler2D tDiffuse;\nvarying vec2 vUv;\n\nvoid main() {\n vec4 texel = texture2D( tDiffuse, vUv );\n\n gl_FragColor = texel;\n}";
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
// src/lib/graphics/shaders/fragment/debug.glsl
|
|
806
|
+
var debug_default;
|
|
807
|
+
var init_debug = __esm({
|
|
808
|
+
"src/lib/graphics/shaders/fragment/debug.glsl"() {
|
|
809
|
+
"use strict";
|
|
810
|
+
debug_default = "varying vec3 vBarycentric;\nuniform vec3 baseColor;\nuniform vec3 wireframeColor;\nuniform float wireframeThickness;\n\nfloat edgeFactor() {\n vec3 d = fwidth(vBarycentric);\n vec3 a3 = smoothstep(vec3(0.0), d * wireframeThickness, vBarycentric);\n return min(min(a3.x, a3.y), a3.z);\n}\n\nvoid main() {\n float edge = edgeFactor();\n\n vec3 wireColor = wireframeColor;\n\n vec3 finalColor = mix(wireColor, baseColor, edge);\n \n gl_FragColor = vec4(finalColor, 1.0);\n}\n";
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
// src/lib/graphics/shaders/vertex/object-shader.glsl
|
|
815
|
+
var object_shader_default;
|
|
816
|
+
var init_object_shader = __esm({
|
|
817
|
+
"src/lib/graphics/shaders/vertex/object-shader.glsl"() {
|
|
818
|
+
"use strict";
|
|
819
|
+
object_shader_default = "uniform vec2 uvScale;\nvarying vec2 vUv;\n\nvoid main() {\n vUv = uv;\n vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n gl_Position = projectionMatrix * mvPosition;\n}";
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
// src/lib/graphics/shaders/vertex/debug.glsl
|
|
824
|
+
var debug_default2;
|
|
825
|
+
var init_debug2 = __esm({
|
|
826
|
+
"src/lib/graphics/shaders/vertex/debug.glsl"() {
|
|
827
|
+
"use strict";
|
|
828
|
+
debug_default2 = "varying vec3 vBarycentric;\n\nvoid main() {\n vec3 barycentric = vec3(0.0);\n int index = gl_VertexID % 3;\n if (index == 0) barycentric = vec3(1.0, 0.0, 0.0);\n else if (index == 1) barycentric = vec3(0.0, 1.0, 0.0);\n else barycentric = vec3(0.0, 0.0, 1.0);\n vBarycentric = barycentric;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n";
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// src/lib/core/preset-shader.ts
|
|
833
|
+
var starShader, fireShader, standardShader, debugShader, shaderMap, preset_shader_default;
|
|
834
|
+
var init_preset_shader = __esm({
|
|
835
|
+
"src/lib/core/preset-shader.ts"() {
|
|
836
|
+
"use strict";
|
|
837
|
+
init_stars();
|
|
838
|
+
init_fire();
|
|
839
|
+
init_standard();
|
|
840
|
+
init_debug();
|
|
841
|
+
init_object_shader();
|
|
842
|
+
init_debug2();
|
|
843
|
+
starShader = {
|
|
844
|
+
fragment: stars_default,
|
|
845
|
+
vertex: object_shader_default
|
|
846
|
+
};
|
|
847
|
+
fireShader = {
|
|
848
|
+
fragment: fire_default,
|
|
849
|
+
vertex: object_shader_default
|
|
850
|
+
};
|
|
851
|
+
standardShader = {
|
|
852
|
+
fragment: standard_default,
|
|
853
|
+
vertex: object_shader_default
|
|
854
|
+
};
|
|
855
|
+
debugShader = {
|
|
856
|
+
fragment: debug_default,
|
|
857
|
+
vertex: debug_default2
|
|
858
|
+
};
|
|
859
|
+
shaderMap = /* @__PURE__ */ new Map();
|
|
860
|
+
shaderMap.set("standard", standardShader);
|
|
861
|
+
shaderMap.set("fire", fireShader);
|
|
862
|
+
shaderMap.set("star", starShader);
|
|
863
|
+
shaderMap.set("debug", debugShader);
|
|
864
|
+
preset_shader_default = shaderMap;
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// src/lib/graphics/material.ts
|
|
869
|
+
import {
|
|
870
|
+
Color as Color2,
|
|
871
|
+
MeshPhongMaterial,
|
|
872
|
+
MeshStandardMaterial,
|
|
873
|
+
RepeatWrapping,
|
|
874
|
+
ShaderMaterial as ShaderMaterial2,
|
|
875
|
+
TextureLoader,
|
|
876
|
+
Vector2,
|
|
877
|
+
Vector3 as Vector32
|
|
878
|
+
} from "three";
|
|
879
|
+
var MaterialBuilder;
|
|
880
|
+
var init_material = __esm({
|
|
881
|
+
"src/lib/graphics/material.ts"() {
|
|
882
|
+
"use strict";
|
|
883
|
+
init_strings();
|
|
884
|
+
init_preset_shader();
|
|
885
|
+
MaterialBuilder = class _MaterialBuilder {
|
|
886
|
+
static batchMaterialMap = /* @__PURE__ */ new Map();
|
|
887
|
+
materials = [];
|
|
888
|
+
batchMaterial(options, entityType) {
|
|
889
|
+
const batchKey = shortHash(sortedStringify(options));
|
|
890
|
+
const mappedObject = _MaterialBuilder.batchMaterialMap.get(batchKey);
|
|
891
|
+
if (mappedObject) {
|
|
892
|
+
const count = mappedObject.geometryMap.get(entityType);
|
|
893
|
+
if (count) {
|
|
894
|
+
mappedObject.geometryMap.set(entityType, count + 1);
|
|
895
|
+
} else {
|
|
896
|
+
mappedObject.geometryMap.set(entityType, 1);
|
|
897
|
+
}
|
|
898
|
+
} else {
|
|
899
|
+
_MaterialBuilder.batchMaterialMap.set(
|
|
900
|
+
batchKey,
|
|
901
|
+
{
|
|
902
|
+
geometryMap: /* @__PURE__ */ new Map([[entityType, 1]]),
|
|
903
|
+
material: this.materials[0]
|
|
904
|
+
}
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
async build(options, entityType) {
|
|
909
|
+
const { path, repeat, color, shader } = options;
|
|
910
|
+
if (shader) this.withShader(shader);
|
|
911
|
+
if (color) this.withColor(color);
|
|
912
|
+
await this.setTexture(path ?? null, repeat);
|
|
913
|
+
if (this.materials.length === 0) {
|
|
914
|
+
this.setColor(new Color2("#ffffff"));
|
|
915
|
+
}
|
|
916
|
+
this.batchMaterial(options, entityType);
|
|
917
|
+
}
|
|
918
|
+
withColor(color) {
|
|
919
|
+
this.setColor(color);
|
|
920
|
+
return this;
|
|
921
|
+
}
|
|
922
|
+
withShader(shaderType) {
|
|
923
|
+
this.setShader(shaderType);
|
|
924
|
+
return this;
|
|
925
|
+
}
|
|
926
|
+
async setTexture(texturePath = null, repeat = new Vector2(1, 1)) {
|
|
927
|
+
if (!texturePath) {
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
const loader = new TextureLoader();
|
|
931
|
+
const texture = await loader.loadAsync(texturePath);
|
|
932
|
+
texture.repeat = repeat;
|
|
933
|
+
texture.wrapS = RepeatWrapping;
|
|
934
|
+
texture.wrapT = RepeatWrapping;
|
|
935
|
+
const material = new MeshPhongMaterial({
|
|
936
|
+
map: texture
|
|
937
|
+
});
|
|
938
|
+
this.materials.push(material);
|
|
939
|
+
}
|
|
940
|
+
setColor(color) {
|
|
941
|
+
const material = new MeshStandardMaterial({
|
|
942
|
+
color,
|
|
943
|
+
emissiveIntensity: 0.5,
|
|
944
|
+
lightMapIntensity: 0.5,
|
|
945
|
+
fog: true
|
|
946
|
+
});
|
|
947
|
+
this.materials.push(material);
|
|
948
|
+
}
|
|
949
|
+
setShader(customShader) {
|
|
950
|
+
const { fragment, vertex } = preset_shader_default.get(customShader) ?? preset_shader_default.get("standard");
|
|
951
|
+
const shader = new ShaderMaterial2({
|
|
952
|
+
uniforms: {
|
|
953
|
+
iResolution: { value: new Vector32(1, 1, 1) },
|
|
954
|
+
iTime: { value: 0 },
|
|
955
|
+
tDiffuse: { value: null },
|
|
956
|
+
tDepth: { value: null },
|
|
957
|
+
tNormal: { value: null }
|
|
958
|
+
},
|
|
959
|
+
vertexShader: vertex,
|
|
960
|
+
fragmentShader: fragment
|
|
961
|
+
});
|
|
962
|
+
this.materials.push(shader);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
// src/lib/entities/builder.ts
|
|
969
|
+
import { BufferGeometry, Mesh as Mesh2, Color as Color3 } from "three";
|
|
970
|
+
var EntityCollisionBuilder, EntityBuilder;
|
|
971
|
+
var init_builder = __esm({
|
|
972
|
+
"src/lib/entities/builder.ts"() {
|
|
973
|
+
"use strict";
|
|
974
|
+
init_collision_builder();
|
|
975
|
+
init_material();
|
|
976
|
+
EntityCollisionBuilder = class extends CollisionBuilder {
|
|
977
|
+
};
|
|
978
|
+
EntityBuilder = class {
|
|
979
|
+
meshBuilder;
|
|
980
|
+
collisionBuilder;
|
|
981
|
+
materialBuilder;
|
|
982
|
+
options;
|
|
983
|
+
entity;
|
|
984
|
+
constructor(options, entity, meshBuilder, collisionBuilder) {
|
|
985
|
+
this.options = options;
|
|
986
|
+
this.entity = entity;
|
|
987
|
+
this.meshBuilder = meshBuilder;
|
|
988
|
+
this.collisionBuilder = collisionBuilder;
|
|
989
|
+
this.materialBuilder = new MaterialBuilder();
|
|
990
|
+
const builders = {
|
|
991
|
+
meshBuilder: this.meshBuilder,
|
|
992
|
+
collisionBuilder: this.collisionBuilder,
|
|
993
|
+
materialBuilder: this.materialBuilder
|
|
994
|
+
};
|
|
995
|
+
this.options._builders = builders;
|
|
996
|
+
}
|
|
997
|
+
withPosition(setupPosition) {
|
|
998
|
+
this.options.position = setupPosition;
|
|
999
|
+
return this;
|
|
1000
|
+
}
|
|
1001
|
+
async withMaterial(options, entityType) {
|
|
1002
|
+
if (this.materialBuilder) {
|
|
1003
|
+
await this.materialBuilder.build(options, entityType);
|
|
1004
|
+
}
|
|
1005
|
+
return this;
|
|
1006
|
+
}
|
|
1007
|
+
applyMaterialToGroup(group, materials) {
|
|
1008
|
+
group.traverse((child) => {
|
|
1009
|
+
if (child instanceof Mesh2) {
|
|
1010
|
+
if (child.type === "SkinnedMesh" && materials[0] && !child.material.map) {
|
|
1011
|
+
child.material = materials[0];
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
child.castShadow = true;
|
|
1015
|
+
child.receiveShadow = true;
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
async build() {
|
|
1019
|
+
const entity = this.entity;
|
|
1020
|
+
if (this.materialBuilder) {
|
|
1021
|
+
entity.materials = this.materialBuilder.materials;
|
|
1022
|
+
}
|
|
1023
|
+
if (this.meshBuilder && entity.materials) {
|
|
1024
|
+
const geometry = this.meshBuilder.build(this.options);
|
|
1025
|
+
entity.mesh = this.meshBuilder._build(this.options, geometry, entity.materials);
|
|
1026
|
+
this.meshBuilder.postBuild();
|
|
1027
|
+
}
|
|
1028
|
+
if (entity.group && entity.materials) {
|
|
1029
|
+
this.applyMaterialToGroup(entity.group, entity.materials);
|
|
1030
|
+
}
|
|
1031
|
+
if (this.collisionBuilder) {
|
|
1032
|
+
this.collisionBuilder.withCollision(this.options?.collision || {});
|
|
1033
|
+
const [bodyDesc, colliderDesc] = this.collisionBuilder.build(this.options);
|
|
1034
|
+
entity.bodyDesc = bodyDesc;
|
|
1035
|
+
entity.colliderDesc = colliderDesc;
|
|
1036
|
+
const { x, y, z } = this.options.position || { x: 0, y: 0, z: 0 };
|
|
1037
|
+
entity.bodyDesc.setTranslation(x, y, z);
|
|
1038
|
+
}
|
|
1039
|
+
if (this.options.collisionType) {
|
|
1040
|
+
entity.collisionType = this.options.collisionType;
|
|
1041
|
+
}
|
|
1042
|
+
if (this.options.color instanceof Color3) {
|
|
1043
|
+
const applyColor = (material) => {
|
|
1044
|
+
const anyMat = material;
|
|
1045
|
+
if (anyMat && anyMat.color && anyMat.color.set) {
|
|
1046
|
+
anyMat.color.set(this.options.color);
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
if (entity.materials?.length) {
|
|
1050
|
+
for (const mat of entity.materials) applyColor(mat);
|
|
1051
|
+
}
|
|
1052
|
+
if (entity.mesh && entity.mesh.material) {
|
|
1053
|
+
const mat = entity.mesh.material;
|
|
1054
|
+
if (Array.isArray(mat)) mat.forEach(applyColor);
|
|
1055
|
+
else applyColor(mat);
|
|
1056
|
+
}
|
|
1057
|
+
if (entity.group) {
|
|
1058
|
+
entity.group.traverse((child) => {
|
|
1059
|
+
if (child instanceof Mesh2 && child.material) {
|
|
1060
|
+
const mat = child.material;
|
|
1061
|
+
if (Array.isArray(mat)) mat.forEach(applyColor);
|
|
1062
|
+
else applyColor(mat);
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return entity;
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
// src/lib/entities/actor.ts
|
|
1074
|
+
import { ActiveCollisionTypes as ActiveCollisionTypes2, ColliderDesc as ColliderDesc2 } from "@dimforge/rapier3d-compat";
|
|
1075
|
+
import { SkinnedMesh, Group as Group3, Vector3 as Vector33 } from "three";
|
|
1076
|
+
var actorDefaults, ACTOR_TYPE, ZylemActor;
|
|
1077
|
+
var init_actor = __esm({
|
|
1078
|
+
"src/lib/entities/actor.ts"() {
|
|
1079
|
+
"use strict";
|
|
1080
|
+
init_entity();
|
|
1081
|
+
init_entity_asset_loader();
|
|
1082
|
+
init_animation();
|
|
1083
|
+
actorDefaults = {
|
|
1084
|
+
position: { x: 0, y: 0, z: 0 },
|
|
1085
|
+
collision: {
|
|
1086
|
+
static: false,
|
|
1087
|
+
size: new Vector33(0.5, 0.5, 0.5),
|
|
1088
|
+
position: new Vector33(0, 0, 0)
|
|
1089
|
+
},
|
|
1090
|
+
material: {
|
|
1091
|
+
shader: "standard"
|
|
1092
|
+
},
|
|
1093
|
+
animations: [],
|
|
1094
|
+
models: []
|
|
1095
|
+
};
|
|
1096
|
+
ACTOR_TYPE = Symbol("Actor");
|
|
1097
|
+
ZylemActor = class extends GameEntity {
|
|
1098
|
+
static type = ACTOR_TYPE;
|
|
1099
|
+
_object = null;
|
|
1100
|
+
_animationDelegate = null;
|
|
1101
|
+
_modelFileNames = [];
|
|
1102
|
+
_assetLoader = new EntityAssetLoader();
|
|
1103
|
+
controlledRotation = false;
|
|
1104
|
+
constructor(options) {
|
|
1105
|
+
super();
|
|
1106
|
+
this.options = { ...actorDefaults, ...options };
|
|
1107
|
+
this.lifeCycleDelegate = {
|
|
1108
|
+
update: [this.actorUpdate.bind(this)]
|
|
1109
|
+
};
|
|
1110
|
+
this.controlledRotation = true;
|
|
1111
|
+
}
|
|
1112
|
+
async load() {
|
|
1113
|
+
this._modelFileNames = this.options.models || [];
|
|
1114
|
+
await this.loadModels();
|
|
1115
|
+
if (this._object) {
|
|
1116
|
+
this._animationDelegate = new AnimationDelegate(this._object);
|
|
1117
|
+
await this._animationDelegate.loadAnimations(this.options.animations || []);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
async data() {
|
|
1121
|
+
return {
|
|
1122
|
+
animations: this._animationDelegate?.animations,
|
|
1123
|
+
objectModel: this._object
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
async actorUpdate(params) {
|
|
1127
|
+
this._animationDelegate?.update(params.delta);
|
|
1128
|
+
}
|
|
1129
|
+
async loadModels() {
|
|
1130
|
+
if (this._modelFileNames.length === 0) return;
|
|
1131
|
+
const promises = this._modelFileNames.map((file) => this._assetLoader.loadFile(file));
|
|
1132
|
+
const results = await Promise.all(promises);
|
|
1133
|
+
if (results[0]?.object) {
|
|
1134
|
+
this._object = results[0].object;
|
|
1135
|
+
}
|
|
1136
|
+
if (this._object) {
|
|
1137
|
+
this.group = new Group3();
|
|
1138
|
+
this.group.attach(this._object);
|
|
1139
|
+
this.group.scale.set(
|
|
1140
|
+
this.options.scale?.x || 1,
|
|
1141
|
+
this.options.scale?.y || 1,
|
|
1142
|
+
this.options.scale?.z || 1
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
playAnimation(animationOptions) {
|
|
1147
|
+
this._animationDelegate?.playAnimation(animationOptions);
|
|
1148
|
+
}
|
|
1149
|
+
get object() {
|
|
1150
|
+
return this._object;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Provide custom debug information for the actor
|
|
1154
|
+
* This will be merged with the default debug information
|
|
1155
|
+
*/
|
|
1156
|
+
getDebugInfo() {
|
|
1157
|
+
const debugInfo = {
|
|
1158
|
+
type: "Actor",
|
|
1159
|
+
models: this._modelFileNames.length > 0 ? this._modelFileNames : "none",
|
|
1160
|
+
modelLoaded: !!this._object,
|
|
1161
|
+
scale: this.options.scale ? `${this.options.scale.x}, ${this.options.scale.y}, ${this.options.scale.z}` : "1, 1, 1"
|
|
1162
|
+
};
|
|
1163
|
+
if (this._animationDelegate) {
|
|
1164
|
+
debugInfo.currentAnimation = this._animationDelegate.currentAnimationKey || "none";
|
|
1165
|
+
debugInfo.animationsCount = this.options.animations?.length || 0;
|
|
1166
|
+
}
|
|
1167
|
+
if (this._object) {
|
|
1168
|
+
let meshCount = 0;
|
|
1169
|
+
let vertexCount = 0;
|
|
1170
|
+
this._object.traverse((child) => {
|
|
1171
|
+
if (child.isMesh) {
|
|
1172
|
+
meshCount++;
|
|
1173
|
+
const geometry = child.geometry;
|
|
1174
|
+
if (geometry && geometry.attributes.position) {
|
|
1175
|
+
vertexCount += geometry.attributes.position.count;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
debugInfo.meshCount = meshCount;
|
|
1180
|
+
debugInfo.vertexCount = vertexCount;
|
|
1181
|
+
}
|
|
1182
|
+
return debugInfo;
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
// src/lib/collision/collision-delegate.ts
|
|
1189
|
+
function isCollisionHandlerDelegate(obj) {
|
|
1190
|
+
return typeof obj?.handlePostCollision === "function" && typeof obj?.handleIntersectionEvent === "function";
|
|
1191
|
+
}
|
|
1192
|
+
var init_collision_delegate = __esm({
|
|
1193
|
+
"src/lib/collision/collision-delegate.ts"() {
|
|
1194
|
+
"use strict";
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
// src/lib/collision/world.ts
|
|
1199
|
+
import RAPIER from "@dimforge/rapier3d-compat";
|
|
1200
|
+
var ZylemWorld;
|
|
1201
|
+
var init_world = __esm({
|
|
1202
|
+
"src/lib/collision/world.ts"() {
|
|
1203
|
+
"use strict";
|
|
1204
|
+
init_game_state();
|
|
1205
|
+
init_actor();
|
|
1206
|
+
init_collision_delegate();
|
|
1207
|
+
ZylemWorld = class {
|
|
1208
|
+
type = "World";
|
|
1209
|
+
world;
|
|
1210
|
+
collisionMap = /* @__PURE__ */ new Map();
|
|
1211
|
+
collisionBehaviorMap = /* @__PURE__ */ new Map();
|
|
1212
|
+
_removalMap = /* @__PURE__ */ new Map();
|
|
1213
|
+
static async loadPhysics(gravity) {
|
|
1214
|
+
await RAPIER.init();
|
|
1215
|
+
const physicsWorld = new RAPIER.World(gravity);
|
|
1216
|
+
return physicsWorld;
|
|
1217
|
+
}
|
|
1218
|
+
constructor(world) {
|
|
1219
|
+
this.world = world;
|
|
1220
|
+
}
|
|
1221
|
+
addEntity(entity) {
|
|
1222
|
+
const rigidBody = this.world.createRigidBody(entity.bodyDesc);
|
|
1223
|
+
entity.body = rigidBody;
|
|
1224
|
+
entity.body.userData = { uuid: entity.uuid, ref: entity };
|
|
1225
|
+
if (this.world.gravity.x === 0 && this.world.gravity.y === 0 && this.world.gravity.z === 0) {
|
|
1226
|
+
entity.body.lockTranslations(true, true);
|
|
1227
|
+
entity.body.lockRotations(true, true);
|
|
1228
|
+
}
|
|
1229
|
+
const collider = this.world.createCollider(entity.colliderDesc, entity.body);
|
|
1230
|
+
entity.collider = collider;
|
|
1231
|
+
if (entity.controlledRotation || entity instanceof ZylemActor) {
|
|
1232
|
+
entity.body.lockRotations(true, true);
|
|
1233
|
+
entity.characterController = this.world.createCharacterController(0.01);
|
|
1234
|
+
entity.characterController.setMaxSlopeClimbAngle(45 * Math.PI / 180);
|
|
1235
|
+
entity.characterController.setMinSlopeSlideAngle(30 * Math.PI / 180);
|
|
1236
|
+
entity.characterController.enableSnapToGround(0.01);
|
|
1237
|
+
entity.characterController.setSlideEnabled(true);
|
|
1238
|
+
entity.characterController.setApplyImpulsesToDynamicBodies(true);
|
|
1239
|
+
entity.characterController.setCharacterMass(1);
|
|
1240
|
+
}
|
|
1241
|
+
this.collisionMap.set(entity.uuid, entity);
|
|
1242
|
+
}
|
|
1243
|
+
setForRemoval(entity) {
|
|
1244
|
+
if (entity.body) {
|
|
1245
|
+
this._removalMap.set(entity.uuid, entity);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
destroyEntity(entity) {
|
|
1249
|
+
if (entity.collider) {
|
|
1250
|
+
this.world.removeCollider(entity.collider, true);
|
|
1251
|
+
}
|
|
1252
|
+
if (entity.body) {
|
|
1253
|
+
this.world.removeRigidBody(entity.body);
|
|
1254
|
+
this.collisionMap.delete(entity.uuid);
|
|
1255
|
+
this._removalMap.delete(entity.uuid);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
setup() {
|
|
1259
|
+
}
|
|
1260
|
+
update(params) {
|
|
1261
|
+
const { delta } = params;
|
|
1262
|
+
if (!this.world) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
this.updateColliders(delta);
|
|
1266
|
+
this.updatePostCollisionBehaviors(delta);
|
|
1267
|
+
this.world.step();
|
|
1268
|
+
}
|
|
1269
|
+
updatePostCollisionBehaviors(delta) {
|
|
1270
|
+
const dictionaryRef = this.collisionBehaviorMap;
|
|
1271
|
+
for (let [id, collider] of dictionaryRef) {
|
|
1272
|
+
const gameEntity = collider;
|
|
1273
|
+
if (!isCollisionHandlerDelegate(gameEntity)) {
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
const active = gameEntity.handlePostCollision({ entity: gameEntity, delta });
|
|
1277
|
+
if (!active) {
|
|
1278
|
+
this.collisionBehaviorMap.delete(id);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
updateColliders(delta) {
|
|
1283
|
+
const dictionaryRef = this.collisionMap;
|
|
1284
|
+
for (let [id, collider] of dictionaryRef) {
|
|
1285
|
+
const gameEntity = collider;
|
|
1286
|
+
if (!gameEntity.body) {
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
if (this._removalMap.get(gameEntity.uuid)) {
|
|
1290
|
+
this.destroyEntity(gameEntity);
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
this.world.contactsWith(gameEntity.body.collider(0), (otherCollider) => {
|
|
1294
|
+
const uuid = otherCollider._parent.userData.uuid;
|
|
1295
|
+
const entity = dictionaryRef.get(uuid);
|
|
1296
|
+
if (!entity) {
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (gameEntity._collision) {
|
|
1300
|
+
gameEntity._collision(entity, state.globals);
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
this.world.intersectionsWith(gameEntity.body.collider(0), (otherCollider) => {
|
|
1304
|
+
const uuid = otherCollider._parent.userData.uuid;
|
|
1305
|
+
const entity = dictionaryRef.get(uuid);
|
|
1306
|
+
if (!entity) {
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (gameEntity._collision) {
|
|
1310
|
+
gameEntity._collision(entity, state.globals);
|
|
1311
|
+
}
|
|
1312
|
+
if (isCollisionHandlerDelegate(entity)) {
|
|
1313
|
+
entity.handleIntersectionEvent({ entity, other: gameEntity, delta });
|
|
1314
|
+
this.collisionBehaviorMap.set(uuid, entity);
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
destroy() {
|
|
1320
|
+
try {
|
|
1321
|
+
for (const [, entity] of this.collisionMap) {
|
|
1322
|
+
try {
|
|
1323
|
+
this.destroyEntity(entity);
|
|
1324
|
+
} catch {
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
this.collisionMap.clear();
|
|
1328
|
+
this.collisionBehaviorMap.clear();
|
|
1329
|
+
this._removalMap.clear();
|
|
1330
|
+
this.world = void 0;
|
|
1331
|
+
} catch {
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1338
|
+
// src/lib/graphics/zylem-scene.ts
|
|
1339
|
+
import {
|
|
1340
|
+
Scene,
|
|
1341
|
+
Color as Color4,
|
|
1342
|
+
AmbientLight,
|
|
1343
|
+
DirectionalLight,
|
|
1344
|
+
Vector3 as Vector34,
|
|
1345
|
+
TextureLoader as TextureLoader2,
|
|
1346
|
+
GridHelper
|
|
1347
|
+
} from "three";
|
|
1348
|
+
var ZylemScene;
|
|
1349
|
+
var init_zylem_scene = __esm({
|
|
1350
|
+
"src/lib/graphics/zylem-scene.ts"() {
|
|
1351
|
+
"use strict";
|
|
1352
|
+
init_debug_state();
|
|
1353
|
+
init_game_state();
|
|
1354
|
+
ZylemScene = class {
|
|
1355
|
+
type = "Scene";
|
|
1356
|
+
_setup;
|
|
1357
|
+
scene;
|
|
1358
|
+
zylemCamera;
|
|
1359
|
+
containerElement = null;
|
|
1360
|
+
update = () => {
|
|
1361
|
+
};
|
|
1362
|
+
_collision;
|
|
1363
|
+
_destroy;
|
|
1364
|
+
name;
|
|
1365
|
+
tag;
|
|
1366
|
+
constructor(id, camera, state2) {
|
|
1367
|
+
const scene = new Scene();
|
|
1368
|
+
const isColor = state2.backgroundColor instanceof Color4;
|
|
1369
|
+
const backgroundColor = isColor ? state2.backgroundColor : new Color4(state2.backgroundColor);
|
|
1370
|
+
scene.background = backgroundColor;
|
|
1371
|
+
if (state2.backgroundImage) {
|
|
1372
|
+
const loader = new TextureLoader2();
|
|
1373
|
+
const texture = loader.load(state2.backgroundImage);
|
|
1374
|
+
scene.background = texture;
|
|
1375
|
+
}
|
|
1376
|
+
this.scene = scene;
|
|
1377
|
+
this.zylemCamera = camera;
|
|
1378
|
+
this.setupLighting(scene);
|
|
1379
|
+
this.setupCamera(scene, camera);
|
|
1380
|
+
if (debugState.enabled) {
|
|
1381
|
+
this.debugScene();
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
setup() {
|
|
1385
|
+
if (this._setup) {
|
|
1386
|
+
this._setup({ me: this, camera: this.zylemCamera, globals: getGlobals() });
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
destroy() {
|
|
1390
|
+
if (this.zylemCamera && this.zylemCamera.destroy) {
|
|
1391
|
+
this.zylemCamera.destroy();
|
|
1392
|
+
}
|
|
1393
|
+
if (this.scene) {
|
|
1394
|
+
this.scene.traverse((obj) => {
|
|
1395
|
+
if (obj.geometry) {
|
|
1396
|
+
obj.geometry.dispose?.();
|
|
1397
|
+
}
|
|
1398
|
+
if (obj.material) {
|
|
1399
|
+
if (Array.isArray(obj.material)) {
|
|
1400
|
+
obj.material.forEach((m) => m.dispose?.());
|
|
1401
|
+
} else {
|
|
1402
|
+
obj.material.dispose?.();
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Setup camera with the scene
|
|
1410
|
+
*/
|
|
1411
|
+
setupCamera(scene, camera) {
|
|
1412
|
+
scene.add(camera.cameraRig);
|
|
1413
|
+
camera.setup(scene);
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Setup scene lighting
|
|
1417
|
+
*/
|
|
1418
|
+
setupLighting(scene) {
|
|
1419
|
+
const ambientLight = new AmbientLight(16777215, 2);
|
|
1420
|
+
scene.add(ambientLight);
|
|
1421
|
+
const directionalLight = new DirectionalLight(16777215, 2);
|
|
1422
|
+
directionalLight.name = "Light";
|
|
1423
|
+
directionalLight.position.set(0, 100, 0);
|
|
1424
|
+
directionalLight.castShadow = true;
|
|
1425
|
+
directionalLight.shadow.camera.near = 0.1;
|
|
1426
|
+
directionalLight.shadow.camera.far = 2e3;
|
|
1427
|
+
directionalLight.shadow.camera.left = -100;
|
|
1428
|
+
directionalLight.shadow.camera.right = 100;
|
|
1429
|
+
directionalLight.shadow.camera.top = 100;
|
|
1430
|
+
directionalLight.shadow.camera.bottom = -100;
|
|
1431
|
+
directionalLight.shadow.mapSize.width = 2048;
|
|
1432
|
+
directionalLight.shadow.mapSize.height = 2048;
|
|
1433
|
+
scene.add(directionalLight);
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Update renderer size - delegates to camera
|
|
1437
|
+
*/
|
|
1438
|
+
updateRenderer(width, height) {
|
|
1439
|
+
this.zylemCamera.resize(width, height);
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Add object to scene
|
|
1443
|
+
*/
|
|
1444
|
+
add(object, position2 = new Vector34(0, 0, 0)) {
|
|
1445
|
+
object.position.set(position2.x, position2.y, position2.z);
|
|
1446
|
+
this.scene.add(object);
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Add game entity to scene
|
|
1450
|
+
*/
|
|
1451
|
+
addEntity(entity) {
|
|
1452
|
+
if (entity.group) {
|
|
1453
|
+
this.add(entity.group, entity.options.position);
|
|
1454
|
+
} else if (entity.mesh) {
|
|
1455
|
+
this.add(entity.mesh, entity.options.position);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Add debug helpers to scene
|
|
1460
|
+
*/
|
|
1461
|
+
debugScene() {
|
|
1462
|
+
const size = 1e3;
|
|
1463
|
+
const divisions = 100;
|
|
1464
|
+
const gridHelper = new GridHelper(size, divisions);
|
|
1465
|
+
this.scene.add(gridHelper);
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
// src/lib/stage/stage-state.ts
|
|
1472
|
+
import { Color as Color5, Vector3 as Vector35 } from "three";
|
|
1473
|
+
import { proxy as proxy3, subscribe as subscribe2 } from "valtio/vanilla";
|
|
1474
|
+
function clearVariables(target) {
|
|
1475
|
+
variableProxyStore.delete(target);
|
|
1476
|
+
}
|
|
1477
|
+
var stageState, setStageBackgroundColor, setStageBackgroundImage, setStageVariables, resetStageVariables, variableProxyStore;
|
|
1478
|
+
var init_stage_state = __esm({
|
|
1479
|
+
"src/lib/stage/stage-state.ts"() {
|
|
1480
|
+
"use strict";
|
|
1481
|
+
stageState = proxy3({
|
|
1482
|
+
backgroundColor: new Color5(Color5.NAMES.cornflowerblue),
|
|
1483
|
+
backgroundImage: null,
|
|
1484
|
+
inputs: {
|
|
1485
|
+
p1: ["gamepad-1", "keyboard-1"],
|
|
1486
|
+
p2: ["gamepad-2", "keyboard-2"]
|
|
1487
|
+
},
|
|
1488
|
+
variables: {},
|
|
1489
|
+
gravity: new Vector35(0, 0, 0),
|
|
1490
|
+
entities: []
|
|
1491
|
+
});
|
|
1492
|
+
setStageBackgroundColor = (value) => {
|
|
1493
|
+
stageState.backgroundColor = value;
|
|
1494
|
+
};
|
|
1495
|
+
setStageBackgroundImage = (value) => {
|
|
1496
|
+
stageState.backgroundImage = value;
|
|
1497
|
+
};
|
|
1498
|
+
setStageVariables = (variables) => {
|
|
1499
|
+
stageState.variables = { ...variables };
|
|
1500
|
+
};
|
|
1501
|
+
resetStageVariables = () => {
|
|
1502
|
+
stageState.variables = {};
|
|
1503
|
+
};
|
|
1504
|
+
variableProxyStore = /* @__PURE__ */ new Map();
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
// src/lib/core/utility/vector.ts
|
|
1509
|
+
import { Color as Color6 } from "three";
|
|
1510
|
+
import { Vector3 as Vector36 } from "@dimforge/rapier3d-compat";
|
|
1511
|
+
var ZylemBlueColor, Vec0, Vec1;
|
|
1512
|
+
var init_vector = __esm({
|
|
1513
|
+
"src/lib/core/utility/vector.ts"() {
|
|
1514
|
+
"use strict";
|
|
1515
|
+
ZylemBlueColor = new Color6("#0333EC");
|
|
1516
|
+
Vec0 = new Vector36(0, 0, 0);
|
|
1517
|
+
Vec1 = new Vector36(1, 1, 1);
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
// src/lib/core/lifecycle-base.ts
|
|
1522
|
+
var LifeCycleBase;
|
|
1523
|
+
var init_lifecycle_base = __esm({
|
|
1524
|
+
"src/lib/core/lifecycle-base.ts"() {
|
|
1525
|
+
"use strict";
|
|
1526
|
+
LifeCycleBase = class {
|
|
1527
|
+
update = () => {
|
|
1528
|
+
};
|
|
1529
|
+
setup = () => {
|
|
1530
|
+
};
|
|
1531
|
+
destroy = () => {
|
|
1532
|
+
};
|
|
1533
|
+
nodeSetup(context) {
|
|
1534
|
+
if (typeof this._setup === "function") {
|
|
1535
|
+
this._setup(context);
|
|
1536
|
+
}
|
|
1537
|
+
if (this.setup) {
|
|
1538
|
+
this.setup(context);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
nodeUpdate(context) {
|
|
1542
|
+
if (typeof this._update === "function") {
|
|
1543
|
+
this._update(context);
|
|
1544
|
+
}
|
|
1545
|
+
if (this.update) {
|
|
1546
|
+
this.update(context);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
nodeDestroy(context) {
|
|
1550
|
+
if (this.destroy) {
|
|
1551
|
+
this.destroy(context);
|
|
1552
|
+
}
|
|
1553
|
+
if (typeof this._destroy === "function") {
|
|
1554
|
+
this._destroy(context);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
// src/lib/camera/perspective.ts
|
|
1562
|
+
var Perspectives;
|
|
1563
|
+
var init_perspective = __esm({
|
|
1564
|
+
"src/lib/camera/perspective.ts"() {
|
|
1565
|
+
"use strict";
|
|
1566
|
+
Perspectives = {
|
|
1567
|
+
FirstPerson: "first-person",
|
|
1568
|
+
ThirdPerson: "third-person",
|
|
1569
|
+
Isometric: "isometric",
|
|
1570
|
+
Flat2D: "flat-2d",
|
|
1571
|
+
Fixed2D: "fixed-2d"
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
// src/lib/camera/third-person.ts
|
|
1577
|
+
import { Vector3 as Vector37 } from "three";
|
|
1578
|
+
var ThirdPersonCamera;
|
|
1579
|
+
var init_third_person = __esm({
|
|
1580
|
+
"src/lib/camera/third-person.ts"() {
|
|
1581
|
+
"use strict";
|
|
1582
|
+
ThirdPersonCamera = class {
|
|
1583
|
+
distance;
|
|
1584
|
+
screenResolution = null;
|
|
1585
|
+
renderer = null;
|
|
1586
|
+
scene = null;
|
|
1587
|
+
cameraRef = null;
|
|
1588
|
+
constructor() {
|
|
1589
|
+
this.distance = new Vector37(0, 5, 8);
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Setup the third person camera controller
|
|
1593
|
+
*/
|
|
1594
|
+
setup(params) {
|
|
1595
|
+
const { screenResolution, renderer, scene, camera } = params;
|
|
1596
|
+
this.screenResolution = screenResolution;
|
|
1597
|
+
this.renderer = renderer;
|
|
1598
|
+
this.scene = scene;
|
|
1599
|
+
this.cameraRef = camera;
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Update the third person camera
|
|
1603
|
+
*/
|
|
1604
|
+
update(delta) {
|
|
1605
|
+
if (!this.cameraRef.target) {
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
const desiredCameraPosition = this.cameraRef.target.group.position.clone().add(this.distance);
|
|
1609
|
+
this.cameraRef.camera.position.lerp(desiredCameraPosition, 0.1);
|
|
1610
|
+
this.cameraRef.camera.lookAt(this.cameraRef.target.group.position);
|
|
1611
|
+
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Handle resize events
|
|
1614
|
+
*/
|
|
1615
|
+
resize(width, height) {
|
|
1616
|
+
if (this.screenResolution) {
|
|
1617
|
+
this.screenResolution.set(width, height);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Set the distance from the target
|
|
1622
|
+
*/
|
|
1623
|
+
setDistance(distance) {
|
|
1624
|
+
this.distance = distance;
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
|
|
1630
|
+
// src/lib/camera/fixed-2d.ts
|
|
1631
|
+
var Fixed2DCamera;
|
|
1632
|
+
var init_fixed_2d = __esm({
|
|
1633
|
+
"src/lib/camera/fixed-2d.ts"() {
|
|
1634
|
+
"use strict";
|
|
1635
|
+
Fixed2DCamera = class {
|
|
1636
|
+
screenResolution = null;
|
|
1637
|
+
renderer = null;
|
|
1638
|
+
scene = null;
|
|
1639
|
+
cameraRef = null;
|
|
1640
|
+
constructor() {
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Setup the fixed 2D camera controller
|
|
1644
|
+
*/
|
|
1645
|
+
setup(params) {
|
|
1646
|
+
const { screenResolution, renderer, scene, camera } = params;
|
|
1647
|
+
this.screenResolution = screenResolution;
|
|
1648
|
+
this.renderer = renderer;
|
|
1649
|
+
this.scene = scene;
|
|
1650
|
+
this.cameraRef = camera;
|
|
1651
|
+
this.cameraRef.camera.position.set(0, 0, 10);
|
|
1652
|
+
this.cameraRef.camera.lookAt(0, 0, 0);
|
|
1653
|
+
}
|
|
1654
|
+
/**
|
|
1655
|
+
* Update the fixed 2D camera
|
|
1656
|
+
* Fixed cameras don't need to update position/rotation automatically
|
|
1657
|
+
*/
|
|
1658
|
+
update(delta) {
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Handle resize events for 2D camera
|
|
1662
|
+
*/
|
|
1663
|
+
resize(width, height) {
|
|
1664
|
+
if (this.screenResolution) {
|
|
1665
|
+
this.screenResolution.set(width, height);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
// src/lib/graphics/shaders/vertex/standard.glsl
|
|
1673
|
+
var standard_default2;
|
|
1674
|
+
var init_standard2 = __esm({
|
|
1675
|
+
"src/lib/graphics/shaders/vertex/standard.glsl"() {
|
|
1676
|
+
"use strict";
|
|
1677
|
+
standard_default2 = "varying vec2 vUv;\n\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}";
|
|
1678
|
+
}
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1681
|
+
// src/lib/graphics/render-pass.ts
|
|
1682
|
+
import * as THREE from "three";
|
|
1683
|
+
import { WebGLRenderTarget } from "three";
|
|
1684
|
+
import { Pass, FullScreenQuad } from "three/addons/postprocessing/Pass.js";
|
|
1685
|
+
var RenderPass;
|
|
1686
|
+
var init_render_pass = __esm({
|
|
1687
|
+
"src/lib/graphics/render-pass.ts"() {
|
|
1688
|
+
"use strict";
|
|
1689
|
+
init_standard();
|
|
1690
|
+
init_standard2();
|
|
1691
|
+
RenderPass = class extends Pass {
|
|
1692
|
+
fsQuad;
|
|
1693
|
+
resolution;
|
|
1694
|
+
scene;
|
|
1695
|
+
camera;
|
|
1696
|
+
rgbRenderTarget;
|
|
1697
|
+
normalRenderTarget;
|
|
1698
|
+
normalMaterial;
|
|
1699
|
+
constructor(resolution, scene, camera) {
|
|
1700
|
+
super();
|
|
1701
|
+
this.resolution = resolution;
|
|
1702
|
+
this.fsQuad = new FullScreenQuad(this.material());
|
|
1703
|
+
this.scene = scene;
|
|
1704
|
+
this.camera = camera;
|
|
1705
|
+
this.rgbRenderTarget = new WebGLRenderTarget(resolution.x * 4, resolution.y * 4);
|
|
1706
|
+
this.normalRenderTarget = new WebGLRenderTarget(resolution.x * 4, resolution.y * 4);
|
|
1707
|
+
this.normalMaterial = new THREE.MeshNormalMaterial();
|
|
1708
|
+
}
|
|
1709
|
+
render(renderer, writeBuffer) {
|
|
1710
|
+
renderer.setRenderTarget(this.rgbRenderTarget);
|
|
1711
|
+
renderer.render(this.scene, this.camera);
|
|
1712
|
+
const overrideMaterial_old = this.scene.overrideMaterial;
|
|
1713
|
+
renderer.setRenderTarget(this.normalRenderTarget);
|
|
1714
|
+
this.scene.overrideMaterial = this.normalMaterial;
|
|
1715
|
+
renderer.render(this.scene, this.camera);
|
|
1716
|
+
this.scene.overrideMaterial = overrideMaterial_old;
|
|
1717
|
+
const uniforms = this.fsQuad.material.uniforms;
|
|
1718
|
+
uniforms.tDiffuse.value = this.rgbRenderTarget.texture;
|
|
1719
|
+
uniforms.tDepth.value = this.rgbRenderTarget.depthTexture;
|
|
1720
|
+
uniforms.tNormal.value = this.normalRenderTarget.texture;
|
|
1721
|
+
uniforms.iTime.value += 0.01;
|
|
1722
|
+
if (this.renderToScreen) {
|
|
1723
|
+
renderer.setRenderTarget(null);
|
|
1724
|
+
} else {
|
|
1725
|
+
renderer.setRenderTarget(writeBuffer);
|
|
1726
|
+
}
|
|
1727
|
+
this.fsQuad.render(renderer);
|
|
1728
|
+
}
|
|
1729
|
+
material() {
|
|
1730
|
+
return new THREE.ShaderMaterial({
|
|
1731
|
+
uniforms: {
|
|
1732
|
+
iTime: { value: 0 },
|
|
1733
|
+
tDiffuse: { value: null },
|
|
1734
|
+
tDepth: { value: null },
|
|
1735
|
+
tNormal: { value: null },
|
|
1736
|
+
resolution: {
|
|
1737
|
+
value: new THREE.Vector4(
|
|
1738
|
+
this.resolution.x,
|
|
1739
|
+
this.resolution.y,
|
|
1740
|
+
1 / this.resolution.x,
|
|
1741
|
+
1 / this.resolution.y
|
|
1742
|
+
)
|
|
1743
|
+
}
|
|
1744
|
+
},
|
|
1745
|
+
vertexShader: standard_default2,
|
|
1746
|
+
fragmentShader: standard_default
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
dispose() {
|
|
1750
|
+
try {
|
|
1751
|
+
this.fsQuad?.dispose?.();
|
|
1752
|
+
} catch {
|
|
1753
|
+
}
|
|
1754
|
+
try {
|
|
1755
|
+
this.rgbRenderTarget?.dispose?.();
|
|
1756
|
+
this.normalRenderTarget?.dispose?.();
|
|
1757
|
+
} catch {
|
|
1758
|
+
}
|
|
1759
|
+
try {
|
|
1760
|
+
this.normalMaterial?.dispose?.();
|
|
1761
|
+
} catch {
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
// src/lib/camera/zylem-camera.ts
|
|
1769
|
+
import { PerspectiveCamera, Vector3 as Vector38, Object3D as Object3D4, OrthographicCamera, WebGLRenderer as WebGLRenderer3 } from "three";
|
|
1770
|
+
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
|
1771
|
+
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
|
|
1772
|
+
var ZylemCamera;
|
|
1773
|
+
var init_zylem_camera = __esm({
|
|
1774
|
+
"src/lib/camera/zylem-camera.ts"() {
|
|
1775
|
+
"use strict";
|
|
1776
|
+
init_perspective();
|
|
1777
|
+
init_third_person();
|
|
1778
|
+
init_fixed_2d();
|
|
1779
|
+
init_render_pass();
|
|
1780
|
+
ZylemCamera = class {
|
|
1781
|
+
cameraRig;
|
|
1782
|
+
camera;
|
|
1783
|
+
screenResolution;
|
|
1784
|
+
renderer;
|
|
1785
|
+
composer;
|
|
1786
|
+
_perspective;
|
|
1787
|
+
orbitControls = null;
|
|
1788
|
+
target = null;
|
|
1789
|
+
sceneRef = null;
|
|
1790
|
+
frustumSize = 10;
|
|
1791
|
+
// Perspective controller delegation
|
|
1792
|
+
perspectiveController = null;
|
|
1793
|
+
debugDelegate = null;
|
|
1794
|
+
debugUnsubscribe = null;
|
|
1795
|
+
debugStateSnapshot = { enabled: false, selected: [] };
|
|
1796
|
+
orbitTarget = null;
|
|
1797
|
+
orbitTargetWorldPos = new Vector38();
|
|
1798
|
+
constructor(perspective, screenResolution, frustumSize = 10) {
|
|
1799
|
+
this._perspective = perspective;
|
|
1800
|
+
this.screenResolution = screenResolution;
|
|
1801
|
+
this.frustumSize = frustumSize;
|
|
1802
|
+
this.renderer = new WebGLRenderer3({ antialias: false, alpha: true });
|
|
1803
|
+
this.renderer.setSize(screenResolution.x, screenResolution.y);
|
|
1804
|
+
this.renderer.shadowMap.enabled = true;
|
|
1805
|
+
this.composer = new EffectComposer(this.renderer);
|
|
1806
|
+
const aspectRatio = screenResolution.x / screenResolution.y;
|
|
1807
|
+
this.camera = this.createCameraForPerspective(aspectRatio);
|
|
1808
|
+
this.cameraRig = new Object3D4();
|
|
1809
|
+
this.cameraRig.position.set(0, 3, 10);
|
|
1810
|
+
this.cameraRig.add(this.camera);
|
|
1811
|
+
this.camera.lookAt(new Vector38(0, 2, 0));
|
|
1812
|
+
this.initializePerspectiveController();
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Setup the camera with a scene
|
|
1816
|
+
*/
|
|
1817
|
+
async setup(scene) {
|
|
1818
|
+
this.sceneRef = scene;
|
|
1819
|
+
if (this.orbitControls === null) {
|
|
1820
|
+
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
1821
|
+
this.orbitControls.enableDamping = true;
|
|
1822
|
+
this.orbitControls.dampingFactor = 0.05;
|
|
1823
|
+
this.orbitControls.screenSpacePanning = false;
|
|
1824
|
+
this.orbitControls.minDistance = 1;
|
|
1825
|
+
this.orbitControls.maxDistance = 500;
|
|
1826
|
+
this.orbitControls.maxPolarAngle = Math.PI / 2;
|
|
1827
|
+
}
|
|
1828
|
+
let renderResolution = this.screenResolution.clone().divideScalar(2);
|
|
1829
|
+
renderResolution.x |= 0;
|
|
1830
|
+
renderResolution.y |= 0;
|
|
1831
|
+
const pass = new RenderPass(renderResolution, scene, this.camera);
|
|
1832
|
+
this.composer.addPass(pass);
|
|
1833
|
+
if (this.perspectiveController) {
|
|
1834
|
+
this.perspectiveController.setup({
|
|
1835
|
+
screenResolution: this.screenResolution,
|
|
1836
|
+
renderer: this.renderer,
|
|
1837
|
+
scene,
|
|
1838
|
+
camera: this
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
this.renderer.setAnimationLoop((delta) => {
|
|
1842
|
+
this.update(delta || 0);
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Update camera and render
|
|
1847
|
+
*/
|
|
1848
|
+
update(delta) {
|
|
1849
|
+
if (this.orbitControls && this.orbitTarget) {
|
|
1850
|
+
this.orbitTarget.getWorldPosition(this.orbitTargetWorldPos);
|
|
1851
|
+
this.orbitControls.target.copy(this.orbitTargetWorldPos);
|
|
1852
|
+
}
|
|
1853
|
+
this.orbitControls?.update();
|
|
1854
|
+
if (this.perspectiveController) {
|
|
1855
|
+
this.perspectiveController.update(delta);
|
|
1856
|
+
}
|
|
1857
|
+
this.composer.render(delta);
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Dispose renderer, composer, controls, and detach from scene
|
|
1861
|
+
*/
|
|
1862
|
+
destroy() {
|
|
1863
|
+
try {
|
|
1864
|
+
this.renderer.setAnimationLoop(null);
|
|
1865
|
+
} catch {
|
|
1866
|
+
}
|
|
1867
|
+
try {
|
|
1868
|
+
this.disableOrbitControls();
|
|
1869
|
+
} catch {
|
|
1870
|
+
}
|
|
1871
|
+
try {
|
|
1872
|
+
this.composer?.passes?.forEach((p) => p.dispose?.());
|
|
1873
|
+
this.composer?.dispose?.();
|
|
1874
|
+
} catch {
|
|
1875
|
+
}
|
|
1876
|
+
try {
|
|
1877
|
+
this.renderer.dispose();
|
|
1878
|
+
} catch {
|
|
1879
|
+
}
|
|
1880
|
+
this.detachDebugDelegate();
|
|
1881
|
+
this.sceneRef = null;
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Attach a delegate to react to debug state changes.
|
|
1885
|
+
*/
|
|
1886
|
+
setDebugDelegate(delegate) {
|
|
1887
|
+
if (this.debugDelegate === delegate) {
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1890
|
+
this.detachDebugDelegate();
|
|
1891
|
+
this.debugDelegate = delegate;
|
|
1892
|
+
if (!delegate) {
|
|
1893
|
+
this.applyDebugState({ enabled: false, selected: [] });
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
const unsubscribe = delegate.subscribe((state2) => {
|
|
1897
|
+
this.applyDebugState(state2);
|
|
1898
|
+
});
|
|
1899
|
+
this.debugUnsubscribe = () => {
|
|
1900
|
+
unsubscribe?.();
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Resize camera and renderer
|
|
1905
|
+
*/
|
|
1906
|
+
resize(width, height) {
|
|
1907
|
+
this.screenResolution.set(width, height);
|
|
1908
|
+
this.renderer.setSize(width, height, false);
|
|
1909
|
+
this.composer.setSize(width, height);
|
|
1910
|
+
if (this.camera instanceof PerspectiveCamera) {
|
|
1911
|
+
this.camera.aspect = width / height;
|
|
1912
|
+
this.camera.updateProjectionMatrix();
|
|
1913
|
+
}
|
|
1914
|
+
if (this.perspectiveController) {
|
|
1915
|
+
this.perspectiveController.resize(width, height);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Update renderer pixel ratio (DPR)
|
|
1920
|
+
*/
|
|
1921
|
+
setPixelRatio(dpr) {
|
|
1922
|
+
const safe = Math.max(1, Number.isFinite(dpr) ? dpr : 1);
|
|
1923
|
+
this.renderer.setPixelRatio(safe);
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Create camera based on perspective type
|
|
1927
|
+
*/
|
|
1928
|
+
createCameraForPerspective(aspectRatio) {
|
|
1929
|
+
switch (this._perspective) {
|
|
1930
|
+
case Perspectives.ThirdPerson:
|
|
1931
|
+
return this.createThirdPersonCamera(aspectRatio);
|
|
1932
|
+
case Perspectives.FirstPerson:
|
|
1933
|
+
return this.createFirstPersonCamera(aspectRatio);
|
|
1934
|
+
case Perspectives.Isometric:
|
|
1935
|
+
return this.createIsometricCamera(aspectRatio);
|
|
1936
|
+
case Perspectives.Flat2D:
|
|
1937
|
+
return this.createFlat2DCamera(aspectRatio);
|
|
1938
|
+
case Perspectives.Fixed2D:
|
|
1939
|
+
return this.createFixed2DCamera(aspectRatio);
|
|
1940
|
+
default:
|
|
1941
|
+
return this.createThirdPersonCamera(aspectRatio);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Initialize perspective-specific controller
|
|
1946
|
+
*/
|
|
1947
|
+
initializePerspectiveController() {
|
|
1948
|
+
switch (this._perspective) {
|
|
1949
|
+
case Perspectives.ThirdPerson:
|
|
1950
|
+
this.perspectiveController = new ThirdPersonCamera();
|
|
1951
|
+
break;
|
|
1952
|
+
case Perspectives.Fixed2D:
|
|
1953
|
+
this.perspectiveController = new Fixed2DCamera();
|
|
1954
|
+
break;
|
|
1955
|
+
default:
|
|
1956
|
+
this.perspectiveController = new ThirdPersonCamera();
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
createThirdPersonCamera(aspectRatio) {
|
|
1960
|
+
return new PerspectiveCamera(75, aspectRatio, 0.1, 1e3);
|
|
1961
|
+
}
|
|
1962
|
+
createFirstPersonCamera(aspectRatio) {
|
|
1963
|
+
return new PerspectiveCamera(75, aspectRatio, 0.1, 1e3);
|
|
1964
|
+
}
|
|
1965
|
+
createIsometricCamera(aspectRatio) {
|
|
1966
|
+
return new OrthographicCamera(
|
|
1967
|
+
this.frustumSize * aspectRatio / -2,
|
|
1968
|
+
this.frustumSize * aspectRatio / 2,
|
|
1969
|
+
this.frustumSize / 2,
|
|
1970
|
+
this.frustumSize / -2,
|
|
1971
|
+
1,
|
|
1972
|
+
1e3
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
createFlat2DCamera(aspectRatio) {
|
|
1976
|
+
return new OrthographicCamera(
|
|
1977
|
+
this.frustumSize * aspectRatio / -2,
|
|
1978
|
+
this.frustumSize * aspectRatio / 2,
|
|
1979
|
+
this.frustumSize / 2,
|
|
1980
|
+
this.frustumSize / -2,
|
|
1981
|
+
1,
|
|
1982
|
+
1e3
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
createFixed2DCamera(aspectRatio) {
|
|
1986
|
+
return this.createFlat2DCamera(aspectRatio);
|
|
1987
|
+
}
|
|
1988
|
+
// Movement methods
|
|
1989
|
+
moveCamera(position2) {
|
|
1990
|
+
if (this._perspective === Perspectives.Flat2D || this._perspective === Perspectives.Fixed2D) {
|
|
1991
|
+
this.frustumSize = position2.z;
|
|
1992
|
+
}
|
|
1993
|
+
this.cameraRig.position.set(position2.x, position2.y, position2.z);
|
|
1994
|
+
}
|
|
1995
|
+
move(position2) {
|
|
1996
|
+
this.moveCamera(position2);
|
|
1997
|
+
}
|
|
1998
|
+
rotate(pitch, yaw, roll) {
|
|
1999
|
+
this.cameraRig.rotateX(pitch);
|
|
2000
|
+
this.cameraRig.rotateY(yaw);
|
|
2001
|
+
this.cameraRig.rotateZ(roll);
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Get the DOM element for the renderer
|
|
2005
|
+
*/
|
|
2006
|
+
getDomElement() {
|
|
2007
|
+
return this.renderer.domElement;
|
|
2008
|
+
}
|
|
2009
|
+
applyDebugState(state2) {
|
|
2010
|
+
this.debugStateSnapshot = {
|
|
2011
|
+
enabled: state2.enabled,
|
|
2012
|
+
selected: [...state2.selected]
|
|
2013
|
+
};
|
|
2014
|
+
if (state2.enabled) {
|
|
2015
|
+
this.enableOrbitControls();
|
|
2016
|
+
this.updateOrbitTargetFromSelection(state2.selected);
|
|
2017
|
+
} else {
|
|
2018
|
+
this.orbitTarget = null;
|
|
2019
|
+
this.disableOrbitControls();
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
enableOrbitControls() {
|
|
2023
|
+
if (this.orbitControls) {
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
2027
|
+
this.orbitControls.enableDamping = true;
|
|
2028
|
+
this.orbitControls.dampingFactor = 0.05;
|
|
2029
|
+
this.orbitControls.screenSpacePanning = false;
|
|
2030
|
+
this.orbitControls.minDistance = 1;
|
|
2031
|
+
this.orbitControls.maxDistance = 500;
|
|
2032
|
+
this.orbitControls.maxPolarAngle = Math.PI / 2;
|
|
2033
|
+
}
|
|
2034
|
+
disableOrbitControls() {
|
|
2035
|
+
if (!this.orbitControls) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
this.orbitControls.dispose();
|
|
2039
|
+
this.orbitControls = null;
|
|
2040
|
+
}
|
|
2041
|
+
updateOrbitTargetFromSelection(selected) {
|
|
2042
|
+
if (!this.debugDelegate || selected.length === 0) {
|
|
2043
|
+
this.orbitTarget = null;
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
for (let i = selected.length - 1; i >= 0; i -= 1) {
|
|
2047
|
+
const uuid = selected[i];
|
|
2048
|
+
const targetObject = this.debugDelegate.resolveTarget(uuid);
|
|
2049
|
+
if (targetObject) {
|
|
2050
|
+
this.orbitTarget = targetObject;
|
|
2051
|
+
if (this.orbitControls) {
|
|
2052
|
+
targetObject.getWorldPosition(this.orbitTargetWorldPos);
|
|
2053
|
+
this.orbitControls.target.copy(this.orbitTargetWorldPos);
|
|
2054
|
+
}
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
this.orbitTarget = null;
|
|
2059
|
+
}
|
|
2060
|
+
detachDebugDelegate() {
|
|
2061
|
+
if (this.debugUnsubscribe) {
|
|
2062
|
+
try {
|
|
2063
|
+
this.debugUnsubscribe();
|
|
2064
|
+
} catch {
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
this.debugUnsubscribe = null;
|
|
2068
|
+
this.debugDelegate = null;
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
|
|
2074
|
+
// src/lib/camera/camera.ts
|
|
2075
|
+
import { Vector2 as Vector24, Vector3 as Vector39 } from "three";
|
|
2076
|
+
var CameraWrapper;
|
|
2077
|
+
var init_camera = __esm({
|
|
2078
|
+
"src/lib/camera/camera.ts"() {
|
|
2079
|
+
"use strict";
|
|
2080
|
+
CameraWrapper = class {
|
|
2081
|
+
cameraRef;
|
|
2082
|
+
constructor(camera) {
|
|
2083
|
+
this.cameraRef = camera;
|
|
2084
|
+
}
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
});
|
|
2088
|
+
|
|
2089
|
+
// src/lib/stage/debug-entity-cursor.ts
|
|
2090
|
+
import {
|
|
2091
|
+
Box3,
|
|
2092
|
+
BoxGeometry,
|
|
2093
|
+
Color as Color7,
|
|
2094
|
+
EdgesGeometry,
|
|
2095
|
+
Group as Group4,
|
|
2096
|
+
LineBasicMaterial,
|
|
2097
|
+
LineSegments,
|
|
2098
|
+
Mesh as Mesh3,
|
|
2099
|
+
MeshBasicMaterial,
|
|
2100
|
+
Vector3 as Vector310
|
|
2101
|
+
} from "three";
|
|
2102
|
+
var DebugEntityCursor;
|
|
2103
|
+
var init_debug_entity_cursor = __esm({
|
|
2104
|
+
"src/lib/stage/debug-entity-cursor.ts"() {
|
|
2105
|
+
"use strict";
|
|
2106
|
+
DebugEntityCursor = class {
|
|
2107
|
+
scene;
|
|
2108
|
+
container;
|
|
2109
|
+
fillMesh;
|
|
2110
|
+
edgeLines;
|
|
2111
|
+
currentColor = new Color7(65280);
|
|
2112
|
+
bbox = new Box3();
|
|
2113
|
+
size = new Vector310();
|
|
2114
|
+
center = new Vector310();
|
|
2115
|
+
constructor(scene) {
|
|
2116
|
+
this.scene = scene;
|
|
2117
|
+
const initialGeometry = new BoxGeometry(1, 1, 1);
|
|
2118
|
+
this.fillMesh = new Mesh3(
|
|
2119
|
+
initialGeometry,
|
|
2120
|
+
new MeshBasicMaterial({
|
|
2121
|
+
color: this.currentColor,
|
|
2122
|
+
transparent: true,
|
|
2123
|
+
opacity: 0.12,
|
|
2124
|
+
depthWrite: false
|
|
2125
|
+
})
|
|
2126
|
+
);
|
|
2127
|
+
const edges = new EdgesGeometry(initialGeometry);
|
|
2128
|
+
this.edgeLines = new LineSegments(
|
|
2129
|
+
edges,
|
|
2130
|
+
new LineBasicMaterial({ color: this.currentColor, linewidth: 1 })
|
|
2131
|
+
);
|
|
2132
|
+
this.container = new Group4();
|
|
2133
|
+
this.container.name = "DebugEntityCursor";
|
|
2134
|
+
this.container.add(this.fillMesh);
|
|
2135
|
+
this.container.add(this.edgeLines);
|
|
2136
|
+
this.container.visible = false;
|
|
2137
|
+
this.scene.add(this.container);
|
|
2138
|
+
}
|
|
2139
|
+
setColor(color) {
|
|
2140
|
+
this.currentColor.set(color);
|
|
2141
|
+
this.fillMesh.material.color.set(this.currentColor);
|
|
2142
|
+
this.edgeLines.material.color.set(this.currentColor);
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
2145
|
+
* Update the cursor to enclose the provided Object3D using a world-space AABB.
|
|
2146
|
+
*/
|
|
2147
|
+
updateFromObject(object) {
|
|
2148
|
+
if (!object) {
|
|
2149
|
+
this.hide();
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
this.bbox.setFromObject(object);
|
|
2153
|
+
if (!isFinite(this.bbox.min.x) || !isFinite(this.bbox.max.x)) {
|
|
2154
|
+
this.hide();
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
this.bbox.getSize(this.size);
|
|
2158
|
+
this.bbox.getCenter(this.center);
|
|
2159
|
+
const newGeom = new BoxGeometry(
|
|
2160
|
+
Math.max(this.size.x, 1e-6),
|
|
2161
|
+
Math.max(this.size.y, 1e-6),
|
|
2162
|
+
Math.max(this.size.z, 1e-6)
|
|
2163
|
+
);
|
|
2164
|
+
this.fillMesh.geometry.dispose();
|
|
2165
|
+
this.fillMesh.geometry = newGeom;
|
|
2166
|
+
const newEdges = new EdgesGeometry(newGeom);
|
|
2167
|
+
this.edgeLines.geometry.dispose();
|
|
2168
|
+
this.edgeLines.geometry = newEdges;
|
|
2169
|
+
this.container.position.copy(this.center);
|
|
2170
|
+
this.container.visible = true;
|
|
2171
|
+
}
|
|
2172
|
+
hide() {
|
|
2173
|
+
this.container.visible = false;
|
|
2174
|
+
}
|
|
2175
|
+
dispose() {
|
|
2176
|
+
this.scene.remove(this.container);
|
|
2177
|
+
this.fillMesh.geometry.dispose();
|
|
2178
|
+
this.fillMesh.material.dispose();
|
|
2179
|
+
this.edgeLines.geometry.dispose();
|
|
2180
|
+
this.edgeLines.material.dispose();
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
|
|
2186
|
+
// src/lib/stage/stage-debug-delegate.ts
|
|
2187
|
+
import { Ray } from "@dimforge/rapier3d-compat";
|
|
2188
|
+
import { BufferAttribute, BufferGeometry as BufferGeometry3, LineBasicMaterial as LineBasicMaterial2, LineSegments as LineSegments2, Raycaster, Vector2 as Vector25 } from "three";
|
|
2189
|
+
var SELECT_TOOL_COLOR, DELETE_TOOL_COLOR, StageDebugDelegate;
|
|
2190
|
+
var init_stage_debug_delegate = __esm({
|
|
2191
|
+
"src/lib/stage/stage-debug-delegate.ts"() {
|
|
2192
|
+
"use strict";
|
|
2193
|
+
init_debug_state();
|
|
2194
|
+
init_debug_entity_cursor();
|
|
2195
|
+
SELECT_TOOL_COLOR = 2293538;
|
|
2196
|
+
DELETE_TOOL_COLOR = 16724787;
|
|
2197
|
+
StageDebugDelegate = class {
|
|
2198
|
+
stage;
|
|
2199
|
+
options;
|
|
2200
|
+
mouseNdc = new Vector25(-2, -2);
|
|
2201
|
+
raycaster = new Raycaster();
|
|
2202
|
+
isMouseDown = false;
|
|
2203
|
+
disposeFns = [];
|
|
2204
|
+
debugCursor = null;
|
|
2205
|
+
debugLines = null;
|
|
2206
|
+
constructor(stage, options) {
|
|
2207
|
+
this.stage = stage;
|
|
2208
|
+
this.options = {
|
|
2209
|
+
maxRayDistance: options?.maxRayDistance ?? 5e3,
|
|
2210
|
+
addEntityFactory: options?.addEntityFactory ?? null
|
|
2211
|
+
};
|
|
2212
|
+
if (this.stage.scene) {
|
|
2213
|
+
this.debugLines = new LineSegments2(
|
|
2214
|
+
new BufferGeometry3(),
|
|
2215
|
+
new LineBasicMaterial2({ vertexColors: true })
|
|
2216
|
+
);
|
|
2217
|
+
this.stage.scene.scene.add(this.debugLines);
|
|
2218
|
+
this.debugLines.visible = true;
|
|
2219
|
+
this.debugCursor = new DebugEntityCursor(this.stage.scene.scene);
|
|
2220
|
+
}
|
|
2221
|
+
this.attachDomListeners();
|
|
2222
|
+
}
|
|
2223
|
+
update() {
|
|
2224
|
+
if (!debugState.enabled) return;
|
|
2225
|
+
if (!this.stage.scene || !this.stage.world || !this.stage.cameraRef) return;
|
|
2226
|
+
const { world, cameraRef } = this.stage;
|
|
2227
|
+
if (this.debugLines) {
|
|
2228
|
+
const { vertices, colors } = world.world.debugRender();
|
|
2229
|
+
this.debugLines.geometry.setAttribute("position", new BufferAttribute(vertices, 3));
|
|
2230
|
+
this.debugLines.geometry.setAttribute("color", new BufferAttribute(colors, 4));
|
|
2231
|
+
}
|
|
2232
|
+
const tool = getDebugTool();
|
|
2233
|
+
const isCursorTool = tool === "select" || tool === "delete";
|
|
2234
|
+
this.raycaster.setFromCamera(this.mouseNdc, cameraRef.camera);
|
|
2235
|
+
const origin = this.raycaster.ray.origin.clone();
|
|
2236
|
+
const direction = this.raycaster.ray.direction.clone().normalize();
|
|
2237
|
+
const rapierRay = new Ray(
|
|
2238
|
+
{ x: origin.x, y: origin.y, z: origin.z },
|
|
2239
|
+
{ x: direction.x, y: direction.y, z: direction.z }
|
|
2240
|
+
);
|
|
2241
|
+
const hit = world.world.castRay(rapierRay, this.options.maxRayDistance, true);
|
|
2242
|
+
if (hit && isCursorTool) {
|
|
2243
|
+
const rigidBody = hit.collider?._parent;
|
|
2244
|
+
const hoveredUuid2 = rigidBody?.userData?.uuid;
|
|
2245
|
+
if (hoveredUuid2) {
|
|
2246
|
+
const entity = this.stage._debugMap.get(hoveredUuid2);
|
|
2247
|
+
if (entity) setHoveredEntity(entity);
|
|
2248
|
+
} else {
|
|
2249
|
+
resetHoveredEntity();
|
|
2250
|
+
}
|
|
2251
|
+
if (this.isMouseDown) {
|
|
2252
|
+
this.handleActionOnHit(hoveredUuid2 ?? null, origin, direction, hit.toi);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
this.isMouseDown = false;
|
|
2256
|
+
const hoveredUuid = getHoveredEntity();
|
|
2257
|
+
if (!hoveredUuid) {
|
|
2258
|
+
this.debugCursor?.hide();
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
const hoveredEntity = this.stage._debugMap.get(`${hoveredUuid}`);
|
|
2262
|
+
const targetObject = hoveredEntity?.group ?? hoveredEntity?.mesh ?? null;
|
|
2263
|
+
if (!targetObject) {
|
|
2264
|
+
this.debugCursor?.hide();
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
switch (tool) {
|
|
2268
|
+
case "select":
|
|
2269
|
+
this.debugCursor?.setColor(SELECT_TOOL_COLOR);
|
|
2270
|
+
break;
|
|
2271
|
+
case "delete":
|
|
2272
|
+
this.debugCursor?.setColor(DELETE_TOOL_COLOR);
|
|
2273
|
+
break;
|
|
2274
|
+
default:
|
|
2275
|
+
this.debugCursor?.setColor(16777215);
|
|
2276
|
+
break;
|
|
2277
|
+
}
|
|
2278
|
+
this.debugCursor?.updateFromObject(targetObject);
|
|
2279
|
+
}
|
|
2280
|
+
dispose() {
|
|
2281
|
+
this.disposeFns.forEach((fn) => fn());
|
|
2282
|
+
this.disposeFns = [];
|
|
2283
|
+
this.debugCursor?.dispose();
|
|
2284
|
+
if (this.debugLines && this.stage.scene) {
|
|
2285
|
+
this.stage.scene.scene.remove(this.debugLines);
|
|
2286
|
+
this.debugLines.geometry.dispose();
|
|
2287
|
+
this.debugLines.material.dispose();
|
|
2288
|
+
this.debugLines = null;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
handleActionOnHit(hoveredUuid, origin, direction, toi) {
|
|
2292
|
+
const tool = getDebugTool();
|
|
2293
|
+
switch (tool) {
|
|
2294
|
+
case "select": {
|
|
2295
|
+
if (hoveredUuid) {
|
|
2296
|
+
const entity = this.stage._debugMap.get(hoveredUuid);
|
|
2297
|
+
if (entity) setSelectedEntity(entity);
|
|
2298
|
+
}
|
|
2299
|
+
break;
|
|
2300
|
+
}
|
|
2301
|
+
case "delete": {
|
|
2302
|
+
if (hoveredUuid) {
|
|
2303
|
+
this.stage.removeEntityByUuid(hoveredUuid);
|
|
2304
|
+
}
|
|
2305
|
+
break;
|
|
2306
|
+
}
|
|
2307
|
+
case "scale": {
|
|
2308
|
+
if (!this.options.addEntityFactory) break;
|
|
2309
|
+
const hitPosition = origin.clone().add(direction.clone().multiplyScalar(toi));
|
|
2310
|
+
const newNode = this.options.addEntityFactory({ position: hitPosition });
|
|
2311
|
+
if (newNode) {
|
|
2312
|
+
Promise.resolve(newNode).then((node) => {
|
|
2313
|
+
if (node) this.stage.spawnEntity(node);
|
|
2314
|
+
}).catch(() => {
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
break;
|
|
2318
|
+
}
|
|
2319
|
+
default:
|
|
2320
|
+
break;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
attachDomListeners() {
|
|
2324
|
+
const canvas = this.stage.cameraRef?.renderer.domElement ?? this.stage.scene?.zylemCamera.renderer.domElement;
|
|
2325
|
+
if (!canvas) return;
|
|
2326
|
+
const onMouseMove = (e) => {
|
|
2327
|
+
const rect = canvas.getBoundingClientRect();
|
|
2328
|
+
const x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
2329
|
+
const y = -((e.clientY - rect.top) / rect.height * 2 - 1);
|
|
2330
|
+
this.mouseNdc.set(x, y);
|
|
2331
|
+
};
|
|
2332
|
+
const onMouseDown = (e) => {
|
|
2333
|
+
this.isMouseDown = true;
|
|
2334
|
+
};
|
|
2335
|
+
canvas.addEventListener("mousemove", onMouseMove);
|
|
2336
|
+
canvas.addEventListener("mousedown", onMouseDown);
|
|
2337
|
+
this.disposeFns.push(() => canvas.removeEventListener("mousemove", onMouseMove));
|
|
2338
|
+
this.disposeFns.push(() => canvas.removeEventListener("mousedown", onMouseDown));
|
|
2339
|
+
}
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
});
|
|
2343
|
+
|
|
2344
|
+
// src/lib/stage/zylem-stage.ts
|
|
2345
|
+
import { addComponent, addEntity, createWorld as createECS, removeEntity } from "bitecs";
|
|
2346
|
+
import { Color as Color8, Vector3 as Vector312, Vector2 as Vector26 } from "three";
|
|
2347
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
2348
|
+
var STAGE_TYPE, ZylemStage;
|
|
2349
|
+
var init_zylem_stage = __esm({
|
|
2350
|
+
"src/lib/stage/zylem-stage.ts"() {
|
|
2351
|
+
"use strict";
|
|
2352
|
+
init_world();
|
|
2353
|
+
init_zylem_scene();
|
|
2354
|
+
init_stage_state();
|
|
2355
|
+
init_vector();
|
|
2356
|
+
init_debug_state();
|
|
2357
|
+
init_game_state();
|
|
2358
|
+
init_lifecycle_base();
|
|
2359
|
+
init_transformable_system();
|
|
2360
|
+
init_base_node();
|
|
2361
|
+
init_perspective();
|
|
2362
|
+
init_camera();
|
|
2363
|
+
init_stage_debug_delegate();
|
|
2364
|
+
init_entity();
|
|
2365
|
+
init_zylem_camera();
|
|
2366
|
+
STAGE_TYPE = "Stage";
|
|
2367
|
+
ZylemStage = class extends LifeCycleBase {
|
|
2368
|
+
type = STAGE_TYPE;
|
|
2369
|
+
state = {
|
|
2370
|
+
backgroundColor: ZylemBlueColor,
|
|
2371
|
+
backgroundImage: null,
|
|
2372
|
+
inputs: {
|
|
2373
|
+
p1: ["gamepad-1", "keyboard"],
|
|
2374
|
+
p2: ["gamepad-2", "keyboard"]
|
|
2375
|
+
},
|
|
2376
|
+
gravity: new Vector312(0, 0, 0),
|
|
2377
|
+
variables: {},
|
|
2378
|
+
entities: []
|
|
2379
|
+
};
|
|
2380
|
+
gravity;
|
|
2381
|
+
world;
|
|
2382
|
+
scene;
|
|
2383
|
+
children = [];
|
|
2384
|
+
_childrenMap = /* @__PURE__ */ new Map();
|
|
2385
|
+
_removalMap = /* @__PURE__ */ new Map();
|
|
2386
|
+
pendingEntities = [];
|
|
2387
|
+
pendingPromises = [];
|
|
2388
|
+
isLoaded = false;
|
|
2389
|
+
_debugMap = /* @__PURE__ */ new Map();
|
|
2390
|
+
entityAddedHandlers = [];
|
|
2391
|
+
loadingHandlers = [];
|
|
2392
|
+
ecs = createECS();
|
|
2393
|
+
testSystem = null;
|
|
2394
|
+
transformSystem = null;
|
|
2395
|
+
debugDelegate = null;
|
|
2396
|
+
cameraDebugDelegate = null;
|
|
2397
|
+
uuid;
|
|
2398
|
+
wrapperRef = null;
|
|
2399
|
+
camera;
|
|
2400
|
+
cameraRef = null;
|
|
2401
|
+
/**
|
|
2402
|
+
* Create a new stage.
|
|
2403
|
+
* @param options Stage options: partial config, camera, and initial entities or factories
|
|
2404
|
+
*/
|
|
2405
|
+
constructor(options = []) {
|
|
2406
|
+
super();
|
|
2407
|
+
this.world = null;
|
|
2408
|
+
this.scene = null;
|
|
2409
|
+
this.uuid = nanoid2();
|
|
2410
|
+
const { config, entities, asyncEntities, camera } = this.parseOptions(options);
|
|
2411
|
+
this.camera = camera;
|
|
2412
|
+
this.children = entities;
|
|
2413
|
+
this.pendingEntities = asyncEntities;
|
|
2414
|
+
this.saveState({ ...this.state, ...config, entities: [] });
|
|
2415
|
+
this.gravity = config.gravity ?? new Vector312(0, 0, 0);
|
|
2416
|
+
}
|
|
2417
|
+
parseOptions(options) {
|
|
2418
|
+
let config = {};
|
|
2419
|
+
const entities = [];
|
|
2420
|
+
const asyncEntities = [];
|
|
2421
|
+
let camera;
|
|
2422
|
+
for (const item of options) {
|
|
2423
|
+
if (this.isCameraWrapper(item)) {
|
|
2424
|
+
camera = item;
|
|
2425
|
+
} else if (this.isBaseNode(item)) {
|
|
2426
|
+
entities.push(item);
|
|
2427
|
+
} else if (this.isEntityInput(item)) {
|
|
2428
|
+
asyncEntities.push(item);
|
|
2429
|
+
} else if (this.isZylemStageConfig(item)) {
|
|
2430
|
+
config = { ...config, ...item };
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return { config, entities, asyncEntities, camera };
|
|
2434
|
+
}
|
|
2435
|
+
isZylemStageConfig(item) {
|
|
2436
|
+
return item && typeof item === "object" && !(item instanceof BaseNode) && !(item instanceof CameraWrapper);
|
|
2437
|
+
}
|
|
2438
|
+
isBaseNode(item) {
|
|
2439
|
+
return item && typeof item === "object" && typeof item.create === "function";
|
|
2440
|
+
}
|
|
2441
|
+
isCameraWrapper(item) {
|
|
2442
|
+
return item && typeof item === "object" && item.constructor.name === "CameraWrapper";
|
|
2443
|
+
}
|
|
2444
|
+
isEntityInput(item) {
|
|
2445
|
+
if (!item) return false;
|
|
2446
|
+
if (this.isBaseNode(item)) return true;
|
|
2447
|
+
if (typeof item === "function") return true;
|
|
2448
|
+
if (typeof item === "object" && typeof item.then === "function") return true;
|
|
2449
|
+
return false;
|
|
2450
|
+
}
|
|
2451
|
+
isThenable(value) {
|
|
2452
|
+
return !!value && typeof value.then === "function";
|
|
2453
|
+
}
|
|
2454
|
+
handleEntityImmediatelyOrQueue(entity) {
|
|
2455
|
+
if (this.isLoaded) {
|
|
2456
|
+
this.spawnEntity(entity);
|
|
2457
|
+
} else {
|
|
2458
|
+
this.children.push(entity);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
handlePromiseWithSpawnOnResolve(promise) {
|
|
2462
|
+
if (this.isLoaded) {
|
|
2463
|
+
promise.then((entity) => this.spawnEntity(entity)).catch((e) => console.error("Failed to build async entity", e));
|
|
2464
|
+
} else {
|
|
2465
|
+
this.pendingPromises.push(promise);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
saveState(state2) {
|
|
2469
|
+
this.state = state2;
|
|
2470
|
+
}
|
|
2471
|
+
setState() {
|
|
2472
|
+
const { backgroundColor, backgroundImage } = this.state;
|
|
2473
|
+
const color = backgroundColor instanceof Color8 ? backgroundColor : new Color8(backgroundColor);
|
|
2474
|
+
setStageBackgroundColor(color);
|
|
2475
|
+
setStageBackgroundImage(backgroundImage);
|
|
2476
|
+
setStageVariables(this.state.variables ?? {});
|
|
2477
|
+
}
|
|
2478
|
+
/**
|
|
2479
|
+
* Load and initialize the stage's scene and world.
|
|
2480
|
+
* @param id DOM element id for the renderer container
|
|
2481
|
+
* @param camera Optional camera override
|
|
2482
|
+
*/
|
|
2483
|
+
async load(id, camera) {
|
|
2484
|
+
this.setState();
|
|
2485
|
+
const zylemCamera = camera || (this.camera ? this.camera.cameraRef : this.createDefaultCamera());
|
|
2486
|
+
this.cameraRef = zylemCamera;
|
|
2487
|
+
this.scene = new ZylemScene(id, zylemCamera, this.state);
|
|
2488
|
+
const physicsWorld = await ZylemWorld.loadPhysics(this.gravity ?? new Vector312(0, 0, 0));
|
|
2489
|
+
this.world = new ZylemWorld(physicsWorld);
|
|
2490
|
+
this.scene.setup();
|
|
2491
|
+
this.emitLoading({ type: "start", message: "Loading stage...", progress: 0 });
|
|
2492
|
+
const total = this.children.length + this.pendingEntities.length + this.pendingPromises.length;
|
|
2493
|
+
let current = 0;
|
|
2494
|
+
for (let child of this.children) {
|
|
2495
|
+
this.spawnEntity(child);
|
|
2496
|
+
current++;
|
|
2497
|
+
this.emitLoading({
|
|
2498
|
+
type: "progress",
|
|
2499
|
+
message: `Loaded entity ${child.name || "unknown"}`,
|
|
2500
|
+
progress: current / total,
|
|
2501
|
+
current,
|
|
2502
|
+
total
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
if (this.pendingEntities.length) {
|
|
2506
|
+
this.enqueue(...this.pendingEntities);
|
|
2507
|
+
current += this.pendingEntities.length;
|
|
2508
|
+
this.pendingEntities = [];
|
|
2509
|
+
}
|
|
2510
|
+
if (this.pendingPromises.length) {
|
|
2511
|
+
for (const promise of this.pendingPromises) {
|
|
2512
|
+
promise.then((entity) => {
|
|
2513
|
+
this.spawnEntity(entity);
|
|
2514
|
+
}).catch((e) => console.error("Failed to resolve pending stage entity", e));
|
|
2515
|
+
}
|
|
2516
|
+
current += this.pendingPromises.length;
|
|
2517
|
+
this.pendingPromises = [];
|
|
2518
|
+
}
|
|
2519
|
+
this.transformSystem = createTransformSystem(this);
|
|
2520
|
+
this.isLoaded = true;
|
|
2521
|
+
this.emitLoading({ type: "complete", message: "Stage loaded", progress: 1 });
|
|
2522
|
+
}
|
|
2523
|
+
createDefaultCamera() {
|
|
2524
|
+
const width = window.innerWidth;
|
|
2525
|
+
const height = window.innerHeight;
|
|
2526
|
+
const screenResolution = new Vector26(width, height);
|
|
2527
|
+
return new ZylemCamera(Perspectives.ThirdPerson, screenResolution);
|
|
2528
|
+
}
|
|
2529
|
+
_setup(params) {
|
|
2530
|
+
if (!this.scene || !this.world) {
|
|
2531
|
+
this.logMissingEntities();
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
if (debugState.enabled) {
|
|
2535
|
+
this.debugDelegate = new StageDebugDelegate(this);
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
_update(params) {
|
|
2539
|
+
const { delta } = params;
|
|
2540
|
+
if (!this.scene || !this.world) {
|
|
2541
|
+
this.logMissingEntities();
|
|
2542
|
+
return;
|
|
2543
|
+
}
|
|
2544
|
+
this.world.update(params);
|
|
2545
|
+
this.transformSystem(this.ecs);
|
|
2546
|
+
this._childrenMap.forEach((child, eid) => {
|
|
2547
|
+
child.nodeUpdate({
|
|
2548
|
+
...params,
|
|
2549
|
+
me: child
|
|
2550
|
+
});
|
|
2551
|
+
if (child.markedForRemoval) {
|
|
2552
|
+
this.removeEntityByUuid(child.uuid);
|
|
2553
|
+
}
|
|
2554
|
+
});
|
|
2555
|
+
this.scene.update({ delta });
|
|
2556
|
+
}
|
|
2557
|
+
outOfLoop() {
|
|
2558
|
+
this.debugUpdate();
|
|
2559
|
+
}
|
|
2560
|
+
/** Update debug overlays and helpers if enabled. */
|
|
2561
|
+
debugUpdate() {
|
|
2562
|
+
if (debugState.enabled) {
|
|
2563
|
+
this.debugDelegate?.update();
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
/** Cleanup owned resources when the stage is destroyed. */
|
|
2567
|
+
_destroy(params) {
|
|
2568
|
+
this._childrenMap.forEach((child) => {
|
|
2569
|
+
try {
|
|
2570
|
+
child.nodeDestroy({ me: child, globals: getGlobals() });
|
|
2571
|
+
} catch {
|
|
2572
|
+
}
|
|
2573
|
+
});
|
|
2574
|
+
this._childrenMap.clear();
|
|
2575
|
+
this._removalMap.clear();
|
|
2576
|
+
this._debugMap.clear();
|
|
2577
|
+
this.world?.destroy();
|
|
2578
|
+
this.scene?.destroy();
|
|
2579
|
+
this.debugDelegate?.dispose();
|
|
2580
|
+
this.cameraRef?.setDebugDelegate(null);
|
|
2581
|
+
this.cameraDebugDelegate = null;
|
|
2582
|
+
this.isLoaded = false;
|
|
2583
|
+
this.world = null;
|
|
2584
|
+
this.scene = null;
|
|
2585
|
+
this.cameraRef = null;
|
|
2586
|
+
resetStageVariables();
|
|
2587
|
+
clearVariables(this);
|
|
2588
|
+
}
|
|
2589
|
+
/**
|
|
2590
|
+
* Create, register, and add an entity to the scene/world.
|
|
2591
|
+
* Safe to call only after `load` when scene/world exist.
|
|
2592
|
+
*/
|
|
2593
|
+
async spawnEntity(child) {
|
|
2594
|
+
if (!this.scene || !this.world) {
|
|
2595
|
+
return;
|
|
2596
|
+
}
|
|
2597
|
+
const entity = child.create();
|
|
2598
|
+
const eid = addEntity(this.ecs);
|
|
2599
|
+
entity.eid = eid;
|
|
2600
|
+
this.scene.addEntity(entity);
|
|
2601
|
+
if (child.behaviors) {
|
|
2602
|
+
for (let behavior of child.behaviors) {
|
|
2603
|
+
addComponent(this.ecs, behavior.component, entity.eid);
|
|
2604
|
+
const keys = Object.keys(behavior.values);
|
|
2605
|
+
for (const key of keys) {
|
|
2606
|
+
behavior.component[key][entity.eid] = behavior.values[key];
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
if (entity.colliderDesc) {
|
|
2611
|
+
this.world.addEntity(entity);
|
|
2612
|
+
}
|
|
2613
|
+
child.nodeSetup({
|
|
2614
|
+
me: child,
|
|
2615
|
+
globals: getGlobals(),
|
|
2616
|
+
camera: this.scene.zylemCamera
|
|
2617
|
+
});
|
|
2618
|
+
this.addEntityToStage(entity);
|
|
2619
|
+
}
|
|
2620
|
+
buildEntityState(child) {
|
|
2621
|
+
if (child instanceof GameEntity) {
|
|
2622
|
+
return { ...child.buildInfo() };
|
|
2623
|
+
}
|
|
2624
|
+
return {
|
|
2625
|
+
uuid: child.uuid,
|
|
2626
|
+
name: child.name,
|
|
2627
|
+
eid: child.eid
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
/** Add the entity to internal maps and notify listeners. */
|
|
2631
|
+
addEntityToStage(entity) {
|
|
2632
|
+
this._childrenMap.set(entity.eid, entity);
|
|
2633
|
+
if (debugState.enabled) {
|
|
2634
|
+
this._debugMap.set(entity.uuid, entity);
|
|
2635
|
+
}
|
|
2636
|
+
for (const handler of this.entityAddedHandlers) {
|
|
2637
|
+
try {
|
|
2638
|
+
handler(entity);
|
|
2639
|
+
} catch (e) {
|
|
2640
|
+
console.error("onEntityAdded handler failed", e);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
/**
|
|
2645
|
+
* Subscribe to entity-added events.
|
|
2646
|
+
* @param callback Invoked for each entity when added
|
|
2647
|
+
* @param options.replayExisting If true and stage already loaded, replays existing entities
|
|
2648
|
+
* @returns Unsubscribe function
|
|
2649
|
+
*/
|
|
2650
|
+
onEntityAdded(callback, options) {
|
|
2651
|
+
this.entityAddedHandlers.push(callback);
|
|
2652
|
+
if (options?.replayExisting && this.isLoaded) {
|
|
2653
|
+
this._childrenMap.forEach((entity) => {
|
|
2654
|
+
try {
|
|
2655
|
+
callback(entity);
|
|
2656
|
+
} catch (e) {
|
|
2657
|
+
console.error("onEntityAdded replay failed", e);
|
|
2658
|
+
}
|
|
2659
|
+
});
|
|
2660
|
+
}
|
|
2661
|
+
return () => {
|
|
2662
|
+
this.entityAddedHandlers = this.entityAddedHandlers.filter((h) => h !== callback);
|
|
2663
|
+
};
|
|
2664
|
+
}
|
|
2665
|
+
onLoading(callback) {
|
|
2666
|
+
this.loadingHandlers.push(callback);
|
|
2667
|
+
return () => {
|
|
2668
|
+
this.loadingHandlers = this.loadingHandlers.filter((h) => h !== callback);
|
|
2669
|
+
};
|
|
2670
|
+
}
|
|
2671
|
+
emitLoading(event) {
|
|
2672
|
+
this.loadingHandlers.forEach((h) => h(event));
|
|
2673
|
+
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Remove an entity and its resources by its UUID.
|
|
2676
|
+
* @returns true if removed, false if not found or stage not ready
|
|
2677
|
+
*/
|
|
2678
|
+
removeEntityByUuid(uuid) {
|
|
2679
|
+
if (!this.scene || !this.world) return false;
|
|
2680
|
+
const mapEntity = this.world.collisionMap.get(uuid);
|
|
2681
|
+
const entity = mapEntity ?? this._debugMap.get(uuid);
|
|
2682
|
+
if (!entity) return false;
|
|
2683
|
+
this.world.destroyEntity(entity);
|
|
2684
|
+
if (entity.group) {
|
|
2685
|
+
this.scene.scene.remove(entity.group);
|
|
2686
|
+
} else if (entity.mesh) {
|
|
2687
|
+
this.scene.scene.remove(entity.mesh);
|
|
2688
|
+
}
|
|
2689
|
+
removeEntity(this.ecs, entity.eid);
|
|
2690
|
+
let foundKey = null;
|
|
2691
|
+
this._childrenMap.forEach((value, key) => {
|
|
2692
|
+
if (value.uuid === uuid) {
|
|
2693
|
+
foundKey = key;
|
|
2694
|
+
}
|
|
2695
|
+
});
|
|
2696
|
+
if (foundKey !== null) {
|
|
2697
|
+
this._childrenMap.delete(foundKey);
|
|
2698
|
+
}
|
|
2699
|
+
this._debugMap.delete(uuid);
|
|
2700
|
+
return true;
|
|
2701
|
+
}
|
|
2702
|
+
/** Get an entity by its name; returns null if not found. */
|
|
2703
|
+
getEntityByName(name) {
|
|
2704
|
+
const arr = Object.entries(Object.fromEntries(this._childrenMap)).map((entry) => entry[1]);
|
|
2705
|
+
const entity = arr.find((child) => child.name === name);
|
|
2706
|
+
if (!entity) {
|
|
2707
|
+
console.warn(`Entity ${name} not found`);
|
|
2708
|
+
}
|
|
2709
|
+
return entity ?? null;
|
|
2710
|
+
}
|
|
2711
|
+
logMissingEntities() {
|
|
2712
|
+
console.warn("Zylem world or scene is null");
|
|
2713
|
+
}
|
|
2714
|
+
/** Resize renderer viewport. */
|
|
2715
|
+
resize(width, height) {
|
|
2716
|
+
if (this.scene) {
|
|
2717
|
+
this.scene.updateRenderer(width, height);
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Enqueue items to be spawned. Items can be:
|
|
2722
|
+
* - BaseNode instances (immediate or deferred until load)
|
|
2723
|
+
* - Factory functions returning BaseNode or Promise<BaseNode>
|
|
2724
|
+
* - Promises resolving to BaseNode
|
|
2725
|
+
*/
|
|
2726
|
+
enqueue(...items) {
|
|
2727
|
+
for (const item of items) {
|
|
2728
|
+
if (!item) continue;
|
|
2729
|
+
if (this.isBaseNode(item)) {
|
|
2730
|
+
this.handleEntityImmediatelyOrQueue(item);
|
|
2731
|
+
continue;
|
|
2732
|
+
}
|
|
2733
|
+
if (typeof item === "function") {
|
|
2734
|
+
try {
|
|
2735
|
+
const result = item();
|
|
2736
|
+
if (this.isBaseNode(result)) {
|
|
2737
|
+
this.handleEntityImmediatelyOrQueue(result);
|
|
2738
|
+
} else if (this.isThenable(result)) {
|
|
2739
|
+
this.handlePromiseWithSpawnOnResolve(result);
|
|
2740
|
+
}
|
|
2741
|
+
} catch (error) {
|
|
2742
|
+
console.error("Error executing entity factory", error);
|
|
2743
|
+
}
|
|
2744
|
+
continue;
|
|
2745
|
+
}
|
|
2746
|
+
if (this.isThenable(item)) {
|
|
2747
|
+
this.handlePromiseWithSpawnOnResolve(item);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
});
|
|
2754
|
+
|
|
2755
|
+
// src/lib/stage/stage-default.ts
|
|
2756
|
+
import { proxy as proxy4 } from "valtio/vanilla";
|
|
2757
|
+
import { Vector3 as Vector313 } from "three";
|
|
2758
|
+
function getStageOptions(options) {
|
|
2759
|
+
const defaults = getStageDefaultConfig();
|
|
2760
|
+
let originalConfig = {};
|
|
2761
|
+
if (typeof options[0] === "object") {
|
|
2762
|
+
originalConfig = options.shift() ?? {};
|
|
2763
|
+
}
|
|
2764
|
+
const combinedConfig = { ...defaults, ...originalConfig };
|
|
2765
|
+
return [combinedConfig, ...options];
|
|
2766
|
+
}
|
|
2767
|
+
function getStageDefaultConfig() {
|
|
2768
|
+
return {
|
|
2769
|
+
backgroundColor: stageDefaultsState.backgroundColor,
|
|
2770
|
+
backgroundImage: stageDefaultsState.backgroundImage ?? null,
|
|
2771
|
+
inputs: stageDefaultsState.inputs ? { ...stageDefaultsState.inputs } : void 0,
|
|
2772
|
+
gravity: stageDefaultsState.gravity,
|
|
2773
|
+
variables: stageDefaultsState.variables ? { ...stageDefaultsState.variables } : void 0
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
var initialDefaults, stageDefaultsState;
|
|
2777
|
+
var init_stage_default = __esm({
|
|
2778
|
+
"src/lib/stage/stage-default.ts"() {
|
|
2779
|
+
"use strict";
|
|
2780
|
+
init_vector();
|
|
2781
|
+
initialDefaults = {
|
|
2782
|
+
backgroundColor: ZylemBlueColor,
|
|
2783
|
+
backgroundImage: null,
|
|
2784
|
+
inputs: {
|
|
2785
|
+
p1: ["gamepad-1", "keyboard"],
|
|
2786
|
+
p2: ["gamepad-2", "keyboard"]
|
|
2787
|
+
},
|
|
2788
|
+
gravity: new Vector313(0, 0, 0),
|
|
2789
|
+
variables: {}
|
|
2790
|
+
};
|
|
2791
|
+
stageDefaultsState = proxy4({
|
|
2792
|
+
...initialDefaults
|
|
2793
|
+
});
|
|
2794
|
+
}
|
|
2795
|
+
});
|
|
2796
|
+
|
|
2797
|
+
// src/lib/stage/stage.ts
|
|
2798
|
+
function createStage(...options) {
|
|
2799
|
+
const _options = getStageOptions(options);
|
|
2800
|
+
return new Stage([..._options]);
|
|
2801
|
+
}
|
|
2802
|
+
var Stage;
|
|
2803
|
+
var init_stage = __esm({
|
|
2804
|
+
"src/lib/stage/stage.ts"() {
|
|
2805
|
+
"use strict";
|
|
2806
|
+
init_zylem_stage();
|
|
2807
|
+
init_camera();
|
|
2808
|
+
init_stage_state();
|
|
2809
|
+
init_stage_default();
|
|
2810
|
+
Stage = class {
|
|
2811
|
+
wrappedStage;
|
|
2812
|
+
options = [];
|
|
2813
|
+
// TODO: these shouldn't be here maybe more like nextFrame(stageInstance, () => {})
|
|
2814
|
+
update = () => {
|
|
2815
|
+
};
|
|
2816
|
+
setup = () => {
|
|
2817
|
+
};
|
|
2818
|
+
destroy = () => {
|
|
2819
|
+
};
|
|
2820
|
+
constructor(options) {
|
|
2821
|
+
this.options = options;
|
|
2822
|
+
this.wrappedStage = null;
|
|
2823
|
+
}
|
|
2824
|
+
async load(id, camera) {
|
|
2825
|
+
stageState.entities = [];
|
|
2826
|
+
this.wrappedStage = new ZylemStage(this.options);
|
|
2827
|
+
this.wrappedStage.wrapperRef = this;
|
|
2828
|
+
const zylemCamera = camera instanceof CameraWrapper ? camera.cameraRef : camera;
|
|
2829
|
+
await this.wrappedStage.load(id, zylemCamera);
|
|
2830
|
+
this.wrappedStage.onEntityAdded((child) => {
|
|
2831
|
+
const next = this.wrappedStage.buildEntityState(child);
|
|
2832
|
+
stageState.entities = [...stageState.entities, next];
|
|
2833
|
+
}, { replayExisting: true });
|
|
2834
|
+
}
|
|
2835
|
+
async addEntities(entities) {
|
|
2836
|
+
this.options.push(...entities);
|
|
2837
|
+
if (!this.wrappedStage) {
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
this.wrappedStage.enqueue(...entities);
|
|
2841
|
+
}
|
|
2842
|
+
add(...inputs) {
|
|
2843
|
+
this.addToBlueprints(...inputs);
|
|
2844
|
+
this.addToStage(...inputs);
|
|
2845
|
+
}
|
|
2846
|
+
addToBlueprints(...inputs) {
|
|
2847
|
+
if (this.wrappedStage) {
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
this.options.push(...inputs);
|
|
2851
|
+
}
|
|
2852
|
+
addToStage(...inputs) {
|
|
2853
|
+
if (!this.wrappedStage) {
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
this.wrappedStage.enqueue(...inputs);
|
|
2857
|
+
}
|
|
2858
|
+
start(params) {
|
|
2859
|
+
this.wrappedStage?.nodeSetup(params);
|
|
2860
|
+
}
|
|
2861
|
+
onUpdate(...callbacks) {
|
|
2862
|
+
if (!this.wrappedStage) {
|
|
2863
|
+
return;
|
|
2864
|
+
}
|
|
2865
|
+
this.wrappedStage.update = (params) => {
|
|
2866
|
+
const extended = { ...params, stage: this };
|
|
2867
|
+
callbacks.forEach((cb) => cb(extended));
|
|
2868
|
+
};
|
|
2869
|
+
}
|
|
2870
|
+
onSetup(callback) {
|
|
2871
|
+
this.wrappedStage.setup = callback;
|
|
2872
|
+
}
|
|
2873
|
+
onDestroy(callback) {
|
|
2874
|
+
this.wrappedStage.destroy = callback;
|
|
2875
|
+
}
|
|
2876
|
+
onLoading(callback) {
|
|
2877
|
+
if (!this.wrappedStage) {
|
|
2878
|
+
return () => {
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
return this.wrappedStage.onLoading(callback);
|
|
2882
|
+
}
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
});
|
|
2886
|
+
|
|
2887
|
+
// src/lib/game/game-default.ts
|
|
2888
|
+
var game_default_exports = {};
|
|
2889
|
+
__export(game_default_exports, {
|
|
2890
|
+
getGameDefaultConfig: () => getGameDefaultConfig
|
|
2891
|
+
});
|
|
2892
|
+
import { proxy as proxy5 } from "valtio/vanilla";
|
|
2893
|
+
function getGameDefaultConfig() {
|
|
2894
|
+
return {
|
|
2895
|
+
id: gameDefaultsState.id ?? "zylem",
|
|
2896
|
+
globals: gameDefaultsState.globals ?? {},
|
|
2897
|
+
stages: gameDefaultsState.stages ?? [createStage()],
|
|
2898
|
+
debug: gameDefaultsState.debug,
|
|
2899
|
+
time: gameDefaultsState.time,
|
|
2900
|
+
input: gameDefaultsState.input
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
var initialDefaults2, gameDefaultsState;
|
|
2904
|
+
var init_game_default = __esm({
|
|
2905
|
+
"src/lib/game/game-default.ts"() {
|
|
2906
|
+
"use strict";
|
|
2907
|
+
init_stage();
|
|
2908
|
+
initialDefaults2 = () => {
|
|
2909
|
+
return {
|
|
2910
|
+
id: "zylem",
|
|
2911
|
+
globals: {},
|
|
2912
|
+
stages: [createStage()],
|
|
2913
|
+
debug: false,
|
|
2914
|
+
time: 0,
|
|
2915
|
+
input: void 0
|
|
2916
|
+
};
|
|
2917
|
+
};
|
|
2918
|
+
gameDefaultsState = proxy5(
|
|
2919
|
+
{ ...initialDefaults2() }
|
|
2920
|
+
);
|
|
2921
|
+
}
|
|
2922
|
+
});
|
|
2923
|
+
|
|
2924
|
+
// src/lib/game/zylem-game.ts
|
|
2925
|
+
init_game_state();
|
|
2926
|
+
init_debug_state();
|
|
2927
|
+
|
|
2928
|
+
// src/lib/input/keyboard-provider.ts
|
|
2929
|
+
var KeyboardProvider = class {
|
|
2930
|
+
keyStates = /* @__PURE__ */ new Map();
|
|
2931
|
+
keyButtonStates = /* @__PURE__ */ new Map();
|
|
2932
|
+
mapping = null;
|
|
2933
|
+
includeDefaultBase = true;
|
|
2934
|
+
constructor(mapping, options) {
|
|
2935
|
+
this.mapping = mapping ?? null;
|
|
2936
|
+
this.includeDefaultBase = options?.includeDefaultBase ?? true;
|
|
2937
|
+
window.addEventListener("keydown", ({ key }) => this.keyStates.set(key, true));
|
|
2938
|
+
window.addEventListener("keyup", ({ key }) => this.keyStates.set(key, false));
|
|
2939
|
+
}
|
|
2940
|
+
isKeyPressed(key) {
|
|
2941
|
+
return this.keyStates.get(key) || false;
|
|
2942
|
+
}
|
|
2943
|
+
handleButtonState(key, delta) {
|
|
2944
|
+
let buttonState = this.keyButtonStates.get(key);
|
|
2945
|
+
const isPressed = this.isKeyPressed(key);
|
|
2946
|
+
if (!buttonState) {
|
|
2947
|
+
buttonState = { pressed: false, released: false, held: 0 };
|
|
2948
|
+
this.keyButtonStates.set(key, buttonState);
|
|
2949
|
+
}
|
|
2950
|
+
if (isPressed) {
|
|
2951
|
+
if (buttonState.held === 0) {
|
|
2952
|
+
buttonState.pressed = true;
|
|
2953
|
+
} else {
|
|
2954
|
+
buttonState.pressed = false;
|
|
2955
|
+
}
|
|
2956
|
+
buttonState.held += delta;
|
|
2957
|
+
buttonState.released = false;
|
|
2958
|
+
} else {
|
|
2959
|
+
if (buttonState.held > 0) {
|
|
2960
|
+
buttonState.released = true;
|
|
2961
|
+
buttonState.held = 0;
|
|
2962
|
+
} else {
|
|
2963
|
+
buttonState.released = false;
|
|
2964
|
+
}
|
|
2965
|
+
buttonState.pressed = false;
|
|
2966
|
+
}
|
|
2967
|
+
return buttonState;
|
|
2968
|
+
}
|
|
2969
|
+
handleAnalogState(negativeKey, positiveKey, delta) {
|
|
2970
|
+
const value = this.getAxisValue(negativeKey, positiveKey);
|
|
2971
|
+
return { value, held: delta };
|
|
2972
|
+
}
|
|
2973
|
+
mergeButtonState(a, b) {
|
|
2974
|
+
return {
|
|
2975
|
+
pressed: a?.pressed || b?.pressed || false,
|
|
2976
|
+
released: a?.released || b?.released || false,
|
|
2977
|
+
held: (a?.held || 0) + (b?.held || 0)
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
applyCustomMapping(input, delta) {
|
|
2981
|
+
if (!this.mapping) return input;
|
|
2982
|
+
for (const [key, targets] of Object.entries(this.mapping)) {
|
|
2983
|
+
if (!targets || targets.length === 0) continue;
|
|
2984
|
+
const state2 = this.handleButtonState(key, delta);
|
|
2985
|
+
for (const target of targets) {
|
|
2986
|
+
const [rawCategory, rawName] = (target || "").split(".");
|
|
2987
|
+
if (!rawCategory || !rawName) continue;
|
|
2988
|
+
const category = rawCategory.toLowerCase();
|
|
2989
|
+
const nameKey = rawName.toLowerCase();
|
|
2990
|
+
if (category === "buttons") {
|
|
2991
|
+
const map = {
|
|
2992
|
+
"a": "A",
|
|
2993
|
+
"b": "B",
|
|
2994
|
+
"x": "X",
|
|
2995
|
+
"y": "Y",
|
|
2996
|
+
"start": "Start",
|
|
2997
|
+
"select": "Select",
|
|
2998
|
+
"l": "L",
|
|
2999
|
+
"r": "R"
|
|
3000
|
+
};
|
|
3001
|
+
const prop = map[nameKey];
|
|
3002
|
+
if (!prop) continue;
|
|
3003
|
+
const nextButtons = input.buttons || {};
|
|
3004
|
+
nextButtons[prop] = this.mergeButtonState(nextButtons[prop], state2);
|
|
3005
|
+
input.buttons = nextButtons;
|
|
3006
|
+
continue;
|
|
3007
|
+
}
|
|
3008
|
+
if (category === "directions") {
|
|
3009
|
+
const map = {
|
|
3010
|
+
"up": "Up",
|
|
3011
|
+
"down": "Down",
|
|
3012
|
+
"left": "Left",
|
|
3013
|
+
"right": "Right"
|
|
3014
|
+
};
|
|
3015
|
+
const prop = map[nameKey];
|
|
3016
|
+
if (!prop) continue;
|
|
3017
|
+
const nextDirections = input.directions || {};
|
|
3018
|
+
nextDirections[prop] = this.mergeButtonState(nextDirections[prop], state2);
|
|
3019
|
+
input.directions = nextDirections;
|
|
3020
|
+
continue;
|
|
3021
|
+
}
|
|
3022
|
+
if (category === "shoulders") {
|
|
3023
|
+
const map = {
|
|
3024
|
+
"ltrigger": "LTrigger",
|
|
3025
|
+
"rtrigger": "RTrigger"
|
|
3026
|
+
};
|
|
3027
|
+
const prop = map[nameKey];
|
|
3028
|
+
if (!prop) continue;
|
|
3029
|
+
const nextShoulders = input.shoulders || {};
|
|
3030
|
+
nextShoulders[prop] = this.mergeButtonState(nextShoulders[prop], state2);
|
|
3031
|
+
input.shoulders = nextShoulders;
|
|
3032
|
+
continue;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
return input;
|
|
3037
|
+
}
|
|
3038
|
+
getInput(delta) {
|
|
3039
|
+
const base = {};
|
|
3040
|
+
if (this.includeDefaultBase) {
|
|
3041
|
+
base.buttons = {
|
|
3042
|
+
A: this.handleButtonState("z", delta),
|
|
3043
|
+
B: this.handleButtonState("x", delta),
|
|
3044
|
+
X: this.handleButtonState("a", delta),
|
|
3045
|
+
Y: this.handleButtonState("s", delta),
|
|
3046
|
+
Start: this.handleButtonState(" ", delta),
|
|
3047
|
+
Select: this.handleButtonState("Enter", delta),
|
|
3048
|
+
L: this.handleButtonState("q", delta),
|
|
3049
|
+
R: this.handleButtonState("e", delta)
|
|
3050
|
+
};
|
|
3051
|
+
base.directions = {
|
|
3052
|
+
Up: this.handleButtonState("ArrowUp", delta),
|
|
3053
|
+
Down: this.handleButtonState("ArrowDown", delta),
|
|
3054
|
+
Left: this.handleButtonState("ArrowLeft", delta),
|
|
3055
|
+
Right: this.handleButtonState("ArrowRight", delta)
|
|
3056
|
+
};
|
|
3057
|
+
base.axes = {
|
|
3058
|
+
Horizontal: this.handleAnalogState("ArrowLeft", "ArrowRight", delta),
|
|
3059
|
+
Vertical: this.handleAnalogState("ArrowUp", "ArrowDown", delta)
|
|
3060
|
+
};
|
|
3061
|
+
base.shoulders = {
|
|
3062
|
+
LTrigger: this.handleButtonState("Q", delta),
|
|
3063
|
+
RTrigger: this.handleButtonState("E", delta)
|
|
3064
|
+
};
|
|
3065
|
+
}
|
|
3066
|
+
return this.applyCustomMapping(base, delta);
|
|
3067
|
+
}
|
|
3068
|
+
getName() {
|
|
3069
|
+
return "keyboard";
|
|
3070
|
+
}
|
|
3071
|
+
getAxisValue(negativeKey, positiveKey) {
|
|
3072
|
+
return (this.isKeyPressed(positiveKey) ? 1 : 0) - (this.isKeyPressed(negativeKey) ? 1 : 0);
|
|
3073
|
+
}
|
|
3074
|
+
isConnected() {
|
|
3075
|
+
return true;
|
|
3076
|
+
}
|
|
3077
|
+
};
|
|
3078
|
+
|
|
3079
|
+
// src/lib/input/gamepad-provider.ts
|
|
3080
|
+
var GamepadProvider = class {
|
|
3081
|
+
gamepadIndex;
|
|
3082
|
+
connected = false;
|
|
3083
|
+
buttonStates = {};
|
|
3084
|
+
constructor(gamepadIndex) {
|
|
3085
|
+
this.gamepadIndex = gamepadIndex;
|
|
3086
|
+
window.addEventListener("gamepadconnected", (e) => {
|
|
3087
|
+
if (e.gamepad.index === this.gamepadIndex) {
|
|
3088
|
+
this.connected = true;
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
window.addEventListener("gamepaddisconnected", (e) => {
|
|
3092
|
+
if (e.gamepad.index === this.gamepadIndex) {
|
|
3093
|
+
this.connected = false;
|
|
3094
|
+
}
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
handleButtonState(index, gamepad, delta) {
|
|
3098
|
+
const isPressed = gamepad.buttons[index].pressed;
|
|
3099
|
+
let buttonState = this.buttonStates[index];
|
|
3100
|
+
if (!buttonState) {
|
|
3101
|
+
buttonState = { pressed: false, released: false, held: 0 };
|
|
3102
|
+
this.buttonStates[index] = buttonState;
|
|
3103
|
+
}
|
|
3104
|
+
if (isPressed) {
|
|
3105
|
+
if (buttonState.held === 0) {
|
|
3106
|
+
buttonState.pressed = true;
|
|
3107
|
+
} else {
|
|
3108
|
+
buttonState.pressed = false;
|
|
3109
|
+
}
|
|
3110
|
+
buttonState.held += delta;
|
|
3111
|
+
buttonState.released = false;
|
|
3112
|
+
} else {
|
|
3113
|
+
if (buttonState.held > 0) {
|
|
3114
|
+
buttonState.released = true;
|
|
3115
|
+
buttonState.held = 0;
|
|
3116
|
+
} else {
|
|
3117
|
+
buttonState.released = false;
|
|
3118
|
+
}
|
|
3119
|
+
buttonState.pressed = false;
|
|
3120
|
+
}
|
|
3121
|
+
return buttonState;
|
|
3122
|
+
}
|
|
3123
|
+
handleAnalogState(index, gamepad, delta) {
|
|
3124
|
+
const value = gamepad.axes[index];
|
|
3125
|
+
return { value, held: delta };
|
|
3126
|
+
}
|
|
3127
|
+
getInput(delta) {
|
|
3128
|
+
const gamepad = navigator.getGamepads()[this.gamepadIndex];
|
|
3129
|
+
if (!gamepad) return {};
|
|
3130
|
+
return {
|
|
3131
|
+
buttons: {
|
|
3132
|
+
A: this.handleButtonState(0, gamepad, delta),
|
|
3133
|
+
B: this.handleButtonState(1, gamepad, delta),
|
|
3134
|
+
X: this.handleButtonState(2, gamepad, delta),
|
|
3135
|
+
Y: this.handleButtonState(3, gamepad, delta),
|
|
3136
|
+
Start: this.handleButtonState(9, gamepad, delta),
|
|
3137
|
+
Select: this.handleButtonState(8, gamepad, delta),
|
|
3138
|
+
L: this.handleButtonState(4, gamepad, delta),
|
|
3139
|
+
R: this.handleButtonState(5, gamepad, delta)
|
|
3140
|
+
},
|
|
3141
|
+
directions: {
|
|
3142
|
+
Up: this.handleButtonState(12, gamepad, delta),
|
|
3143
|
+
Down: this.handleButtonState(13, gamepad, delta),
|
|
3144
|
+
Left: this.handleButtonState(14, gamepad, delta),
|
|
3145
|
+
Right: this.handleButtonState(15, gamepad, delta)
|
|
3146
|
+
},
|
|
3147
|
+
axes: {
|
|
3148
|
+
Horizontal: this.handleAnalogState(0, gamepad, delta),
|
|
3149
|
+
Vertical: this.handleAnalogState(1, gamepad, delta)
|
|
3150
|
+
},
|
|
3151
|
+
shoulders: {
|
|
3152
|
+
LTrigger: this.handleButtonState(6, gamepad, delta),
|
|
3153
|
+
RTrigger: this.handleButtonState(7, gamepad, delta)
|
|
3154
|
+
}
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
getName() {
|
|
3158
|
+
return `gamepad-${this.gamepadIndex + 1}`;
|
|
3159
|
+
}
|
|
3160
|
+
isConnected() {
|
|
3161
|
+
return this.connected;
|
|
3162
|
+
}
|
|
3163
|
+
};
|
|
3164
|
+
|
|
3165
|
+
// src/lib/input/input-manager.ts
|
|
3166
|
+
var InputManager = class {
|
|
3167
|
+
inputMap = /* @__PURE__ */ new Map();
|
|
3168
|
+
currentInputs = {};
|
|
3169
|
+
previousInputs = {};
|
|
3170
|
+
constructor(config) {
|
|
3171
|
+
if (config?.p1?.key) {
|
|
3172
|
+
this.addInputProvider(1, new KeyboardProvider(config.p1.key, { includeDefaultBase: false }));
|
|
3173
|
+
} else {
|
|
3174
|
+
this.addInputProvider(1, new KeyboardProvider());
|
|
3175
|
+
}
|
|
3176
|
+
this.addInputProvider(1, new GamepadProvider(0));
|
|
3177
|
+
if (config?.p2?.key) {
|
|
3178
|
+
this.addInputProvider(2, new KeyboardProvider(config.p2.key, { includeDefaultBase: false }));
|
|
3179
|
+
}
|
|
3180
|
+
this.addInputProvider(2, new GamepadProvider(1));
|
|
3181
|
+
if (config?.p3?.key) {
|
|
3182
|
+
this.addInputProvider(3, new KeyboardProvider(config.p3.key, { includeDefaultBase: false }));
|
|
3183
|
+
}
|
|
3184
|
+
this.addInputProvider(3, new GamepadProvider(2));
|
|
3185
|
+
if (config?.p4?.key) {
|
|
3186
|
+
this.addInputProvider(4, new KeyboardProvider(config.p4.key, { includeDefaultBase: false }));
|
|
3187
|
+
}
|
|
3188
|
+
this.addInputProvider(4, new GamepadProvider(3));
|
|
3189
|
+
if (config?.p5?.key) {
|
|
3190
|
+
this.addInputProvider(5, new KeyboardProvider(config.p5.key, { includeDefaultBase: false }));
|
|
3191
|
+
}
|
|
3192
|
+
this.addInputProvider(5, new GamepadProvider(4));
|
|
3193
|
+
if (config?.p6?.key) {
|
|
3194
|
+
this.addInputProvider(6, new KeyboardProvider(config.p6.key, { includeDefaultBase: false }));
|
|
3195
|
+
}
|
|
3196
|
+
this.addInputProvider(6, new GamepadProvider(5));
|
|
3197
|
+
if (config?.p7?.key) {
|
|
3198
|
+
this.addInputProvider(7, new KeyboardProvider(config.p7.key, { includeDefaultBase: false }));
|
|
3199
|
+
}
|
|
3200
|
+
this.addInputProvider(7, new GamepadProvider(6));
|
|
3201
|
+
if (config?.p8?.key) {
|
|
3202
|
+
this.addInputProvider(8, new KeyboardProvider(config.p8.key, { includeDefaultBase: false }));
|
|
3203
|
+
}
|
|
3204
|
+
this.addInputProvider(8, new GamepadProvider(7));
|
|
3205
|
+
}
|
|
3206
|
+
addInputProvider(playerNumber, provider) {
|
|
3207
|
+
if (!this.inputMap.has(playerNumber)) {
|
|
3208
|
+
this.inputMap.set(playerNumber, []);
|
|
3209
|
+
}
|
|
3210
|
+
this.inputMap.get(playerNumber)?.push(provider);
|
|
3211
|
+
}
|
|
3212
|
+
getInputs(delta) {
|
|
3213
|
+
const inputs = {};
|
|
3214
|
+
this.inputMap.forEach((providers, playerNumber) => {
|
|
3215
|
+
const playerKey = `p${playerNumber}`;
|
|
3216
|
+
const mergedInput = providers.reduce((acc, provider) => {
|
|
3217
|
+
const input = provider.getInput(delta);
|
|
3218
|
+
return this.mergeInputs(acc, input);
|
|
3219
|
+
}, {});
|
|
3220
|
+
inputs[playerKey] = {
|
|
3221
|
+
playerNumber,
|
|
3222
|
+
...mergedInput
|
|
3223
|
+
};
|
|
3224
|
+
});
|
|
3225
|
+
return inputs;
|
|
3226
|
+
}
|
|
3227
|
+
mergeButtonState(a, b) {
|
|
3228
|
+
return {
|
|
3229
|
+
pressed: a?.pressed || b?.pressed || false,
|
|
3230
|
+
released: a?.released || b?.released || false,
|
|
3231
|
+
held: (a?.held || 0) + (b?.held || 0)
|
|
3232
|
+
};
|
|
3233
|
+
}
|
|
3234
|
+
mergeAnalogState(a, b) {
|
|
3235
|
+
return {
|
|
3236
|
+
value: (a?.value || 0) + (b?.value || 0),
|
|
3237
|
+
held: (a?.held || 0) + (b?.held || 0)
|
|
3238
|
+
};
|
|
3239
|
+
}
|
|
3240
|
+
mergeInputs(a, b) {
|
|
3241
|
+
return {
|
|
3242
|
+
buttons: {
|
|
3243
|
+
A: this.mergeButtonState(a.buttons?.A, b.buttons?.A),
|
|
3244
|
+
B: this.mergeButtonState(a.buttons?.B, b.buttons?.B),
|
|
3245
|
+
X: this.mergeButtonState(a.buttons?.X, b.buttons?.X),
|
|
3246
|
+
Y: this.mergeButtonState(a.buttons?.Y, b.buttons?.Y),
|
|
3247
|
+
Start: this.mergeButtonState(a.buttons?.Start, b.buttons?.Start),
|
|
3248
|
+
Select: this.mergeButtonState(a.buttons?.Select, b.buttons?.Select),
|
|
3249
|
+
L: this.mergeButtonState(a.buttons?.L, b.buttons?.L),
|
|
3250
|
+
R: this.mergeButtonState(a.buttons?.R, b.buttons?.R)
|
|
3251
|
+
},
|
|
3252
|
+
directions: {
|
|
3253
|
+
Up: this.mergeButtonState(a.directions?.Up, b.directions?.Up),
|
|
3254
|
+
Down: this.mergeButtonState(a.directions?.Down, b.directions?.Down),
|
|
3255
|
+
Left: this.mergeButtonState(a.directions?.Left, b.directions?.Left),
|
|
3256
|
+
Right: this.mergeButtonState(a.directions?.Right, b.directions?.Right)
|
|
3257
|
+
},
|
|
3258
|
+
axes: {
|
|
3259
|
+
Horizontal: this.mergeAnalogState(a.axes?.Horizontal, b.axes?.Horizontal),
|
|
3260
|
+
Vertical: this.mergeAnalogState(a.axes?.Vertical, b.axes?.Vertical)
|
|
3261
|
+
},
|
|
3262
|
+
shoulders: {
|
|
3263
|
+
LTrigger: this.mergeButtonState(a.shoulders?.LTrigger, b.shoulders?.LTrigger),
|
|
3264
|
+
RTrigger: this.mergeButtonState(a.shoulders?.RTrigger, b.shoulders?.RTrigger)
|
|
3265
|
+
}
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
};
|
|
3269
|
+
|
|
3270
|
+
// src/lib/core/three-addons/Timer.ts
|
|
3271
|
+
var Timer = class {
|
|
3272
|
+
_previousTime;
|
|
3273
|
+
_currentTime;
|
|
3274
|
+
_startTime;
|
|
3275
|
+
_delta;
|
|
3276
|
+
_elapsed;
|
|
3277
|
+
_timescale;
|
|
3278
|
+
_document;
|
|
3279
|
+
_pageVisibilityHandler;
|
|
3280
|
+
/**
|
|
3281
|
+
* Constructs a new timer.
|
|
3282
|
+
*/
|
|
3283
|
+
constructor() {
|
|
3284
|
+
this._previousTime = 0;
|
|
3285
|
+
this._currentTime = 0;
|
|
3286
|
+
this._startTime = now();
|
|
3287
|
+
this._delta = 0;
|
|
3288
|
+
this._elapsed = 0;
|
|
3289
|
+
this._timescale = 1;
|
|
3290
|
+
this._document = null;
|
|
3291
|
+
this._pageVisibilityHandler = null;
|
|
3292
|
+
}
|
|
3293
|
+
/**
|
|
3294
|
+
* Connect the timer to the given document.Calling this method is not mandatory to
|
|
3295
|
+
* use the timer but enables the usage of the Page Visibility API to avoid large time
|
|
3296
|
+
* delta values.
|
|
3297
|
+
*
|
|
3298
|
+
* @param {Document} document - The document.
|
|
3299
|
+
*/
|
|
3300
|
+
connect(document2) {
|
|
3301
|
+
this._document = document2;
|
|
3302
|
+
if (document2.hidden !== void 0) {
|
|
3303
|
+
this._pageVisibilityHandler = handleVisibilityChange.bind(this);
|
|
3304
|
+
document2.addEventListener("visibilitychange", this._pageVisibilityHandler, false);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
/**
|
|
3308
|
+
* Disconnects the timer from the DOM and also disables the usage of the Page Visibility API.
|
|
3309
|
+
*/
|
|
3310
|
+
disconnect() {
|
|
3311
|
+
if (this._pageVisibilityHandler !== null) {
|
|
3312
|
+
this._document.removeEventListener("visibilitychange", this._pageVisibilityHandler);
|
|
3313
|
+
this._pageVisibilityHandler = null;
|
|
3314
|
+
}
|
|
3315
|
+
this._document = null;
|
|
3316
|
+
}
|
|
3317
|
+
/**
|
|
3318
|
+
* Returns the time delta in seconds.
|
|
3319
|
+
*
|
|
3320
|
+
* @return {number} The time delta in second.
|
|
3321
|
+
*/
|
|
3322
|
+
getDelta() {
|
|
3323
|
+
return this._delta / 1e3;
|
|
3324
|
+
}
|
|
3325
|
+
/**
|
|
3326
|
+
* Returns the elapsed time in seconds.
|
|
3327
|
+
*
|
|
3328
|
+
* @return {number} The elapsed time in second.
|
|
3329
|
+
*/
|
|
3330
|
+
getElapsed() {
|
|
3331
|
+
return this._elapsed / 1e3;
|
|
3332
|
+
}
|
|
3333
|
+
/**
|
|
3334
|
+
* Returns the timescale.
|
|
3335
|
+
*
|
|
3336
|
+
* @return {number} The timescale.
|
|
3337
|
+
*/
|
|
3338
|
+
getTimescale() {
|
|
3339
|
+
return this._timescale;
|
|
3340
|
+
}
|
|
3341
|
+
/**
|
|
3342
|
+
* Sets the given timescale which scale the time delta computation
|
|
3343
|
+
* in `update()`.
|
|
3344
|
+
*
|
|
3345
|
+
* @param {number} timescale - The timescale to set.
|
|
3346
|
+
* @return {Timer} A reference to this timer.
|
|
3347
|
+
*/
|
|
3348
|
+
setTimescale(timescale) {
|
|
3349
|
+
this._timescale = timescale;
|
|
3350
|
+
return this;
|
|
3351
|
+
}
|
|
3352
|
+
/**
|
|
3353
|
+
* Resets the time computation for the current simulation step.
|
|
3354
|
+
*
|
|
3355
|
+
* @return {Timer} A reference to this timer.
|
|
3356
|
+
*/
|
|
3357
|
+
reset() {
|
|
3358
|
+
this._currentTime = now() - this._startTime;
|
|
3359
|
+
return this;
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* Can be used to free all internal resources. Usually called when
|
|
3363
|
+
* the timer instance isn't required anymore.
|
|
3364
|
+
*/
|
|
3365
|
+
dispose() {
|
|
3366
|
+
this.disconnect();
|
|
3367
|
+
}
|
|
3368
|
+
/**
|
|
3369
|
+
* Updates the internal state of the timer. This method should be called
|
|
3370
|
+
* once per simulation step and before you perform queries against the timer
|
|
3371
|
+
* (e.g. via `getDelta()`).
|
|
3372
|
+
*
|
|
3373
|
+
* @param {number} timestamp - The current time in milliseconds. Can be obtained
|
|
3374
|
+
* from the `requestAnimationFrame` callback argument. If not provided, the current
|
|
3375
|
+
* time will be determined with `performance.now`.
|
|
3376
|
+
* @return {Timer} A reference to this timer.
|
|
3377
|
+
*/
|
|
3378
|
+
update(timestamp) {
|
|
3379
|
+
if (this._pageVisibilityHandler !== null && this._document.hidden === true) {
|
|
3380
|
+
this._delta = 0;
|
|
3381
|
+
} else {
|
|
3382
|
+
this._previousTime = this._currentTime;
|
|
3383
|
+
this._currentTime = (timestamp !== void 0 ? timestamp : now()) - this._startTime;
|
|
3384
|
+
this._delta = (this._currentTime - this._previousTime) * this._timescale;
|
|
3385
|
+
this._elapsed += this._delta;
|
|
3386
|
+
}
|
|
3387
|
+
return this;
|
|
3388
|
+
}
|
|
3389
|
+
};
|
|
3390
|
+
function now() {
|
|
3391
|
+
return performance.now();
|
|
3392
|
+
}
|
|
3393
|
+
function handleVisibilityChange() {
|
|
3394
|
+
if (this._document.hidden === false) this.reset();
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
// src/lib/device/aspect-ratio.ts
|
|
3398
|
+
var AspectRatio = {
|
|
3399
|
+
FourByThree: 4 / 3,
|
|
3400
|
+
SixteenByNine: 16 / 9,
|
|
3401
|
+
TwentyOneByNine: 21 / 9,
|
|
3402
|
+
OneByOne: 1 / 1
|
|
3403
|
+
};
|
|
3404
|
+
var AspectRatioDelegate = class {
|
|
3405
|
+
container;
|
|
3406
|
+
canvas;
|
|
3407
|
+
aspectRatio;
|
|
3408
|
+
onResize;
|
|
3409
|
+
handleResizeBound;
|
|
3410
|
+
constructor(params) {
|
|
3411
|
+
this.container = params.container;
|
|
3412
|
+
this.canvas = params.canvas;
|
|
3413
|
+
this.aspectRatio = typeof params.aspectRatio === "number" ? params.aspectRatio : params.aspectRatio;
|
|
3414
|
+
this.onResize = params.onResize;
|
|
3415
|
+
this.handleResizeBound = this.apply.bind(this);
|
|
3416
|
+
}
|
|
3417
|
+
/** Attach window resize listener and apply once. */
|
|
3418
|
+
attach() {
|
|
3419
|
+
window.addEventListener("resize", this.handleResizeBound);
|
|
3420
|
+
this.apply();
|
|
3421
|
+
}
|
|
3422
|
+
/** Detach window resize listener. */
|
|
3423
|
+
detach() {
|
|
3424
|
+
window.removeEventListener("resize", this.handleResizeBound);
|
|
3425
|
+
}
|
|
3426
|
+
/** Compute the largest size that fits container while preserving aspect. */
|
|
3427
|
+
measure() {
|
|
3428
|
+
const containerWidth = this.container.clientWidth || window.innerWidth;
|
|
3429
|
+
const containerHeight = this.container.clientHeight || window.innerHeight;
|
|
3430
|
+
const containerRatio = containerWidth / containerHeight;
|
|
3431
|
+
if (containerRatio > this.aspectRatio) {
|
|
3432
|
+
const height = containerHeight;
|
|
3433
|
+
const width = Math.round(height * this.aspectRatio);
|
|
3434
|
+
return { width, height };
|
|
3435
|
+
} else {
|
|
3436
|
+
const width = containerWidth;
|
|
3437
|
+
const height = Math.round(width / this.aspectRatio);
|
|
3438
|
+
return { width, height };
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
/** Apply measured size to canvas and notify. */
|
|
3442
|
+
apply() {
|
|
3443
|
+
const { width, height } = this.measure();
|
|
3444
|
+
this.canvas.style.width = `${width}px`;
|
|
3445
|
+
this.canvas.style.height = `${height}px`;
|
|
3446
|
+
this.onResize?.(width, height);
|
|
3447
|
+
}
|
|
3448
|
+
};
|
|
3449
|
+
|
|
3450
|
+
// src/lib/game/game-retro-resolutions.ts
|
|
3451
|
+
var RetroPresets = {
|
|
3452
|
+
NES: {
|
|
3453
|
+
displayAspect: 4 / 3,
|
|
3454
|
+
resolutions: [
|
|
3455
|
+
{ key: "256x240", width: 256, height: 240, notes: "Standard NTSC; effective 240p." }
|
|
3456
|
+
]
|
|
3457
|
+
},
|
|
3458
|
+
SNES: {
|
|
3459
|
+
displayAspect: 4 / 3,
|
|
3460
|
+
resolutions: [
|
|
3461
|
+
{ key: "256x224", width: 256, height: 224, notes: "Common 240p-equivalent mode." },
|
|
3462
|
+
{ key: "512x448", width: 512, height: 448, notes: "Hi-res interlaced (480i)." }
|
|
3463
|
+
]
|
|
3464
|
+
},
|
|
3465
|
+
N64: {
|
|
3466
|
+
displayAspect: 4 / 3,
|
|
3467
|
+
resolutions: [
|
|
3468
|
+
{ key: "320x240", width: 320, height: 240, notes: "Common 240p mode." },
|
|
3469
|
+
{ key: "640x480", width: 640, height: 480, notes: "Higher resolution (480i)." }
|
|
3470
|
+
]
|
|
3471
|
+
},
|
|
3472
|
+
PS1: {
|
|
3473
|
+
displayAspect: 4 / 3,
|
|
3474
|
+
resolutions: [
|
|
3475
|
+
{ key: "320x240", width: 320, height: 240, notes: "Progressive 240p common." },
|
|
3476
|
+
{ key: "640x480", width: 640, height: 480, notes: "Interlaced 480i for higher detail." }
|
|
3477
|
+
]
|
|
3478
|
+
},
|
|
3479
|
+
PS2: {
|
|
3480
|
+
displayAspect: 4 / 3,
|
|
3481
|
+
resolutions: [
|
|
3482
|
+
{ key: "640x480", width: 640, height: 480, notes: "480i/480p baseline." },
|
|
3483
|
+
{ key: "720x480", width: 720, height: 480, notes: "480p (widescreen capable in some titles)." },
|
|
3484
|
+
{ key: "1280x720", width: 1280, height: 720, notes: "720p (select titles)." }
|
|
3485
|
+
]
|
|
3486
|
+
},
|
|
3487
|
+
PS5: {
|
|
3488
|
+
displayAspect: 16 / 9,
|
|
3489
|
+
resolutions: [
|
|
3490
|
+
{ key: "720x480", width: 720, height: 480, notes: "Legacy compatibility." },
|
|
3491
|
+
{ key: "1280x720", width: 1280, height: 720, notes: "720p." },
|
|
3492
|
+
{ key: "1920x1080", width: 1920, height: 1080, notes: "1080p." },
|
|
3493
|
+
{ key: "2560x1440", width: 2560, height: 1440, notes: "1440p." },
|
|
3494
|
+
{ key: "3840x2160", width: 3840, height: 2160, notes: "4K (up to 120Hz)." },
|
|
3495
|
+
{ key: "7680x4320", width: 7680, height: 4320, notes: "8K (limited)." }
|
|
3496
|
+
]
|
|
3497
|
+
},
|
|
3498
|
+
XboxOne: {
|
|
3499
|
+
displayAspect: 16 / 9,
|
|
3500
|
+
resolutions: [
|
|
3501
|
+
{ key: "1280x720", width: 1280, height: 720, notes: "720p (original)." },
|
|
3502
|
+
{ key: "1920x1080", width: 1920, height: 1080, notes: "1080p (original)." },
|
|
3503
|
+
{ key: "3840x2160", width: 3840, height: 2160, notes: "4K UHD (S/X models)." }
|
|
3504
|
+
]
|
|
3505
|
+
}
|
|
3506
|
+
};
|
|
3507
|
+
function getDisplayAspect(preset) {
|
|
3508
|
+
return RetroPresets[preset].displayAspect;
|
|
3509
|
+
}
|
|
3510
|
+
function getPresetResolution(preset, key) {
|
|
3511
|
+
const list = RetroPresets[preset]?.resolutions || [];
|
|
3512
|
+
if (!key) return list[0];
|
|
3513
|
+
const normalized = key.toLowerCase().replace(/\s+/g, "").replace("\xD7", "x");
|
|
3514
|
+
return list.find((r) => r.key.toLowerCase() === normalized);
|
|
3515
|
+
}
|
|
3516
|
+
function parseResolution(text2) {
|
|
3517
|
+
if (!text2) return null;
|
|
3518
|
+
const normalized = String(text2).toLowerCase().trim().replace(/\s+/g, "").replace("\xD7", "x");
|
|
3519
|
+
const match = normalized.match(/^(\d+)x(\d+)$/);
|
|
3520
|
+
if (!match) return null;
|
|
3521
|
+
const width = parseInt(match[1], 10);
|
|
3522
|
+
const height = parseInt(match[2], 10);
|
|
3523
|
+
if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
|
|
3524
|
+
return { width, height };
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
// src/lib/game/game-config.ts
|
|
3528
|
+
var GameConfig = class {
|
|
3529
|
+
constructor(id, globals, stages, debug, time, input, aspectRatio, internalResolution, fullscreen, bodyBackground, container, containerId, canvas) {
|
|
3530
|
+
this.id = id;
|
|
3531
|
+
this.globals = globals;
|
|
3532
|
+
this.stages = stages;
|
|
3533
|
+
this.debug = debug;
|
|
3534
|
+
this.time = time;
|
|
3535
|
+
this.input = input;
|
|
3536
|
+
this.aspectRatio = aspectRatio;
|
|
3537
|
+
this.internalResolution = internalResolution;
|
|
3538
|
+
this.fullscreen = fullscreen;
|
|
3539
|
+
this.bodyBackground = bodyBackground;
|
|
3540
|
+
this.container = container;
|
|
3541
|
+
this.containerId = containerId;
|
|
3542
|
+
this.canvas = canvas;
|
|
3543
|
+
}
|
|
3544
|
+
};
|
|
3545
|
+
function ensureContainer(containerId, existing) {
|
|
3546
|
+
if (existing) return existing;
|
|
3547
|
+
if (containerId) {
|
|
3548
|
+
const found = document.getElementById(containerId);
|
|
3549
|
+
if (found) return found;
|
|
3550
|
+
}
|
|
3551
|
+
const id = containerId || "zylem-root";
|
|
3552
|
+
const el = document.createElement("main");
|
|
3553
|
+
el.setAttribute("id", id);
|
|
3554
|
+
el.style.position = "relative";
|
|
3555
|
+
el.style.width = "100%";
|
|
3556
|
+
el.style.height = "100%";
|
|
3557
|
+
document.body.appendChild(el);
|
|
3558
|
+
return el;
|
|
3559
|
+
}
|
|
3560
|
+
function createDefaultGameConfig(base) {
|
|
3561
|
+
const id = base?.id ?? "zylem";
|
|
3562
|
+
const container = ensureContainer(id);
|
|
3563
|
+
return new GameConfig(
|
|
3564
|
+
id,
|
|
3565
|
+
base?.globals ?? {},
|
|
3566
|
+
base?.stages ?? [],
|
|
3567
|
+
Boolean(base?.debug),
|
|
3568
|
+
base?.time ?? 0,
|
|
3569
|
+
base?.input,
|
|
3570
|
+
AspectRatio.SixteenByNine,
|
|
3571
|
+
void 0,
|
|
3572
|
+
true,
|
|
3573
|
+
"#000000",
|
|
3574
|
+
container,
|
|
3575
|
+
id,
|
|
3576
|
+
void 0
|
|
3577
|
+
);
|
|
3578
|
+
}
|
|
3579
|
+
function resolveGameConfig(user) {
|
|
3580
|
+
const defaults = createDefaultGameConfig({
|
|
3581
|
+
id: user?.id ?? "zylem",
|
|
3582
|
+
debug: Boolean(user?.debug),
|
|
3583
|
+
time: user?.time ?? 0,
|
|
3584
|
+
input: user?.input,
|
|
3585
|
+
stages: user?.stages ?? [],
|
|
3586
|
+
globals: user?.globals ?? {}
|
|
3587
|
+
});
|
|
3588
|
+
const containerId = user?.containerId ?? defaults.containerId;
|
|
3589
|
+
const container = ensureContainer(containerId, user?.container ?? null);
|
|
3590
|
+
const explicitAspect = user?.aspectRatio;
|
|
3591
|
+
let aspectRatio = defaults.aspectRatio;
|
|
3592
|
+
if (typeof explicitAspect === "number" || explicitAspect && typeof explicitAspect === "string") {
|
|
3593
|
+
aspectRatio = typeof explicitAspect === "number" ? explicitAspect : AspectRatio[explicitAspect] ?? defaults.aspectRatio;
|
|
3594
|
+
} else if (user?.preset) {
|
|
3595
|
+
try {
|
|
3596
|
+
aspectRatio = getDisplayAspect(user.preset) || defaults.aspectRatio;
|
|
3597
|
+
} catch {
|
|
3598
|
+
aspectRatio = defaults.aspectRatio;
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
const fullscreen = user?.fullscreen ?? defaults.fullscreen;
|
|
3602
|
+
const bodyBackground = user?.bodyBackground ?? defaults.bodyBackground;
|
|
3603
|
+
let internalResolution;
|
|
3604
|
+
if (user?.resolution) {
|
|
3605
|
+
if (typeof user.resolution === "string") {
|
|
3606
|
+
const parsed = parseResolution(user.resolution);
|
|
3607
|
+
if (parsed) internalResolution = parsed;
|
|
3608
|
+
if (!internalResolution && user.preset) {
|
|
3609
|
+
const res = getPresetResolution(user.preset, user.resolution);
|
|
3610
|
+
if (res) internalResolution = { width: res.width, height: res.height };
|
|
3611
|
+
}
|
|
3612
|
+
} else if (typeof user.resolution === "object") {
|
|
3613
|
+
const w = user.resolution.width;
|
|
3614
|
+
const h = user.resolution.height;
|
|
3615
|
+
if (Number.isFinite(w) && Number.isFinite(h)) {
|
|
3616
|
+
internalResolution = { width: w, height: h };
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
const canvas = user?.canvas ?? void 0;
|
|
3621
|
+
return new GameConfig(
|
|
3622
|
+
user?.id ?? defaults.id,
|
|
3623
|
+
user?.globals ?? defaults.globals,
|
|
3624
|
+
user?.stages ?? defaults.stages,
|
|
3625
|
+
Boolean(user?.debug ?? defaults.debug),
|
|
3626
|
+
user?.time ?? defaults.time,
|
|
3627
|
+
user?.input ?? defaults.input,
|
|
3628
|
+
aspectRatio,
|
|
3629
|
+
internalResolution,
|
|
3630
|
+
fullscreen,
|
|
3631
|
+
bodyBackground,
|
|
3632
|
+
container,
|
|
3633
|
+
containerId,
|
|
3634
|
+
canvas
|
|
3635
|
+
);
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
// src/lib/game/game-canvas.ts
|
|
3639
|
+
var GameCanvas = class {
|
|
3640
|
+
id;
|
|
3641
|
+
container;
|
|
3642
|
+
canvas;
|
|
3643
|
+
bodyBackground;
|
|
3644
|
+
fullscreen;
|
|
3645
|
+
aspectRatio;
|
|
3646
|
+
ratioDelegate = null;
|
|
3647
|
+
constructor(options) {
|
|
3648
|
+
this.id = options.id;
|
|
3649
|
+
this.container = this.ensureContainer(options.containerId ?? options.id, options.container);
|
|
3650
|
+
this.canvas = options.canvas ?? document.createElement("canvas");
|
|
3651
|
+
this.bodyBackground = options.bodyBackground;
|
|
3652
|
+
this.fullscreen = Boolean(options.fullscreen);
|
|
3653
|
+
this.aspectRatio = typeof options.aspectRatio === "number" ? options.aspectRatio : options.aspectRatio;
|
|
3654
|
+
}
|
|
3655
|
+
applyBodyBackground() {
|
|
3656
|
+
if (this.bodyBackground) {
|
|
3657
|
+
document.body.style.background = this.bodyBackground;
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
mountCanvas() {
|
|
3661
|
+
while (this.container.firstChild) {
|
|
3662
|
+
this.container.removeChild(this.container.firstChild);
|
|
3663
|
+
}
|
|
3664
|
+
this.container.appendChild(this.canvas);
|
|
3665
|
+
}
|
|
3666
|
+
mountRenderer(rendererDom, onResize) {
|
|
3667
|
+
while (this.container.firstChild) {
|
|
3668
|
+
this.container.removeChild(this.container.firstChild);
|
|
3669
|
+
}
|
|
3670
|
+
this.container.appendChild(rendererDom);
|
|
3671
|
+
this.canvas = rendererDom;
|
|
3672
|
+
this.attachAspectRatio(onResize);
|
|
3673
|
+
}
|
|
3674
|
+
centerIfFullscreen() {
|
|
3675
|
+
if (!this.fullscreen) return;
|
|
3676
|
+
const style = this.container.style;
|
|
3677
|
+
style.display = "flex";
|
|
3678
|
+
style.alignItems = "center";
|
|
3679
|
+
style.justifyContent = "center";
|
|
3680
|
+
style.position = "relative";
|
|
3681
|
+
style.inset = "0";
|
|
3682
|
+
}
|
|
3683
|
+
attachAspectRatio(onResize) {
|
|
3684
|
+
if (!this.ratioDelegate) {
|
|
3685
|
+
this.ratioDelegate = new AspectRatioDelegate({
|
|
3686
|
+
container: this.container,
|
|
3687
|
+
canvas: this.canvas,
|
|
3688
|
+
aspectRatio: this.aspectRatio,
|
|
3689
|
+
onResize
|
|
3690
|
+
});
|
|
3691
|
+
this.ratioDelegate.attach();
|
|
3692
|
+
} else {
|
|
3693
|
+
this.ratioDelegate.canvas = this.canvas;
|
|
3694
|
+
this.ratioDelegate.onResize = onResize;
|
|
3695
|
+
this.ratioDelegate.aspectRatio = this.aspectRatio;
|
|
3696
|
+
this.ratioDelegate.apply();
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
destroy() {
|
|
3700
|
+
this.ratioDelegate?.detach();
|
|
3701
|
+
this.ratioDelegate = null;
|
|
3702
|
+
}
|
|
3703
|
+
ensureContainer(containerId, existing) {
|
|
3704
|
+
if (existing) return existing;
|
|
3705
|
+
if (containerId) {
|
|
3706
|
+
const found = document.getElementById(containerId);
|
|
3707
|
+
if (found) return found;
|
|
3708
|
+
}
|
|
3709
|
+
const id = containerId || this.id || "zylem-root";
|
|
3710
|
+
const el = document.createElement("main");
|
|
3711
|
+
el.setAttribute("id", id);
|
|
3712
|
+
el.style.position = "relative";
|
|
3713
|
+
el.style.width = "100%";
|
|
3714
|
+
el.style.height = "100%";
|
|
3715
|
+
document.body.appendChild(el);
|
|
3716
|
+
return el;
|
|
3717
|
+
}
|
|
3718
|
+
};
|
|
3719
|
+
|
|
3720
|
+
// src/lib/game/zylem-game.ts
|
|
3721
|
+
import Stats from "stats.js";
|
|
3722
|
+
var ZylemGame = class _ZylemGame {
|
|
3723
|
+
id;
|
|
3724
|
+
initialGlobals = {};
|
|
3725
|
+
customSetup = null;
|
|
3726
|
+
customUpdate = null;
|
|
3727
|
+
customDestroy = null;
|
|
3728
|
+
stages = [];
|
|
3729
|
+
stageMap = /* @__PURE__ */ new Map();
|
|
3730
|
+
currentStageId = "";
|
|
3731
|
+
previousTimeStamp = 0;
|
|
3732
|
+
totalTime = 0;
|
|
3733
|
+
timer;
|
|
3734
|
+
inputManager;
|
|
3735
|
+
wrapperRef;
|
|
3736
|
+
statsRef = null;
|
|
3737
|
+
defaultCamera = null;
|
|
3738
|
+
container = null;
|
|
3739
|
+
canvas = null;
|
|
3740
|
+
aspectRatioDelegate = null;
|
|
3741
|
+
resolvedConfig = null;
|
|
3742
|
+
gameCanvas = null;
|
|
3743
|
+
animationFrameId = null;
|
|
3744
|
+
isDisposed = false;
|
|
3745
|
+
static FRAME_LIMIT = 120;
|
|
3746
|
+
static FRAME_DURATION = 1e3 / _ZylemGame.FRAME_LIMIT;
|
|
3747
|
+
static MAX_DELTA_SECONDS = 1 / 30;
|
|
3748
|
+
constructor(options, wrapperRef) {
|
|
3749
|
+
this.wrapperRef = wrapperRef;
|
|
3750
|
+
this.inputManager = new InputManager(options.input);
|
|
3751
|
+
this.timer = new Timer();
|
|
3752
|
+
this.timer.connect(document);
|
|
3753
|
+
const config = resolveGameConfig(options);
|
|
3754
|
+
this.id = config.id;
|
|
3755
|
+
this.stages = config.stages || [];
|
|
3756
|
+
this.container = config.container;
|
|
3757
|
+
this.canvas = config.canvas ?? null;
|
|
3758
|
+
this.resolvedConfig = config;
|
|
3759
|
+
this.loadGameCanvas(config);
|
|
3760
|
+
this.loadDebugOptions(options);
|
|
3761
|
+
this.setGlobals(options);
|
|
3762
|
+
}
|
|
3763
|
+
loadGameCanvas(config) {
|
|
3764
|
+
this.gameCanvas = new GameCanvas({
|
|
3765
|
+
id: config.id,
|
|
3766
|
+
container: config.container,
|
|
3767
|
+
containerId: config.containerId,
|
|
3768
|
+
canvas: this.canvas ?? void 0,
|
|
3769
|
+
bodyBackground: config.bodyBackground,
|
|
3770
|
+
fullscreen: config.fullscreen,
|
|
3771
|
+
aspectRatio: config.aspectRatio
|
|
3772
|
+
});
|
|
3773
|
+
this.gameCanvas.applyBodyBackground();
|
|
3774
|
+
this.gameCanvas.mountCanvas();
|
|
3775
|
+
this.gameCanvas.centerIfFullscreen();
|
|
3776
|
+
}
|
|
3777
|
+
loadDebugOptions(options) {
|
|
3778
|
+
debugState.enabled = Boolean(options.debug);
|
|
3779
|
+
if (options.debug) {
|
|
3780
|
+
this.statsRef = new Stats();
|
|
3781
|
+
this.statsRef.showPanel(0);
|
|
3782
|
+
this.statsRef.dom.style.position = "absolute";
|
|
3783
|
+
this.statsRef.dom.style.bottom = "0";
|
|
3784
|
+
this.statsRef.dom.style.right = "0";
|
|
3785
|
+
this.statsRef.dom.style.top = "auto";
|
|
3786
|
+
this.statsRef.dom.style.left = "auto";
|
|
3787
|
+
document.body.appendChild(this.statsRef.dom);
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
async loadStage(stage) {
|
|
3791
|
+
this.unloadCurrentStage();
|
|
3792
|
+
const config = stage.options[0];
|
|
3793
|
+
await stage.load(this.id, config?.camera);
|
|
3794
|
+
this.stageMap.set(stage.wrappedStage.uuid, stage);
|
|
3795
|
+
this.currentStageId = stage.wrappedStage.uuid;
|
|
3796
|
+
this.defaultCamera = stage.wrappedStage.cameraRef;
|
|
3797
|
+
if (this.container && this.defaultCamera) {
|
|
3798
|
+
const dom = this.defaultCamera.getDomElement();
|
|
3799
|
+
const internal = this.resolvedConfig?.internalResolution;
|
|
3800
|
+
this.gameCanvas?.mountRenderer(dom, (cssW, cssH) => {
|
|
3801
|
+
if (!this.defaultCamera) return;
|
|
3802
|
+
if (internal) {
|
|
3803
|
+
this.defaultCamera.setPixelRatio(1);
|
|
3804
|
+
this.defaultCamera.resize(internal.width, internal.height);
|
|
3805
|
+
} else {
|
|
3806
|
+
const dpr = window.devicePixelRatio || 1;
|
|
3807
|
+
this.defaultCamera.setPixelRatio(dpr);
|
|
3808
|
+
this.defaultCamera.resize(cssW, cssH);
|
|
3809
|
+
}
|
|
3810
|
+
});
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
unloadCurrentStage() {
|
|
3814
|
+
if (!this.currentStageId) return;
|
|
3815
|
+
const current = this.getStage(this.currentStageId);
|
|
3816
|
+
if (!current) return;
|
|
3817
|
+
if (current?.wrappedStage) {
|
|
3818
|
+
try {
|
|
3819
|
+
current.wrappedStage.nodeDestroy({
|
|
3820
|
+
me: current.wrappedStage,
|
|
3821
|
+
globals: state.globals
|
|
3822
|
+
});
|
|
3823
|
+
} catch (e) {
|
|
3824
|
+
console.error("Failed to destroy previous stage", e);
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
this.stageMap.delete(this.currentStageId);
|
|
3828
|
+
}
|
|
3829
|
+
setGlobals(options) {
|
|
3830
|
+
this.initialGlobals = { ...options.globals };
|
|
3831
|
+
for (const variable in this.initialGlobals) {
|
|
3832
|
+
const value = this.initialGlobals[variable];
|
|
3833
|
+
if (value === void 0) {
|
|
3834
|
+
console.error(`global ${variable} is undefined`);
|
|
3835
|
+
}
|
|
3836
|
+
setGlobal(variable, value);
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
params() {
|
|
3840
|
+
const stage = this.currentStage();
|
|
3841
|
+
const delta = this.timer.getDelta();
|
|
3842
|
+
const inputs = this.inputManager.getInputs(delta);
|
|
3843
|
+
const camera = stage?.wrappedStage?.cameraRef || this.defaultCamera;
|
|
3844
|
+
return {
|
|
3845
|
+
delta,
|
|
3846
|
+
inputs,
|
|
3847
|
+
globals: getGlobals(),
|
|
3848
|
+
me: this,
|
|
3849
|
+
camera
|
|
3850
|
+
};
|
|
3851
|
+
}
|
|
3852
|
+
start() {
|
|
3853
|
+
const stage = this.currentStage();
|
|
3854
|
+
const params = this.params();
|
|
3855
|
+
stage.start({ ...params, me: stage.wrappedStage });
|
|
3856
|
+
if (this.customSetup) {
|
|
3857
|
+
this.customSetup(params);
|
|
3858
|
+
}
|
|
3859
|
+
this.loop(0);
|
|
3860
|
+
}
|
|
3861
|
+
loop(timestamp) {
|
|
3862
|
+
this.statsRef && this.statsRef.begin();
|
|
3863
|
+
if (!isPaused()) {
|
|
3864
|
+
this.timer.update(timestamp);
|
|
3865
|
+
const stage = this.currentStage();
|
|
3866
|
+
const params = this.params();
|
|
3867
|
+
const clampedDelta = Math.min(Math.max(params.delta, 0), _ZylemGame.MAX_DELTA_SECONDS);
|
|
3868
|
+
const clampedParams = { ...params, delta: clampedDelta };
|
|
3869
|
+
if (this.customUpdate) {
|
|
3870
|
+
this.customUpdate(clampedParams);
|
|
3871
|
+
}
|
|
3872
|
+
if (stage) {
|
|
3873
|
+
stage.wrappedStage.nodeUpdate({ ...clampedParams, me: stage.wrappedStage });
|
|
3874
|
+
}
|
|
3875
|
+
this.totalTime += clampedParams.delta;
|
|
3876
|
+
state.time = this.totalTime;
|
|
3877
|
+
this.previousTimeStamp = timestamp;
|
|
3878
|
+
}
|
|
3879
|
+
this.statsRef && this.statsRef.end();
|
|
3880
|
+
this.outOfLoop();
|
|
3881
|
+
if (!this.isDisposed) {
|
|
3882
|
+
this.animationFrameId = requestAnimationFrame(this.loop.bind(this));
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
dispose() {
|
|
3886
|
+
this.isDisposed = true;
|
|
3887
|
+
if (this.animationFrameId !== null) {
|
|
3888
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
3889
|
+
this.animationFrameId = null;
|
|
3890
|
+
}
|
|
3891
|
+
this.unloadCurrentStage();
|
|
3892
|
+
if (this.statsRef && this.statsRef.dom && this.statsRef.dom.parentNode) {
|
|
3893
|
+
this.statsRef.dom.parentNode.removeChild(this.statsRef.dom);
|
|
3894
|
+
}
|
|
3895
|
+
this.timer.dispose();
|
|
3896
|
+
if (this.customDestroy) {
|
|
3897
|
+
this.customDestroy({
|
|
3898
|
+
me: this,
|
|
3899
|
+
globals: state.globals
|
|
3900
|
+
});
|
|
3901
|
+
}
|
|
3902
|
+
resetGlobals();
|
|
3903
|
+
}
|
|
3904
|
+
outOfLoop() {
|
|
3905
|
+
const currentStage = this.currentStage();
|
|
3906
|
+
if (!currentStage) return;
|
|
3907
|
+
currentStage.wrappedStage.outOfLoop();
|
|
3908
|
+
}
|
|
3909
|
+
getStage(id) {
|
|
3910
|
+
return this.stageMap.get(id);
|
|
3911
|
+
}
|
|
3912
|
+
currentStage() {
|
|
3913
|
+
return this.getStage(this.currentStageId);
|
|
3914
|
+
}
|
|
3915
|
+
};
|
|
3916
|
+
|
|
3917
|
+
// src/lib/game/game.ts
|
|
3918
|
+
init_debug_state();
|
|
3919
|
+
|
|
3920
|
+
// src/lib/core/utility/nodes.ts
|
|
3921
|
+
init_base_node();
|
|
3922
|
+
init_stage();
|
|
3923
|
+
init_entity();
|
|
3924
|
+
async function convertNodes(_options) {
|
|
3925
|
+
const { getGameDefaultConfig: getGameDefaultConfig2 } = await Promise.resolve().then(() => (init_game_default(), game_default_exports));
|
|
3926
|
+
let converted = { ...getGameDefaultConfig2() };
|
|
3927
|
+
const configurations = [];
|
|
3928
|
+
const stages = [];
|
|
3929
|
+
const entities = [];
|
|
3930
|
+
Object.values(_options).forEach((node) => {
|
|
3931
|
+
if (node instanceof Stage) {
|
|
3932
|
+
stages.push(node);
|
|
3933
|
+
} else if (node instanceof GameEntity) {
|
|
3934
|
+
entities.push(node);
|
|
3935
|
+
} else if (node instanceof BaseNode) {
|
|
3936
|
+
entities.push(node);
|
|
3937
|
+
} else if (node?.constructor?.name === "Object" && typeof node === "object") {
|
|
3938
|
+
const configuration = Object.assign({ ...getGameDefaultConfig2() }, { ...node });
|
|
3939
|
+
configurations.push(configuration);
|
|
3940
|
+
}
|
|
3941
|
+
});
|
|
3942
|
+
configurations.forEach((configuration) => {
|
|
3943
|
+
converted = Object.assign(converted, { ...configuration });
|
|
3944
|
+
});
|
|
3945
|
+
stages.forEach((stageInstance) => {
|
|
3946
|
+
stageInstance.addEntities(entities);
|
|
3947
|
+
});
|
|
3948
|
+
if (stages.length) {
|
|
3949
|
+
converted.stages = stages;
|
|
3950
|
+
} else {
|
|
3951
|
+
converted.stages[0].addEntities(entities);
|
|
3952
|
+
}
|
|
3953
|
+
return converted;
|
|
3954
|
+
}
|
|
3955
|
+
function hasStages(_options) {
|
|
3956
|
+
const stage = _options.find((option) => option instanceof Stage);
|
|
3957
|
+
return Boolean(stage);
|
|
3958
|
+
}
|
|
3959
|
+
function extractGlobalsFromOptions(_options) {
|
|
3960
|
+
for (const option of _options) {
|
|
3961
|
+
if (option && typeof option === "object" && !(option instanceof Stage) && !(option instanceof BaseNode) && !(option instanceof GameEntity)) {
|
|
3962
|
+
const config = option;
|
|
3963
|
+
if (config.globals) {
|
|
3964
|
+
return config.globals;
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
return void 0;
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
// src/lib/game/game.ts
|
|
3972
|
+
init_stage();
|
|
3973
|
+
|
|
3974
|
+
// src/lib/stage/stage-manager.ts
|
|
3975
|
+
import { proxy as proxy6 } from "valtio/vanilla";
|
|
3976
|
+
import { get, set } from "idb-keyval";
|
|
3977
|
+
import { pack, unpack } from "msgpackr";
|
|
3978
|
+
var stageState2 = proxy6({
|
|
3979
|
+
previous: null,
|
|
3980
|
+
current: null,
|
|
3981
|
+
next: null,
|
|
3982
|
+
isLoading: false
|
|
3983
|
+
});
|
|
3984
|
+
var StageManager = {
|
|
3985
|
+
staticRegistry: /* @__PURE__ */ new Map(),
|
|
3986
|
+
registerStaticStage(id, blueprint) {
|
|
3987
|
+
this.staticRegistry.set(id, blueprint);
|
|
3988
|
+
},
|
|
3989
|
+
async loadStageData(stageId) {
|
|
3990
|
+
try {
|
|
3991
|
+
const saved = await get(stageId);
|
|
3992
|
+
if (saved) {
|
|
3993
|
+
return unpack(saved);
|
|
3994
|
+
}
|
|
3995
|
+
} catch (e) {
|
|
3996
|
+
console.warn(`Failed to load stage ${stageId} from storage`, e);
|
|
3997
|
+
}
|
|
3998
|
+
if (this.staticRegistry.has(stageId)) {
|
|
3999
|
+
return this.staticRegistry.get(stageId);
|
|
4000
|
+
}
|
|
4001
|
+
throw new Error(`Stage ${stageId} not found in storage and static loading not fully implemented.`);
|
|
4002
|
+
},
|
|
4003
|
+
async transitionForward(nextStageId, loadStaticStage) {
|
|
4004
|
+
if (stageState2.isLoading) return;
|
|
4005
|
+
stageState2.isLoading = true;
|
|
4006
|
+
try {
|
|
4007
|
+
if (stageState2.current) {
|
|
4008
|
+
await set(stageState2.current.id, pack(stageState2.current));
|
|
4009
|
+
}
|
|
4010
|
+
stageState2.previous = stageState2.current;
|
|
4011
|
+
stageState2.current = stageState2.next;
|
|
4012
|
+
if (stageState2.current?.id !== nextStageId) {
|
|
4013
|
+
if (loadStaticStage) {
|
|
4014
|
+
stageState2.current = await loadStaticStage(nextStageId);
|
|
4015
|
+
} else {
|
|
4016
|
+
stageState2.current = await this.loadStageData(nextStageId);
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
stageState2.next = null;
|
|
4020
|
+
} catch (error) {
|
|
4021
|
+
console.error("Failed to transition stage:", error);
|
|
4022
|
+
} finally {
|
|
4023
|
+
stageState2.isLoading = false;
|
|
4024
|
+
}
|
|
4025
|
+
},
|
|
4026
|
+
/**
|
|
4027
|
+
* Manually set the next stage to pre-load it.
|
|
4028
|
+
*/
|
|
4029
|
+
async preloadNext(stageId, loadStaticStage) {
|
|
4030
|
+
if (loadStaticStage) {
|
|
4031
|
+
stageState2.next = await loadStaticStage(stageId);
|
|
4032
|
+
} else {
|
|
4033
|
+
stageState2.next = await this.loadStageData(stageId);
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
};
|
|
4037
|
+
|
|
4038
|
+
// src/lib/stage/stage-factory.ts
|
|
4039
|
+
init_stage();
|
|
4040
|
+
|
|
4041
|
+
// src/lib/entities/text.ts
|
|
4042
|
+
init_entity();
|
|
4043
|
+
init_builder();
|
|
4044
|
+
init_create();
|
|
4045
|
+
import { Color as Color9, Group as Group5, Sprite as ThreeSprite, SpriteMaterial, CanvasTexture, LinearFilter, Vector2 as Vector27, ClampToEdgeWrapping } from "three";
|
|
4046
|
+
|
|
4047
|
+
// src/lib/entities/delegates/debug.ts
|
|
4048
|
+
import { MeshStandardMaterial as MeshStandardMaterial2, MeshBasicMaterial as MeshBasicMaterial2, MeshPhongMaterial as MeshPhongMaterial2 } from "three";
|
|
4049
|
+
function hasDebugInfo(obj) {
|
|
4050
|
+
return obj && typeof obj.getDebugInfo === "function";
|
|
4051
|
+
}
|
|
4052
|
+
var DebugDelegate = class {
|
|
4053
|
+
entity;
|
|
4054
|
+
constructor(entity) {
|
|
4055
|
+
this.entity = entity;
|
|
4056
|
+
}
|
|
4057
|
+
/**
|
|
4058
|
+
* Get formatted position string
|
|
4059
|
+
*/
|
|
4060
|
+
getPositionString() {
|
|
4061
|
+
if (this.entity.mesh) {
|
|
4062
|
+
const { x: x2, y: y2, z: z2 } = this.entity.mesh.position;
|
|
4063
|
+
return `${x2.toFixed(2)}, ${y2.toFixed(2)}, ${z2.toFixed(2)}`;
|
|
4064
|
+
}
|
|
4065
|
+
const { x, y, z } = this.entity.options.position || { x: 0, y: 0, z: 0 };
|
|
4066
|
+
return `${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)}`;
|
|
4067
|
+
}
|
|
4068
|
+
/**
|
|
4069
|
+
* Get formatted rotation string (in degrees)
|
|
4070
|
+
*/
|
|
4071
|
+
getRotationString() {
|
|
4072
|
+
if (this.entity.mesh) {
|
|
4073
|
+
const { x: x2, y: y2, z: z2 } = this.entity.mesh.rotation;
|
|
4074
|
+
const toDeg2 = (rad) => (rad * 180 / Math.PI).toFixed(1);
|
|
4075
|
+
return `${toDeg2(x2)}\xB0, ${toDeg2(y2)}\xB0, ${toDeg2(z2)}\xB0`;
|
|
4076
|
+
}
|
|
4077
|
+
const { x, y, z } = this.entity.options.rotation || { x: 0, y: 0, z: 0 };
|
|
4078
|
+
const toDeg = (rad) => (rad * 180 / Math.PI).toFixed(1);
|
|
4079
|
+
return `${toDeg(x)}\xB0, ${toDeg(y)}\xB0, ${toDeg(z)}\xB0`;
|
|
4080
|
+
}
|
|
4081
|
+
/**
|
|
4082
|
+
* Get material information
|
|
4083
|
+
*/
|
|
4084
|
+
getMaterialInfo() {
|
|
4085
|
+
if (!this.entity.mesh || !this.entity.mesh.material) {
|
|
4086
|
+
return { type: "none" };
|
|
4087
|
+
}
|
|
4088
|
+
const material = Array.isArray(this.entity.mesh.material) ? this.entity.mesh.material[0] : this.entity.mesh.material;
|
|
4089
|
+
const info = {
|
|
4090
|
+
type: material.type
|
|
4091
|
+
};
|
|
4092
|
+
if (material instanceof MeshStandardMaterial2 || material instanceof MeshBasicMaterial2 || material instanceof MeshPhongMaterial2) {
|
|
4093
|
+
info.color = `#${material.color.getHexString()}`;
|
|
4094
|
+
info.opacity = material.opacity;
|
|
4095
|
+
info.transparent = material.transparent;
|
|
4096
|
+
}
|
|
4097
|
+
if ("roughness" in material) {
|
|
4098
|
+
info.roughness = material.roughness;
|
|
4099
|
+
}
|
|
4100
|
+
if ("metalness" in material) {
|
|
4101
|
+
info.metalness = material.metalness;
|
|
4102
|
+
}
|
|
4103
|
+
return info;
|
|
4104
|
+
}
|
|
4105
|
+
getPhysicsInfo() {
|
|
4106
|
+
if (!this.entity.body) {
|
|
4107
|
+
return null;
|
|
4108
|
+
}
|
|
4109
|
+
const info = {
|
|
4110
|
+
type: this.entity.body.bodyType(),
|
|
4111
|
+
mass: this.entity.body.mass(),
|
|
4112
|
+
isEnabled: this.entity.body.isEnabled(),
|
|
4113
|
+
isSleeping: this.entity.body.isSleeping()
|
|
4114
|
+
};
|
|
4115
|
+
const velocity = this.entity.body.linvel();
|
|
4116
|
+
info.velocity = `${velocity.x.toFixed(2)}, ${velocity.y.toFixed(2)}, ${velocity.z.toFixed(2)}`;
|
|
4117
|
+
return info;
|
|
4118
|
+
}
|
|
4119
|
+
buildDebugInfo() {
|
|
4120
|
+
const defaultInfo = {
|
|
4121
|
+
name: this.entity.name || this.entity.uuid,
|
|
4122
|
+
uuid: this.entity.uuid,
|
|
4123
|
+
position: this.getPositionString(),
|
|
4124
|
+
rotation: this.getRotationString(),
|
|
4125
|
+
material: this.getMaterialInfo()
|
|
4126
|
+
};
|
|
4127
|
+
const physicsInfo = this.getPhysicsInfo();
|
|
4128
|
+
if (physicsInfo) {
|
|
4129
|
+
defaultInfo.physics = physicsInfo;
|
|
4130
|
+
}
|
|
4131
|
+
if (this.entity.behaviors.length > 0) {
|
|
4132
|
+
defaultInfo.behaviors = this.entity.behaviors.map((b) => b.constructor.name);
|
|
4133
|
+
}
|
|
4134
|
+
if (hasDebugInfo(this.entity)) {
|
|
4135
|
+
const customInfo = this.entity.getDebugInfo();
|
|
4136
|
+
return { ...defaultInfo, ...customInfo };
|
|
4137
|
+
}
|
|
4138
|
+
return defaultInfo;
|
|
4139
|
+
}
|
|
4140
|
+
};
|
|
4141
|
+
|
|
4142
|
+
// src/lib/entities/text.ts
|
|
4143
|
+
var textDefaults = {
|
|
4144
|
+
position: void 0,
|
|
4145
|
+
text: "",
|
|
4146
|
+
fontFamily: 'Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
|
4147
|
+
fontSize: 18,
|
|
4148
|
+
fontColor: "#FFFFFF",
|
|
4149
|
+
backgroundColor: null,
|
|
4150
|
+
padding: 4,
|
|
4151
|
+
stickToViewport: true,
|
|
4152
|
+
screenPosition: new Vector27(24, 24),
|
|
4153
|
+
zDistance: 1
|
|
4154
|
+
};
|
|
4155
|
+
var TextBuilder = class extends EntityBuilder {
|
|
4156
|
+
createEntity(options) {
|
|
4157
|
+
return new ZylemText(options);
|
|
4158
|
+
}
|
|
4159
|
+
};
|
|
4160
|
+
var TEXT_TYPE = Symbol("Text");
|
|
4161
|
+
var ZylemText = class _ZylemText extends GameEntity {
|
|
4162
|
+
static type = TEXT_TYPE;
|
|
4163
|
+
_sprite = null;
|
|
4164
|
+
_texture = null;
|
|
4165
|
+
_canvas = null;
|
|
4166
|
+
_ctx = null;
|
|
4167
|
+
_cameraRef = null;
|
|
4168
|
+
_lastCanvasW = 0;
|
|
4169
|
+
_lastCanvasH = 0;
|
|
4170
|
+
constructor(options) {
|
|
4171
|
+
super();
|
|
4172
|
+
this.options = { ...textDefaults, ...options };
|
|
4173
|
+
this.group = new Group5();
|
|
4174
|
+
this.createSprite();
|
|
4175
|
+
this.lifeCycleDelegate = {
|
|
4176
|
+
setup: [this.textSetup.bind(this)],
|
|
4177
|
+
update: [this.textUpdate.bind(this)]
|
|
4178
|
+
};
|
|
4179
|
+
}
|
|
4180
|
+
createSprite() {
|
|
4181
|
+
this._canvas = document.createElement("canvas");
|
|
4182
|
+
this._ctx = this._canvas.getContext("2d");
|
|
4183
|
+
this._texture = new CanvasTexture(this._canvas);
|
|
4184
|
+
this._texture.minFilter = LinearFilter;
|
|
4185
|
+
this._texture.magFilter = LinearFilter;
|
|
4186
|
+
const material = new SpriteMaterial({
|
|
4187
|
+
map: this._texture,
|
|
4188
|
+
transparent: true,
|
|
4189
|
+
depthTest: false,
|
|
4190
|
+
depthWrite: false,
|
|
4191
|
+
alphaTest: 0.5
|
|
4192
|
+
});
|
|
4193
|
+
this._sprite = new ThreeSprite(material);
|
|
4194
|
+
this.group?.add(this._sprite);
|
|
4195
|
+
this.redrawText(this.options.text ?? "");
|
|
4196
|
+
}
|
|
4197
|
+
measureAndResizeCanvas(text2, fontSize, fontFamily, padding) {
|
|
4198
|
+
if (!this._canvas || !this._ctx) return { sizeChanged: false };
|
|
4199
|
+
this._ctx.font = `${fontSize}px ${fontFamily}`;
|
|
4200
|
+
const metrics = this._ctx.measureText(text2);
|
|
4201
|
+
const textWidth = Math.ceil(metrics.width);
|
|
4202
|
+
const textHeight = Math.ceil(fontSize * 1.4);
|
|
4203
|
+
const nextW = Math.max(2, textWidth + padding * 2);
|
|
4204
|
+
const nextH = Math.max(2, textHeight + padding * 2);
|
|
4205
|
+
const sizeChanged = nextW !== this._lastCanvasW || nextH !== this._lastCanvasH;
|
|
4206
|
+
this._canvas.width = nextW;
|
|
4207
|
+
this._canvas.height = nextH;
|
|
4208
|
+
this._lastCanvasW = nextW;
|
|
4209
|
+
this._lastCanvasH = nextH;
|
|
4210
|
+
return { sizeChanged };
|
|
4211
|
+
}
|
|
4212
|
+
drawCenteredText(text2, fontSize, fontFamily) {
|
|
4213
|
+
if (!this._canvas || !this._ctx) return;
|
|
4214
|
+
this._ctx.font = `${fontSize}px ${fontFamily}`;
|
|
4215
|
+
this._ctx.textAlign = "center";
|
|
4216
|
+
this._ctx.textBaseline = "middle";
|
|
4217
|
+
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
|
4218
|
+
if (this.options.backgroundColor) {
|
|
4219
|
+
this._ctx.fillStyle = this.toCssColor(this.options.backgroundColor);
|
|
4220
|
+
this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
|
|
4221
|
+
}
|
|
4222
|
+
this._ctx.fillStyle = this.toCssColor(this.options.fontColor ?? "#FFFFFF");
|
|
4223
|
+
this._ctx.fillText(text2, this._canvas.width / 2, this._canvas.height / 2);
|
|
4224
|
+
}
|
|
4225
|
+
updateTexture(sizeChanged) {
|
|
4226
|
+
if (!this._texture || !this._canvas) return;
|
|
4227
|
+
if (sizeChanged) {
|
|
4228
|
+
this._texture.dispose();
|
|
4229
|
+
this._texture = new CanvasTexture(this._canvas);
|
|
4230
|
+
this._texture.minFilter = LinearFilter;
|
|
4231
|
+
this._texture.magFilter = LinearFilter;
|
|
4232
|
+
this._texture.wrapS = ClampToEdgeWrapping;
|
|
4233
|
+
this._texture.wrapT = ClampToEdgeWrapping;
|
|
4234
|
+
}
|
|
4235
|
+
this._texture.image = this._canvas;
|
|
4236
|
+
this._texture.needsUpdate = true;
|
|
4237
|
+
if (this._sprite && this._sprite.material) {
|
|
4238
|
+
this._sprite.material.map = this._texture;
|
|
4239
|
+
this._sprite.material.needsUpdate = true;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
redrawText(_text) {
|
|
4243
|
+
if (!this._canvas || !this._ctx) return;
|
|
4244
|
+
const fontSize = this.options.fontSize ?? 18;
|
|
4245
|
+
const fontFamily = this.options.fontFamily ?? textDefaults.fontFamily;
|
|
4246
|
+
const padding = this.options.padding ?? 4;
|
|
4247
|
+
const { sizeChanged } = this.measureAndResizeCanvas(_text, fontSize, fontFamily, padding);
|
|
4248
|
+
this.drawCenteredText(_text, fontSize, fontFamily);
|
|
4249
|
+
this.updateTexture(Boolean(sizeChanged));
|
|
4250
|
+
if (this.options.stickToViewport && this._cameraRef) {
|
|
4251
|
+
this.updateStickyTransform();
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
toCssColor(color) {
|
|
4255
|
+
if (typeof color === "string") return color;
|
|
4256
|
+
const c = color instanceof Color9 ? color : new Color9(color);
|
|
4257
|
+
return `#${c.getHexString()}`;
|
|
4258
|
+
}
|
|
4259
|
+
textSetup(params) {
|
|
4260
|
+
this._cameraRef = params.camera;
|
|
4261
|
+
if (this.options.stickToViewport && this._cameraRef) {
|
|
4262
|
+
this._cameraRef.camera.add(this.group);
|
|
4263
|
+
this.updateStickyTransform();
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
textUpdate(params) {
|
|
4267
|
+
if (!this._sprite) return;
|
|
4268
|
+
if (this.options.stickToViewport && this._cameraRef) {
|
|
4269
|
+
this.updateStickyTransform();
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
getResolution() {
|
|
4273
|
+
return {
|
|
4274
|
+
width: this._cameraRef?.screenResolution.x ?? 1,
|
|
4275
|
+
height: this._cameraRef?.screenResolution.y ?? 1
|
|
4276
|
+
};
|
|
4277
|
+
}
|
|
4278
|
+
getScreenPixels(sp, width, height) {
|
|
4279
|
+
const isPercentX = sp.x >= 0 && sp.x <= 1;
|
|
4280
|
+
const isPercentY = sp.y >= 0 && sp.y <= 1;
|
|
4281
|
+
return {
|
|
4282
|
+
px: isPercentX ? sp.x * width : sp.x,
|
|
4283
|
+
py: isPercentY ? sp.y * height : sp.y
|
|
4284
|
+
};
|
|
4285
|
+
}
|
|
4286
|
+
computeWorldExtents(camera, zDist) {
|
|
4287
|
+
let worldHalfW = 1;
|
|
4288
|
+
let worldHalfH = 1;
|
|
4289
|
+
if (camera.isPerspectiveCamera) {
|
|
4290
|
+
const pc = camera;
|
|
4291
|
+
const halfH = Math.tan(pc.fov * Math.PI / 180 / 2) * zDist;
|
|
4292
|
+
const halfW = halfH * pc.aspect;
|
|
4293
|
+
worldHalfW = halfW;
|
|
4294
|
+
worldHalfH = halfH;
|
|
4295
|
+
} else if (camera.isOrthographicCamera) {
|
|
4296
|
+
const oc = camera;
|
|
4297
|
+
worldHalfW = (oc.right - oc.left) / 2;
|
|
4298
|
+
worldHalfH = (oc.top - oc.bottom) / 2;
|
|
4299
|
+
}
|
|
4300
|
+
return { worldHalfW, worldHalfH };
|
|
4301
|
+
}
|
|
4302
|
+
updateSpriteScale(worldHalfH, viewportHeight) {
|
|
4303
|
+
if (!this._canvas || !this._sprite) return;
|
|
4304
|
+
const planeH = worldHalfH * 2;
|
|
4305
|
+
const unitsPerPixel = planeH / viewportHeight;
|
|
4306
|
+
const pixelH = this._canvas.height;
|
|
4307
|
+
const scaleY = Math.max(1e-4, pixelH * unitsPerPixel);
|
|
4308
|
+
const aspect = this._canvas.width / this._canvas.height;
|
|
4309
|
+
const scaleX = scaleY * aspect;
|
|
4310
|
+
this._sprite.scale.set(scaleX, scaleY, 1);
|
|
4311
|
+
}
|
|
4312
|
+
updateStickyTransform() {
|
|
4313
|
+
if (!this._sprite || !this._cameraRef) return;
|
|
4314
|
+
const camera = this._cameraRef.camera;
|
|
4315
|
+
const { width, height } = this.getResolution();
|
|
4316
|
+
const sp = this.options.screenPosition ?? new Vector27(24, 24);
|
|
4317
|
+
const { px, py } = this.getScreenPixels(sp, width, height);
|
|
4318
|
+
const zDist = Math.max(1e-3, this.options.zDistance ?? 1);
|
|
4319
|
+
const { worldHalfW, worldHalfH } = this.computeWorldExtents(camera, zDist);
|
|
4320
|
+
const ndcX = px / width * 2 - 1;
|
|
4321
|
+
const ndcY = 1 - py / height * 2;
|
|
4322
|
+
const localX = ndcX * worldHalfW;
|
|
4323
|
+
const localY = ndcY * worldHalfH;
|
|
4324
|
+
this.group?.position.set(localX, localY, -zDist);
|
|
4325
|
+
this.updateSpriteScale(worldHalfH, height);
|
|
4326
|
+
}
|
|
4327
|
+
updateText(_text) {
|
|
4328
|
+
this.options.text = _text;
|
|
4329
|
+
this.redrawText(_text);
|
|
4330
|
+
if (this.options.stickToViewport && this._cameraRef) {
|
|
4331
|
+
this.updateStickyTransform();
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
buildInfo() {
|
|
4335
|
+
const delegate = new DebugDelegate(this);
|
|
4336
|
+
const baseInfo = delegate.buildDebugInfo();
|
|
4337
|
+
return {
|
|
4338
|
+
...baseInfo,
|
|
4339
|
+
type: String(_ZylemText.type),
|
|
4340
|
+
text: this.options.text ?? "",
|
|
4341
|
+
sticky: this.options.stickToViewport
|
|
4342
|
+
};
|
|
4343
|
+
}
|
|
4344
|
+
};
|
|
4345
|
+
async function text(...args) {
|
|
4346
|
+
return createEntity({
|
|
4347
|
+
args,
|
|
4348
|
+
defaultConfig: { ...textDefaults },
|
|
4349
|
+
EntityClass: ZylemText,
|
|
4350
|
+
BuilderClass: TextBuilder,
|
|
4351
|
+
entityType: ZylemText.type
|
|
4352
|
+
});
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
// src/lib/entities/sprite.ts
|
|
4356
|
+
init_entity();
|
|
4357
|
+
init_builder();
|
|
4358
|
+
init_builder();
|
|
4359
|
+
init_create();
|
|
4360
|
+
import { ColliderDesc as ColliderDesc3 } from "@dimforge/rapier3d-compat";
|
|
4361
|
+
import { Color as Color10, Euler, Group as Group6, Quaternion as Quaternion2, Vector3 as Vector314 } from "three";
|
|
4362
|
+
import {
|
|
4363
|
+
TextureLoader as TextureLoader3,
|
|
4364
|
+
SpriteMaterial as SpriteMaterial2,
|
|
4365
|
+
Sprite as ThreeSprite2
|
|
4366
|
+
} from "three";
|
|
4367
|
+
var spriteDefaults = {
|
|
4368
|
+
size: new Vector314(1, 1, 1),
|
|
4369
|
+
position: new Vector314(0, 0, 0),
|
|
4370
|
+
collision: {
|
|
4371
|
+
static: false
|
|
4372
|
+
},
|
|
4373
|
+
material: {
|
|
4374
|
+
color: new Color10("#ffffff"),
|
|
4375
|
+
shader: "standard"
|
|
4376
|
+
},
|
|
4377
|
+
images: [],
|
|
4378
|
+
animations: []
|
|
4379
|
+
};
|
|
4380
|
+
var SpriteCollisionBuilder = class extends EntityCollisionBuilder {
|
|
4381
|
+
collider(options) {
|
|
4382
|
+
const size = options.collisionSize || options.size || new Vector314(1, 1, 1);
|
|
4383
|
+
const half = { x: size.x / 2, y: size.y / 2, z: size.z / 2 };
|
|
4384
|
+
let colliderDesc = ColliderDesc3.cuboid(half.x, half.y, half.z);
|
|
4385
|
+
return colliderDesc;
|
|
4386
|
+
}
|
|
4387
|
+
};
|
|
4388
|
+
var SpriteBuilder = class extends EntityBuilder {
|
|
4389
|
+
createEntity(options) {
|
|
4390
|
+
return new ZylemSprite(options);
|
|
4391
|
+
}
|
|
4392
|
+
};
|
|
4393
|
+
var SPRITE_TYPE = Symbol("Sprite");
|
|
4394
|
+
var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
4395
|
+
static type = SPRITE_TYPE;
|
|
4396
|
+
sprites = [];
|
|
4397
|
+
spriteMap = /* @__PURE__ */ new Map();
|
|
4398
|
+
currentSpriteIndex = 0;
|
|
4399
|
+
animations = /* @__PURE__ */ new Map();
|
|
4400
|
+
currentAnimation = null;
|
|
4401
|
+
currentAnimationFrame = "";
|
|
4402
|
+
currentAnimationIndex = 0;
|
|
4403
|
+
currentAnimationTime = 0;
|
|
4404
|
+
constructor(options) {
|
|
4405
|
+
super();
|
|
4406
|
+
this.options = { ...spriteDefaults, ...options };
|
|
4407
|
+
this.createSpritesFromImages(options?.images || []);
|
|
4408
|
+
this.createAnimations(options?.animations || []);
|
|
4409
|
+
this.lifeCycleDelegate = {
|
|
4410
|
+
update: [this.spriteUpdate.bind(this)],
|
|
4411
|
+
destroy: [this.spriteDestroy.bind(this)]
|
|
4412
|
+
};
|
|
4413
|
+
}
|
|
4414
|
+
createSpritesFromImages(images) {
|
|
4415
|
+
const textureLoader = new TextureLoader3();
|
|
4416
|
+
images.forEach((image, index) => {
|
|
4417
|
+
const spriteMap = textureLoader.load(image.file);
|
|
4418
|
+
const material = new SpriteMaterial2({
|
|
4419
|
+
map: spriteMap,
|
|
4420
|
+
transparent: true
|
|
4421
|
+
});
|
|
4422
|
+
const _sprite = new ThreeSprite2(material);
|
|
4423
|
+
_sprite.position.normalize();
|
|
4424
|
+
this.sprites.push(_sprite);
|
|
4425
|
+
this.spriteMap.set(image.name, index);
|
|
4426
|
+
});
|
|
4427
|
+
this.group = new Group6();
|
|
4428
|
+
this.group.add(...this.sprites);
|
|
4429
|
+
}
|
|
4430
|
+
createAnimations(animations) {
|
|
4431
|
+
animations.forEach((animation) => {
|
|
4432
|
+
const { name, frames, loop = false, speed = 1 } = animation;
|
|
4433
|
+
const internalAnimation = {
|
|
4434
|
+
frames: frames.map((frame, index) => ({
|
|
4435
|
+
key: frame,
|
|
4436
|
+
index,
|
|
4437
|
+
time: (typeof speed === "number" ? speed : speed[index]) * (index + 1),
|
|
4438
|
+
duration: typeof speed === "number" ? speed : speed[index]
|
|
4439
|
+
})),
|
|
4440
|
+
loop
|
|
4441
|
+
};
|
|
4442
|
+
this.animations.set(name, internalAnimation);
|
|
4443
|
+
});
|
|
4444
|
+
}
|
|
4445
|
+
setSprite(key) {
|
|
4446
|
+
const spriteIndex = this.spriteMap.get(key);
|
|
4447
|
+
const useIndex = spriteIndex ?? 0;
|
|
4448
|
+
this.currentSpriteIndex = useIndex;
|
|
4449
|
+
this.sprites.forEach((_sprite, i) => {
|
|
4450
|
+
_sprite.visible = this.currentSpriteIndex === i;
|
|
4451
|
+
});
|
|
4452
|
+
}
|
|
4453
|
+
setAnimation(name, delta) {
|
|
4454
|
+
const animation = this.animations.get(name);
|
|
4455
|
+
if (!animation) return;
|
|
4456
|
+
const { loop, frames } = animation;
|
|
4457
|
+
const frame = frames[this.currentAnimationIndex];
|
|
4458
|
+
if (name === this.currentAnimation) {
|
|
4459
|
+
this.currentAnimationFrame = frame.key;
|
|
4460
|
+
this.currentAnimationTime += delta;
|
|
4461
|
+
this.setSprite(this.currentAnimationFrame);
|
|
4462
|
+
} else {
|
|
4463
|
+
this.currentAnimation = name;
|
|
4464
|
+
}
|
|
4465
|
+
if (this.currentAnimationTime > frame.time) {
|
|
4466
|
+
this.currentAnimationIndex++;
|
|
4467
|
+
}
|
|
4468
|
+
if (this.currentAnimationIndex >= frames.length) {
|
|
4469
|
+
if (loop) {
|
|
4470
|
+
this.currentAnimationIndex = 0;
|
|
4471
|
+
this.currentAnimationTime = 0;
|
|
4472
|
+
} else {
|
|
4473
|
+
this.currentAnimationTime = frames[this.currentAnimationIndex].time;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
async spriteUpdate(params) {
|
|
4478
|
+
this.sprites.forEach((_sprite) => {
|
|
4479
|
+
if (_sprite.material) {
|
|
4480
|
+
const q = this.body?.rotation();
|
|
4481
|
+
if (q) {
|
|
4482
|
+
const quat = new Quaternion2(q.x, q.y, q.z, q.w);
|
|
4483
|
+
const euler = new Euler().setFromQuaternion(quat, "XYZ");
|
|
4484
|
+
_sprite.material.rotation = euler.z;
|
|
4485
|
+
}
|
|
4486
|
+
_sprite.scale.set(this.options.size?.x ?? 1, this.options.size?.y ?? 1, this.options.size?.z ?? 1);
|
|
4487
|
+
}
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4490
|
+
async spriteDestroy(params) {
|
|
4491
|
+
this.sprites.forEach((_sprite) => {
|
|
4492
|
+
_sprite.removeFromParent();
|
|
4493
|
+
});
|
|
4494
|
+
this.group?.remove(...this.sprites);
|
|
4495
|
+
this.group?.removeFromParent();
|
|
4496
|
+
}
|
|
4497
|
+
buildInfo() {
|
|
4498
|
+
const delegate = new DebugDelegate(this);
|
|
4499
|
+
const baseInfo = delegate.buildDebugInfo();
|
|
4500
|
+
return {
|
|
4501
|
+
...baseInfo,
|
|
4502
|
+
type: String(_ZylemSprite.type)
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
};
|
|
4506
|
+
async function sprite(...args) {
|
|
4507
|
+
return createEntity({
|
|
4508
|
+
args,
|
|
4509
|
+
defaultConfig: spriteDefaults,
|
|
4510
|
+
EntityClass: ZylemSprite,
|
|
4511
|
+
BuilderClass: SpriteBuilder,
|
|
4512
|
+
CollisionBuilderClass: SpriteCollisionBuilder,
|
|
4513
|
+
entityType: ZylemSprite.type
|
|
4514
|
+
});
|
|
4515
|
+
}
|
|
4516
|
+
|
|
4517
|
+
// src/lib/entities/entity-factory.ts
|
|
4518
|
+
var EntityFactory = {
|
|
4519
|
+
registry: /* @__PURE__ */ new Map(),
|
|
4520
|
+
register(type, creator) {
|
|
4521
|
+
this.registry.set(type, creator);
|
|
4522
|
+
},
|
|
4523
|
+
async createFromBlueprint(blueprint) {
|
|
4524
|
+
const creator = this.registry.get(blueprint.type);
|
|
4525
|
+
if (!creator) {
|
|
4526
|
+
throw new Error(`Unknown entity type: ${blueprint.type}`);
|
|
4527
|
+
}
|
|
4528
|
+
const options = {
|
|
4529
|
+
...blueprint.data,
|
|
4530
|
+
position: blueprint.position ? { x: blueprint.position[0], y: blueprint.position[1], z: 0 } : void 0,
|
|
4531
|
+
name: blueprint.id
|
|
4532
|
+
};
|
|
4533
|
+
const entity = await creator(options);
|
|
4534
|
+
return entity;
|
|
4535
|
+
}
|
|
4536
|
+
};
|
|
4537
|
+
EntityFactory.register("text", async (opts) => await text(opts));
|
|
4538
|
+
EntityFactory.register("sprite", async (opts) => await sprite(opts));
|
|
4539
|
+
|
|
4540
|
+
// src/lib/stage/stage-factory.ts
|
|
4541
|
+
var StageFactory = {
|
|
4542
|
+
async createFromBlueprint(blueprint) {
|
|
4543
|
+
const stage = createStage({
|
|
4544
|
+
// Map blueprint properties to stage options if needed
|
|
4545
|
+
// e.g. name: blueprint.name
|
|
4546
|
+
});
|
|
4547
|
+
if (blueprint.entities) {
|
|
4548
|
+
for (const entityBlueprint of blueprint.entities) {
|
|
4549
|
+
try {
|
|
4550
|
+
const entity = await EntityFactory.createFromBlueprint(entityBlueprint);
|
|
4551
|
+
stage.add(entity);
|
|
4552
|
+
} catch (e) {
|
|
4553
|
+
console.error(`Failed to create entity ${entityBlueprint.id} for stage ${blueprint.id}`, e);
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
return stage;
|
|
4558
|
+
}
|
|
4559
|
+
};
|
|
4560
|
+
|
|
4561
|
+
// src/lib/game/game.ts
|
|
4562
|
+
init_game_state();
|
|
4563
|
+
var Game = class {
|
|
4564
|
+
wrappedGame = null;
|
|
4565
|
+
options;
|
|
4566
|
+
update = () => {
|
|
4567
|
+
};
|
|
4568
|
+
setup = () => {
|
|
4569
|
+
};
|
|
4570
|
+
destroy = () => {
|
|
4571
|
+
};
|
|
4572
|
+
refErrorMessage = "lost reference to game";
|
|
4573
|
+
constructor(options) {
|
|
4574
|
+
this.options = options;
|
|
4575
|
+
if (!hasStages(options)) {
|
|
4576
|
+
this.options.push(createStage());
|
|
4577
|
+
}
|
|
4578
|
+
const globals = extractGlobalsFromOptions(options);
|
|
4579
|
+
if (globals) {
|
|
4580
|
+
initGlobals(globals);
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
async start() {
|
|
4584
|
+
const game = await this.load();
|
|
4585
|
+
this.wrappedGame = game;
|
|
4586
|
+
this.setOverrides();
|
|
4587
|
+
game.start();
|
|
4588
|
+
return this;
|
|
4589
|
+
}
|
|
4590
|
+
async load() {
|
|
4591
|
+
const options = await convertNodes(this.options);
|
|
4592
|
+
const resolved = resolveGameConfig(options);
|
|
4593
|
+
const game = new ZylemGame({
|
|
4594
|
+
...options,
|
|
4595
|
+
...resolved
|
|
4596
|
+
}, this);
|
|
4597
|
+
await game.loadStage(options.stages[0]);
|
|
4598
|
+
return game;
|
|
4599
|
+
}
|
|
4600
|
+
setOverrides() {
|
|
4601
|
+
if (!this.wrappedGame) {
|
|
4602
|
+
console.error(this.refErrorMessage);
|
|
4603
|
+
return;
|
|
4604
|
+
}
|
|
4605
|
+
this.wrappedGame.customSetup = this.setup;
|
|
4606
|
+
this.wrappedGame.customUpdate = this.update;
|
|
4607
|
+
this.wrappedGame.customDestroy = this.destroy;
|
|
4608
|
+
}
|
|
4609
|
+
async pause() {
|
|
4610
|
+
setPaused(true);
|
|
4611
|
+
}
|
|
4612
|
+
async resume() {
|
|
4613
|
+
setPaused(false);
|
|
4614
|
+
if (this.wrappedGame) {
|
|
4615
|
+
this.wrappedGame.previousTimeStamp = 0;
|
|
4616
|
+
this.wrappedGame.timer.reset();
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
async reset() {
|
|
4620
|
+
if (!this.wrappedGame) {
|
|
4621
|
+
console.error(this.refErrorMessage);
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4624
|
+
await this.wrappedGame.loadStage(this.wrappedGame.stages[0]);
|
|
4625
|
+
}
|
|
4626
|
+
async previousStage() {
|
|
4627
|
+
if (!this.wrappedGame) {
|
|
4628
|
+
console.error(this.refErrorMessage);
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
const currentStageId = this.wrappedGame.currentStageId;
|
|
4632
|
+
const currentIndex = this.wrappedGame.stages.findIndex((s) => s.wrappedStage.uuid === currentStageId);
|
|
4633
|
+
const previousStage = this.wrappedGame.stages[currentIndex - 1];
|
|
4634
|
+
if (!previousStage) {
|
|
4635
|
+
console.error("previous stage called on first stage");
|
|
4636
|
+
return;
|
|
4637
|
+
}
|
|
4638
|
+
await this.wrappedGame.loadStage(previousStage);
|
|
4639
|
+
}
|
|
4640
|
+
async loadStageFromId(stageId) {
|
|
4641
|
+
if (!this.wrappedGame) {
|
|
4642
|
+
console.error(this.refErrorMessage);
|
|
4643
|
+
return;
|
|
4644
|
+
}
|
|
4645
|
+
try {
|
|
4646
|
+
const blueprint = await StageManager.loadStageData(stageId);
|
|
4647
|
+
const stage = await StageFactory.createFromBlueprint(blueprint);
|
|
4648
|
+
await this.wrappedGame.loadStage(stage);
|
|
4649
|
+
stageState2.current = blueprint;
|
|
4650
|
+
} catch (e) {
|
|
4651
|
+
console.error(`Failed to load stage ${stageId}`, e);
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
async nextStage() {
|
|
4655
|
+
if (!this.wrappedGame) {
|
|
4656
|
+
console.error(this.refErrorMessage);
|
|
4657
|
+
return;
|
|
4658
|
+
}
|
|
4659
|
+
if (stageState2.next) {
|
|
4660
|
+
const nextId = stageState2.next.id;
|
|
4661
|
+
await StageManager.transitionForward(nextId);
|
|
4662
|
+
if (stageState2.current) {
|
|
4663
|
+
const stage = await StageFactory.createFromBlueprint(stageState2.current);
|
|
4664
|
+
await this.wrappedGame.loadStage(stage);
|
|
4665
|
+
return;
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
const currentStageId = this.wrappedGame.currentStageId;
|
|
4669
|
+
const currentIndex = this.wrappedGame.stages.findIndex((s) => s.wrappedStage.uuid === currentStageId);
|
|
4670
|
+
const nextStage = this.wrappedGame.stages[currentIndex + 1];
|
|
4671
|
+
if (!nextStage) {
|
|
4672
|
+
console.error("next stage called on last stage");
|
|
4673
|
+
return;
|
|
4674
|
+
}
|
|
4675
|
+
await this.wrappedGame.loadStage(nextStage);
|
|
4676
|
+
}
|
|
4677
|
+
async goToStage() {
|
|
4678
|
+
}
|
|
4679
|
+
async end() {
|
|
4680
|
+
}
|
|
4681
|
+
dispose() {
|
|
4682
|
+
if (this.wrappedGame) {
|
|
4683
|
+
this.wrappedGame.dispose();
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
onLoading(callback) {
|
|
4687
|
+
}
|
|
4688
|
+
};
|
|
4689
|
+
function createGame(...options) {
|
|
4690
|
+
return new Game(options);
|
|
4691
|
+
}
|
|
4692
|
+
|
|
4693
|
+
// src/lib/core/vessel.ts
|
|
4694
|
+
init_base_node();
|
|
4695
|
+
var VESSEL_TYPE = Symbol("vessel");
|
|
4696
|
+
var Vessel = class extends BaseNode {
|
|
4697
|
+
static type = VESSEL_TYPE;
|
|
4698
|
+
_setup(_params) {
|
|
4699
|
+
}
|
|
4700
|
+
async _loaded(_params) {
|
|
4701
|
+
}
|
|
4702
|
+
_update(_params) {
|
|
4703
|
+
}
|
|
4704
|
+
_destroy(_params) {
|
|
4705
|
+
}
|
|
4706
|
+
async _cleanup(_params) {
|
|
4707
|
+
}
|
|
4708
|
+
create() {
|
|
4709
|
+
return this;
|
|
4710
|
+
}
|
|
4711
|
+
};
|
|
4712
|
+
function vessel(...args) {
|
|
4713
|
+
const instance = new Vessel();
|
|
4714
|
+
args.forEach((arg) => instance.add(arg));
|
|
4715
|
+
return instance;
|
|
4716
|
+
}
|
|
4717
|
+
|
|
4718
|
+
// src/lib/actions/global-change.ts
|
|
4719
|
+
function globalChange(key, callback) {
|
|
4720
|
+
let previousValue = void 0;
|
|
4721
|
+
return (ctx) => {
|
|
4722
|
+
const currentValue = ctx.globals?.[key];
|
|
4723
|
+
if (previousValue !== currentValue) {
|
|
4724
|
+
if (!(previousValue === void 0 && currentValue === void 0)) {
|
|
4725
|
+
callback(currentValue, ctx);
|
|
4726
|
+
}
|
|
4727
|
+
previousValue = currentValue;
|
|
4728
|
+
}
|
|
4729
|
+
};
|
|
4730
|
+
}
|
|
4731
|
+
function globalChanges(keys, callback) {
|
|
4732
|
+
let previousValues = new Array(keys.length).fill(void 0);
|
|
4733
|
+
return (ctx) => {
|
|
4734
|
+
const currentValues = keys.map((k) => ctx.globals?.[k]);
|
|
4735
|
+
const hasAnyChange = currentValues.some((val, idx) => previousValues[idx] !== val);
|
|
4736
|
+
if (hasAnyChange) {
|
|
4737
|
+
const allPrevUndef = previousValues.every((v) => v === void 0);
|
|
4738
|
+
const allCurrUndef = currentValues.every((v) => v === void 0);
|
|
4739
|
+
if (!(allPrevUndef && allCurrUndef)) {
|
|
4740
|
+
callback(currentValues, ctx);
|
|
4741
|
+
}
|
|
4742
|
+
previousValues = currentValues;
|
|
4743
|
+
}
|
|
4744
|
+
};
|
|
4745
|
+
}
|
|
4746
|
+
function variableChange(key, callback) {
|
|
4747
|
+
let previousValue = void 0;
|
|
4748
|
+
return (ctx) => {
|
|
4749
|
+
const currentValue = ctx.stage?.getVariable?.(key) ?? void 0;
|
|
4750
|
+
if (previousValue !== currentValue) {
|
|
4751
|
+
if (!(previousValue === void 0 && currentValue === void 0)) {
|
|
4752
|
+
callback(currentValue, ctx);
|
|
4753
|
+
}
|
|
4754
|
+
previousValue = currentValue;
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
}
|
|
4758
|
+
function variableChanges(keys, callback) {
|
|
4759
|
+
let previousValues = new Array(keys.length).fill(void 0);
|
|
4760
|
+
return (ctx) => {
|
|
4761
|
+
const reader = (k) => ctx.stage?.getVariable?.(k);
|
|
4762
|
+
const currentValues = keys.map(reader);
|
|
4763
|
+
const hasAnyChange = currentValues.some((val, idx) => previousValues[idx] !== val);
|
|
4764
|
+
if (hasAnyChange) {
|
|
4765
|
+
const allPrevUndef = previousValues.every((v) => v === void 0);
|
|
4766
|
+
const allCurrUndef = currentValues.every((v) => v === void 0);
|
|
4767
|
+
if (!(allPrevUndef && allCurrUndef)) {
|
|
4768
|
+
callback(currentValues, ctx);
|
|
4769
|
+
}
|
|
4770
|
+
previousValues = currentValues;
|
|
4771
|
+
}
|
|
4772
|
+
};
|
|
4773
|
+
}
|
|
4
4774
|
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4775
|
+
createGame,
|
|
4776
|
+
globalChange,
|
|
4777
|
+
globalChanges,
|
|
4778
|
+
variableChange,
|
|
4779
|
+
variableChanges,
|
|
4780
|
+
vessel
|
|
11
4781
|
};
|
|
4782
|
+
//# sourceMappingURL=core.js.map
|