sa2kit 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -94
- package/dist/AliyunOSSProvider-2UZRIGT3.mjs +6 -0
- package/dist/{AliyunOSSProvider-GQMSDJGZ.mjs.map → AliyunOSSProvider-2UZRIGT3.mjs.map} +1 -1
- package/dist/AliyunOSSProvider-XCTK3QQA.js +15 -0
- package/dist/{AliyunOSSProvider-7JLMJDXK.js.map → AliyunOSSProvider-XCTK3QQA.js.map} +1 -1
- package/dist/{chunk-3RFBUDRA.js → chunk-EXT3IKQA.js} +39 -6
- package/dist/chunk-EXT3IKQA.js.map +1 -0
- package/dist/{chunk-YVBU7QDJ.mjs → chunk-OHUE7EL6.mjs} +34 -5
- package/dist/chunk-OHUE7EL6.mjs.map +1 -0
- package/dist/mmd/admin/index.d.mts +1 -1
- package/dist/mmd/admin/index.d.ts +1 -1
- package/dist/mmd/admin/index.js +7 -1
- package/dist/mmd/admin/index.js.map +1 -1
- package/dist/mmd/admin/index.mjs +7 -1
- package/dist/mmd/admin/index.mjs.map +1 -1
- package/dist/mmd/index.d.mts +52 -6
- package/dist/mmd/index.d.ts +52 -6
- package/dist/mmd/index.js +276 -74
- package/dist/mmd/index.js.map +1 -1
- package/dist/mmd/index.mjs +277 -76
- package/dist/mmd/index.mjs.map +1 -1
- package/dist/mmd/server/index.d.mts +1 -1
- package/dist/mmd/server/index.d.ts +1 -1
- package/dist/{types-CsTSddwu.d.mts → types-DxYJqqes.d.mts} +34 -2
- package/dist/{types-CsTSddwu.d.ts → types-DxYJqqes.d.ts} +34 -2
- package/dist/universalFile/server/index.d.mts +15 -0
- package/dist/universalFile/server/index.d.ts +15 -0
- package/dist/universalFile/server/index.js +3 -3
- package/dist/universalFile/server/index.mjs +2 -2
- package/package.json +4 -1
- package/dist/AliyunOSSProvider-7JLMJDXK.js +0 -15
- package/dist/AliyunOSSProvider-GQMSDJGZ.mjs +0 -6
- package/dist/chunk-3RFBUDRA.js.map +0 -1
- package/dist/chunk-YVBU7QDJ.mjs.map +0 -1
package/dist/mmd/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { e as MMDPlayerBaseProps, f as MMDPlayerBaseRef, g as MMDPlayerEnhancedProps, h as MMDPlaylistProps, b as MMDPlaylistNode, d as MMDResources, i as MMDStage, j as MobileOptimization } from '../types-
|
|
2
|
-
export { M as MMDPlaylistConfig, a as MMDResourceItem, c as MMDResourceOptions, R as ResourceOption } from '../types-
|
|
1
|
+
import { e as MMDPlayerBaseProps, f as MMDPlayerBaseRef, g as MMDPlayerEnhancedProps, h as MMDPlaylistProps, b as MMDPlaylistNode, d as MMDResources, i as MMDStage, j as MobileOptimization } from '../types-DxYJqqes.js';
|
|
2
|
+
export { B as BloomOptions, M as MMDPlaylistConfig, a as MMDResourceItem, c as MMDResourceOptions, O as OutlineOptions, R as ResourceOption } from '../types-DxYJqqes.js';
|
|
3
3
|
import React__default from 'react';
|
|
4
4
|
import 'three';
|
|
5
5
|
|
|
@@ -87,17 +87,50 @@ interface DialogueLine {
|
|
|
87
87
|
expression?: string;
|
|
88
88
|
/** 对话时播放的音效(可选) */
|
|
89
89
|
voicePath?: string;
|
|
90
|
+
/** 对话中插入的分支选项(可选,若有则显示选项) */
|
|
91
|
+
choices?: DialogueChoice[];
|
|
92
|
+
/** 视觉特效(可选) */
|
|
93
|
+
effect?: VisualEffect;
|
|
94
|
+
}
|
|
95
|
+
/** 视觉特效配置 */
|
|
96
|
+
interface VisualEffect {
|
|
97
|
+
/** 特效类型 */
|
|
98
|
+
type: 'flash' | 'gif' | 'shake';
|
|
99
|
+
/** 闪屏颜色,如 'white', 'red' */
|
|
100
|
+
color?: string;
|
|
101
|
+
/** 持续时间(毫秒) */
|
|
102
|
+
duration?: number;
|
|
103
|
+
/** GIF 动画地址 */
|
|
104
|
+
url?: string;
|
|
105
|
+
/** GIF 显示位置 */
|
|
106
|
+
position?: 'center' | 'full';
|
|
107
|
+
}
|
|
108
|
+
/** 分支判定逻辑 */
|
|
109
|
+
interface BranchCondition {
|
|
110
|
+
/** 变量名 */
|
|
111
|
+
key: string;
|
|
112
|
+
/** 变量值与节点索引的映射 */
|
|
113
|
+
map: Record<string | number, number>;
|
|
114
|
+
/** 默认跳转的节点索引 */
|
|
115
|
+
defaultIndex: number;
|
|
90
116
|
}
|
|
91
117
|
/** 对话分支选项 */
|
|
92
118
|
interface DialogueChoice {
|
|
93
119
|
/** 选项文字 */
|
|
94
120
|
text: string;
|
|
95
|
-
/**
|
|
96
|
-
nextNodeIndex
|
|
121
|
+
/** 跳转到的节点索引(可选,若不填则继续当前剧情) */
|
|
122
|
+
nextNodeIndex?: number;
|
|
97
123
|
/** 跳转到的对话索引(可选,默认 0) */
|
|
98
124
|
nextDialogueIndex?: number;
|
|
125
|
+
/** 设置变量(可选,用于后续剧情判定) */
|
|
126
|
+
setVariable?: {
|
|
127
|
+
key: string;
|
|
128
|
+
value: string | number | boolean;
|
|
129
|
+
};
|
|
99
130
|
/** 选项点击后的回调(可选) */
|
|
100
131
|
onSelect?: () => void;
|
|
132
|
+
/** 点击选项后触发的特效(可选) */
|
|
133
|
+
effect?: VisualEffect;
|
|
101
134
|
}
|
|
102
135
|
/** 视觉小说播放节点 */
|
|
103
136
|
interface VisualNovelNode {
|
|
@@ -111,8 +144,10 @@ interface VisualNovelNode {
|
|
|
111
144
|
dialogues: DialogueLine[];
|
|
112
145
|
/** 节点特定的舞台配置(可选,覆盖全局配置) */
|
|
113
146
|
stage?: MMDStage;
|
|
114
|
-
/**
|
|
147
|
+
/** 节点结束时的分支选项(可选,已废弃,建议使用 DialogueLine.choices) */
|
|
115
148
|
choices?: DialogueChoice[];
|
|
149
|
+
/** 节点结束时的分支判定逻辑(可选,根据变量跳转不同节点) */
|
|
150
|
+
nextCondition?: BranchCondition;
|
|
116
151
|
/** 节点开始时播放的背景音乐(可选) */
|
|
117
152
|
bgmPath?: string;
|
|
118
153
|
/** 背景音乐音量 0-1(默认 0.5) */
|
|
@@ -240,6 +275,10 @@ interface MMDVisualNovelRef {
|
|
|
240
275
|
getCurrentDialogueIndex: () => number;
|
|
241
276
|
/** 获取对话历史 */
|
|
242
277
|
getHistory: () => DialogueHistoryItem[];
|
|
278
|
+
/** 获取当前剧情变量 */
|
|
279
|
+
getVariables: () => Record<string, string | number | boolean>;
|
|
280
|
+
/** 设置剧情变量 */
|
|
281
|
+
setVariable: (key: string, value: string | number | boolean) => void;
|
|
243
282
|
/** 设置自动播放模式 */
|
|
244
283
|
setAutoMode: (enabled: boolean) => void;
|
|
245
284
|
/** 跳过当前打字动画 */
|
|
@@ -344,6 +383,13 @@ interface StartScreenProps {
|
|
|
344
383
|
*/
|
|
345
384
|
declare const StartScreen: React__default.FC<StartScreenProps>;
|
|
346
385
|
|
|
386
|
+
interface ChoiceMenuProps {
|
|
387
|
+
choices: DialogueChoice[];
|
|
388
|
+
onSelect: (choice: DialogueChoice) => void;
|
|
389
|
+
theme?: DialogueBoxTheme;
|
|
390
|
+
}
|
|
391
|
+
declare const ChoiceMenu: React__default.FC<ChoiceMenuProps>;
|
|
392
|
+
|
|
347
393
|
/** 音乐曲目配置 */
|
|
348
394
|
interface MusicTrack {
|
|
349
395
|
/** 唯一标识 */
|
|
@@ -459,4 +505,4 @@ interface TrackInfoProps {
|
|
|
459
505
|
}
|
|
460
506
|
declare const TrackInfo: React__default.FC<TrackInfoProps>;
|
|
461
507
|
|
|
462
|
-
export { DialogueBox, type DialogueBoxProps, type DialogueBoxTheme, type DialogueChoice, type DialogueHistoryItem, type DialogueLine, HistoryPanel, LoadingOverlay, type LoadingOverlayProps, LoadingScreen, type LoadingScreenProps, MMDMusicPlayer, type MMDMusicPlayerConfig, type MMDMusicPlayerProps, type MMDMusicPlayerRef, MMDPlayerBase, MMDPlayerBaseProps, MMDPlayerBaseRef, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlayerEnhancedProps, MMDPlaylist, MMDPlaylistDebugInfo, MMDPlaylistNode, MMDPlaylistProps, MMDResources, MMDStage, MMDVisualNovel, type MMDVisualNovelProps, type MMDVisualNovelRef, MobileOptimization, MusicControls, type MusicControlsProps, type MusicTrack, PlaylistPanel, type PlaylistPanelProps, StartScreen, type StartScreenProps, TrackInfo, type TrackInfoProps, type VisualNovelNode, type VisualNovelScript, loadAmmo };
|
|
508
|
+
export { type BranchCondition, ChoiceMenu, type ChoiceMenuProps, DialogueBox, type DialogueBoxProps, type DialogueBoxTheme, type DialogueChoice, type DialogueHistoryItem, type DialogueLine, HistoryPanel, LoadingOverlay, type LoadingOverlayProps, LoadingScreen, type LoadingScreenProps, MMDMusicPlayer, type MMDMusicPlayerConfig, type MMDMusicPlayerProps, type MMDMusicPlayerRef, MMDPlayerBase, MMDPlayerBaseProps, MMDPlayerBaseRef, MMDPlayerEnhanced, MMDPlayerEnhancedDebugInfo, MMDPlayerEnhancedProps, MMDPlaylist, MMDPlaylistDebugInfo, MMDPlaylistNode, MMDPlaylistProps, MMDResources, MMDStage, MMDVisualNovel, type MMDVisualNovelProps, type MMDVisualNovelRef, MobileOptimization, MusicControls, type MusicControlsProps, type MusicTrack, PlaylistPanel, type PlaylistPanelProps, StartScreen, type StartScreenProps, TrackInfo, type TrackInfoProps, type VisualEffect, type VisualNovelNode, type VisualNovelScript, loadAmmo };
|
package/dist/mmd/index.js
CHANGED
|
@@ -163,8 +163,37 @@ async function waitForMaterialsReady(object, renderer, scene, camera) {
|
|
|
163
163
|
for (let i = 0; i < 3; i++) {
|
|
164
164
|
await new Promise((resolve) => {
|
|
165
165
|
requestAnimationFrame(() => {
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
try {
|
|
167
|
+
object.traverse((obj) => {
|
|
168
|
+
if (obj.isMesh) {
|
|
169
|
+
const mesh = obj;
|
|
170
|
+
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
|
|
171
|
+
const hasMorphAttributes = mesh.geometry.morphAttributes && Object.keys(mesh.geometry.morphAttributes).length > 0;
|
|
172
|
+
materials.forEach((m) => {
|
|
173
|
+
if (!hasMorphAttributes) {
|
|
174
|
+
m.morphTargets = false;
|
|
175
|
+
if (mesh.geometry.morphAttributes) {
|
|
176
|
+
mesh.geometry.morphAttributes = {};
|
|
177
|
+
}
|
|
178
|
+
if (mesh.morphTargetInfluences) {
|
|
179
|
+
mesh.morphTargetInfluences = [];
|
|
180
|
+
}
|
|
181
|
+
if (mesh.morphTargetDictionary) {
|
|
182
|
+
mesh.morphTargetDictionary = {};
|
|
183
|
+
}
|
|
184
|
+
m.needsUpdate = true;
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
if (mesh.updateMorphTargets) {
|
|
188
|
+
mesh.updateMorphTargets();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
renderer.render(scene, camera);
|
|
193
|
+
console.log(`[MMDPlayerBase] Warmup render ${i + 1}/3`);
|
|
194
|
+
} catch (renderError) {
|
|
195
|
+
console.warn("[MMDPlayerBase] Warmup render failed (shader error?), skipping...", renderError);
|
|
196
|
+
}
|
|
168
197
|
resolve();
|
|
169
198
|
});
|
|
170
199
|
});
|
|
@@ -192,10 +221,15 @@ var MMDPlayerBase = React6.forwardRef((props, ref) => {
|
|
|
192
221
|
className,
|
|
193
222
|
style
|
|
194
223
|
} = props;
|
|
224
|
+
const renderEffect = props.renderEffect || stage.renderEffect || "default";
|
|
225
|
+
const outlineOptions = { ...stage.outlineOptions, ...props.outlineOptions };
|
|
226
|
+
const bloomOptions = { ...stage.bloomOptions, ...props.bloomOptions };
|
|
195
227
|
const containerRef = React6.useRef(null);
|
|
196
228
|
const sceneRef = React6.useRef(null);
|
|
197
229
|
const cameraRef = React6.useRef(null);
|
|
198
230
|
const rendererRef = React6.useRef(null);
|
|
231
|
+
const outlineEffectRef = React6.useRef(null);
|
|
232
|
+
const composerRef = React6.useRef(null);
|
|
199
233
|
const controlsRef = React6.useRef(null);
|
|
200
234
|
const helperRef = React6.useRef(null);
|
|
201
235
|
const axesHelperRef = React6.useRef(null);
|
|
@@ -403,10 +437,28 @@ var MMDPlayerBase = React6.forwardRef((props, ref) => {
|
|
|
403
437
|
}
|
|
404
438
|
container.appendChild(renderer.domElement);
|
|
405
439
|
rendererRef.current = renderer;
|
|
440
|
+
const effect = new threeStdlib.OutlineEffect(renderer, {
|
|
441
|
+
defaultThickness: outlineOptions.thickness ?? 3e-3,
|
|
442
|
+
defaultColor: new THREE__namespace.Color(outlineOptions.color ?? "#000000").toArray(),
|
|
443
|
+
defaultAlpha: 1,
|
|
444
|
+
defaultKeepAlive: true
|
|
445
|
+
});
|
|
446
|
+
outlineEffectRef.current = effect;
|
|
447
|
+
const composer = new threeStdlib.EffectComposer(renderer);
|
|
448
|
+
const renderPass = new threeStdlib.RenderPass(scene, camera);
|
|
449
|
+
composer.addPass(renderPass);
|
|
450
|
+
const bloomPass = new threeStdlib.UnrealBloomPass(
|
|
451
|
+
new THREE__namespace.Vector2(width, height),
|
|
452
|
+
bloomOptions.strength ?? 1,
|
|
453
|
+
bloomOptions.radius ?? 0.4,
|
|
454
|
+
bloomOptions.threshold ?? 0.8
|
|
455
|
+
);
|
|
456
|
+
composer.addPass(bloomPass);
|
|
457
|
+
composerRef.current = composer;
|
|
406
458
|
const ambientLight = new THREE__namespace.AmbientLight(16777215, stage.ambientLightIntensity ?? 0.5);
|
|
407
459
|
scene.add(ambientLight);
|
|
408
460
|
const dirLight = new THREE__namespace.DirectionalLight(16777215, stage.directionalLightIntensity ?? 0.8);
|
|
409
|
-
dirLight.position.set(
|
|
461
|
+
dirLight.position.set(0, 10, 0);
|
|
410
462
|
if (stage.enableShadow !== false) {
|
|
411
463
|
dirLight.castShadow = true;
|
|
412
464
|
dirLight.shadow.mapSize.width = mobileOptimization.enabled ? 1024 : 2048;
|
|
@@ -441,6 +493,9 @@ var MMDPlayerBase = React6.forwardRef((props, ref) => {
|
|
|
441
493
|
cameraRef.current.aspect = w / h;
|
|
442
494
|
cameraRef.current.updateProjectionMatrix();
|
|
443
495
|
rendererRef.current.setSize(w, h);
|
|
496
|
+
if (composerRef.current) {
|
|
497
|
+
composerRef.current.setSize(w, h);
|
|
498
|
+
}
|
|
444
499
|
};
|
|
445
500
|
const resizeObserver = new ResizeObserver(onResize);
|
|
446
501
|
resizeObserver.observe(container);
|
|
@@ -448,7 +503,11 @@ var MMDPlayerBase = React6.forwardRef((props, ref) => {
|
|
|
448
503
|
onResize();
|
|
449
504
|
console.log("[MMDPlayerBase] Starting render loop (animation paused)");
|
|
450
505
|
animate();
|
|
451
|
-
console.log("[MMDPlayerBase] Start loading resources...",
|
|
506
|
+
console.log("[MMDPlayerBase] Start loading resources...", {
|
|
507
|
+
model: resources.modelPath,
|
|
508
|
+
stage: resources.stageModelPath,
|
|
509
|
+
motion: resources.motionPath
|
|
510
|
+
});
|
|
452
511
|
const loader = new threeStdlib.MMDLoader();
|
|
453
512
|
const helper = new threeStdlib.MMDAnimationHelper({
|
|
454
513
|
afterglow: 2
|
|
@@ -621,51 +680,67 @@ var MMDPlayerBase = React6.forwardRef((props, ref) => {
|
|
|
621
680
|
(err) => console.error("Failed to load audio:", err)
|
|
622
681
|
);
|
|
623
682
|
}
|
|
624
|
-
|
|
625
|
-
|
|
683
|
+
const stagePaths = Array.isArray(resources.stageModelPath) ? resources.stageModelPath : resources.stageModelPath ? [resources.stageModelPath] : [];
|
|
684
|
+
for (const stagePath of stagePaths) {
|
|
626
685
|
try {
|
|
627
|
-
|
|
686
|
+
console.log(`[MMDPlayerBase] Loading stage from: ${stagePath}`);
|
|
687
|
+
const stageMesh = await new Promise((resolve, reject) => {
|
|
628
688
|
loader.load(
|
|
629
|
-
|
|
689
|
+
stagePath,
|
|
630
690
|
(mesh2) => resolve(mesh2),
|
|
631
|
-
|
|
691
|
+
(xhr) => {
|
|
692
|
+
if (xhr.lengthComputable) {
|
|
693
|
+
const percent = xhr.loaded / xhr.total * 100;
|
|
694
|
+
if (Math.round(percent) % 20 === 0) console.log(`[MMDPlayerBase] Stage loading: ${percent.toFixed(1)}%`);
|
|
695
|
+
}
|
|
696
|
+
},
|
|
632
697
|
(err) => reject(err)
|
|
633
698
|
);
|
|
634
699
|
});
|
|
635
700
|
if (checkCancelled()) return;
|
|
636
|
-
console.log(
|
|
637
|
-
stageMesh.
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
701
|
+
console.log(`[MMDPlayerBase] Stage model loaded: ${stagePath}`, stageMesh);
|
|
702
|
+
stageMesh.traverse((child) => {
|
|
703
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
704
|
+
child.castShadow = true;
|
|
705
|
+
child.receiveShadow = true;
|
|
706
|
+
const mesh2 = child;
|
|
707
|
+
const materials = Array.isArray(mesh2.material) ? mesh2.material : [mesh2.material];
|
|
708
|
+
materials.forEach((m, idx) => {
|
|
709
|
+
if (m.morphTargets) {
|
|
710
|
+
m.morphTargets = false;
|
|
711
|
+
m.needsUpdate = true;
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
if (mesh2.geometry.morphAttributes) {
|
|
715
|
+
mesh2.geometry.morphAttributes = {};
|
|
716
|
+
}
|
|
717
|
+
if (mesh2.morphTargetInfluences) {
|
|
718
|
+
mesh2.morphTargetInfluences = [];
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
try {
|
|
723
|
+
await waitForMaterialsReady(stageMesh, renderer, scene, camera);
|
|
724
|
+
} catch (e) {
|
|
725
|
+
console.warn(`[MMDPlayerBase] Warmup error for stage ${stagePath}:`, e);
|
|
726
|
+
}
|
|
644
727
|
if (checkCancelled()) return;
|
|
645
|
-
console.log("[MMDPlayerBase] \u2705 Stage materials and textures loaded");
|
|
646
728
|
scene.add(stageMesh);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
animation: stageAnimation
|
|
660
|
-
});
|
|
661
|
-
console.log("[MMDPlayerBase] \u2705 Stage motion bound successfully");
|
|
662
|
-
},
|
|
663
|
-
void 0,
|
|
664
|
-
(err) => console.error("Failed to load stage motion:", err)
|
|
665
|
-
);
|
|
729
|
+
const stageBox = new THREE__namespace.Box3().setFromObject(stageMesh);
|
|
730
|
+
const stageSize = stageBox.getSize(new THREE__namespace.Vector3());
|
|
731
|
+
if (stageSize.length() < 1) {
|
|
732
|
+
stageMesh.scale.multiplyScalar(100);
|
|
733
|
+
} else if (stageSize.y < 5) {
|
|
734
|
+
stageMesh.scale.multiplyScalar(10);
|
|
735
|
+
}
|
|
736
|
+
console.log(`[MMDPlayerBase] \u2705 Stage added: ${stagePath}`);
|
|
737
|
+
if (resources.stageMotionPath) {
|
|
738
|
+
loader.loadAnimation(resources.stageMotionPath, stageMesh, (anim) => {
|
|
739
|
+
if (!checkCancelled()) helper.add(stageMesh, { animation: anim });
|
|
740
|
+
});
|
|
666
741
|
}
|
|
667
742
|
} catch (err) {
|
|
668
|
-
console.error(
|
|
743
|
+
console.error(`Failed to load stage ${stagePath}:`, err);
|
|
669
744
|
}
|
|
670
745
|
}
|
|
671
746
|
if (checkCancelled()) return;
|
|
@@ -1043,6 +1118,13 @@ ${errorMessage}
|
|
|
1043
1118
|
}
|
|
1044
1119
|
if (rendererRef.current) {
|
|
1045
1120
|
try {
|
|
1121
|
+
if (composerRef.current) {
|
|
1122
|
+
composerRef.current.passes.forEach((pass) => {
|
|
1123
|
+
if (pass.dispose) pass.dispose();
|
|
1124
|
+
});
|
|
1125
|
+
composerRef.current = null;
|
|
1126
|
+
}
|
|
1127
|
+
outlineEffectRef.current = null;
|
|
1046
1128
|
const renderer = rendererRef.current;
|
|
1047
1129
|
if (renderer.renderLists) {
|
|
1048
1130
|
renderer.renderLists.dispose();
|
|
@@ -1109,6 +1191,17 @@ ${errorMessage}
|
|
|
1109
1191
|
audioRef.current.setLoop(loop);
|
|
1110
1192
|
}
|
|
1111
1193
|
}, [loop]);
|
|
1194
|
+
React6.useEffect(() => {
|
|
1195
|
+
if (outlineEffectRef.current) ;
|
|
1196
|
+
if (composerRef.current) {
|
|
1197
|
+
const bloomPass = composerRef.current.passes.find((p) => p instanceof threeStdlib.UnrealBloomPass);
|
|
1198
|
+
if (bloomPass) {
|
|
1199
|
+
bloomPass.strength = bloomOptions.strength ?? 1;
|
|
1200
|
+
bloomPass.radius = bloomOptions.radius ?? 0.4;
|
|
1201
|
+
bloomPass.threshold = bloomOptions.threshold ?? 0.8;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}, [bloomOptions.strength, bloomOptions.radius, bloomOptions.threshold]);
|
|
1112
1205
|
React6.useEffect(() => {
|
|
1113
1206
|
if (!isReadyRef.current) return;
|
|
1114
1207
|
if (sceneRef.current) {
|
|
@@ -1157,7 +1250,15 @@ ${errorMessage}
|
|
|
1157
1250
|
latestCallbacks.current.onEnded?.();
|
|
1158
1251
|
}
|
|
1159
1252
|
}
|
|
1160
|
-
|
|
1253
|
+
const useOutline = renderEffect === "outline" || renderEffect === "outline+bloom";
|
|
1254
|
+
const useBloom = renderEffect === "bloom" || renderEffect === "outline+bloom";
|
|
1255
|
+
if (useBloom && composerRef.current) {
|
|
1256
|
+
composerRef.current.render();
|
|
1257
|
+
} else if (useOutline && outlineEffectRef.current) {
|
|
1258
|
+
outlineEffectRef.current.render(sceneRef.current, cameraRef.current);
|
|
1259
|
+
} else {
|
|
1260
|
+
rendererRef.current.render(sceneRef.current, cameraRef.current);
|
|
1261
|
+
}
|
|
1161
1262
|
}
|
|
1162
1263
|
};
|
|
1163
1264
|
return /* @__PURE__ */ React6__default.default.createElement(
|
|
@@ -2852,6 +2953,8 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
2852
2953
|
const [pendingNodeIndex, setPendingNodeIndex] = React6.useState(null);
|
|
2853
2954
|
const [showChoices, setShowChoices] = React6.useState(false);
|
|
2854
2955
|
const [isCameraManual, setIsCameraManual] = React6.useState(false);
|
|
2956
|
+
const [variables, setVariables] = React6.useState({});
|
|
2957
|
+
const [activeEffect, setActiveEffect] = React6.useState(null);
|
|
2855
2958
|
const playerRef = React6.useRef(null);
|
|
2856
2959
|
const containerRef = React6.useRef(null);
|
|
2857
2960
|
const autoTimerRef = React6.useRef(null);
|
|
@@ -2859,6 +2962,7 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
2859
2962
|
const isStartedRef = React6.useRef(autoStart);
|
|
2860
2963
|
const lastAnimationTimeRef = React6.useRef(0);
|
|
2861
2964
|
const isVmdFinishedRef = React6.useRef(false);
|
|
2965
|
+
const effectTimerRef = React6.useRef(null);
|
|
2862
2966
|
const currentNode = nodes[currentNodeIndex];
|
|
2863
2967
|
const currentDialogue = currentNode?.dialogues[currentDialogueIndex] || null;
|
|
2864
2968
|
const addToHistory = React6.useCallback((dialogue, nodeIndex, dialogueIndex) => {
|
|
@@ -2873,32 +2977,17 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
2873
2977
|
}
|
|
2874
2978
|
]);
|
|
2875
2979
|
}, []);
|
|
2876
|
-
const
|
|
2877
|
-
if (!
|
|
2878
|
-
if (
|
|
2879
|
-
clearTimeout(
|
|
2880
|
-
autoTimerRef.current = null;
|
|
2980
|
+
const triggerEffect = React6.useCallback((effect) => {
|
|
2981
|
+
if (!effect) return;
|
|
2982
|
+
if (effectTimerRef.current) {
|
|
2983
|
+
clearTimeout(effectTimerRef.current);
|
|
2881
2984
|
}
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
typingCompleteRef.current = false;
|
|
2889
|
-
} else if (currentNode.choices && currentNode.choices.length > 0) {
|
|
2890
|
-
setShowChoices(true);
|
|
2891
|
-
} else {
|
|
2892
|
-
const nextNodeIndex = currentNodeIndex + 1;
|
|
2893
|
-
if (nextNodeIndex < nodes.length) {
|
|
2894
|
-
goToNode(nextNodeIndex);
|
|
2895
|
-
} else if (loop) {
|
|
2896
|
-
goToNode(0);
|
|
2897
|
-
} else {
|
|
2898
|
-
onScriptComplete?.();
|
|
2899
|
-
}
|
|
2900
|
-
}
|
|
2901
|
-
}, [currentNode, currentDialogueIndex, currentNodeIndex, nodes.length, loop, addToHistory, onDialogueChange, onScriptComplete]);
|
|
2985
|
+
setActiveEffect(effect);
|
|
2986
|
+
effectTimerRef.current = setTimeout(() => {
|
|
2987
|
+
setActiveEffect(null);
|
|
2988
|
+
effectTimerRef.current = null;
|
|
2989
|
+
}, effect.duration || 1e3);
|
|
2990
|
+
}, []);
|
|
2902
2991
|
const goToNode = React6.useCallback(
|
|
2903
2992
|
(nodeIndex, force = false) => {
|
|
2904
2993
|
if (nodeIndex < 0 || nodeIndex >= nodes.length) return;
|
|
@@ -2937,8 +3026,52 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
2937
3026
|
}, 100);
|
|
2938
3027
|
}, 300);
|
|
2939
3028
|
},
|
|
2940
|
-
[nodes, isTransitioning, addToHistory, onNodeChange, onDialogueChange, currentNodeIndex
|
|
3029
|
+
[nodes, isTransitioning, addToHistory, onNodeChange, onDialogueChange, currentNodeIndex]
|
|
2941
3030
|
);
|
|
3031
|
+
const triggerNodeTransition = React6.useCallback(() => {
|
|
3032
|
+
if (!currentNode) return;
|
|
3033
|
+
let nextNodeIndex = currentNodeIndex + 1;
|
|
3034
|
+
if (currentNode.nextCondition) {
|
|
3035
|
+
const { key, map, defaultIndex } = currentNode.nextCondition;
|
|
3036
|
+
const val = variables[key];
|
|
3037
|
+
if (val !== void 0 && map[val] !== void 0) {
|
|
3038
|
+
nextNodeIndex = map[val];
|
|
3039
|
+
console.log(`[MMDVisualNovel] Branching: ${key}=${val} -> node ${nextNodeIndex}`);
|
|
3040
|
+
} else {
|
|
3041
|
+
nextNodeIndex = defaultIndex;
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
if (nextNodeIndex < nodes.length && nextNodeIndex >= 0) {
|
|
3045
|
+
goToNode(nextNodeIndex);
|
|
3046
|
+
} else if (loop) {
|
|
3047
|
+
goToNode(0);
|
|
3048
|
+
} else {
|
|
3049
|
+
onScriptComplete?.();
|
|
3050
|
+
}
|
|
3051
|
+
}, [currentNode, currentNodeIndex, nodes.length, loop, variables, goToNode, onScriptComplete]);
|
|
3052
|
+
const goToNextDialogue = React6.useCallback(() => {
|
|
3053
|
+
if (!currentNode) return;
|
|
3054
|
+
if (currentDialogue?.choices && currentDialogue.choices.length > 0 && !showChoices) {
|
|
3055
|
+
setShowChoices(true);
|
|
3056
|
+
return;
|
|
3057
|
+
}
|
|
3058
|
+
if (autoTimerRef.current) {
|
|
3059
|
+
clearTimeout(autoTimerRef.current);
|
|
3060
|
+
autoTimerRef.current = null;
|
|
3061
|
+
}
|
|
3062
|
+
const nextDialogueIndex = currentDialogueIndex + 1;
|
|
3063
|
+
if (nextDialogueIndex < currentNode.dialogues.length && currentNode?.dialogues[nextDialogueIndex] !== void 0) {
|
|
3064
|
+
const nextDialogue = currentNode.dialogues[nextDialogueIndex];
|
|
3065
|
+
setCurrentDialogueIndex(nextDialogueIndex);
|
|
3066
|
+
addToHistory(nextDialogue, currentNodeIndex, nextDialogueIndex);
|
|
3067
|
+
onDialogueChange?.(nextDialogue, nextDialogueIndex, currentNodeIndex);
|
|
3068
|
+
typingCompleteRef.current = false;
|
|
3069
|
+
} else if (currentNode.choices && currentNode.choices.length > 0) {
|
|
3070
|
+
setShowChoices(true);
|
|
3071
|
+
} else {
|
|
3072
|
+
triggerNodeTransition();
|
|
3073
|
+
}
|
|
3074
|
+
}, [currentNode, currentDialogue, currentDialogueIndex, currentNodeIndex, nodes.length, loop, addToHistory, onDialogueChange, onScriptComplete, showChoices, variables, goToNode, triggerNodeTransition]);
|
|
2942
3075
|
const goToDialogue = React6.useCallback(
|
|
2943
3076
|
(dialogueIndex) => {
|
|
2944
3077
|
if (!currentNode) return;
|
|
@@ -2982,6 +3115,11 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
2982
3115
|
}
|
|
2983
3116
|
return void 0;
|
|
2984
3117
|
}, [currentDialogue, handleTypingComplete]);
|
|
3118
|
+
React6.useEffect(() => {
|
|
3119
|
+
if (currentDialogue?.effect) {
|
|
3120
|
+
triggerEffect(currentDialogue.effect);
|
|
3121
|
+
}
|
|
3122
|
+
}, [currentNodeIndex, currentDialogueIndex, triggerEffect]);
|
|
2985
3123
|
const toggleAutoMode = React6.useCallback(() => {
|
|
2986
3124
|
setIsAutoMode((prev) => !prev);
|
|
2987
3125
|
}, []);
|
|
@@ -3017,12 +3155,17 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
3017
3155
|
getCurrentNodeIndex: () => currentNodeIndex,
|
|
3018
3156
|
getCurrentDialogueIndex: () => currentDialogueIndex,
|
|
3019
3157
|
getHistory: () => history,
|
|
3158
|
+
getVariables: () => variables,
|
|
3159
|
+
setVariable: (key, value) => {
|
|
3160
|
+
setVariables((prev) => ({ ...prev, [key]: value }));
|
|
3161
|
+
},
|
|
3020
3162
|
setAutoMode: setIsAutoMode,
|
|
3021
3163
|
skipTyping: () => {
|
|
3022
3164
|
typingCompleteRef.current = true;
|
|
3023
|
-
}
|
|
3165
|
+
},
|
|
3166
|
+
triggerEffect
|
|
3024
3167
|
}),
|
|
3025
|
-
[goToNode, goToDialogue, currentNodeIndex, currentDialogueIndex, history]
|
|
3168
|
+
[goToNode, goToDialogue, currentNodeIndex, currentDialogueIndex, history, triggerEffect]
|
|
3026
3169
|
);
|
|
3027
3170
|
React6.useEffect(() => {
|
|
3028
3171
|
if (autoStart && currentNode && currentNode.dialogues.length > 0 && history.length === 0 && currentNode?.dialogues[0] !== void 0) {
|
|
@@ -3034,6 +3177,9 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
3034
3177
|
if (autoTimerRef.current) {
|
|
3035
3178
|
clearTimeout(autoTimerRef.current);
|
|
3036
3179
|
}
|
|
3180
|
+
if (effectTimerRef.current) {
|
|
3181
|
+
clearTimeout(effectTimerRef.current);
|
|
3182
|
+
}
|
|
3037
3183
|
};
|
|
3038
3184
|
}, []);
|
|
3039
3185
|
if (!currentNode) {
|
|
@@ -3106,6 +3252,38 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
3106
3252
|
}
|
|
3107
3253
|
)
|
|
3108
3254
|
),
|
|
3255
|
+
activeEffect && /* @__PURE__ */ React6__default.default.createElement(
|
|
3256
|
+
"div",
|
|
3257
|
+
{
|
|
3258
|
+
className: "pointer-events-none absolute inset-0 flex items-center justify-center",
|
|
3259
|
+
style: { zIndex: 999 }
|
|
3260
|
+
},
|
|
3261
|
+
activeEffect.type === "flash" && /* @__PURE__ */ React6__default.default.createElement(
|
|
3262
|
+
"div",
|
|
3263
|
+
{
|
|
3264
|
+
className: "h-full w-full",
|
|
3265
|
+
style: {
|
|
3266
|
+
backgroundColor: activeEffect.color || "white",
|
|
3267
|
+
animation: `flash-anim ${activeEffect.duration || 500}ms ease-out forwards`
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
),
|
|
3271
|
+
activeEffect.type === "gif" && activeEffect.url && /* @__PURE__ */ React6__default.default.createElement(
|
|
3272
|
+
"img",
|
|
3273
|
+
{
|
|
3274
|
+
src: activeEffect.url,
|
|
3275
|
+
alt: "effect",
|
|
3276
|
+
className: activeEffect.position === "full" ? "h-full w-full object-cover" : "max-h-full max-w-full"
|
|
3277
|
+
}
|
|
3278
|
+
),
|
|
3279
|
+
/* @__PURE__ */ React6__default.default.createElement("style", null, `
|
|
3280
|
+
@keyframes flash-anim {
|
|
3281
|
+
0% { opacity: 0; }
|
|
3282
|
+
25% { opacity: 1; }
|
|
3283
|
+
100% { opacity: 0; }
|
|
3284
|
+
}
|
|
3285
|
+
`)
|
|
3286
|
+
),
|
|
3109
3287
|
/* @__PURE__ */ React6__default.default.createElement(
|
|
3110
3288
|
LoadingOverlay,
|
|
3111
3289
|
{
|
|
@@ -3177,18 +3355,41 @@ var MMDVisualNovel = React6.forwardRef(
|
|
|
3177
3355
|
}
|
|
3178
3356
|
}
|
|
3179
3357
|
),
|
|
3180
|
-
showChoices && currentNode.choices && /* @__PURE__ */ React6__default.default.createElement(
|
|
3358
|
+
showChoices && (currentDialogue?.choices || currentNode.choices) && /* @__PURE__ */ React6__default.default.createElement(
|
|
3181
3359
|
ChoiceMenu,
|
|
3182
3360
|
{
|
|
3183
|
-
choices: currentNode.choices,
|
|
3361
|
+
choices: currentDialogue?.choices || currentNode.choices,
|
|
3184
3362
|
theme: dialogueTheme,
|
|
3185
3363
|
onSelect: (choice) => {
|
|
3364
|
+
if (choice.setVariable) {
|
|
3365
|
+
const { key, value } = choice.setVariable;
|
|
3366
|
+
setVariables((prev) => ({ ...prev, [key]: value }));
|
|
3367
|
+
console.log(`[MMDVisualNovel] Variable set: ${key} = ${value}`);
|
|
3368
|
+
}
|
|
3186
3369
|
choice.onSelect?.();
|
|
3187
|
-
if (choice.
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3370
|
+
if (choice.effect) {
|
|
3371
|
+
triggerEffect(choice.effect);
|
|
3372
|
+
}
|
|
3373
|
+
setShowChoices(false);
|
|
3374
|
+
if (choice.nextNodeIndex !== void 0) {
|
|
3375
|
+
if (choice.nextNodeIndex === currentNodeIndex) {
|
|
3376
|
+
goToDialogue(choice.nextDialogueIndex || 0);
|
|
3377
|
+
} else {
|
|
3378
|
+
goToNode(choice.nextNodeIndex, true);
|
|
3379
|
+
}
|
|
3380
|
+
} else if (currentDialogue?.choices) {
|
|
3381
|
+
const nextIdx = currentDialogueIndex + 1;
|
|
3382
|
+
if (currentNode && nextIdx < currentNode.dialogues.length) {
|
|
3383
|
+
const nextDialogue = currentNode.dialogues[nextIdx];
|
|
3384
|
+
if (nextDialogue) {
|
|
3385
|
+
setCurrentDialogueIndex(nextIdx);
|
|
3386
|
+
addToHistory(nextDialogue, currentNodeIndex, nextIdx);
|
|
3387
|
+
onDialogueChange?.(nextDialogue, nextIdx, currentNodeIndex);
|
|
3388
|
+
typingCompleteRef.current = false;
|
|
3389
|
+
}
|
|
3390
|
+
} else {
|
|
3391
|
+
triggerNodeTransition();
|
|
3392
|
+
}
|
|
3192
3393
|
}
|
|
3193
3394
|
}
|
|
3194
3395
|
}
|
|
@@ -3607,6 +3808,7 @@ var MMDMusicPlayer = React6.forwardRef(
|
|
|
3607
3808
|
);
|
|
3608
3809
|
MMDMusicPlayer.displayName = "MMDMusicPlayer";
|
|
3609
3810
|
|
|
3811
|
+
exports.ChoiceMenu = ChoiceMenu;
|
|
3610
3812
|
exports.DialogueBox = DialogueBox;
|
|
3611
3813
|
exports.HistoryPanel = HistoryPanel;
|
|
3612
3814
|
exports.LoadingOverlay = LoadingOverlay;
|