Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions packages/webgal/src/Core/Modules/gamePlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class Gameplay {
public autoTimeout: ReturnType<typeof setTimeout> | null = null;
public pixiStage: PixiStage | null = null;
public performController = new PerformController();
public isFastPreview = false;

/* 有图标状态需求 */
private _isAuto = false;
Expand All @@ -34,6 +35,7 @@ export class Gameplay {
public resetGamePlay() {
this.isAuto = false;
this.isFast = false;
this.isFastPreview = false;
const autoInterval = this.autoInterval;
if (autoInterval !== null) clearInterval(autoInterval);
this.autoInterval = null;
Expand Down
35 changes: 30 additions & 5 deletions packages/webgal/src/Core/gameScripts/choose/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useEscape from '@/hooks/useEscape';
import useApplyStyle from '@/hooks/useApplyStyle';
import { Provider } from 'react-redux';
import { useFontFamily } from '@/hooks/useFontFamily';
import { getNumberArgByKey } from '@/Core/util/getSentenceArg';

class ChooseOption {
/**
Expand Down Expand Up @@ -58,6 +59,8 @@ class ChooseOption {
export const choose = (sentence: ISentence): IPerform => {
const chooseOptionScripts = sentence.content.split(/(?<!\\)\|/);
const chooseOptions = chooseOptionScripts.map((e) => ChooseOption.parse(e.trim()));
const defaultChoose = getNumberArgByKey(sentence, 'defaultChoose');
const previewChoice = getDefaultPreviewChoice(chooseOptions, defaultChoose);

// eslint-disable-next-line react/no-deprecated
ReactDOM.render(
Expand All @@ -66,6 +69,12 @@ export const choose = (sentence: ISentence): IPerform => {
</Provider>,
document.getElementById('chooseContainer'),
);
if (previewChoice) {
setTimeout(() => {
selectChooseOption(previewChoice);
WebGAL.gameplay.performController.unmountPerform('choose');
}, 0);
}
return {
performName: 'choose',
duration: 1000 * 60 * 60 * 24,
Expand All @@ -80,6 +89,26 @@ export const choose = (sentence: ISentence): IPerform => {
};
};

function getDefaultPreviewChoice(chooseOptions: ChooseOption[], defaultChoose: number | null): ChooseOption | null {
if (!WebGAL.gameplay.isFastPreview || defaultChoose === null) {
return null;
}
const chooseIndex = Math.floor(defaultChoose) - 1;
if (chooseIndex < 0) {
return null;
}
const defaultOption = chooseOptions[chooseIndex];
return defaultOption ?? null;
}
Comment on lines +92 to +102
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The getDefaultPreviewChoice function should verify if the selected default option is actually visible and enabled based on its showCondition and enableCondition. Automatically selecting a hidden or disabled option during preview could lead to inconsistent game states that are impossible to reach during normal gameplay.

Additionally, adding an explicit bounds check for chooseIndex and a check for NaN would make the function more robust.

function getDefaultPreviewChoice(chooseOptions: ChooseOption[], defaultChoose: number | null): ChooseOption | null {
  if (!WebGAL.gameplay.isFastPreview || defaultChoose === null || Number.isNaN(defaultChoose)) {
    return null;
  }
  const chooseIndex = Math.floor(defaultChoose) - 1;
  if (chooseIndex < 0 || chooseIndex >= chooseOptions.length) {
    return null;
  }
  const defaultOption = chooseOptions[chooseIndex];
  // Ensure the option is both visible and enabled before auto-selecting
  if (!whenChecker(defaultOption.showCondition) || !whenChecker(defaultOption.enableCondition)) {
    return null;
  }
  return defaultOption;
}


function selectChooseOption(option: ChooseOption) {
if (option.jumpToScene) {
changeScene(option.jump, option.text);
} else {
jmp(option.jump);
}
}

function Choose(props: { chooseOptions: ChooseOption[] }) {
const font = useFontFamily();
const { playSeEnter, playSeClick } = useSEByWebgalStore();
Expand All @@ -96,11 +125,7 @@ function Choose(props: { chooseOptions: ChooseOption[] }) {
const onClick = enable
? () => {
playSeClick();
if (e.jumpToScene) {
changeScene(e.jump, e.text);
} else {
jmp(e.jump);
}
selectChooseOption(e);
WebGAL.gameplay.performController.unmountPerform('choose');
}
: () => {};
Expand Down
48 changes: 28 additions & 20 deletions packages/webgal/src/Core/util/syncWithEditor/syncWithOrigine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let syncFastTimeout: ReturnType<typeof setTimeout> | undefined;

export const syncWithOrigine = (sceneName: string, sentenceId: number, expermental = false) => {
logger.warn('正在跳转到' + sceneName + ':' + sentenceId);
WebGAL.gameplay.isFastPreview = false;
const dispatch = webgalStore.dispatch;
dispatch(setVisibility({ component: 'showTitle', visibility: false }));
dispatch(setVisibility({ component: 'showMenuPanel', visibility: false }));
Expand All @@ -27,26 +28,32 @@ export const syncWithOrigine = (sceneName: string, sentenceId: number, experment
// 重新获取场景
const sceneUrl: string = assetSetter(sceneName, fileType.scene);
// 场景写入到运行时
sceneFetcher(sceneUrl).then((rawScene) => {
// 等等,先检查一下能不能恢复场景
const lastSameSentence = findLastSameSentence(pastScene, WebGAL.sceneManager.sceneData.currentScene, sentenceId);
const lastRecoverySentenceId = Math.min(sentenceId, lastSameSentence);
const recId = findLastAvailableBacklog(lastRecoverySentenceId, sceneName);
const isCanRec = recId >= 0 && expermental;
resetStage(!isCanRec);
WebGAL.sceneManager.sceneData.currentScene = sceneParser(rawScene, sceneName, sceneUrl);
// 开始快进到指定语句
const currentSceneName = WebGAL.sceneManager.sceneData.currentScene.sceneName;
WebGAL.gameplay.isFast = true;
if (isCanRec) {
jumpFromBacklog(recId, false);
}
if (syncFastTimeout) {
// 之前发生的跳转要清理掉
clearTimeout(syncFastTimeout);
}
syncFast(sentenceId, currentSceneName);
});
sceneFetcher(sceneUrl)
.then((rawScene) => {
// 等等,先检查一下能不能恢复场景
const lastSameSentence = findLastSameSentence(pastScene, WebGAL.sceneManager.sceneData.currentScene, sentenceId);
const lastRecoverySentenceId = Math.min(sentenceId, lastSameSentence);
const recId = findLastAvailableBacklog(lastRecoverySentenceId, sceneName);
const isCanRec = recId >= 0 && expermental;
resetStage(!isCanRec);
WebGAL.sceneManager.sceneData.currentScene = sceneParser(rawScene, sceneName, sceneUrl);
// 开始快进到指定语句
const currentSceneName = WebGAL.sceneManager.sceneData.currentScene.sceneName;
WebGAL.gameplay.isFast = true;
WebGAL.gameplay.isFastPreview = true;
if (isCanRec) {
jumpFromBacklog(recId, false);
}
if (syncFastTimeout) {
// 之前发生的跳转要清理掉
clearTimeout(syncFastTimeout);
}
syncFast(sentenceId, currentSceneName);
})
.catch((e) => {
WebGAL.gameplay.isFastPreview = false;
logger.error('快速预览跳转错误', e);
});
};

export function syncFast(sentenceId: number, currentSceneName: string) {
Expand All @@ -58,6 +65,7 @@ export function syncFast(sentenceId: number, currentSceneName: string) {
syncFastTimeout = setTimeout(() => syncFast(sentenceId, currentSceneName), 2);
} else {
WebGAL.gameplay.isFast = false;
WebGAL.gameplay.isFastPreview = false;
}
}

Expand Down
Loading