@rpgjs/client 5.0.0-beta.1 → 5.0.0-beta.11
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/CHANGELOG.md +49 -0
- package/LICENSE +19 -0
- package/dist/Game/AnimationManager.d.ts +1 -1
- package/dist/Game/AnimationManager.js +18 -9
- package/dist/Game/AnimationManager.js.map +1 -1
- package/dist/Game/AnimationManager.spec.d.ts +1 -0
- package/dist/Game/Event.js.map +1 -1
- package/dist/Game/Map.d.ts +9 -1
- package/dist/Game/Map.js +63 -5
- package/dist/Game/Map.js.map +1 -1
- package/dist/Game/Object.d.ts +47 -15
- package/dist/Game/Object.js +82 -38
- package/dist/Game/Object.js.map +1 -1
- package/dist/Game/Player.js.map +1 -1
- package/dist/Game/ProjectileManager.d.ts +89 -0
- package/dist/Game/ProjectileManager.js +179 -0
- package/dist/Game/ProjectileManager.js.map +1 -0
- package/dist/Game/ProjectileManager.spec.d.ts +1 -0
- package/dist/Gui/Gui.d.ts +17 -4
- package/dist/Gui/Gui.js +78 -48
- package/dist/Gui/Gui.js.map +1 -1
- package/dist/Gui/Gui.spec.d.ts +1 -0
- package/dist/Gui/NotificationManager.js.map +1 -1
- package/dist/Resource.js +1 -1
- package/dist/Resource.js.map +1 -1
- package/dist/RpgClient.d.ts +110 -15
- package/dist/RpgClientEngine.d.ts +86 -10
- package/dist/RpgClientEngine.js +306 -49
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/Sound.js.map +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/decorateMetadata.js +1 -1
- package/dist/components/animations/animation.ce.js +4 -5
- package/dist/components/animations/animation.ce.js.map +1 -1
- package/dist/components/animations/hit.ce.js +19 -25
- package/dist/components/animations/hit.ce.js.map +1 -1
- package/dist/components/animations/index.js +4 -4
- package/dist/components/animations/index.js.map +1 -1
- package/dist/components/character.ce.js +422 -240
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/dynamics/bar.ce.js +97 -0
- package/dist/components/dynamics/bar.ce.js.map +1 -0
- package/dist/components/dynamics/image.ce.js +24 -0
- package/dist/components/dynamics/image.ce.js.map +1 -0
- package/dist/components/dynamics/parse-value.d.ts +3 -0
- package/dist/components/dynamics/parse-value.js +54 -35
- package/dist/components/dynamics/parse-value.js.map +1 -1
- package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
- package/dist/components/dynamics/shape-utils.d.ts +16 -0
- package/dist/components/dynamics/shape-utils.js +73 -0
- package/dist/components/dynamics/shape-utils.js.map +1 -0
- package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
- package/dist/components/dynamics/shape.ce.js +84 -0
- package/dist/components/dynamics/shape.ce.js.map +1 -0
- package/dist/components/dynamics/text.ce.js +34 -56
- package/dist/components/dynamics/text.ce.js.map +1 -1
- package/dist/components/gui/box.ce.js +6 -8
- package/dist/components/gui/box.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +56 -62
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/gameover.ce.js +42 -65
- package/dist/components/gui/gameover.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js +21 -30
- package/dist/components/gui/hud/hud.ce.js.map +1 -1
- package/dist/components/gui/menu/equip-menu.ce.js +112 -165
- package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/exit-menu.ce.js +8 -6
- package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/items-menu.ce.js +52 -69
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js +75 -92
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/options-menu.ce.js +5 -4
- package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/skills-menu.ce.js +12 -17
- package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
- package/dist/components/gui/mobile/index.js +2 -2
- package/dist/components/gui/mobile/index.js.map +1 -1
- package/dist/components/gui/mobile/mobile.ce.js +5 -4
- package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
- package/dist/components/gui/notification/notification.ce.js +22 -24
- package/dist/components/gui/notification/notification.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js +72 -249
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js +90 -127
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/components/gui/title-screen.ce.js +45 -70
- package/dist/components/gui/title-screen.ce.js.map +1 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +1 -0
- package/dist/components/player-components-utils.d.ts +67 -0
- package/dist/components/player-components-utils.js +162 -0
- package/dist/components/player-components-utils.js.map +1 -0
- package/dist/components/player-components-utils.spec.d.ts +1 -0
- package/dist/components/player-components.ce.js +189 -0
- package/dist/components/player-components.ce.js.map +1 -0
- package/dist/components/prebuilt/hp-bar.ce.js +42 -44
- package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
- package/dist/components/prebuilt/light-halo.ce.js +36 -59
- package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
- package/dist/components/scenes/canvas.ce.js +165 -21
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/draw-map.ce.js +25 -32
- package/dist/components/scenes/draw-map.ce.js.map +1 -1
- package/dist/components/scenes/event-layer.ce.js +9 -8
- package/dist/components/scenes/event-layer.ce.js.map +1 -1
- package/dist/core/inject.js +1 -1
- package/dist/core/inject.js.map +1 -1
- package/dist/core/setup.js +1 -1
- package/dist/core/setup.js.map +1 -1
- package/dist/decorators/spritesheet.d.ts +1 -0
- package/dist/decorators/spritesheet.js +11 -0
- package/dist/decorators/spritesheet.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +26 -21
- package/dist/module.js +15 -1
- package/dist/module.js.map +1 -1
- package/dist/node_modules/.pnpm/{@signe_di@2.9.0 → @signe_di@3.0.1}/node_modules/@signe/di/dist/index.js +7 -117
- package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js +239 -0
- package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js +696 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js +44 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js.map +1 -0
- package/dist/node_modules/.pnpm/{@signe_sync@2.9.0 → @signe_sync@3.0.1}/node_modules/@signe/sync/dist/index.js +57 -141
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -1
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -1
- package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +27 -27
- package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -1
- package/dist/presets/animation.js.map +1 -1
- package/dist/presets/faceset.js.map +1 -1
- package/dist/presets/icon.js.map +1 -1
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/lpc.js.map +1 -1
- package/dist/presets/rmspritesheet.js.map +1 -1
- package/dist/services/AbstractSocket.js.map +1 -1
- package/dist/services/actionInput.d.ts +12 -0
- package/dist/services/actionInput.js +27 -0
- package/dist/services/actionInput.js.map +1 -0
- package/dist/services/actionInput.spec.d.ts +1 -0
- package/dist/services/keyboardControls.js.map +1 -1
- package/dist/services/loadMap.d.ts +6 -0
- package/dist/services/loadMap.js +1 -1
- package/dist/services/loadMap.js.map +1 -1
- package/dist/services/mmorpg-connection.d.ts +5 -0
- package/dist/services/mmorpg-connection.js +50 -0
- package/dist/services/mmorpg-connection.js.map +1 -0
- package/dist/services/mmorpg-connection.spec.d.ts +1 -0
- package/dist/services/mmorpg.d.ts +10 -4
- package/dist/services/mmorpg.js +56 -33
- package/dist/services/mmorpg.js.map +1 -1
- package/dist/services/pointerContext.d.ts +11 -0
- package/dist/services/pointerContext.js +48 -0
- package/dist/services/pointerContext.js.map +1 -0
- package/dist/services/pointerContext.spec.d.ts +1 -0
- package/dist/services/save.js.map +1 -1
- package/dist/services/save.spec.d.ts +1 -0
- package/dist/services/standalone-message.d.ts +1 -0
- package/dist/services/standalone-message.js +9 -0
- package/dist/services/standalone-message.js.map +1 -0
- package/dist/services/standalone.js +4 -3
- package/dist/services/standalone.js.map +1 -1
- package/dist/services/standalone.spec.d.ts +1 -0
- package/dist/utils/getEntityProp.js +4 -3
- package/dist/utils/getEntityProp.js.map +1 -1
- package/dist/utils/getEntityProp.spec.d.ts +1 -0
- package/dist/utils/readPropValue.d.ts +2 -0
- package/dist/utils/readPropValue.js +13 -0
- package/dist/utils/readPropValue.js.map +1 -0
- package/package.json +13 -14
- package/src/Game/AnimationManager.spec.ts +30 -0
- package/src/Game/AnimationManager.ts +22 -10
- package/src/Game/Map.ts +91 -2
- package/src/Game/Object.ts +148 -69
- package/src/Game/ProjectileManager.spec.ts +338 -0
- package/src/Game/ProjectileManager.ts +324 -0
- package/src/Gui/Gui.spec.ts +273 -0
- package/src/Gui/Gui.ts +105 -50
- package/src/Resource.ts +1 -2
- package/src/RpgClient.ts +125 -17
- package/src/RpgClientEngine.ts +457 -87
- package/src/components/character.ce +441 -32
- package/src/components/dynamics/bar.ce +88 -0
- package/src/components/dynamics/image.ce +21 -0
- package/src/components/dynamics/parse-value.spec.ts +83 -0
- package/src/components/dynamics/parse-value.ts +111 -37
- package/src/components/dynamics/shape-utils.spec.ts +46 -0
- package/src/components/dynamics/shape-utils.ts +61 -0
- package/src/components/dynamics/shape.ce +90 -0
- package/src/components/dynamics/text.ce +35 -149
- package/src/components/gui/dialogbox/index.ce +18 -8
- package/src/components/gui/gameover.ce +2 -1
- package/src/components/gui/menu/equip-menu.ce +2 -1
- package/src/components/gui/menu/exit-menu.ce +2 -1
- package/src/components/gui/menu/items-menu.ce +3 -2
- package/src/components/gui/menu/main-menu.ce +2 -1
- package/src/components/gui/save-load.ce +2 -1
- package/src/components/gui/shop/shop.ce +3 -2
- package/src/components/gui/title-screen.ce +2 -1
- package/src/components/index.ts +2 -1
- package/src/components/player-components-utils.spec.ts +109 -0
- package/src/components/player-components-utils.ts +205 -0
- package/src/components/player-components.ce +222 -0
- package/src/components/prebuilt/hp-bar.ce +4 -3
- package/src/components/prebuilt/light-halo.ce +2 -2
- package/src/components/scenes/canvas.ce +175 -8
- package/src/components/scenes/draw-map.ce +18 -17
- package/src/components/scenes/event-layer.ce +1 -2
- package/src/core/setup.ts +2 -2
- package/src/decorators/spritesheet.ts +8 -0
- package/src/index.ts +4 -0
- package/src/module.ts +18 -1
- package/src/services/actionInput.spec.ts +101 -0
- package/src/services/actionInput.ts +53 -0
- package/src/services/loadMap.ts +2 -0
- package/src/services/mmorpg-connection.spec.ts +99 -0
- package/src/services/mmorpg-connection.ts +69 -0
- package/src/services/mmorpg.ts +68 -36
- package/src/services/pointerContext.spec.ts +36 -0
- package/src/services/pointerContext.ts +84 -0
- package/src/services/save.spec.ts +127 -0
- package/src/services/standalone-message.ts +7 -0
- package/src/services/standalone.spec.ts +34 -0
- package/src/services/standalone.ts +3 -2
- package/src/utils/getEntityProp.spec.ts +96 -0
- package/src/utils/getEntityProp.ts +4 -3
- package/src/utils/readPropValue.ts +16 -0
- package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js +0 -457
- package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +0 -463
- package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +0 -2191
- package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +0 -10
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +0 -91
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +0 -14
- package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +0 -1
package/dist/Game/Object.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Object.js","names":[],"sources":["../../src/Game/Object.ts"],"sourcesContent":["import { Hooks, ModulesToken, RpgCommonPlayer } from \"@rpgjs/common\";\nimport { trigger, signal, effect } from \"canvasengine\";\nimport { filter, from, map, Subscription, switchMap } from \"rxjs\";\nimport { inject } from \"../core/inject\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport TextComponent from \"../components/dynamics/text.ce\";\n\nconst DYNAMIC_COMPONENTS = {\n text: TextComponent,\n}\n\ntype Frame = { x: number; y: number; ts: number };\n\nexport abstract class RpgClientObject extends RpgCommonPlayer {\n abstract _type: string;\n emitParticleTrigger = trigger();\n particleName = signal(\"\");\n animationCurrentIndex = signal(0);\n animationIsPlaying = signal(false);\n _param = signal({});\n frames: Frame[] = [];\n graphicsSignals = signal<any[]>([]);\n _component = {} // temporary component memory\n flashTrigger = trigger();\n\n constructor() {\n super();\n this.hooks.callHooks(\"client-sprite-onInit\", this).subscribe();\n\n this._frames.observable.subscribe(({ items }) => {\n if (!this.id) return;\n //if (this.id == this.engine.playerIdSignal()!) return;\n const nextFrames = items.flatMap((item): Frame[] =>\n Array.isArray(item) ? item : [item]\n );\n this.frames = [...this.frames, ...nextFrames];\n });\n\n this.graphics.observable\n .pipe(\n map(({ items }) => items),\n filter(graphics => graphics.length > 0),\n switchMap(graphics => from(Promise.all(graphics.map(graphic => this.engine.getSpriteSheet(graphic)))))\n )\n .subscribe((sheets) => { \n this.graphicsSignals.set(sheets);\n });\n\n this.componentsTop.observable\n .pipe(\n filter(value => value !== null && value !== undefined),\n map((value) => typeof value === 'string' ? JSON.parse(value) : value),\n )\n .subscribe(({components}) => {\n for (const component of components) {\n for (const [key, value] of Object.entries(component)) {\n this._component = value as any; // temporary component memory\n console.log(value)\n const type = (value as any).type as keyof typeof DYNAMIC_COMPONENTS;\n if (DYNAMIC_COMPONENTS[type]) {\n this.engine.addSpriteComponentInFront(DYNAMIC_COMPONENTS[type]);\n }\n }\n }\n });\n\n this.engine.tick\n .pipe\n //throttleTime(10)\n ()\n .subscribe(() => {\n const frame = this.frames.shift();\n if (frame) {\n if (typeof frame.x !== \"number\" || typeof frame.y !== \"number\") return;\n this.engine.scene.setBodyPosition(\n this.id,\n frame.x,\n frame.y,\n \"top-left\"\n );\n }\n });\n }\n\n /**\n * Access the shared client hook registry.\n *\n * @returns The hook service used to register and trigger client-side hooks.\n */\n get hooks() {\n return inject<Hooks>(ModulesToken);\n }\n\n /**\n * Access the current client engine instance.\n *\n * @returns The active {@link RpgClientEngine} instance.\n */\n get engine() {\n return inject(RpgClientEngine);\n }\n\n private animationSubscription?: Subscription;\n\n /**\n * Trigger a flash animation on this sprite\n * \n * This method triggers a flash effect using CanvasEngine's flash directive.\n * The flash can be configured with various options including type (alpha, tint, or both),\n * duration, cycles, and color.\n * \n * ## Design\n * \n * The flash uses a trigger system that is connected to the flash directive in the\n * character component. This allows for flexible configuration and can be triggered\n * from both server events and client-side code.\n * \n * @param options - Flash configuration options\n * @param options.type - Type of flash effect: 'alpha' (opacity), 'tint' (color), or 'both' (default: 'alpha')\n * @param options.duration - Duration of the flash animation in milliseconds (default: 300)\n * @param options.cycles - Number of flash cycles (flash on/off) (default: 1)\n * @param options.alpha - Alpha value when flashing, from 0 to 1 (default: 0.3)\n * @param options.tint - Tint color when flashing as hex value or color name (default: 0xffffff - white)\n * \n * @example\n * ```ts\n * // Simple flash with default settings (alpha flash)\n * player.flash();\n * \n * // Flash with red tint\n * player.flash({ type: 'tint', tint: 0xff0000 });\n * \n * // Flash with both alpha and tint\n * player.flash({ \n * type: 'both', \n * alpha: 0.5, \n * tint: 0xff0000,\n * duration: 200,\n * cycles: 2\n * });\n * \n * // Quick damage flash\n * player.flash({ \n * type: 'tint', \n * tint: 0xff0000, \n * duration: 150,\n * cycles: 1\n * });\n * ```\n */\n flash(options?: {\n type?: 'alpha' | 'tint' | 'both';\n duration?: number;\n cycles?: number;\n alpha?: number;\n tint?: number | string;\n }): void {\n const flashOptions = {\n type: options?.type || 'alpha',\n duration: options?.duration ?? 300,\n cycles: options?.cycles ?? 1,\n alpha: options?.alpha ?? 0.3,\n tint: options?.tint ?? 0xffffff,\n };\n \n // Convert color name to hex if needed\n let tintValue = flashOptions.tint;\n if (typeof tintValue === 'string') {\n // Common color name to hex mapping\n const colorMap: Record<string, number> = {\n 'white': 0xffffff,\n 'red': 0xff0000,\n 'green': 0x00ff00,\n 'blue': 0x0000ff,\n 'yellow': 0xffff00,\n 'cyan': 0x00ffff,\n 'magenta': 0xff00ff,\n 'black': 0x000000,\n };\n tintValue = colorMap[tintValue.toLowerCase()] ?? 0xffffff;\n }\n \n this.flashTrigger.start({\n ...flashOptions,\n tint: tintValue,\n });\n }\n\n /**\n * Reset animation state when animation changes externally\n *\n * This method should be called when the animation changes due to movement\n * or other external factors to ensure the animation system doesn't get stuck\n *\n * @example\n * ```ts\n * // Reset when player starts moving\n * player.resetAnimationState();\n * ```\n */\n resetAnimationState() {\n this.animationIsPlaying.set(false);\n this.animationCurrentIndex.set(0);\n if (this.animationSubscription) {\n this.animationSubscription.unsubscribe();\n this.animationSubscription = undefined;\n }\n }\n\n /**\n * Set a custom animation for a specific number of times\n *\n * Plays a custom animation for the specified number of repetitions.\n * The animation system prevents overlapping animations and automatically\n * returns to the previous animation when complete.\n *\n * @param animationName - Name of the animation to play\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n *\n * @example\n * ```ts\n * // Play attack animation 3 times\n * player.setAnimation('attack', 3);\n *\n * // Play continuous spell animation\n * player.setAnimation('spell');\n * ```\n */\n setAnimation(animationName: string, nbTimes?: number): void;\n /**\n * Set a custom animation with temporary graphic change\n *\n * Plays a custom animation for the specified number of repetitions and temporarily\n * changes the player's graphic (sprite sheet) during the animation. The graphic\n * is automatically reset when the animation finishes.\n *\n * @param animationName - Name of the animation to play\n * @param graphic - The graphic(s) to temporarily use during the animation\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n *\n * @example\n * ```ts\n * // Play attack animation with temporary graphic change\n * player.setAnimation('attack', 'hero_attack', 3);\n * ```\n */\n setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number): void;\n setAnimation(animationName: string, graphicOrNbTimes?: string | string[] | number, nbTimes?: number): void {\n if (this.animationIsPlaying()) return;\n this.animationIsPlaying.set(true);\n const previousAnimationName = this.animationName();\n const previousGraphics = this.graphics();\n this.animationCurrentIndex.set(0);\n\n let graphic: string | string[] | undefined;\n let finalNbTimes: number = Infinity;\n\n // Handle overloads\n if (typeof graphicOrNbTimes === 'number') {\n // setAnimation(animationName, nbTimes)\n finalNbTimes = graphicOrNbTimes;\n } else if (graphicOrNbTimes !== undefined) {\n // setAnimation(animationName, graphic, nbTimes)\n graphic = graphicOrNbTimes;\n finalNbTimes = nbTimes ?? Infinity;\n } else {\n // setAnimation(animationName) - nbTimes remains Infinity\n finalNbTimes = Infinity;\n }\n\n // Temporarily change graphic if provided\n if (graphic !== undefined) {\n if (Array.isArray(graphic)) {\n this.graphics.set(graphic);\n } else {\n this.graphics.set([graphic]);\n }\n }\n\n // Clean up any existing subscription\n if (this.animationSubscription) {\n this.animationSubscription.unsubscribe();\n }\n\n this.animationSubscription =\n this.animationCurrentIndex.observable.subscribe((index) => {\n if (index >= finalNbTimes) {\n this.animationCurrentIndex.set(0);\n this.animationName.set(previousAnimationName);\n // Reset graphic to previous value if it was changed\n if (graphic !== undefined) {\n this.graphics.set(previousGraphics);\n }\n this.animationIsPlaying.set(false);\n if (this.animationSubscription) {\n this.animationSubscription.unsubscribe();\n this.animationSubscription = undefined;\n }\n }\n });\n this.animationName.set(animationName);\n }\n\n /**\n * Display a registered component animation effect on this object.\n *\n * @param id - Identifier of the component animation to play.\n * @param params - Parameters forwarded to the animation effect.\n */\n showComponentAnimation(id: string, params: any) {\n const engine = inject(RpgClientEngine);\n engine.getComponentAnimation(id).displayEffect(params, this);\n }\n \n /**\n * Check whether this client object represents an event.\n *\n * @returns `true` if the object type is `event`, otherwise `false`.\n */\n isEvent(): boolean {\n return this._type === 'event';\n }\n\n /**\n * Check whether this client object represents a player.\n *\n * @returns `true` if the object type is `player`, otherwise `false`.\n */\n isPlayer(): boolean {\n return this._type === 'player';\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Object.js","names":[],"sources":["../../src/Game/Object.ts"],"sourcesContent":["import { Hooks, ModulesToken, RpgCommonPlayer } from \"@rpgjs/common\";\nimport { trigger, signal, type Trigger } from \"canvasengine\";\nimport { from, map, of, Subscription, switchMap } from \"rxjs\";\nimport { inject } from \"../core/inject\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\ntype Frame = { x: number; y: number; ts: number };\n\ntype AnimationRestoreOptions = {\n restoreAnimationName?: string;\n restoreGraphics?: any[];\n timeoutMs?: number;\n};\n\ntype FlashType = 'alpha' | 'tint' | 'both';\n\ntype FlashOptions = {\n type?: FlashType;\n duration?: number;\n cycles?: number;\n alpha?: number;\n tint?: number | string;\n};\n\ntype FlashTriggerOptions = Omit<FlashOptions, \"tint\"> & {\n tint: number;\n};\n\ntype ConfigurableTrigger<T> = Omit<Trigger<T>, \"start\"> & {\n start(config?: T): Promise<void>;\n};\n\nexport abstract class RpgClientObject extends RpgCommonPlayer {\n abstract _type: string;\n emitParticleTrigger = trigger();\n particleName = signal(\"\");\n animationCurrentIndex = signal(0);\n animationIsPlaying = signal(false);\n _param = signal({});\n frames: Frame[] = [];\n graphicsSignals = signal<any[]>([]);\n flashTrigger: ConfigurableTrigger<FlashTriggerOptions> = trigger<FlashTriggerOptions>();\n private animationRestoreState?: {\n animationName: string;\n graphics: any[];\n };\n\n constructor() {\n super();\n this.hooks.callHooks(\"client-sprite-onInit\", this).subscribe();\n\n this._frames.observable.subscribe(({ items }) => {\n if (!this.id) return;\n //if (this.id == this.engine.playerIdSignal()!) return;\n const nextFrames = items.flatMap((item): Frame[] =>\n Array.isArray(item) ? item : [item]\n );\n this.frames = [...this.frames, ...nextFrames];\n });\n\n this.graphics.observable\n .pipe(\n map(({ items }) => items),\n switchMap(graphics => {\n if (graphics.length === 0) return of([]);\n return from(Promise.all(graphics.map(graphic => this.engine.getSpriteSheet(graphic))));\n })\n )\n .subscribe((sheets) => { \n this.graphicsSignals.set(sheets);\n });\n\n this.engine.tick\n .pipe\n //throttleTime(10)\n ()\n .subscribe(() => {\n const frame = this.frames.shift();\n if (frame) {\n if (typeof frame.x !== \"number\" || typeof frame.y !== \"number\") return;\n this.engine.scene.setBodyPosition(\n this.id,\n frame.x,\n frame.y,\n \"top-left\"\n );\n }\n });\n }\n\n /**\n * Access the shared client hook registry.\n *\n * @returns The hook service used to register and trigger client-side hooks.\n */\n get hooks() {\n return inject<Hooks>(ModulesToken);\n }\n\n /**\n * Access the current client engine instance.\n *\n * @returns The active {@link RpgClientEngine} instance.\n */\n get engine() {\n return inject(RpgClientEngine);\n }\n\n private animationSubscription?: Subscription;\n private animationResetTimeout?: ReturnType<typeof setTimeout>;\n private animationWaitResolve?: () => void;\n\n private clearAnimationControls() {\n if (this.animationSubscription) {\n this.animationSubscription.unsubscribe();\n this.animationSubscription = undefined;\n }\n if (this.animationResetTimeout) {\n clearTimeout(this.animationResetTimeout);\n this.animationResetTimeout = undefined;\n }\n }\n\n private resolveAnimationWait() {\n const resolve = this.animationWaitResolve;\n this.animationWaitResolve = undefined;\n resolve?.();\n }\n\n private finishTemporaryAnimation() {\n const restoreState = this.animationRestoreState;\n this.clearAnimationControls();\n this.animationCurrentIndex.set(0);\n if (restoreState) {\n this.animationName.set(restoreState.animationName);\n this.graphics.set([...restoreState.graphics]);\n }\n this.animationRestoreState = undefined;\n this.animationIsPlaying.set(false);\n this.resolveAnimationWait();\n }\n\n /**\n * Trigger a flash animation on this sprite\n * \n * This method triggers a flash effect using CanvasEngine's flash directive.\n * The flash can be configured with various options including type (alpha, tint, or both),\n * duration, cycles, and color.\n * \n * ## Design\n * \n * The flash uses a trigger system that is connected to the flash directive in the\n * character component. This allows for flexible configuration and can be triggered\n * from both server events and client-side code.\n * \n * @param options - Flash configuration options\n * @param options.type - Type of flash effect: 'alpha' (opacity), 'tint' (color), or 'both' (default: 'alpha')\n * @param options.duration - Duration of the flash animation in milliseconds (default: 300)\n * @param options.cycles - Number of flash cycles (flash on/off) (default: 1)\n * @param options.alpha - Alpha value when flashing, from 0 to 1 (default: 0.3)\n * @param options.tint - Tint color when flashing as hex value or color name (default: 0xffffff - white)\n * \n * @example\n * ```ts\n * // Simple flash with default settings (alpha flash)\n * player.flash();\n * \n * // Flash with red tint\n * player.flash({ type: 'tint', tint: 0xff0000 });\n * \n * // Flash with both alpha and tint\n * player.flash({ \n * type: 'both', \n * alpha: 0.5, \n * tint: 0xff0000,\n * duration: 200,\n * cycles: 2\n * });\n * \n * // Quick damage flash\n * player.flash({ \n * type: 'tint', \n * tint: 0xff0000, \n * duration: 150,\n * cycles: 1\n * });\n * ```\n */\n flash(options?: FlashOptions): void {\n const flashOptions = {\n type: options?.type || 'alpha',\n duration: options?.duration ?? 300,\n cycles: options?.cycles ?? 1,\n alpha: options?.alpha ?? 0.3,\n tint: options?.tint ?? 0xffffff,\n };\n \n // Convert color name to hex if needed\n let tintValue = flashOptions.tint;\n if (typeof tintValue === 'string') {\n // Common color name to hex mapping\n const colorMap: Record<string, number> = {\n 'white': 0xffffff,\n 'red': 0xff0000,\n 'green': 0x00ff00,\n 'blue': 0x0000ff,\n 'yellow': 0xffff00,\n 'cyan': 0x00ffff,\n 'magenta': 0xff00ff,\n 'black': 0x000000,\n };\n tintValue = colorMap[tintValue.toLowerCase()] ?? 0xffffff;\n }\n \n this.flashTrigger.start({\n ...flashOptions,\n tint: tintValue,\n });\n }\n\n /**\n * Reset animation state when animation changes externally\n *\n * This method should be called when the animation changes due to movement\n * or other external factors to ensure the animation system doesn't get stuck\n *\n * @example\n * ```ts\n * // Reset when player starts moving\n * player.resetAnimationState();\n * ```\n */\n resetAnimationState() {\n if (this.animationRestoreState) {\n this.finishTemporaryAnimation();\n return;\n }\n this.animationIsPlaying.set(false);\n this.animationCurrentIndex.set(0);\n this.clearAnimationControls();\n this.resolveAnimationWait();\n }\n\n /**\n * Set a custom animation for a specific number of times\n *\n * Plays a custom animation for the specified number of repetitions.\n * The animation system prevents overlapping animations and automatically\n * returns to the previous animation when complete.\n *\n * @param animationName - Name of the animation to play\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n * @param options - Restore and timeout options\n * @returns A promise resolved when a finite animation finishes, is interrupted, or times out\n *\n * @example\n * ```ts\n * // Play attack animation 3 times\n * await player.setAnimation('attack', 3);\n *\n * // Play continuous spell animation\n * player.setAnimation('spell');\n * ```\n */\n setAnimation(animationName: string, nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;\n /**\n * Set a custom animation with temporary graphic change\n *\n * Plays a custom animation for the specified number of repetitions and temporarily\n * changes the player's graphic (sprite sheet) during the animation. The graphic\n * is automatically reset when the animation finishes.\n *\n * @param animationName - Name of the animation to play\n * @param graphic - The graphic(s) to temporarily use during the animation\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n * @param options - Restore and timeout options\n * @returns A promise resolved when a finite animation finishes, is interrupted, or times out\n *\n * @example\n * ```ts\n * // Play attack animation with temporary graphic change\n * await player.setAnimation('attack', 'hero_attack', 3);\n * ```\n */\n setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;\n setAnimation(\n animationName: string,\n graphicOrNbTimes?: string | string[] | number,\n nbTimesOrOptions?: number | AnimationRestoreOptions,\n options?: AnimationRestoreOptions\n ): Promise<void> {\n let graphic: string | string[] | undefined;\n let finalNbTimes: number = Infinity;\n let restoreOptions: AnimationRestoreOptions | undefined = options;\n\n // Handle overloads\n if (typeof graphicOrNbTimes === 'number') {\n // setAnimation(animationName, nbTimes)\n finalNbTimes = graphicOrNbTimes;\n restoreOptions = typeof nbTimesOrOptions === 'object' ? nbTimesOrOptions : options;\n } else if (graphicOrNbTimes !== undefined) {\n // setAnimation(animationName, graphic, nbTimes)\n graphic = graphicOrNbTimes;\n if (typeof nbTimesOrOptions === 'number') {\n finalNbTimes = nbTimesOrOptions;\n } else {\n finalNbTimes = Infinity;\n restoreOptions = nbTimesOrOptions ?? options;\n }\n } else {\n // setAnimation(animationName) - nbTimes remains Infinity\n finalNbTimes = Infinity;\n }\n\n if (this.animationIsPlaying()) {\n this.finishTemporaryAnimation();\n }\n\n const waitPromise =\n finalNbTimes === Infinity\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n this.animationWaitResolve = resolve;\n });\n\n this.animationIsPlaying.set(true);\n const previousAnimationName =\n restoreOptions?.restoreAnimationName ?? this.animationName();\n const previousGraphics = restoreOptions?.restoreGraphics\n ? [...restoreOptions.restoreGraphics]\n : [...this.graphics()];\n this.animationRestoreState = {\n animationName: previousAnimationName,\n graphics: previousGraphics,\n };\n this.animationCurrentIndex.set(0);\n\n // Temporarily change graphic if provided\n if (graphic !== undefined) {\n if (Array.isArray(graphic)) {\n this.graphics.set(graphic);\n } else {\n this.graphics.set([graphic]);\n }\n }\n\n this.clearAnimationControls();\n\n this.animationSubscription =\n this.animationCurrentIndex.observable.subscribe((index) => {\n if (index >= finalNbTimes) {\n this.finishTemporaryAnimation();\n }\n });\n\n if (finalNbTimes !== Infinity) {\n this.animationResetTimeout = setTimeout(() => {\n if (this.animationIsPlaying()) {\n this.finishTemporaryAnimation();\n }\n }, restoreOptions?.timeoutMs ?? Math.max(1000, finalNbTimes * 1000));\n }\n\n this.animationName.set(animationName);\n\n return waitPromise;\n }\n\n /**\n * Display a registered component animation effect on this object.\n *\n * @param id - Identifier of the component animation to play.\n * @param params - Parameters forwarded to the animation effect.\n * @returns A promise resolved when the animation component calls `onFinish`.\n */\n showComponentAnimation(id: string, params: any): Promise<void> {\n const engine = inject(RpgClientEngine);\n return engine.getComponentAnimation(id).displayEffect(params, this);\n }\n\n /**\n * Display a registered spritesheet animation effect on this object.\n *\n * @param graphic - Identifier of the spritesheet to use.\n * @param animationName - Name of the animation inside the spritesheet.\n * @returns A promise resolved when the animation component calls `onFinish`.\n */\n showAnimation(graphic: string, animationName: string = 'default'): Promise<void> {\n return this.showComponentAnimation('animation', {\n graphic,\n animationName,\n });\n }\n \n /**\n * Check whether this client object represents an event.\n *\n * @returns `true` if the object type is `event`, otherwise `false`.\n */\n isEvent(): boolean {\n return this._type === 'event';\n }\n\n /**\n * Check whether this client object represents a player.\n *\n * @returns `true` if the object type is `player`, otherwise `false`.\n */\n isPlayer(): boolean {\n return this._type === 'player';\n }\n}\n"],"mappings":"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"}
|
package/dist/Game/Player.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Player.js","names":[],"sources":["../../src/Game/Player.ts"],"sourcesContent":["import { RpgClientObject } from \"./Object\";\n\nexport class RpgClientPlayer extends RpgClientObject {\n _type = 'player'\n} "],"mappings":";;AAEA,IAAa,kBAAb,cAAqC,gBAAgB;;;eACzC"}
|
|
1
|
+
{"version":3,"file":"Player.js","names":[],"sources":["../../src/Game/Player.ts"],"sourcesContent":["import { RpgClientObject } from \"./Object\";\n\nexport class RpgClientPlayer extends RpgClientObject {\n _type = 'player'\n} "],"mappings":";;AAEA,IAAa,kBAAb,cAAqC,gBAAgB;;;eACzC;;AACZ"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Hooks } from '@rpgjs/common';
|
|
2
|
+
export interface ClientProjectileSpawn {
|
|
3
|
+
id: string;
|
|
4
|
+
type: string;
|
|
5
|
+
ownerId?: string;
|
|
6
|
+
origin: {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
};
|
|
10
|
+
direction: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
};
|
|
14
|
+
speed: number;
|
|
15
|
+
range: number;
|
|
16
|
+
ttl: number;
|
|
17
|
+
spawnTick: number;
|
|
18
|
+
delay?: number;
|
|
19
|
+
index?: number;
|
|
20
|
+
count?: number;
|
|
21
|
+
params?: Record<string, unknown>;
|
|
22
|
+
collisionMask?: number;
|
|
23
|
+
ignoreOwner?: boolean;
|
|
24
|
+
predictImpact?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ClientProjectileImpact {
|
|
27
|
+
id: string;
|
|
28
|
+
targetId?: string;
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
distance?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface ClientProjectileDestroy {
|
|
34
|
+
id: string;
|
|
35
|
+
reason?: string;
|
|
36
|
+
targetId?: string;
|
|
37
|
+
x?: number;
|
|
38
|
+
y?: number;
|
|
39
|
+
distance?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface RenderedProjectileProps extends ClientProjectileSpawn {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
angle: number;
|
|
45
|
+
distance: number;
|
|
46
|
+
elapsed: number;
|
|
47
|
+
progress: number;
|
|
48
|
+
impact?: ClientProjectileImpact;
|
|
49
|
+
impactElapsed?: number;
|
|
50
|
+
impactProgress?: number;
|
|
51
|
+
destroyed?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface RenderedProjectile {
|
|
54
|
+
id: string;
|
|
55
|
+
type: string;
|
|
56
|
+
component: any;
|
|
57
|
+
props: RenderedProjectileProps;
|
|
58
|
+
}
|
|
59
|
+
export type ProjectilePredictionResolver = (projectile: ClientProjectileSpawn) => ClientProjectileImpact | null | undefined;
|
|
60
|
+
export interface ProjectileSpawnClock {
|
|
61
|
+
now?: number;
|
|
62
|
+
currentServerTick?: number;
|
|
63
|
+
tickDurationMs?: number;
|
|
64
|
+
}
|
|
65
|
+
export declare class ProjectileManager {
|
|
66
|
+
private readonly hooks;
|
|
67
|
+
private readonly predictionResolver?;
|
|
68
|
+
private readonly components;
|
|
69
|
+
private readonly projectiles;
|
|
70
|
+
private readonly version;
|
|
71
|
+
private readonly impactDurationMs;
|
|
72
|
+
constructor(hooks: Hooks, predictionResolver?: ProjectilePredictionResolver | undefined);
|
|
73
|
+
current: import('canvasengine').ComputedSignal<RenderedProjectile[]>;
|
|
74
|
+
register(type: string, component: any): any;
|
|
75
|
+
get(type: string): any;
|
|
76
|
+
spawnBatch(projectiles: ClientProjectileSpawn[], clock?: ProjectileSpawnClock): void;
|
|
77
|
+
impactBatch(impacts: ClientProjectileImpact[]): void;
|
|
78
|
+
destroyBatch(projectiles: ClientProjectileDestroy[]): void;
|
|
79
|
+
clear(): void;
|
|
80
|
+
step(): void;
|
|
81
|
+
private toProps;
|
|
82
|
+
private isWaitingForDelay;
|
|
83
|
+
private setPredictedImpact;
|
|
84
|
+
private getActivePredictedImpact;
|
|
85
|
+
private setImpact;
|
|
86
|
+
private resolveVisualImpact;
|
|
87
|
+
private isSameTarget;
|
|
88
|
+
private touch;
|
|
89
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { computed, signal } from "canvasengine";
|
|
2
|
+
//#region src/Game/ProjectileManager.ts
|
|
3
|
+
var ProjectileManager = class {
|
|
4
|
+
constructor(hooks, predictionResolver) {
|
|
5
|
+
this.hooks = hooks;
|
|
6
|
+
this.predictionResolver = predictionResolver;
|
|
7
|
+
this.components = /* @__PURE__ */ new Map();
|
|
8
|
+
this.projectiles = /* @__PURE__ */ new Map();
|
|
9
|
+
this.version = signal(0);
|
|
10
|
+
this.impactDurationMs = 350;
|
|
11
|
+
this.current = computed(() => {
|
|
12
|
+
this.version();
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
const rendered = [];
|
|
15
|
+
for (const projectile of this.projectiles.values()) {
|
|
16
|
+
const props = this.toProps(projectile, now);
|
|
17
|
+
if (!props) continue;
|
|
18
|
+
rendered.push({
|
|
19
|
+
id: projectile.spawn.id,
|
|
20
|
+
type: projectile.spawn.type,
|
|
21
|
+
component: projectile.component,
|
|
22
|
+
props
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return rendered;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
register(type, component) {
|
|
29
|
+
this.components.set(type, component);
|
|
30
|
+
return component;
|
|
31
|
+
}
|
|
32
|
+
get(type) {
|
|
33
|
+
return this.components.get(type);
|
|
34
|
+
}
|
|
35
|
+
spawnBatch(projectiles, clock = {}) {
|
|
36
|
+
const now = clock.now ?? Date.now();
|
|
37
|
+
for (const projectile of projectiles) {
|
|
38
|
+
const component = this.components.get(projectile.type);
|
|
39
|
+
if (!component) continue;
|
|
40
|
+
const runtime = {
|
|
41
|
+
spawn: {
|
|
42
|
+
...projectile,
|
|
43
|
+
delay: projectile.delay ?? 0,
|
|
44
|
+
index: projectile.index ?? 0,
|
|
45
|
+
count: projectile.count ?? 1
|
|
46
|
+
},
|
|
47
|
+
component,
|
|
48
|
+
createdAt: now
|
|
49
|
+
};
|
|
50
|
+
this.setPredictedImpact(runtime);
|
|
51
|
+
this.projectiles.set(projectile.id, runtime);
|
|
52
|
+
this.hooks.callHooks("client-projectiles-onSpawn", runtime.spawn).subscribe();
|
|
53
|
+
}
|
|
54
|
+
this.touch();
|
|
55
|
+
}
|
|
56
|
+
impactBatch(impacts) {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
for (const impact of impacts) {
|
|
59
|
+
const projectile = this.projectiles.get(impact.id);
|
|
60
|
+
if (!projectile) continue;
|
|
61
|
+
this.setImpact(projectile, impact, now);
|
|
62
|
+
this.hooks.callHooks("client-projectiles-onImpact", this.toProps(projectile, now)).subscribe();
|
|
63
|
+
}
|
|
64
|
+
this.touch();
|
|
65
|
+
}
|
|
66
|
+
destroyBatch(projectiles) {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
for (const destroyed of projectiles) {
|
|
69
|
+
const projectile = this.projectiles.get(destroyed.id);
|
|
70
|
+
if (!projectile) continue;
|
|
71
|
+
if (destroyed.reason === "hit") {
|
|
72
|
+
const current = this.toProps(projectile, now);
|
|
73
|
+
this.setImpact(projectile, {
|
|
74
|
+
id: destroyed.id,
|
|
75
|
+
targetId: destroyed.targetId ?? projectile.impact?.targetId,
|
|
76
|
+
x: destroyed.x ?? projectile.impact?.x ?? current?.x ?? projectile.spawn.origin.x,
|
|
77
|
+
y: destroyed.y ?? projectile.impact?.y ?? current?.y ?? projectile.spawn.origin.y,
|
|
78
|
+
distance: destroyed.distance ?? projectile.impact?.distance ?? current?.distance
|
|
79
|
+
}, now);
|
|
80
|
+
}
|
|
81
|
+
projectile.destroyReason = destroyed.reason;
|
|
82
|
+
projectile.destroyAt = projectile.destroyAt ?? (projectile.impact && projectile.impactStartedAt !== void 0 ? projectile.impactStartedAt + this.impactDurationMs : now);
|
|
83
|
+
this.hooks.callHooks("client-projectiles-onDestroy", this.toProps(projectile, now)).subscribe();
|
|
84
|
+
}
|
|
85
|
+
this.touch();
|
|
86
|
+
}
|
|
87
|
+
clear() {
|
|
88
|
+
this.projectiles.clear();
|
|
89
|
+
this.touch();
|
|
90
|
+
}
|
|
91
|
+
step() {
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
let changed = false;
|
|
94
|
+
for (const [id, projectile] of this.projectiles) if (!this.toProps(projectile, now) && !this.isWaitingForDelay(projectile, now) || projectile.destroyAt !== void 0 && now >= projectile.destroyAt) {
|
|
95
|
+
this.projectiles.delete(id);
|
|
96
|
+
changed = true;
|
|
97
|
+
}
|
|
98
|
+
this.touch(changed || this.projectiles.size > 0);
|
|
99
|
+
}
|
|
100
|
+
toProps(projectile, now) {
|
|
101
|
+
const spawn = projectile.spawn;
|
|
102
|
+
const delayMs = (spawn.delay ?? 0) * 1e3;
|
|
103
|
+
const elapsedMs = now - projectile.createdAt - delayMs;
|
|
104
|
+
if (elapsedMs < 0) return null;
|
|
105
|
+
const elapsed = elapsedMs / 1e3;
|
|
106
|
+
const ttl = Math.max(.001, spawn.ttl);
|
|
107
|
+
const rawDistance = Math.min(spawn.speed * elapsed, spawn.range);
|
|
108
|
+
const predictedImpact = this.getActivePredictedImpact(projectile, now, rawDistance);
|
|
109
|
+
const visualImpact = projectile.visualImpact ?? projectile.impact;
|
|
110
|
+
const distance = visualImpact?.distance ?? predictedImpact?.distance ?? rawDistance;
|
|
111
|
+
const progress = Math.min(1, distance / spawn.range);
|
|
112
|
+
const x = visualImpact?.x ?? predictedImpact?.x ?? spawn.origin.x + spawn.direction.x * distance;
|
|
113
|
+
const y = visualImpact?.y ?? predictedImpact?.y ?? spawn.origin.y + spawn.direction.y * distance;
|
|
114
|
+
const impactElapsedMs = projectile.impactStartedAt !== void 0 ? Math.max(0, now - projectile.impactStartedAt) : void 0;
|
|
115
|
+
return {
|
|
116
|
+
...spawn,
|
|
117
|
+
x,
|
|
118
|
+
y,
|
|
119
|
+
angle: Math.atan2(spawn.direction.y, spawn.direction.x),
|
|
120
|
+
distance,
|
|
121
|
+
elapsed,
|
|
122
|
+
progress,
|
|
123
|
+
impact: projectile.impact,
|
|
124
|
+
impactElapsed: impactElapsedMs === void 0 ? void 0 : impactElapsedMs / 1e3,
|
|
125
|
+
impactProgress: impactElapsedMs === void 0 ? void 0 : Math.min(1, impactElapsedMs / this.impactDurationMs),
|
|
126
|
+
destroyed: projectile.destroyAt !== void 0,
|
|
127
|
+
ttl
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
isWaitingForDelay(projectile, now) {
|
|
131
|
+
const delayMs = (projectile.spawn.delay ?? 0) * 1e3;
|
|
132
|
+
return now - projectile.createdAt - delayMs < 0;
|
|
133
|
+
}
|
|
134
|
+
setPredictedImpact(projectile) {
|
|
135
|
+
if (projectile.spawn.predictImpact === false) return;
|
|
136
|
+
const impact = this.predictionResolver?.(projectile.spawn);
|
|
137
|
+
if (!impact || !Number.isFinite(impact.x) || !Number.isFinite(impact.y)) return;
|
|
138
|
+
const distance = typeof impact.distance === "number" && Number.isFinite(impact.distance) ? impact.distance : Math.hypot(impact.x - projectile.spawn.origin.x, impact.y - projectile.spawn.origin.y);
|
|
139
|
+
if (!Number.isFinite(distance) || distance < 0 || distance > projectile.spawn.range) return;
|
|
140
|
+
projectile.predictedImpact = {
|
|
141
|
+
...impact,
|
|
142
|
+
distance
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
getActivePredictedImpact(projectile, now, rawDistance) {
|
|
146
|
+
if (!projectile.predictedImpact || projectile.impact) return;
|
|
147
|
+
const distance = projectile.predictedImpact.distance;
|
|
148
|
+
if (distance === void 0 || rawDistance < distance) return;
|
|
149
|
+
return projectile.predictedImpact;
|
|
150
|
+
}
|
|
151
|
+
setImpact(projectile, impact, now) {
|
|
152
|
+
projectile.visualImpact = this.resolveVisualImpact(projectile, impact, now);
|
|
153
|
+
projectile.impact = impact;
|
|
154
|
+
projectile.predictedImpact = void 0;
|
|
155
|
+
projectile.impactStartedAt = projectile.impactStartedAt ?? now;
|
|
156
|
+
const impactDestroyAt = projectile.impactStartedAt + this.impactDurationMs;
|
|
157
|
+
projectile.destroyAt = Math.max(projectile.destroyAt ?? 0, impactDestroyAt);
|
|
158
|
+
}
|
|
159
|
+
resolveVisualImpact(projectile, impact, now) {
|
|
160
|
+
const predicted = projectile.predictedImpact;
|
|
161
|
+
if (!predicted || !this.isSameTarget(predicted, impact)) return impact;
|
|
162
|
+
const distance = predicted.distance;
|
|
163
|
+
if (distance === void 0) return impact;
|
|
164
|
+
const delayMs = (projectile.spawn.delay ?? 0) * 1e3;
|
|
165
|
+
const elapsedMs = now - projectile.createdAt - delayMs;
|
|
166
|
+
if (elapsedMs < 0) return impact;
|
|
167
|
+
return Math.min(projectile.spawn.speed * (elapsedMs / 1e3), projectile.spawn.range) >= distance ? predicted : impact;
|
|
168
|
+
}
|
|
169
|
+
isSameTarget(a, b) {
|
|
170
|
+
return a.targetId !== void 0 && a.targetId === b.targetId;
|
|
171
|
+
}
|
|
172
|
+
touch(force = true) {
|
|
173
|
+
if (force) this.version.update((value) => value + 1);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
//#endregion
|
|
177
|
+
export { ProjectileManager };
|
|
178
|
+
|
|
179
|
+
//# sourceMappingURL=ProjectileManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectileManager.js","names":[],"sources":["../../src/Game/ProjectileManager.ts"],"sourcesContent":["import { computed, signal } from \"canvasengine\";\nimport { Hooks } from \"@rpgjs/common\";\n\nexport interface ClientProjectileSpawn {\n id: string;\n type: string;\n ownerId?: string;\n origin: { x: number; y: number };\n direction: { x: number; y: number };\n speed: number;\n range: number;\n ttl: number;\n spawnTick: number;\n delay?: number;\n index?: number;\n count?: number;\n params?: Record<string, unknown>;\n collisionMask?: number;\n ignoreOwner?: boolean;\n predictImpact?: boolean;\n}\n\nexport interface ClientProjectileImpact {\n id: string;\n targetId?: string;\n x: number;\n y: number;\n distance?: number;\n}\n\nexport interface ClientProjectileDestroy {\n id: string;\n reason?: string;\n targetId?: string;\n x?: number;\n y?: number;\n distance?: number;\n}\n\nexport interface RenderedProjectileProps extends ClientProjectileSpawn {\n x: number;\n y: number;\n angle: number;\n distance: number;\n elapsed: number;\n progress: number;\n impact?: ClientProjectileImpact;\n impactElapsed?: number;\n impactProgress?: number;\n destroyed?: boolean;\n}\n\nexport interface RenderedProjectile {\n id: string;\n type: string;\n component: any;\n props: RenderedProjectileProps;\n}\n\nexport type ProjectilePredictionResolver = (\n projectile: ClientProjectileSpawn,\n) => ClientProjectileImpact | null | undefined;\n\nexport interface ProjectileSpawnClock {\n now?: number;\n currentServerTick?: number;\n tickDurationMs?: number;\n}\n\ninterface RuntimeProjectile {\n spawn: ClientProjectileSpawn;\n component: any;\n createdAt: number;\n impact?: ClientProjectileImpact;\n visualImpact?: ClientProjectileImpact;\n predictedImpact?: ClientProjectileImpact;\n impactStartedAt?: number;\n destroyAt?: number;\n destroyReason?: string;\n}\n\nexport class ProjectileManager {\n private readonly components = new Map<string, any>();\n private readonly projectiles = new Map<string, RuntimeProjectile>();\n private readonly version = signal(0);\n private readonly impactDurationMs = 350;\n\n constructor(\n private readonly hooks: Hooks,\n private readonly predictionResolver?: ProjectilePredictionResolver,\n ) {}\n\n current = computed<RenderedProjectile[]>(() => {\n this.version();\n const now = Date.now();\n const rendered: RenderedProjectile[] = [];\n for (const projectile of this.projectiles.values()) {\n const props = this.toProps(projectile, now);\n if (!props) {\n continue;\n }\n rendered.push({\n id: projectile.spawn.id,\n type: projectile.spawn.type,\n component: projectile.component,\n props,\n });\n }\n return rendered;\n });\n\n register(type: string, component: any): any {\n this.components.set(type, component);\n return component;\n }\n\n get(type: string): any {\n return this.components.get(type);\n }\n\n spawnBatch(projectiles: ClientProjectileSpawn[], clock: ProjectileSpawnClock = {}): void {\n const now = clock.now ?? Date.now();\n for (const projectile of projectiles) {\n const component = this.components.get(projectile.type);\n if (!component) {\n continue;\n }\n const runtime: RuntimeProjectile = {\n spawn: {\n ...projectile,\n delay: projectile.delay ?? 0,\n index: projectile.index ?? 0,\n count: projectile.count ?? 1,\n },\n component,\n createdAt: now,\n };\n this.setPredictedImpact(runtime);\n this.projectiles.set(projectile.id, runtime);\n this.hooks.callHooks(\"client-projectiles-onSpawn\", runtime.spawn).subscribe();\n }\n this.touch();\n }\n\n impactBatch(impacts: ClientProjectileImpact[]): void {\n const now = Date.now();\n for (const impact of impacts) {\n const projectile = this.projectiles.get(impact.id);\n if (!projectile) {\n continue;\n }\n this.setImpact(projectile, impact, now);\n this.hooks.callHooks(\"client-projectiles-onImpact\", this.toProps(projectile, now)).subscribe();\n }\n this.touch();\n }\n\n destroyBatch(projectiles: ClientProjectileDestroy[]): void {\n const now = Date.now();\n for (const destroyed of projectiles) {\n const projectile = this.projectiles.get(destroyed.id);\n if (!projectile) {\n continue;\n }\n if (destroyed.reason === \"hit\") {\n const current = this.toProps(projectile, now);\n this.setImpact(projectile, {\n id: destroyed.id,\n targetId: destroyed.targetId ?? projectile.impact?.targetId,\n x: destroyed.x ?? projectile.impact?.x ?? current?.x ?? projectile.spawn.origin.x,\n y: destroyed.y ?? projectile.impact?.y ?? current?.y ?? projectile.spawn.origin.y,\n distance: destroyed.distance ?? projectile.impact?.distance ?? current?.distance,\n }, now);\n }\n projectile.destroyReason = destroyed.reason;\n projectile.destroyAt = projectile.destroyAt ?? (\n projectile.impact && projectile.impactStartedAt !== undefined\n ? projectile.impactStartedAt + this.impactDurationMs\n : now\n );\n this.hooks.callHooks(\"client-projectiles-onDestroy\", this.toProps(projectile, now)).subscribe();\n }\n this.touch();\n }\n\n clear(): void {\n this.projectiles.clear();\n this.touch();\n }\n\n step(): void {\n const now = Date.now();\n let changed = false;\n for (const [id, projectile] of this.projectiles) {\n const props = this.toProps(projectile, now);\n if (\n (!props && !this.isWaitingForDelay(projectile, now)) ||\n (projectile.destroyAt !== undefined && now >= projectile.destroyAt)\n ) {\n this.projectiles.delete(id);\n changed = true;\n }\n }\n this.touch(changed || this.projectiles.size > 0);\n }\n\n private toProps(projectile: RuntimeProjectile, now: number): RenderedProjectileProps | null {\n const spawn = projectile.spawn;\n const delayMs = (spawn.delay ?? 0) * 1000;\n const elapsedMs = now - projectile.createdAt - delayMs;\n if (elapsedMs < 0) {\n return null;\n }\n const elapsed = elapsedMs / 1000;\n const ttl = Math.max(0.001, spawn.ttl);\n const rawDistance = Math.min(spawn.speed * elapsed, spawn.range);\n const predictedImpact = this.getActivePredictedImpact(projectile, now, rawDistance);\n const visualImpact = projectile.visualImpact ?? projectile.impact;\n const distance = visualImpact?.distance ?? predictedImpact?.distance ?? rawDistance;\n const progress = Math.min(1, distance / spawn.range);\n const x = visualImpact?.x ?? predictedImpact?.x ?? spawn.origin.x + spawn.direction.x * distance;\n const y = visualImpact?.y ?? predictedImpact?.y ?? spawn.origin.y + spawn.direction.y * distance;\n const impactElapsedMs = projectile.impactStartedAt !== undefined\n ? Math.max(0, now - projectile.impactStartedAt)\n : undefined;\n return {\n ...spawn,\n x,\n y,\n angle: Math.atan2(spawn.direction.y, spawn.direction.x),\n distance,\n elapsed,\n progress,\n impact: projectile.impact,\n impactElapsed: impactElapsedMs === undefined ? undefined : impactElapsedMs / 1000,\n impactProgress: impactElapsedMs === undefined\n ? undefined\n : Math.min(1, impactElapsedMs / this.impactDurationMs),\n destroyed: projectile.destroyAt !== undefined,\n ttl,\n };\n }\n\n private isWaitingForDelay(projectile: RuntimeProjectile, now: number): boolean {\n const delayMs = (projectile.spawn.delay ?? 0) * 1000;\n return now - projectile.createdAt - delayMs < 0;\n }\n\n private setPredictedImpact(projectile: RuntimeProjectile): void {\n if (projectile.spawn.predictImpact === false) {\n return;\n }\n const impact = this.predictionResolver?.(projectile.spawn);\n if (!impact || !Number.isFinite(impact.x) || !Number.isFinite(impact.y)) {\n return;\n }\n const distance = typeof impact.distance === \"number\" && Number.isFinite(impact.distance)\n ? impact.distance\n : Math.hypot(impact.x - projectile.spawn.origin.x, impact.y - projectile.spawn.origin.y);\n if (!Number.isFinite(distance) || distance < 0 || distance > projectile.spawn.range) {\n return;\n }\n projectile.predictedImpact = {\n ...impact,\n distance,\n };\n }\n\n private getActivePredictedImpact(\n projectile: RuntimeProjectile,\n now: number,\n rawDistance: number,\n ): ClientProjectileImpact | undefined {\n if (!projectile.predictedImpact || projectile.impact) {\n return undefined;\n }\n const distance = projectile.predictedImpact.distance;\n if (distance === undefined || rawDistance < distance) {\n return undefined;\n }\n return projectile.predictedImpact;\n }\n\n private setImpact(projectile: RuntimeProjectile, impact: ClientProjectileImpact, now: number): void {\n projectile.visualImpact = this.resolveVisualImpact(projectile, impact, now);\n projectile.impact = impact;\n projectile.predictedImpact = undefined;\n projectile.impactStartedAt = projectile.impactStartedAt ?? now;\n const impactDestroyAt = projectile.impactStartedAt + this.impactDurationMs;\n projectile.destroyAt = Math.max(projectile.destroyAt ?? 0, impactDestroyAt);\n }\n\n private resolveVisualImpact(\n projectile: RuntimeProjectile,\n impact: ClientProjectileImpact,\n now: number,\n ): ClientProjectileImpact {\n const predicted = projectile.predictedImpact;\n if (!predicted || !this.isSameTarget(predicted, impact)) {\n return impact;\n }\n const distance = predicted.distance;\n if (distance === undefined) {\n return impact;\n }\n const delayMs = (projectile.spawn.delay ?? 0) * 1000;\n const elapsedMs = now - projectile.createdAt - delayMs;\n if (elapsedMs < 0) {\n return impact;\n }\n const rawDistance = Math.min(projectile.spawn.speed * (elapsedMs / 1000), projectile.spawn.range);\n return rawDistance >= distance ? predicted : impact;\n }\n\n private isSameTarget(a: ClientProjectileImpact, b: ClientProjectileImpact): boolean {\n return a.targetId !== undefined && a.targetId === b.targetId;\n }\n\n private touch(force = true): void {\n if (force) {\n this.version.update((value) => value + 1);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/Gui/Gui.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Subscription } from 'rxjs';
|
|
|
4
4
|
interface GuiOptions {
|
|
5
5
|
name?: string;
|
|
6
6
|
id?: string;
|
|
7
|
-
component
|
|
7
|
+
component?: any;
|
|
8
8
|
display?: boolean;
|
|
9
9
|
data?: any;
|
|
10
10
|
/**
|
|
@@ -24,14 +24,18 @@ interface GuiOptions {
|
|
|
24
24
|
* @default false
|
|
25
25
|
*/
|
|
26
26
|
attachToSprite?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Vue v4 compatibility flag. Prefer attachToSprite in v5 projects.
|
|
29
|
+
*/
|
|
30
|
+
rpgAttachToSprite?: boolean;
|
|
27
31
|
}
|
|
28
|
-
interface GuiInstance {
|
|
32
|
+
export interface GuiInstance {
|
|
29
33
|
name: string;
|
|
30
34
|
component: any;
|
|
31
35
|
display: WritableSignal<boolean>;
|
|
32
36
|
data: WritableSignal<any>;
|
|
33
37
|
autoDisplay: boolean;
|
|
34
|
-
dependencies?:
|
|
38
|
+
dependencies?: Signal[];
|
|
35
39
|
subscription?: Subscription;
|
|
36
40
|
attachToSprite?: boolean;
|
|
37
41
|
}
|
|
@@ -113,7 +117,7 @@ export declare class RpgGui {
|
|
|
113
117
|
* });
|
|
114
118
|
* ```
|
|
115
119
|
*/
|
|
116
|
-
add(gui: GuiOptions): void;
|
|
120
|
+
add(gui: GuiOptions | any): void;
|
|
117
121
|
registerOptimisticReducer(guiId: string, reducer: OptimisticReducer): void;
|
|
118
122
|
/**
|
|
119
123
|
* Get all attached GUI components (attachToSprite: true)
|
|
@@ -130,6 +134,8 @@ export declare class RpgGui {
|
|
|
130
134
|
* ```
|
|
131
135
|
*/
|
|
132
136
|
getAttachedGuis(): GuiInstance[];
|
|
137
|
+
getVueGuis(): GuiInstance[];
|
|
138
|
+
getAttachedVueGuis(): GuiInstance[];
|
|
133
139
|
/**
|
|
134
140
|
* Check if a player should display attached GUIs
|
|
135
141
|
*
|
|
@@ -187,6 +193,13 @@ export declare class RpgGui {
|
|
|
187
193
|
*/
|
|
188
194
|
hide(id: string): void;
|
|
189
195
|
private isVueComponent;
|
|
196
|
+
private isVueComponentInstance;
|
|
197
|
+
private removeCanvasGui;
|
|
198
|
+
private removeVueGui;
|
|
199
|
+
private resolveComponent;
|
|
200
|
+
private resolveGuiId;
|
|
201
|
+
private resolveAttachToSprite;
|
|
202
|
+
private toGuiState;
|
|
190
203
|
private clearPendingActions;
|
|
191
204
|
private applyReducers;
|
|
192
205
|
private applyOptimisticAction;
|
package/dist/Gui/Gui.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { inject } from "../node_modules/.pnpm/@signe_di@
|
|
1
|
+
import { inject } from "../node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js";
|
|
2
2
|
import { WebSocketToken } from "../services/AbstractSocket.js";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
3
|
+
import __ce_component from "../components/gui/dialogbox/index.ce.js";
|
|
4
|
+
import __ce_component$1 from "../components/gui/shop/shop.ce.js";
|
|
5
|
+
import __ce_component$2 from "../components/gui/save-load.ce.js";
|
|
6
|
+
import __ce_component$3 from "../components/gui/menu/main-menu.ce.js";
|
|
7
|
+
import __ce_component$4 from "../components/gui/notification/notification.ce.js";
|
|
8
|
+
import __ce_component$5 from "../components/gui/title-screen.ce.js";
|
|
9
|
+
import __ce_component$6 from "../components/gui/gameover.ce.js";
|
|
10
10
|
import "../components/gui/index.js";
|
|
11
11
|
import { signal } from "canvasengine";
|
|
12
12
|
import { PrebuiltGui } from "@rpgjs/common";
|
|
@@ -86,32 +86,32 @@ var RpgGui = class {
|
|
|
86
86
|
this.webSocket = inject(context, WebSocketToken);
|
|
87
87
|
this.add({
|
|
88
88
|
name: "rpg-dialog",
|
|
89
|
-
component
|
|
89
|
+
component: __ce_component
|
|
90
90
|
});
|
|
91
91
|
this.add({
|
|
92
92
|
name: PrebuiltGui.MainMenu,
|
|
93
|
-
component:
|
|
93
|
+
component: __ce_component$3
|
|
94
94
|
});
|
|
95
95
|
this.add({
|
|
96
96
|
name: PrebuiltGui.Shop,
|
|
97
|
-
component:
|
|
97
|
+
component: __ce_component$1
|
|
98
98
|
});
|
|
99
99
|
this.add({
|
|
100
100
|
name: PrebuiltGui.Notification,
|
|
101
|
-
component:
|
|
101
|
+
component: __ce_component$4,
|
|
102
102
|
autoDisplay: true
|
|
103
103
|
});
|
|
104
104
|
this.add({
|
|
105
105
|
name: PrebuiltGui.Save,
|
|
106
|
-
component:
|
|
106
|
+
component: __ce_component$2
|
|
107
107
|
});
|
|
108
108
|
this.add({
|
|
109
109
|
name: PrebuiltGui.TitleScreen,
|
|
110
|
-
component:
|
|
110
|
+
component: __ce_component$5
|
|
111
111
|
});
|
|
112
112
|
this.add({
|
|
113
113
|
name: PrebuiltGui.Gameover,
|
|
114
|
-
component:
|
|
114
|
+
component: __ce_component$6
|
|
115
115
|
});
|
|
116
116
|
this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
|
|
117
117
|
}
|
|
@@ -146,6 +146,7 @@ var RpgGui = class {
|
|
|
146
146
|
*/
|
|
147
147
|
_setVueGuiInstance(vueGuiInstance) {
|
|
148
148
|
this.vueGuiInstance = vueGuiInstance;
|
|
149
|
+
this._initializeVueComponents();
|
|
149
150
|
}
|
|
150
151
|
/**
|
|
151
152
|
* Notify VueGui about GUI state changes
|
|
@@ -156,35 +157,16 @@ var RpgGui = class {
|
|
|
156
157
|
* @param data - Component data
|
|
157
158
|
*/
|
|
158
159
|
_notifyVueGui(guiId, display, data = {}) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
this.vueGuiInstance.vm.gui[guiId] = {
|
|
163
|
-
name: guiId,
|
|
164
|
-
display,
|
|
165
|
-
data,
|
|
166
|
-
attachToSprite: extraGui.attachToSprite || false
|
|
167
|
-
};
|
|
168
|
-
this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
160
|
+
const extraGui = this.extraGuis.find((gui) => gui.name === guiId);
|
|
161
|
+
if (!extraGui) return;
|
|
162
|
+
this.vueGuiInstance?.updateGuiState?.(this.toGuiState(extraGui, display, data));
|
|
171
163
|
}
|
|
172
164
|
/**
|
|
173
165
|
* Initialize Vue components in the VueGui instance
|
|
174
166
|
* This should be called after VueGui is mounted
|
|
175
167
|
*/
|
|
176
168
|
_initializeVueComponents() {
|
|
177
|
-
|
|
178
|
-
this.extraGuis.forEach((gui) => {
|
|
179
|
-
this.vueGuiInstance.vm.gui[gui.name] = {
|
|
180
|
-
name: gui.name,
|
|
181
|
-
display: gui.display(),
|
|
182
|
-
data: gui.data(),
|
|
183
|
-
attachToSprite: gui.attachToSprite || false
|
|
184
|
-
};
|
|
185
|
-
});
|
|
186
|
-
this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
|
|
187
|
-
}
|
|
169
|
+
this.vueGuiInstance?.initializeGuiStates?.(this.extraGuis.map((gui) => this.toGuiState(gui)));
|
|
188
170
|
}
|
|
189
171
|
guiInteraction(guiId, name, data) {
|
|
190
172
|
const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;
|
|
@@ -244,24 +226,34 @@ var RpgGui = class {
|
|
|
244
226
|
* ```
|
|
245
227
|
*/
|
|
246
228
|
add(gui) {
|
|
247
|
-
const
|
|
229
|
+
const component = this.resolveComponent(gui);
|
|
230
|
+
const guiId = this.resolveGuiId(gui, component);
|
|
248
231
|
if (!guiId) throw new Error("GUI must have a name or id");
|
|
232
|
+
const attachToSprite = this.resolveAttachToSprite(gui, component);
|
|
249
233
|
const guiInstance = {
|
|
250
234
|
name: guiId,
|
|
251
|
-
component
|
|
235
|
+
component,
|
|
252
236
|
display: signal(gui.display || false),
|
|
253
237
|
data: signal(gui.data || {}),
|
|
254
238
|
autoDisplay: gui.autoDisplay || false,
|
|
255
239
|
dependencies: gui.dependencies ? gui.dependencies() : [],
|
|
256
|
-
attachToSprite
|
|
240
|
+
attachToSprite
|
|
257
241
|
};
|
|
258
|
-
if (
|
|
259
|
-
|
|
260
|
-
this.extraGuis.
|
|
261
|
-
if (
|
|
242
|
+
if (this.isVueComponentInstance(guiInstance)) {
|
|
243
|
+
this.removeCanvasGui(guiId);
|
|
244
|
+
const existingIndex = this.extraGuis.findIndex((existing) => existing.name === guiId);
|
|
245
|
+
if (existingIndex >= 0) {
|
|
246
|
+
this.extraGuis[existingIndex].subscription?.unsubscribe();
|
|
247
|
+
this.extraGuis[existingIndex] = guiInstance;
|
|
248
|
+
} else this.extraGuis.push(guiInstance);
|
|
249
|
+
this._initializeVueComponents();
|
|
250
|
+
if (guiInstance.autoDisplay) this.display(guiId, gui.data);
|
|
251
|
+
else this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());
|
|
262
252
|
return;
|
|
263
253
|
}
|
|
254
|
+
this.removeVueGui(guiId);
|
|
264
255
|
this.gui()[guiId] = guiInstance;
|
|
256
|
+
this._initializeVueComponents();
|
|
265
257
|
if (guiInstance.autoDisplay && typeof gui.component === "function") this.display(guiId, gui.data);
|
|
266
258
|
}
|
|
267
259
|
registerOptimisticReducer(guiId, reducer) {
|
|
@@ -283,8 +275,13 @@ var RpgGui = class {
|
|
|
283
275
|
* ```
|
|
284
276
|
*/
|
|
285
277
|
getAttachedGuis() {
|
|
286
|
-
|
|
287
|
-
|
|
278
|
+
return Object.values(this.gui()).filter((gui) => gui.attachToSprite === true);
|
|
279
|
+
}
|
|
280
|
+
getVueGuis() {
|
|
281
|
+
return [...this.extraGuis];
|
|
282
|
+
}
|
|
283
|
+
getAttachedVueGuis() {
|
|
284
|
+
return this.extraGuis.filter((gui) => gui.attachToSprite === true);
|
|
288
285
|
}
|
|
289
286
|
/**
|
|
290
287
|
* Check if a player should display attached GUIs
|
|
@@ -358,7 +355,7 @@ var RpgGui = class {
|
|
|
358
355
|
guiInstance.subscription.unsubscribe();
|
|
359
356
|
guiInstance.subscription = void 0;
|
|
360
357
|
}
|
|
361
|
-
const deps = dependencies.length > 0 ? dependencies : guiInstance.dependencies
|
|
358
|
+
const deps = dependencies.length > 0 ? dependencies : guiInstance.dependencies ?? [];
|
|
362
359
|
if (deps.length > 0) {
|
|
363
360
|
guiInstance.subscription = combineLatest(deps.map((dependency) => dependency.observable)).subscribe((values) => {
|
|
364
361
|
if (values.every((value) => value !== void 0)) {
|
|
@@ -399,6 +396,39 @@ var RpgGui = class {
|
|
|
399
396
|
isVueComponent(id) {
|
|
400
397
|
return this.extraGuis.some((gui) => gui.name === id);
|
|
401
398
|
}
|
|
399
|
+
isVueComponentInstance(gui) {
|
|
400
|
+
return typeof gui.component !== "function";
|
|
401
|
+
}
|
|
402
|
+
removeCanvasGui(guiId) {
|
|
403
|
+
const current = this.gui();
|
|
404
|
+
if (!(guiId in current)) return;
|
|
405
|
+
const next = { ...current };
|
|
406
|
+
delete next[guiId];
|
|
407
|
+
this.gui.set(next);
|
|
408
|
+
}
|
|
409
|
+
removeVueGui(guiId) {
|
|
410
|
+
const removed = this.extraGuis.filter((existing) => existing.name === guiId);
|
|
411
|
+
removed.forEach((gui) => gui.subscription?.unsubscribe());
|
|
412
|
+
if (removed.length > 0) this.extraGuis = this.extraGuis.filter((existing) => existing.name !== guiId);
|
|
413
|
+
}
|
|
414
|
+
resolveComponent(gui) {
|
|
415
|
+
return gui?.component ?? gui;
|
|
416
|
+
}
|
|
417
|
+
resolveGuiId(gui, component) {
|
|
418
|
+
return gui?.name || gui?.id || component?.name || component?.__name;
|
|
419
|
+
}
|
|
420
|
+
resolveAttachToSprite(gui, component) {
|
|
421
|
+
return !!(gui?.attachToSprite || gui?.rpgAttachToSprite || component?.attachToSprite || component?.rpgAttachToSprite);
|
|
422
|
+
}
|
|
423
|
+
toGuiState(gui, display = gui.display(), data = gui.data()) {
|
|
424
|
+
return {
|
|
425
|
+
name: gui.name,
|
|
426
|
+
component: gui.component,
|
|
427
|
+
display,
|
|
428
|
+
data,
|
|
429
|
+
attachToSprite: gui.attachToSprite || false
|
|
430
|
+
};
|
|
431
|
+
}
|
|
402
432
|
clearPendingActions(guiId) {
|
|
403
433
|
this.pendingActions.delete(guiId);
|
|
404
434
|
}
|