@luna-editor/engine 0.5.12 → 0.5.13
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/dist/Player.js
CHANGED
|
@@ -16,6 +16,7 @@ import { BackgroundLayer } from "./components/BackgroundLayer";
|
|
|
16
16
|
import { ClickWaitIndicator } from "./components/ClickWaitIndicator";
|
|
17
17
|
import { ConversationBranchBox } from "./components/ConversationBranchBox";
|
|
18
18
|
import { DialogueBox } from "./components/DialogueBox";
|
|
19
|
+
import { EndScreen } from "./components/EndScreen";
|
|
19
20
|
import { FullscreenTextBox } from "./components/FullscreenTextBox";
|
|
20
21
|
import { GameScreen } from "./components/GameScreen";
|
|
21
22
|
import { OverlayUI } from "./components/OverlayUI";
|
|
@@ -40,7 +41,7 @@ import { BranchNavigator } from "./utils/branchNavigator";
|
|
|
40
41
|
import { VariableManager } from "./utils/variableManager";
|
|
41
42
|
const EMPTY_PLUGINS = [];
|
|
42
43
|
const EMPTY_SOUNDS = [];
|
|
43
|
-
export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGINS, sounds = EMPTY_SOUNDS, onEnd, onScenarioEnd, onScenarioStart, onScenarioCancelled, onSettingsChange, className, autoplay = false, preventDefaultScroll = true, screenSize: screenSizeProp, disableKeyboardNavigation = false, }) => {
|
|
44
|
+
export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGINS, sounds = EMPTY_SOUNDS, onEnd, onScenarioEnd, onScenarioStart, onScenarioCancelled, onSettingsChange, className, autoplay = false, preventDefaultScroll = true, screenSize: screenSizeProp, disableKeyboardNavigation = false, hideEndScreen = false, }) => {
|
|
44
45
|
var _a, _b, _c, _d;
|
|
45
46
|
// scenario.blocks が存在しない場合は空の配列を使用
|
|
46
47
|
const scenario = useMemo(() => {
|
|
@@ -456,7 +457,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGI
|
|
|
456
457
|
return blocks;
|
|
457
458
|
}, [scenario.blocks, displayableBlockIndices, state.currentBlockIndex]);
|
|
458
459
|
// サウンド再生フック
|
|
459
|
-
useSoundPlayer({
|
|
460
|
+
const { stopAll: stopAllSounds } = useSoundPlayer({
|
|
460
461
|
soundBlocks: soundBlocksToProcess,
|
|
461
462
|
isFirstRenderComplete,
|
|
462
463
|
muteAudio: mergedSettings.muteAudio,
|
|
@@ -624,6 +625,8 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGI
|
|
|
624
625
|
scenarioName: scenario.name || "scenario",
|
|
625
626
|
});
|
|
626
627
|
}
|
|
628
|
+
// リスタート時にすべての音声を停止
|
|
629
|
+
stopAllSounds();
|
|
627
630
|
setIsFirstRenderComplete(false);
|
|
628
631
|
setHasStarted(false); // これにより次の useEffect で onScenarioStart が呼ばれる
|
|
629
632
|
setCurrentBranchBlock(null);
|
|
@@ -643,6 +646,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGI
|
|
|
643
646
|
pluginManager,
|
|
644
647
|
scenario.id,
|
|
645
648
|
scenario.name,
|
|
649
|
+
stopAllSounds,
|
|
646
650
|
]);
|
|
647
651
|
// プラグインからシナリオを中断するためのコールバック
|
|
648
652
|
const cancelScenario = useCallback(() => {
|
|
@@ -760,6 +764,8 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGI
|
|
|
760
764
|
}
|
|
761
765
|
else {
|
|
762
766
|
setState((prev) => (Object.assign(Object.assign({}, prev), { isEnded: true, isPlaying: false })));
|
|
767
|
+
// シナリオ終了時にすべての音声を停止
|
|
768
|
+
stopAllSounds();
|
|
763
769
|
// プラグインのonScenarioEndフックを呼び出し
|
|
764
770
|
pluginManager.callHook("onScenarioEnd", {
|
|
765
771
|
scenarioId: scenario.id,
|
|
@@ -779,6 +785,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGI
|
|
|
779
785
|
pluginManager,
|
|
780
786
|
scenario.id,
|
|
781
787
|
scenario.name,
|
|
788
|
+
stopAllSounds,
|
|
782
789
|
]);
|
|
783
790
|
// プラグインからAUTO再生を制御するためのコールバック
|
|
784
791
|
const toggleAutoPlay = useCallback(() => {
|
|
@@ -967,7 +974,7 @@ export const Player = ({ scenario: scenarioProp, settings, plugins = EMPTY_PLUGI
|
|
|
967
974
|
return width / height;
|
|
968
975
|
};
|
|
969
976
|
// 条件付きレンダリングを JSX で処理(フックの後、early return なし)
|
|
970
|
-
return (_jsxs(_Fragment, { children: [!currentBlock && !state.isEnded && (_jsx("div", { className: clsx("flex items-center justify-center p-8", className), children: _jsx("p", { className: "text-gray-500", children: "\u30B7\u30CA\u30EA\u30AA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093" }) })), currentBlock && !state.isEnded && (_jsxs(_Fragment, { children: [(!imagesLoaded ||
|
|
977
|
+
return (_jsxs(_Fragment, { children: [!currentBlock && !state.isEnded && (_jsx("div", { className: clsx("flex items-center justify-center p-8", className), children: _jsx("p", { className: "text-gray-500", children: "\u30B7\u30CA\u30EA\u30AA\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093" }) })), state.isEnded && !hideEndScreen && (_jsx(EndScreen, { scenarioName: scenario.name, onRestart: restart, className: className })), currentBlock && !state.isEnded && (_jsxs(_Fragment, { children: [(!imagesLoaded ||
|
|
971
978
|
!fontsLoaded ||
|
|
972
979
|
!isFirstRenderComplete ||
|
|
973
980
|
!pluginsLoaded) && (_jsx("div", { className: clsx("luna-player fixed inset-0 bg-black overflow-hidden flex items-center justify-center z-50", className), style: {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
export var useImagePreloader = function (displayedCharacters, singleImageUrl) {
|
|
3
|
+
var _a = useState(true),
|
|
4
|
+
isLoading = _a[0],
|
|
5
|
+
setIsLoading = _a[1];
|
|
6
|
+
useEffect(
|
|
7
|
+
function () {
|
|
8
|
+
var imageUrls = [];
|
|
9
|
+
// 複数キャラクター表示の場合
|
|
10
|
+
if (displayedCharacters.length > 0) {
|
|
11
|
+
displayedCharacters.forEach(function (char) {
|
|
12
|
+
if (char.entityState.imageUrl) {
|
|
13
|
+
imageUrls.push(char.entityState.imageUrl);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
} else if (singleImageUrl) {
|
|
17
|
+
// 単一キャラクター表示の場合
|
|
18
|
+
imageUrls.push(singleImageUrl);
|
|
19
|
+
}
|
|
20
|
+
// 画像がない場合は即座に完了
|
|
21
|
+
if (imageUrls.length === 0) {
|
|
22
|
+
setIsLoading(false);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
setIsLoading(true);
|
|
26
|
+
// すべての画像を並列でプリロード
|
|
27
|
+
var loadPromises = imageUrls.map(function (url) {
|
|
28
|
+
return new Promise(function (resolve, reject) {
|
|
29
|
+
var img = new Image();
|
|
30
|
+
img.onload = function () {
|
|
31
|
+
return resolve();
|
|
32
|
+
};
|
|
33
|
+
img.onerror = function () {
|
|
34
|
+
return reject(new Error("Failed to load image: ".concat(url)));
|
|
35
|
+
};
|
|
36
|
+
img.src = url;
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
// すべての画像の読み込みを待つ
|
|
40
|
+
Promise.all(loadPromises)
|
|
41
|
+
.then(function () {
|
|
42
|
+
setIsLoading(false);
|
|
43
|
+
})
|
|
44
|
+
.catch(function (error) {
|
|
45
|
+
console.error("Image preload error:", error);
|
|
46
|
+
// エラーが発生しても続行
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
[displayedCharacters, singleImageUrl]
|
|
51
|
+
);
|
|
52
|
+
return isLoading;
|
|
53
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* プラグインAPIからReactインスタンスを設定
|
|
3
|
+
*/
|
|
4
|
+
export declare function setReactRuntime(react: any): void;
|
|
5
|
+
/**
|
|
6
|
+
* JSX Transform用のjsx関数
|
|
7
|
+
*/
|
|
8
|
+
export declare function jsx(type: any, props: any, key?: any): any;
|
|
9
|
+
/**
|
|
10
|
+
* JSX Transform用のjsxs関数(複数子要素用)
|
|
11
|
+
*/
|
|
12
|
+
export declare function jsxs(type: any, props: any, key?: any): any;
|
|
13
|
+
/**
|
|
14
|
+
* Fragment用
|
|
15
|
+
*/
|
|
16
|
+
export declare function Fragment(props: {
|
|
17
|
+
children?: any;
|
|
18
|
+
}): any;
|
|
19
|
+
/**
|
|
20
|
+
* Reactフックと関数のプロキシ
|
|
21
|
+
*/
|
|
22
|
+
export declare const useState: (...args: any[]) => any;
|
|
23
|
+
export declare const useEffect: (...args: any[]) => any;
|
|
24
|
+
export declare const useCallback: (...args: any[]) => any;
|
|
25
|
+
export declare const useMemo: (...args: any[]) => any;
|
|
26
|
+
export declare const useRef: (...args: any[]) => any;
|
|
27
|
+
export declare const useContext: (...args: any[]) => any;
|
|
28
|
+
export declare const useReducer: (...args: any[]) => any;
|
|
29
|
+
export declare const createElement: (...args: any[]) => any;
|
|
30
|
+
declare const _default: {
|
|
31
|
+
createElement: (...args: any[]) => any;
|
|
32
|
+
Fragment: typeof Fragment;
|
|
33
|
+
useState: (...args: any[]) => any;
|
|
34
|
+
useEffect: (...args: any[]) => any;
|
|
35
|
+
useCallback: (...args: any[]) => any;
|
|
36
|
+
useMemo: (...args: any[]) => any;
|
|
37
|
+
useRef: (...args: any[]) => any;
|
|
38
|
+
useContext: (...args: any[]) => any;
|
|
39
|
+
useReducer: (...args: any[]) => any;
|
|
40
|
+
};
|
|
41
|
+
export default _default;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable import/no-anonymous-default-export */
|
|
3
|
+
let runtimeReact = null;
|
|
4
|
+
/**
|
|
5
|
+
* プラグインAPIからReactインスタンスを設定
|
|
6
|
+
*/
|
|
7
|
+
export function setReactRuntime(react) {
|
|
8
|
+
runtimeReact = react;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* JSX Transform用のjsx関数
|
|
12
|
+
*/
|
|
13
|
+
export function jsx(type, props, key) {
|
|
14
|
+
if (!runtimeReact) {
|
|
15
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
16
|
+
}
|
|
17
|
+
return runtimeReact.createElement(type, key ? Object.assign(Object.assign({}, props), { key }) : props);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* JSX Transform用のjsxs関数(複数子要素用)
|
|
21
|
+
*/
|
|
22
|
+
export function jsxs(type, props, key) {
|
|
23
|
+
if (!runtimeReact) {
|
|
24
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
25
|
+
}
|
|
26
|
+
return runtimeReact.createElement(type, key ? Object.assign(Object.assign({}, props), { key }) : props);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fragment用
|
|
30
|
+
*/
|
|
31
|
+
export function Fragment(props) {
|
|
32
|
+
if (!runtimeReact) {
|
|
33
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
34
|
+
}
|
|
35
|
+
return runtimeReact.createElement(runtimeReact.Fragment, null, props.children);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Reactフックと関数のプロキシ
|
|
39
|
+
*/
|
|
40
|
+
export const useState = (...args) => {
|
|
41
|
+
if (!runtimeReact) {
|
|
42
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
43
|
+
}
|
|
44
|
+
return runtimeReact.useState(...args);
|
|
45
|
+
};
|
|
46
|
+
export const useEffect = (...args) => {
|
|
47
|
+
if (!runtimeReact) {
|
|
48
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
49
|
+
}
|
|
50
|
+
return runtimeReact.useEffect(...args);
|
|
51
|
+
};
|
|
52
|
+
export const useCallback = (...args) => {
|
|
53
|
+
if (!runtimeReact) {
|
|
54
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
55
|
+
}
|
|
56
|
+
return runtimeReact.useCallback(...args);
|
|
57
|
+
};
|
|
58
|
+
export const useMemo = (...args) => {
|
|
59
|
+
if (!runtimeReact) {
|
|
60
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
61
|
+
}
|
|
62
|
+
return runtimeReact.useMemo(...args);
|
|
63
|
+
};
|
|
64
|
+
export const useRef = (...args) => {
|
|
65
|
+
if (!runtimeReact) {
|
|
66
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
67
|
+
}
|
|
68
|
+
return runtimeReact.useRef(...args);
|
|
69
|
+
};
|
|
70
|
+
export const useContext = (...args) => {
|
|
71
|
+
if (!runtimeReact) {
|
|
72
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
73
|
+
}
|
|
74
|
+
return runtimeReact.useContext(...args);
|
|
75
|
+
};
|
|
76
|
+
export const useReducer = (...args) => {
|
|
77
|
+
if (!runtimeReact) {
|
|
78
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
79
|
+
}
|
|
80
|
+
return runtimeReact.useReducer(...args);
|
|
81
|
+
};
|
|
82
|
+
export const createElement = (...args) => {
|
|
83
|
+
if (!runtimeReact) {
|
|
84
|
+
throw new Error("React runtime not initialized. Make sure plugin is loaded properly.");
|
|
85
|
+
}
|
|
86
|
+
return runtimeReact.createElement(...args);
|
|
87
|
+
};
|
|
88
|
+
// デフォルトエクスポート(互換性用)
|
|
89
|
+
export default {
|
|
90
|
+
createElement,
|
|
91
|
+
Fragment,
|
|
92
|
+
useState,
|
|
93
|
+
useEffect,
|
|
94
|
+
useCallback,
|
|
95
|
+
useMemo,
|
|
96
|
+
useRef,
|
|
97
|
+
useContext,
|
|
98
|
+
useReducer,
|
|
99
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -374,6 +374,8 @@ export interface PlayerProps {
|
|
|
374
374
|
};
|
|
375
375
|
/** キーボードナビゲーションを無効化するかどうか(デフォルト: false) */
|
|
376
376
|
disableKeyboardNavigation?: boolean;
|
|
377
|
+
/** 終了画面(リスタートボタン等)を非表示にするかどうか(デフォルト: false) */
|
|
378
|
+
hideEndScreen?: boolean;
|
|
377
379
|
}
|
|
378
380
|
export type VariableValue = string | number | boolean;
|
|
379
381
|
export interface PlayerState {
|