@luna-editor/engine 0.2.0 → 0.3.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/dist/Player.d.ts +1 -1
- package/dist/Player.js +676 -95
- package/dist/api/conversationBranch.d.ts +4 -0
- package/dist/api/conversationBranch.js +83 -0
- package/dist/components/BackgroundLayer.d.ts +19 -0
- package/dist/components/BackgroundLayer.js +220 -0
- package/dist/components/ClickWaitIndicator.d.ts +10 -0
- package/dist/components/ClickWaitIndicator.js +31 -0
- package/dist/components/ConversationBranchBox.d.ts +2 -0
- package/dist/components/ConversationBranchBox.js +29 -0
- package/dist/components/DialogueBox.js +16 -1
- package/dist/components/FontSettingsPanel.d.ts +10 -0
- package/dist/components/FontSettingsPanel.js +30 -0
- package/dist/components/FullscreenTextBox.d.ts +6 -0
- package/dist/components/FullscreenTextBox.js +70 -0
- package/dist/components/GameScreen.d.ts +9 -2
- package/dist/components/GameScreen.js +396 -80
- package/dist/components/OverlayUI.d.ts +2 -3
- package/dist/components/OverlayUI.js +3 -14
- package/dist/components/PluginComponentProvider.d.ts +3 -3
- package/dist/components/PluginComponentProvider.js +22 -4
- package/dist/components/TimeWaitIndicator.d.ts +15 -0
- package/dist/components/TimeWaitIndicator.js +17 -0
- package/dist/contexts/AudioContext.d.ts +1 -0
- package/dist/contexts/AudioContext.js +1 -0
- package/dist/contexts/DataContext.d.ts +3 -1
- package/dist/contexts/DataContext.js +104 -17
- package/dist/contexts/PlaybackTextContext.d.ts +32 -0
- package/dist/contexts/PlaybackTextContext.js +29 -0
- package/dist/data-api-types.d.ts +251 -0
- package/dist/data-api-types.js +6 -0
- package/dist/emotion-effect-types.d.ts +86 -0
- package/dist/emotion-effect-types.js +6 -0
- package/dist/hooks/useBacklog.js +3 -1
- package/dist/hooks/useConversationBranch.d.ts +16 -0
- package/dist/hooks/useConversationBranch.js +125 -0
- package/dist/hooks/useFontLoader.d.ts +30 -0
- package/dist/hooks/useFontLoader.js +192 -0
- package/dist/hooks/useFullscreenText.d.ts +17 -0
- package/dist/hooks/useFullscreenText.js +120 -0
- package/dist/hooks/useImagePreloader.d.ts +5 -0
- package/dist/hooks/useImagePreloader.js +53 -0
- package/dist/hooks/usePlayerLogic.d.ts +10 -3
- package/dist/hooks/usePlayerLogic.js +115 -18
- package/dist/hooks/usePluginAPI.js +1 -1
- package/dist/hooks/usePluginEvents.d.ts +4 -1
- package/dist/hooks/usePluginEvents.js +16 -11
- package/dist/hooks/usePreloadImages.js +27 -7
- package/dist/hooks/useSoundPlayer.d.ts +15 -0
- package/dist/hooks/useSoundPlayer.js +209 -0
- package/dist/hooks/useTypewriter.d.ts +6 -2
- package/dist/hooks/useTypewriter.js +42 -6
- package/dist/hooks/useVoice.js +4 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -1
- package/dist/plugin/PluginManager.d.ts +86 -5
- package/dist/plugin/PluginManager.js +427 -94
- package/dist/sdk.d.ts +133 -162
- package/dist/sdk.js +39 -4
- package/dist/types.d.ts +300 -4
- package/dist/utils/branchBlockConverter.d.ts +2 -0
- package/dist/utils/branchBlockConverter.js +21 -0
- package/dist/utils/branchNavigator.d.ts +14 -0
- package/dist/utils/branchNavigator.js +55 -0
- package/dist/utils/facePositionCalculator.js +0 -1
- package/dist/utils/variableManager.d.ts +18 -0
- package/dist/utils/variableManager.js +159 -0
- package/package.json +6 -6
- package/dist/components/ConversationLogUI.d.ts +0 -2
- package/dist/components/ConversationLogUI.js +0 -115
- package/dist/hooks/useConversationLog.d.ts +0 -14
- package/dist/hooks/useConversationLog.js +0 -82
- package/dist/hooks/useUIVisibility.d.ts +0 -9
- package/dist/hooks/useUIVisibility.js +0 -19
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* 時間待ちコンポーネント
|
|
5
|
+
* time_waitブロックで使用され、指定秒数経過後に自動で次に進む
|
|
6
|
+
* UIは表示せず、タイマー機能のみを提供
|
|
7
|
+
*/
|
|
8
|
+
export const TimeWaitIndicator = ({ duration, onComplete, }) => {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const timeoutId = setTimeout(() => {
|
|
11
|
+
onComplete();
|
|
12
|
+
}, duration * 1000);
|
|
13
|
+
return () => clearTimeout(timeoutId);
|
|
14
|
+
}, [duration, onComplete]);
|
|
15
|
+
// UIは表示しない
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
@@ -6,6 +6,7 @@ const AudioContext = createContext({
|
|
|
6
6
|
voiceVolume: 1.0,
|
|
7
7
|
effectVolume: 1.0,
|
|
8
8
|
textSoundVolume: 1.0,
|
|
9
|
+
muteAudio: false,
|
|
9
10
|
});
|
|
10
11
|
export const AudioProvider = ({ children, settings, }) => {
|
|
11
12
|
return (_jsx(AudioContext.Provider, { value: settings, children: children }));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type { DataAPI, DataContext, PlayerSettingsData } from "../sdk";
|
|
2
|
+
import type { DataAPI, DataContext, EmotionEffectState, PlayerSettingsData } from "../sdk";
|
|
3
3
|
type SettingsUpdater = (settings: Partial<PlayerSettingsData>) => void;
|
|
4
|
+
type EmotionEffectUpdater = (state: EmotionEffectState | null) => void;
|
|
4
5
|
/**
|
|
5
6
|
* データプロバイダー
|
|
6
7
|
* プラグインがシナリオ情報にリアクティブにアクセスするためのProvider
|
|
@@ -17,6 +18,7 @@ type SettingsUpdater = (settings: Partial<PlayerSettingsData>) => void;
|
|
|
17
18
|
export declare const DataProvider: React.FC<{
|
|
18
19
|
data: DataContext;
|
|
19
20
|
onSettingsUpdate?: SettingsUpdater;
|
|
21
|
+
onEmotionEffectUpdate?: EmotionEffectUpdater;
|
|
20
22
|
children: ReactNode;
|
|
21
23
|
}>;
|
|
22
24
|
/**
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, } from "react";
|
|
3
3
|
const DataContextInstance = createContext(null);
|
|
4
|
+
// データへの参照を共有するContext(Context value自体は変わらない)
|
|
5
|
+
const DataRefContext = createContext(null);
|
|
4
6
|
const SubscribersContext = createContext(null);
|
|
5
7
|
const SettingsUpdaterContext = createContext(null);
|
|
8
|
+
const EmotionEffectUpdaterContext = createContext(null);
|
|
6
9
|
/**
|
|
7
10
|
* データプロバイダー
|
|
8
11
|
* プラグインがシナリオ情報にリアクティブにアクセスするためのProvider
|
|
@@ -16,10 +19,15 @@ const SettingsUpdaterContext = createContext(null);
|
|
|
16
19
|
* </DataProvider>
|
|
17
20
|
* ```
|
|
18
21
|
*/
|
|
19
|
-
export const DataProvider = ({ data, onSettingsUpdate, children }) => {
|
|
20
|
-
const subscribers = useMemo(() => new Map(),
|
|
21
|
-
[]);
|
|
22
|
+
export const DataProvider = ({ data, onSettingsUpdate, onEmotionEffectUpdate, children }) => {
|
|
23
|
+
const subscribers = useMemo(() => new Map(), []);
|
|
22
24
|
const previousDataRef = useRef(data);
|
|
25
|
+
// 安定したデータ参照ホルダー(Context valueとして提供)
|
|
26
|
+
// オブジェクト自体の参照は変わらず、currentプロパティを更新
|
|
27
|
+
// これにより、useContextを使うコンポーネントは再レンダリングされない
|
|
28
|
+
const dataRefHolder = useMemo(() => ({ current: data }), [data]);
|
|
29
|
+
// dataが変わるたびにcurrentを更新
|
|
30
|
+
dataRefHolder.current = data;
|
|
23
31
|
// データ変更時に購読者に通知
|
|
24
32
|
useEffect(() => {
|
|
25
33
|
const prevData = previousDataRef.current;
|
|
@@ -32,16 +40,20 @@ export const DataProvider = ({ data, onSettingsUpdate, children }) => {
|
|
|
32
40
|
if (prevValue !== currentValue) {
|
|
33
41
|
// カテゴリ全体の購読者に通知
|
|
34
42
|
const categorySubscribers = subscribers.get(key);
|
|
35
|
-
categorySubscribers === null || categorySubscribers === void 0 ? void 0 : categorySubscribers.forEach((callback) =>
|
|
43
|
+
categorySubscribers === null || categorySubscribers === void 0 ? void 0 : categorySubscribers.forEach((callback) => {
|
|
44
|
+
callback(currentValue);
|
|
45
|
+
});
|
|
36
46
|
// 個別プロパティの変更をチェック
|
|
37
47
|
if (typeof currentValue === "object" && currentValue !== null) {
|
|
38
48
|
for (const prop of Object.keys(currentValue)) {
|
|
39
|
-
const prevPropValue = prevValue === null || prevValue === void 0 ? void 0 : prevValue[prop];
|
|
40
|
-
const currentPropValue = currentValue[prop];
|
|
49
|
+
const prevPropValue = prevValue === null || prevValue === void 0 ? void 0 : prevValue[prop];
|
|
50
|
+
const currentPropValue = currentValue[prop];
|
|
41
51
|
if (prevPropValue !== currentPropValue) {
|
|
42
52
|
const propKey = `${key}.${prop}`;
|
|
43
53
|
const propSubscribers = subscribers.get(propKey);
|
|
44
|
-
propSubscribers === null || propSubscribers === void 0 ? void 0 : propSubscribers.forEach((callback) =>
|
|
54
|
+
propSubscribers === null || propSubscribers === void 0 ? void 0 : propSubscribers.forEach((callback) => {
|
|
55
|
+
callback(currentPropValue);
|
|
56
|
+
});
|
|
45
57
|
}
|
|
46
58
|
}
|
|
47
59
|
}
|
|
@@ -49,26 +61,38 @@ export const DataProvider = ({ data, onSettingsUpdate, children }) => {
|
|
|
49
61
|
}
|
|
50
62
|
previousDataRef.current = data;
|
|
51
63
|
}, [data, subscribers]);
|
|
52
|
-
return (_jsx(SubscribersContext.Provider, { value: subscribers, children: _jsx(SettingsUpdaterContext.Provider, { value: onSettingsUpdate !== null && onSettingsUpdate !== void 0 ? onSettingsUpdate : null, children: _jsx(DataContextInstance.Provider, { value: data, children: children }) }) }));
|
|
64
|
+
return (_jsx(SubscribersContext.Provider, { value: subscribers, children: _jsx(SettingsUpdaterContext.Provider, { value: onSettingsUpdate !== null && onSettingsUpdate !== void 0 ? onSettingsUpdate : null, children: _jsx(EmotionEffectUpdaterContext.Provider, { value: onEmotionEffectUpdate !== null && onEmotionEffectUpdate !== void 0 ? onEmotionEffectUpdate : null, children: _jsx(DataRefContext.Provider, { value: dataRefHolder, children: _jsx(DataContextInstance.Provider, { value: data, children: children }) }) }) }) }));
|
|
53
65
|
};
|
|
54
66
|
/**
|
|
55
67
|
* DataAPI実装を提供するフック
|
|
56
68
|
* プラグインから呼び出される
|
|
57
69
|
*/
|
|
58
70
|
export function useDataAPI() {
|
|
59
|
-
|
|
71
|
+
var _a;
|
|
72
|
+
// DataRefContextから安定したデータ参照を取得
|
|
73
|
+
// これはContext valueが変わらないので、再レンダリングを引き起こさない
|
|
74
|
+
const dataRefHolder = useContext(DataRefContext);
|
|
60
75
|
const subscribers = useContext(SubscribersContext);
|
|
61
76
|
const settingsUpdater = useContext(SettingsUpdaterContext);
|
|
62
|
-
|
|
77
|
+
const emotionEffectUpdater = useContext(EmotionEffectUpdaterContext);
|
|
78
|
+
// 注意: usePlaybackTextOptional()をここで呼ぶと、PlaybackTextContextの更新で
|
|
79
|
+
// useDataAPIを使う全コンポーネントが再レンダリングされてしまう
|
|
80
|
+
// 代わりに、displayTextが必要なコンポーネントはusePlaybackText()を直接使う
|
|
81
|
+
if (!dataRefHolder || !subscribers) {
|
|
63
82
|
throw new Error("useDataAPI must be used within DataProvider");
|
|
64
83
|
}
|
|
65
84
|
const get = useCallback((key, property) => {
|
|
66
|
-
|
|
85
|
+
const currentData = dataRefHolder.current;
|
|
67
86
|
if (property !== undefined) {
|
|
68
|
-
|
|
87
|
+
const value = currentData[key];
|
|
88
|
+
if (value && typeof value === "object" && property in value) {
|
|
89
|
+
return value[property];
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
69
92
|
}
|
|
70
|
-
return
|
|
71
|
-
}, [
|
|
93
|
+
return currentData[key];
|
|
94
|
+
}, [dataRefHolder.current] // 依存配列を空にして、get関数を安定化
|
|
95
|
+
);
|
|
72
96
|
const subscribe = useCallback((key, callback) => {
|
|
73
97
|
var _a;
|
|
74
98
|
const subscriberKey = key;
|
|
@@ -79,7 +103,8 @@ export function useDataAPI() {
|
|
|
79
103
|
// unsubscribe関数を返す
|
|
80
104
|
return () => {
|
|
81
105
|
var _a;
|
|
82
|
-
(_a = subscribers
|
|
106
|
+
(_a = subscribers
|
|
107
|
+
.get(subscriberKey)) === null || _a === void 0 ? void 0 : _a.delete(callback);
|
|
83
108
|
};
|
|
84
109
|
}, [subscribers]);
|
|
85
110
|
const watch = useCallback((key, property, callback) => {
|
|
@@ -92,7 +117,8 @@ export function useDataAPI() {
|
|
|
92
117
|
// unsubscribe関数を返す
|
|
93
118
|
return () => {
|
|
94
119
|
var _a;
|
|
95
|
-
(_a = subscribers
|
|
120
|
+
(_a = subscribers
|
|
121
|
+
.get(subscriberKey)) === null || _a === void 0 ? void 0 : _a.delete(callback);
|
|
96
122
|
};
|
|
97
123
|
}, [subscribers]);
|
|
98
124
|
const updateSettings = useCallback((settings) => {
|
|
@@ -103,10 +129,71 @@ export function useDataAPI() {
|
|
|
103
129
|
console.warn("updateSettings called but no settings updater provided");
|
|
104
130
|
}
|
|
105
131
|
}, [settingsUpdater]);
|
|
132
|
+
const setBgmVolume = useCallback((volume) => {
|
|
133
|
+
const clampedVolume = Math.max(0, Math.min(1, volume));
|
|
134
|
+
updateSettings({ bgmVolume: clampedVolume });
|
|
135
|
+
}, [updateSettings]);
|
|
136
|
+
const setSeVolume = useCallback((volume) => {
|
|
137
|
+
const clampedVolume = Math.max(0, Math.min(1, volume));
|
|
138
|
+
updateSettings({ seVolume: clampedVolume });
|
|
139
|
+
}, [updateSettings]);
|
|
140
|
+
const setVoiceVolume = useCallback((volume) => {
|
|
141
|
+
const clampedVolume = Math.max(0, Math.min(1, volume));
|
|
142
|
+
updateSettings({ voiceVolume: clampedVolume });
|
|
143
|
+
}, [updateSettings]);
|
|
144
|
+
const setVolumes = useCallback((volumes) => {
|
|
145
|
+
const settings = {};
|
|
146
|
+
if (volumes.bgm !== undefined) {
|
|
147
|
+
settings.bgmVolume = Math.max(0, Math.min(1, volumes.bgm));
|
|
148
|
+
}
|
|
149
|
+
if (volumes.se !== undefined) {
|
|
150
|
+
settings.seVolume = Math.max(0, Math.min(1, volumes.se));
|
|
151
|
+
}
|
|
152
|
+
if (volumes.voice !== undefined) {
|
|
153
|
+
settings.voiceVolume = Math.max(0, Math.min(1, volumes.voice));
|
|
154
|
+
}
|
|
155
|
+
if (Object.keys(settings).length > 0) {
|
|
156
|
+
updateSettings(settings);
|
|
157
|
+
}
|
|
158
|
+
}, [updateSettings]);
|
|
159
|
+
const getBlockOption = useCallback((key) => {
|
|
160
|
+
var _a;
|
|
161
|
+
const currentBlock = (_a = dataRefHolder.current.playback) === null || _a === void 0 ? void 0 : _a.currentBlock;
|
|
162
|
+
if (!(currentBlock === null || currentBlock === void 0 ? void 0 : currentBlock.options)) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
return currentBlock.options[key];
|
|
166
|
+
}, [(_a = dataRefHolder.current.playback) === null || _a === void 0 ? void 0 : _a.currentBlock] // 依存配列を空にして安定化
|
|
167
|
+
);
|
|
168
|
+
const updateEmotionEffect = useCallback((state) => {
|
|
169
|
+
if (emotionEffectUpdater) {
|
|
170
|
+
emotionEffectUpdater(state);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.warn("updateEmotionEffect called but no emotion effect updater provided");
|
|
174
|
+
}
|
|
175
|
+
}, [emotionEffectUpdater]);
|
|
106
176
|
return useMemo(() => ({
|
|
107
177
|
get,
|
|
108
178
|
subscribe,
|
|
109
179
|
watch,
|
|
110
180
|
updateSettings,
|
|
111
|
-
|
|
181
|
+
setBgmVolume,
|
|
182
|
+
setSeVolume,
|
|
183
|
+
setVoiceVolume,
|
|
184
|
+
setVolumes,
|
|
185
|
+
getBlockOption,
|
|
186
|
+
updateEmotionEffect,
|
|
187
|
+
}), [
|
|
188
|
+
get,
|
|
189
|
+
subscribe,
|
|
190
|
+
watch,
|
|
191
|
+
updateSettings,
|
|
192
|
+
setBgmVolume,
|
|
193
|
+
setSeVolume,
|
|
194
|
+
setVoiceVolume,
|
|
195
|
+
setVolumes,
|
|
196
|
+
getBlockOption,
|
|
197
|
+
updateEmotionEffect,
|
|
198
|
+
]);
|
|
112
199
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* 文字送り用の軽量コンテキスト
|
|
4
|
+
* displayText と isTyping は頻繁に更新されるため、
|
|
5
|
+
* DataContext から分離して不要な再レンダリングを防ぐ
|
|
6
|
+
*/
|
|
7
|
+
interface PlaybackTextContextValue {
|
|
8
|
+
/** 現在表示中のテキスト(文字送り中は部分テキスト) */
|
|
9
|
+
displayText: string | React.ReactNode;
|
|
10
|
+
/** 文字送り中かどうか */
|
|
11
|
+
isTyping: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* PlaybackTextProvider
|
|
15
|
+
* 文字送り状態を提供するProvider
|
|
16
|
+
* DataProviderとは別に、displayTextとisTypingのみを管理
|
|
17
|
+
*/
|
|
18
|
+
export declare const PlaybackTextProvider: React.FC<{
|
|
19
|
+
displayText: string | React.ReactNode;
|
|
20
|
+
isTyping: boolean;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* 文字送り状態を取得するフック
|
|
25
|
+
*/
|
|
26
|
+
export declare function usePlaybackText(): PlaybackTextContextValue;
|
|
27
|
+
/**
|
|
28
|
+
* 文字送り状態を取得するフック(オプショナル版)
|
|
29
|
+
* PlaybackTextProviderの外でも使用可能(その場合はnullを返す)
|
|
30
|
+
*/
|
|
31
|
+
export declare function usePlaybackTextOptional(): PlaybackTextContextValue | null;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
const PlaybackTextContextInstance = createContext(null);
|
|
4
|
+
/**
|
|
5
|
+
* PlaybackTextProvider
|
|
6
|
+
* 文字送り状態を提供するProvider
|
|
7
|
+
* DataProviderとは別に、displayTextとisTypingのみを管理
|
|
8
|
+
*/
|
|
9
|
+
export const PlaybackTextProvider = ({ displayText, isTyping, children }) => {
|
|
10
|
+
const value = useMemo(() => ({ displayText, isTyping }), [displayText, isTyping]);
|
|
11
|
+
return (_jsx(PlaybackTextContextInstance.Provider, { value: value, children: children }));
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* 文字送り状態を取得するフック
|
|
15
|
+
*/
|
|
16
|
+
export function usePlaybackText() {
|
|
17
|
+
const context = useContext(PlaybackTextContextInstance);
|
|
18
|
+
if (!context) {
|
|
19
|
+
throw new Error("usePlaybackText must be used within PlaybackTextProvider");
|
|
20
|
+
}
|
|
21
|
+
return context;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 文字送り状態を取得するフック(オプショナル版)
|
|
25
|
+
* PlaybackTextProviderの外でも使用可能(その場合はnullを返す)
|
|
26
|
+
*/
|
|
27
|
+
export function usePlaybackTextOptional() {
|
|
28
|
+
return useContext(PlaybackTextContextInstance);
|
|
29
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type { EmotionEffectState } from "./emotion-effect-types";
|
|
3
|
+
import type { BackgroundData, BacklogEntry, BranchChoiceHistory, ConversationBranchState, DisplayedCharacter, ScenarioBlock, WorkFont } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* シナリオ再生の動的データ
|
|
6
|
+
*/
|
|
7
|
+
export interface ScenarioPlaybackData {
|
|
8
|
+
/** 現在のブロックインデックス(0始まり) */
|
|
9
|
+
currentBlockIndex: number;
|
|
10
|
+
/** 総ブロック数 */
|
|
11
|
+
totalBlocks: number;
|
|
12
|
+
/** 現在のシナリオID */
|
|
13
|
+
scenarioId: string;
|
|
14
|
+
/** シナリオ名 */
|
|
15
|
+
scenarioName: string;
|
|
16
|
+
/** 現在のブロック */
|
|
17
|
+
currentBlock: ScenarioBlock | null;
|
|
18
|
+
/** 表示中のテキスト(改行は自動的に<br />に変換されます) */
|
|
19
|
+
displayText?: string | React.ReactNode;
|
|
20
|
+
/** テキストアニメーション中かどうか */
|
|
21
|
+
isTyping?: boolean;
|
|
22
|
+
/** 表示中のキャラクター */
|
|
23
|
+
displayedCharacters?: DisplayedCharacter[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* バックログデータ
|
|
27
|
+
*/
|
|
28
|
+
export interface BacklogData {
|
|
29
|
+
/** バックログエントリ */
|
|
30
|
+
entries: BacklogEntry[];
|
|
31
|
+
/** 総エントリ数 */
|
|
32
|
+
totalEntries: number;
|
|
33
|
+
/** ログエントリを追加 */
|
|
34
|
+
addLogEntry?: (entry: BacklogEntry) => void;
|
|
35
|
+
/** ログをクリア */
|
|
36
|
+
clearLogs?: () => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* プレイヤー設定データ
|
|
40
|
+
*/
|
|
41
|
+
export interface PlayerSettingsData {
|
|
42
|
+
/** テキスト速度 (ミリ秒/文字) */
|
|
43
|
+
textSpeed: number;
|
|
44
|
+
/** 自動再生速度 (秒) */
|
|
45
|
+
autoPlaySpeed: number;
|
|
46
|
+
/** BGM音量 (0-1) */
|
|
47
|
+
bgmVolume: number;
|
|
48
|
+
/** SE音量 (0-1) */
|
|
49
|
+
seVolume: number;
|
|
50
|
+
/** ボイス音量 (0-1) */
|
|
51
|
+
voiceVolume: number;
|
|
52
|
+
/** スキップ設定 */
|
|
53
|
+
skipMode?: "all" | "unread" | "none";
|
|
54
|
+
/** 選択されたフォントファミリー(テキスト用) */
|
|
55
|
+
selectedFontFamily?: string;
|
|
56
|
+
/** 選択されたUIフォントファミリー(メニュー・ラベル等) */
|
|
57
|
+
selectedUIFontFamily?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* プラグインアセットデータ
|
|
61
|
+
*/
|
|
62
|
+
export interface PluginAssetsData {
|
|
63
|
+
/**
|
|
64
|
+
* プラグインのアセットURLを取得
|
|
65
|
+
* @param pluginName - プラグインのパッケージ名
|
|
66
|
+
* @param filename - アセットファイル名
|
|
67
|
+
* @returns アセットのURL
|
|
68
|
+
*/
|
|
69
|
+
getAssetUrl: (pluginName: string, filename: string) => string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* フォントデータ
|
|
73
|
+
*/
|
|
74
|
+
export interface FontsData {
|
|
75
|
+
/** 利用可能なフォント一覧 */
|
|
76
|
+
fonts: WorkFont[];
|
|
77
|
+
/** 選択されているフォントファミリー(テキスト用) */
|
|
78
|
+
selectedFontFamily: string | undefined;
|
|
79
|
+
/** 選択されているUIフォントファミリー(メニュー・ラベル等) */
|
|
80
|
+
selectedUIFontFamily: string | undefined;
|
|
81
|
+
/** フォントが読み込み完了しているかどうか */
|
|
82
|
+
isLoaded: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 全データコンテキスト
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* このインターフェースは、プラグインがDataAPI経由でアクセスできる全てのデータカテゴリを定義します。
|
|
89
|
+
* 各プロパティは、プラグインが特定のデータにアクセスする際のキーとして使用されます。
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* // 現在のブロックインデックスを取得
|
|
94
|
+
* const blockIndex = api.data.get('playback', 'currentBlockIndex');
|
|
95
|
+
*
|
|
96
|
+
* // 再生データ全体を購読
|
|
97
|
+
* api.data.subscribe('playback', (data) => {
|
|
98
|
+
* console.log('Playback updated:', data);
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export interface DataContext {
|
|
103
|
+
/** シナリオ再生情報 */
|
|
104
|
+
playback: ScenarioPlaybackData;
|
|
105
|
+
/** バックログ情報 */
|
|
106
|
+
backlog: BacklogData;
|
|
107
|
+
/** プレイヤー設定 */
|
|
108
|
+
settings: PlayerSettingsData;
|
|
109
|
+
/** プラグインアセット */
|
|
110
|
+
pluginAssets: PluginAssetsData;
|
|
111
|
+
/** 会話分岐データ */
|
|
112
|
+
branchData?: ConversationBranchState;
|
|
113
|
+
/** 分岐履歴 */
|
|
114
|
+
branchHistory?: BranchChoiceHistory[];
|
|
115
|
+
/** 現在の背景データ(単一、後方互換性用) */
|
|
116
|
+
background?: BackgroundData | null;
|
|
117
|
+
/** 現在の背景データ(複数、背景グループ対応) */
|
|
118
|
+
backgrounds?: BackgroundData[];
|
|
119
|
+
/** フォント設定 */
|
|
120
|
+
fonts?: FontsData;
|
|
121
|
+
/**
|
|
122
|
+
* 【機能概要】: 感情エフェクトの表示状態
|
|
123
|
+
* 【実装方針】: オプショナルプロパティとして追加し、後方互換性を保つ
|
|
124
|
+
* 【テスト対応】: TC-007, TC-014を通すための実装
|
|
125
|
+
* 🔵 信頼性レベル: 要件定義書(行101-111)とnote.md(行244-251)で明確に定義されている
|
|
126
|
+
*
|
|
127
|
+
* @see EmotionEffectState - 完全な型定義は emotion-effect-types.ts を参照
|
|
128
|
+
*/
|
|
129
|
+
emotionEffect?: EmotionEffectState | null;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* データ購読コールバック
|
|
133
|
+
*
|
|
134
|
+
* @typeParam T - 購読するデータの型
|
|
135
|
+
*/
|
|
136
|
+
export type DataSubscriber<T> = (data: T) => void;
|
|
137
|
+
/**
|
|
138
|
+
* データAPI - プラグインからのデータアクセスインターフェース
|
|
139
|
+
*
|
|
140
|
+
* @remarks
|
|
141
|
+
* このAPIは、プラグインがシナリオエンジンの状態データにアクセスするための2つのアプローチを提供します:
|
|
142
|
+
* - Pull型: `get()` メソッドで現在の値を取得
|
|
143
|
+
* - Push型: `subscribe()` / `watch()` メソッドで変更を監視
|
|
144
|
+
*
|
|
145
|
+
* @example Pull型(現在値の取得)
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const blockIndex = api.data.get('playback', 'currentBlockIndex');
|
|
148
|
+
* const playback = api.data.get('playback');
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @example Push型(変更の監視)
|
|
152
|
+
* ```typescript
|
|
153
|
+
* const unsubscribe = api.data.subscribe('playback', (data) => {
|
|
154
|
+
* console.log('Playback updated:', data);
|
|
155
|
+
* });
|
|
156
|
+
* // cleanup
|
|
157
|
+
* unsubscribe();
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @example プロパティ単位での監視
|
|
161
|
+
* ```typescript
|
|
162
|
+
* api.data.watch('playback', 'currentBlockIndex', (index) => {
|
|
163
|
+
* console.log('Block changed to:', index);
|
|
164
|
+
* });
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export interface DataAPI {
|
|
168
|
+
/**
|
|
169
|
+
* データの現在値を取得(Pull型)
|
|
170
|
+
* @param key - データカテゴリ
|
|
171
|
+
*/
|
|
172
|
+
get<K extends keyof DataContext>(key: K): DataContext[K];
|
|
173
|
+
/**
|
|
174
|
+
* データの特定プロパティを取得(Pull型)
|
|
175
|
+
* @param key - データカテゴリ
|
|
176
|
+
* @param property - プロパティ名
|
|
177
|
+
*/
|
|
178
|
+
get<K extends keyof DataContext, P extends keyof DataContext[K]>(key: K, property: P): DataContext[K][P];
|
|
179
|
+
/**
|
|
180
|
+
* データカテゴリ全体の変更を監視(Push型)
|
|
181
|
+
* @param key - データカテゴリ
|
|
182
|
+
* @param callback - 変更時のコールバック
|
|
183
|
+
* @returns unsubscribe関数
|
|
184
|
+
*/
|
|
185
|
+
subscribe<K extends keyof DataContext>(key: K, callback: DataSubscriber<DataContext[K]>): () => void;
|
|
186
|
+
/**
|
|
187
|
+
* 特定プロパティの変更を監視(Push型)
|
|
188
|
+
* @param key - データカテゴリ
|
|
189
|
+
* @param property - プロパティ名
|
|
190
|
+
* @param callback - 変更時のコールバック
|
|
191
|
+
* @returns unsubscribe関数
|
|
192
|
+
*/
|
|
193
|
+
watch<K extends keyof DataContext, P extends keyof DataContext[K]>(key: K, property: P, callback: DataSubscriber<DataContext[K][P]>): () => void;
|
|
194
|
+
/**
|
|
195
|
+
* プレイヤー設定を更新
|
|
196
|
+
* @param settings - 更新する設定値(部分更新可能)
|
|
197
|
+
*/
|
|
198
|
+
updateSettings(settings: Partial<PlayerSettingsData>): void;
|
|
199
|
+
/**
|
|
200
|
+
* BGM音量を設定
|
|
201
|
+
* @param volume - 音量(0-1の範囲)
|
|
202
|
+
*/
|
|
203
|
+
setBgmVolume(volume: number): void;
|
|
204
|
+
/**
|
|
205
|
+
* SE音量を設定
|
|
206
|
+
* @param volume - 音量(0-1の範囲)
|
|
207
|
+
*/
|
|
208
|
+
setSeVolume(volume: number): void;
|
|
209
|
+
/**
|
|
210
|
+
* ボイス音量を設定
|
|
211
|
+
* @param volume - 音量(0-1の範囲)
|
|
212
|
+
*/
|
|
213
|
+
setVoiceVolume(volume: number): void;
|
|
214
|
+
/**
|
|
215
|
+
* 全カテゴリの音量を一括設定
|
|
216
|
+
* @param volumes - カテゴリ別音量設定
|
|
217
|
+
*/
|
|
218
|
+
setVolumes(volumes: {
|
|
219
|
+
bgm?: number;
|
|
220
|
+
se?: number;
|
|
221
|
+
voice?: number;
|
|
222
|
+
}): void;
|
|
223
|
+
/**
|
|
224
|
+
* 現在のブロックのオプション値を型安全に取得
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* // プラグイン側で型を定義
|
|
229
|
+
* type MyBlockOptions = {
|
|
230
|
+
* ultemist_bubble_style: "normal" | "monologue" | "star";
|
|
231
|
+
* ultemist_effect_type: "none" | "shake" | "fade";
|
|
232
|
+
* };
|
|
233
|
+
*
|
|
234
|
+
* // コンポーネントで使用
|
|
235
|
+
* const bubbleStyle = dataAPI.getBlockOption<MyBlockOptions, "ultemist_bubble_style">("ultemist_bubble_style");
|
|
236
|
+
* // 型: "normal" | "monologue" | "star" | undefined
|
|
237
|
+
*
|
|
238
|
+
* // または簡易版(キーのみ指定)
|
|
239
|
+
* const bubbleStyle = dataAPI.getBlockOption<MyBlockOptions>("ultemist_bubble_style");
|
|
240
|
+
* ```
|
|
241
|
+
*
|
|
242
|
+
* @param key - オプションのキー
|
|
243
|
+
* @returns オプションの値、または未設定の場合undefined
|
|
244
|
+
*/
|
|
245
|
+
getBlockOption<T extends Record<string, unknown>, K extends keyof T = keyof T>(key: K): T[K] | undefined;
|
|
246
|
+
/**
|
|
247
|
+
* 感情エフェクトの状態を更新
|
|
248
|
+
* @param state - 更新する感情エフェクトの状態
|
|
249
|
+
*/
|
|
250
|
+
updateEmotionEffect(state: EmotionEffectState | null): void;
|
|
251
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Data API Types - プラグインからのデータアクセス用型定義
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// このファイルはDataAPIで使用される型定義を提供します
|
|
5
|
+
// SDK全体のファイルサイズを管理するために分離されています
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 【機能概要】: 7種類の感情エフェクトタイプを定義
|
|
3
|
+
* 【実装方針】: 文字列リテラル型のユニオン型として定義し、型安全性を保証
|
|
4
|
+
* 【テスト対応】: TC-001, TC-010を通すための実装
|
|
5
|
+
* 🔵 信頼性レベル: 要件定義書(sdk-datacontext-extension-requirements.md行60-62)とnote.md(行201-210)で明確に定義されている
|
|
6
|
+
*/
|
|
7
|
+
export type EmotionType = "runrun" | "shonbori" | "kirakira" | "akire" | "heart" | "bikkuri" | "punpun";
|
|
8
|
+
/**
|
|
9
|
+
* 【機能概要】: エフェクト配置の基準位置タイプを定義
|
|
10
|
+
* 【実装方針】: キャラクター全体または顔を基準とする2択を文字列リテラル型で表現
|
|
11
|
+
* 【テスト対応】: TC-002, TC-011を通すための実装
|
|
12
|
+
* 🔵 信頼性レベル: 要件定義書(行64-67)とnote.md(行212-214)で明確に定義されている
|
|
13
|
+
*/
|
|
14
|
+
export type PositionType = "character" | "face";
|
|
15
|
+
/**
|
|
16
|
+
* 【機能概要】: エフェクトの相対位置を定義
|
|
17
|
+
* 【実装方針】: 7種類の位置指定を文字列リテラル型で表現
|
|
18
|
+
* 【テスト対応】: TC-003, TC-012を通すための実装
|
|
19
|
+
* 🔵 信頼性レベル: 要件定義書(行69-72)とnote.md(行216-224)で明確に定義されている
|
|
20
|
+
*/
|
|
21
|
+
export type EffectPosition = "above" | "below" | "left" | "right" | "center" | "top-left" | "bottom-left";
|
|
22
|
+
/**
|
|
23
|
+
* 【機能概要】: キャラクターの顔位置情報を定義
|
|
24
|
+
* 【実装方針】: 顔の中心座標と幅・高さを表現するインターフェース
|
|
25
|
+
* 【テスト対応】: TC-004, TC-009, TC-020を通すための実装
|
|
26
|
+
* 🔵 信頼性レベル: 要件定義書(行82-88)、note.md(行234-243)、既存のCharacterSpeakContext定義(sdk.ts行552-558)に基づく
|
|
27
|
+
*/
|
|
28
|
+
export interface FacePosition {
|
|
29
|
+
/** 顔中心のX座標(キャラクター要素内の相対位置、単位: px) */
|
|
30
|
+
x: number;
|
|
31
|
+
/** 顔中心のY座標(キャラクター要素内の相対位置、単位: px) */
|
|
32
|
+
y: number;
|
|
33
|
+
/** 顔の幅(単位: px) */
|
|
34
|
+
width: number;
|
|
35
|
+
/** 顔の高さ(単位: px) */
|
|
36
|
+
height: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 【機能概要】: エフェクトの位置設定を統合的に管理
|
|
40
|
+
* 【実装方針】: 基準位置タイプ、相対位置、オフセット値を組み合わせた設定オブジェクト
|
|
41
|
+
* 【テスト対応】: TC-005, TC-015, TC-016, TC-017を通すための実装
|
|
42
|
+
* 🔵 信頼性レベル: 要件定義書(行74-80)とnote.md(行226-231)で明確に定義されている
|
|
43
|
+
*/
|
|
44
|
+
export interface EmotionEffectPosition {
|
|
45
|
+
/** 基準位置タイプ(キャラクター全体または顔) */
|
|
46
|
+
positionType: PositionType;
|
|
47
|
+
/** エフェクトの相対位置 */
|
|
48
|
+
position: EffectPosition;
|
|
49
|
+
/** X軸オフセット(-500〜500px、デフォルト0) */
|
|
50
|
+
offsetX: number;
|
|
51
|
+
/** Y軸オフセット(-500〜500px、デフォルト0) */
|
|
52
|
+
offsetY: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 【機能概要】: 感情エフェクトの表示状態を包括的に管理
|
|
56
|
+
* 【実装方針】: エフェクト表示に必要な全ての状態情報を集約したインターフェース
|
|
57
|
+
* 【テスト対応】: TC-006, TC-008, TC-009, TC-013, TC-018, TC-021, TC-022を通すための実装
|
|
58
|
+
* 🔵 信頼性レベル: 要件定義書(行88-97)とnote.md(行187-198)で明確に定義されている
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* このインターフェースは、感情エフェクトの表示に必要な全ての状態を表現します。
|
|
62
|
+
* - speaker と facePosition はオプショナルで、プラグインがキャラクター情報を必要とする場合に使用します
|
|
63
|
+
* - targetCharacterId が null の場合、現在の発話キャラクターを対象とします
|
|
64
|
+
* - position には基準位置タイプと相対位置、オフセット値が含まれます
|
|
65
|
+
*/
|
|
66
|
+
export interface EmotionEffectState {
|
|
67
|
+
/** エフェクト表示が有効か(true: 表示、false: 非表示) */
|
|
68
|
+
enabled: boolean;
|
|
69
|
+
/** 感情タイプ(7種類のいずれか) */
|
|
70
|
+
emotionType: EmotionType;
|
|
71
|
+
/** 対象キャラクターID(nullは発話キャラクター) */
|
|
72
|
+
targetCharacterId: string | null;
|
|
73
|
+
/** エフェクト位置設定 */
|
|
74
|
+
position: EmotionEffectPosition;
|
|
75
|
+
/** 音声再生するか(true: 再生、false: 再生しない) */
|
|
76
|
+
playSound: boolean;
|
|
77
|
+
/** 発話キャラクター情報(オプショナル) */
|
|
78
|
+
speaker?: {
|
|
79
|
+
id: string;
|
|
80
|
+
name: string;
|
|
81
|
+
state?: string;
|
|
82
|
+
position?: number;
|
|
83
|
+
};
|
|
84
|
+
/** 顔位置情報(FacePosition型、オプショナル) */
|
|
85
|
+
facePosition?: FacePosition;
|
|
86
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Emotion Effect Types - 感情エフェクト関連の型定義
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// このファイルは感情エフェクトプラグインで使用される型定義を提供します
|
|
5
|
+
// SDK全体のファイルサイズを管理するために分離されています
|
|
6
|
+
export {};
|