Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
010696c
feat: add createFrame and removeFrame
xiaoxustudio Mar 11, 2026
ab43666
feat: add iframe api and reactive store
xiaoxustudio Mar 12, 2026
8d5c075
refactor: extract iframe interfaces
xiaoxustudio Mar 12, 2026
a073635
feat: add iframe wait and return value support
xiaoxustudio Mar 12, 2026
2a83f74
fix: handle iframe load error
xiaoxustudio Mar 12, 2026
e1976ee
chore: resolve some issues
xiaoxustudio Mar 12, 2026
dd454ff
fix: restrict iframe postMessage origin
xiaoxustudio Mar 12, 2026
3b82c42
feat: add iframe setGameVar and closeFrame API
xiaoxustudio Mar 12, 2026
d422882
refactor: simplify iframe component logic
xiaoxustudio Mar 13, 2026
3f00f64
feat: add iframe nextSentence and isBlockSentence API
xiaoxustudio Mar 13, 2026
029788c
feat: add iframe save and load events
xiaoxustudio Mar 13, 2026
3722839
fix: iframe width and height is invalid
xiaoxustudio Mar 13, 2026
a10af41
chore: unified name
xiaoxustudio Mar 14, 2026
33aee01
fix: unified function name
xiaoxustudio Mar 14, 2026
0cc054f
fix: build error
xiaoxustudio Mar 14, 2026
36b6017
fix: rename closeFrame to closeIframe
xiaoxustudio Mar 15, 2026
3545310
fix: reset iframe state when continue game
xiaoxustudio Mar 15, 2026
e65cc77
fix: isBlockSentence always return false when the second
xiaoxustudio Mar 15, 2026
d43cdb4
feat: add iframe persistent data API
xiaoxustudio Mar 15, 2026
32f6aee
feat: add iframe open/close and isActive state
xiaoxustudio Mar 17, 2026
ddbce3f
fix: set iframe isActive to true when adding
xiaoxustudio Mar 17, 2026
60440ed
feat: add command input parameters
xiaoxustudio Mar 19, 2026
ea691a8
feat: add postIframeMessage API
xiaoxustudio Mar 19, 2026
8ba8618
feat: add hidden parameter and style support for iframe
xiaoxustudio Mar 19, 2026
de02567
Merge branch 'dev' into feat/createFrame-command
xiaoxustudio Jun 6, 2026
cac53e2
chore: migrate to WebGal 4.6.0
xiaoxustudio Jun 6, 2026
c2556b6
refactor(iframe): refactor iframe reactivity and add stage commit for…
xiaoxustudio Jun 6, 2026
af85019
fix: isBlockSentence is no effective
xiaoxustudio Jun 6, 2026
cd4c1b2
refactor(iframe): IWebGALBridge API
xiaoxustudio Jun 7, 2026
e06f96b
feat(iframe): remove internal properties from IWebGALBridge interface
xiaoxustudio Jun 7, 2026
fadfc66
fix(iframe): enhance sandbox security and use logger
xiaoxustudio Jun 7, 2026
957ecd1
chore: remove unuse markdown
xiaoxustudio Jun 10, 2026
6176972
feat(iframe): enhance security and add hide mode for iframe management
xiaoxustudio Jun 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/parser/src/config/scriptConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {commandType} from '../interface/sceneInterface';
import { commandType } from '../interface/sceneInterface';

export const SCRIPT_CONFIG = [
{ scriptString: 'say', scriptType: commandType.say },
Expand Down Expand Up @@ -31,14 +31,19 @@ export const SCRIPT_CONFIG = [
{ scriptString: 'setTextbox', scriptType: commandType.setTextbox },
{ scriptString: 'setAnimation', scriptType: commandType.setAnimation },
{ scriptString: 'playEffect', scriptType: commandType.playEffect },
{ scriptString: 'setTempAnimation', scriptType: commandType.setTempAnimation },
{
scriptString: 'setTempAnimation',
scriptType: commandType.setTempAnimation,
},
// comment?
{ scriptString: 'setTransform', scriptType: commandType.setTransform },
{ scriptString: 'setTransition', scriptType: commandType.setTransition },
{ scriptString: 'getUserInput', scriptType: commandType.getUserInput },
{ scriptString: 'applyStyle', scriptType: commandType.applyStyle },
{ scriptString: 'wait', scriptType: commandType.wait },
{ scriptString: 'callSteam', scriptType: commandType.callSteam },
{ scriptString: 'createIframe', scriptType: commandType.createIframe },
{ scriptString: 'removeIframe', scriptType: commandType.removeIframe },
];
export const ADD_NEXT_ARG_LIST = [
commandType.bgm,
Expand Down
2 changes: 2 additions & 0 deletions packages/parser/src/interface/sceneInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export enum commandType {
applyStyle,
wait,
callSteam, // 调用Steam功能
createIframe, // 创建框架
removeIframe, // 移除框架
}

/**
Expand Down
1 change: 0 additions & 1 deletion packages/webgal/src/Core/Modules/animationFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { baseTransform } from '@/Core/Modules/stage/stageInterface';
import { generateTimelineObj } from '@/Core/controller/stage/pixi/animations/timeline';
import { WebGAL } from '@/Core/WebGAL';
import PixiStage, { IAnimationObject } from '@/Core/controller/stage/pixi/PixiController';
import { IUserAnimation } from './animations';
import { pickBy } from 'lodash';
import {
DEFAULT_BG_IN_DURATION,
Expand Down
3 changes: 3 additions & 0 deletions packages/webgal/src/Core/Modules/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export class Events {
public fullscreenDbClick = formEvent('fullscreen-dbclick');
public styleUpdate = formEvent('style-update');
public afterStyleUpdate = formEvent('after-style-update');
public save = formEvent('save');
public load = formEvent('load');
public sceneChange = formEvent('scene-change');
}

const eventBus = mitt();
Expand Down
16 changes: 16 additions & 0 deletions packages/webgal/src/Core/Modules/stage/stageInterface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { ISentence } from '@/Core/controller/scene/sceneInterface';
import { BlinkParam, FocusParam } from '@/Core/live2DCore';
import { CSSProperties } from 'react';

export interface IIFrame {
id: string;
src: string;
sandbox: string;
width?: string;
height?: string;
style: CSSProperties;
isActive?: boolean;
wait?: boolean; // 是否等待iframe完成
returnValue?: string | null; // iframe返回值的变量名
persistentData?: Record<string, any>; // iframe的持久化数据
injectArgs: Record<string, any>; // iframe注入的参数,将挂载在window.webgal.params中
}

/**
* 游戏内变量
Expand Down Expand Up @@ -253,6 +268,7 @@ export interface IStageState {
isDisableTextbox: boolean;
replacedUIlable: Record<string, string>;
figureMetaData: figureMetaData;
iframes: IIFrame[];
}

/**
Expand Down
41 changes: 40 additions & 1 deletion packages/webgal/src/Core/Modules/stage/stageStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
IEffect,
IFigureMetadata,
IFreeFigure,
IIFrame,
ILive2DBlink,
ILive2DExpression,
ILive2DFocus,
Expand Down Expand Up @@ -78,6 +79,7 @@ export const initState: IStageState = {
isDisableTextbox: false,
replacedUIlable: {},
figureMetaData: {},
iframes: [],
};

/**
Expand Down Expand Up @@ -344,14 +346,51 @@ export class StageStateManager {
}

public clearUncommittedNonHoldPerforms() {
this.calculationStageState.PerformList = this.calculationStageState.PerformList.filter((perform) => perform.isHoldOn);
this.calculationStageState.PerformList = this.calculationStageState.PerformList.filter(
(perform) => perform.isHoldOn,
);
}

public removeNonHoldPerformsAndCommit() {
this.clearUncommittedNonHoldPerforms();
this.commit();
}

public addIframe(payload: IIFrame) {
payload.isActive = true;
const existing = this.calculationStageState.iframes.findIndex((e) => e.id === payload.id);
if (existing > -1) {
this.calculationStageState.iframes[existing] = payload;
} else {
this.calculationStageState.iframes.push(payload);
}
this.commit();
}

public removeIframe(payload: { id: string; save?: boolean }) {
if (payload.save) {
this.calculationStageState.iframes = this.calculationStageState.iframes.map((e) =>
e.id === payload.id ? { ...e, isActive: false } : e,
);
} else {
this.calculationStageState.iframes = this.calculationStageState.iframes.filter((e) => e.id !== payload.id);
}
this.commit();
}

public resetIframe() {
this.calculationStageState.iframes = [];
this.commit();
}

public updateIframePersistentData(payload: { id: string; persistentData: Record<string, any> }) {
const iframe = this.calculationStageState.iframes.find((e) => e.id === payload.id);
if (iframe) {
iframe.persistentData = { ...iframe.persistentData, ...payload.persistentData };
}
this.commit();
}

public commit(options: IStageCommitOptions = {}) {
const resolvedOptions: IResolvedStageCommitOptions = {
syncPixiStage: options.syncPixiStage ?? true,
Expand Down
14 changes: 12 additions & 2 deletions packages/webgal/src/Core/controller/gamePlay/autoPlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ export const stopAuto = () => {
}
};

/**
* 开始自动播放
*/
export const startAuto = () => {
if (WebGAL.gameplay.autoInterval !== null) {
return;
}
WebGAL.gameplay.isAuto = true;
WebGAL.gameplay.autoInterval = setInterval(autoPlay, 100);
};

/**
* 切换自动播放状态
*/
Expand All @@ -42,8 +53,7 @@ export const switchAuto = () => {
stopAuto();
} else {
// 当前不在自动播放
WebGAL.gameplay.isAuto = true;
WebGAL.gameplay.autoInterval = setInterval(autoPlay, 100);
startAuto();
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,20 @@ export async function continueGame() {
* 重设模糊背景
*/
setEbg(stageStateManager.getViewStageState().bgName);
if ((await hasFastSaveRecord())) {
if (await hasFastSaveRecord()) {
webgalStore.dispatch(setVisibility({ component: 'showTitle', visibility: false }));
// 恢复记录
await loadFastSaveGame();
return;
}
stageStateManager.resetIframe();
if (
WebGAL.sceneManager.sceneData.currentSentenceId === 0 &&
WebGAL.sceneManager.sceneData.currentScene.sceneName === 'start.txt'
) {
// 如果游戏没有开始,开始游戏
nextSentence();
} else {
restorePerform();
}
}
2 changes: 2 additions & 0 deletions packages/webgal/src/Core/controller/scene/sceneInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export enum commandType {
applyStyle,
wait,
callSteam, // 调用Steam功能
createIframe, // 创建框架
removeIframe, // 移除框架
}

/**
Expand Down
3 changes: 3 additions & 0 deletions packages/webgal/src/Core/controller/stage/resetStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ export const resetStage = (resetBacklog: boolean, resetSceneAndVar = true) => {
if (!resetSceneAndVar) {
stageStateManager.setStageAndCommit('GameVar', currentVars);
}

// 清空frames
stageStateManager.resetIframe();
};
14 changes: 14 additions & 0 deletions packages/webgal/src/Core/controller/storage/loadGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const loadGame = (index: number) => {
logger.debug('读取的存档数据', loadFile);
// 加载存档
loadGameFromStageData(loadFile);
WebGAL.events.load.emit(index);
};

export function loadGameFromStageData(stageData: ISaveData) {
Expand All @@ -45,6 +46,8 @@ export function loadGameFromStageData(stageData: ISaveData) {

// 强制停止所有演出
stopAllPerform();
// 清空frames
stageStateManager.resetIframe();

// 恢复backlog
const newBacklog = loadFile.backlog;
Expand All @@ -55,10 +58,21 @@ export function loadGameFromStageData(stageData: ISaveData) {

// 恢复舞台状态
const newStageState = cloneDeep(loadFile.nowStageState);
// 保存iframes的持久化数据
const iframePersistentData = new Map<string, Record<string, any>>();
newStageState.iframes.forEach((iframe) => {
if (iframe.persistentData) {
iframePersistentData.set(iframe.id, iframe.persistentData);
}
});
// iframes将被指令创建,我们不需要使用存档中的iframes
newStageState.iframes = [];
// 确保原先未读的文本在 load 时能正确显示为已读文本
newStageState.isRead = true;
const dispatch = webgalStore.dispatch;
stageStateManager.replaceCalculationStageState(newStageState);
// 将持久化数据存储到全局变量中,供后续创建iframe时使用
(window as any).__iframePersistentData = iframePersistentData;

// 恢复演出
setTimeout(restorePerform, 0);
Expand Down
1 change: 1 addition & 0 deletions packages/webgal/src/Core/controller/storage/saveGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const saveGame = (index: number) => {
const saveData: ISaveData = generateCurrentStageData(index);
webgalStore.dispatch(saveActions.saveGame({ index, saveData }));
dumpSavesToStorage(index, index);
WebGAL.events.save.emit(index);
};

/**
Expand Down
Loading
Loading