feat: callScene supports args - #1011
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b13a39c02
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| sceneUrl: WebGAL.sceneManager.sceneData.currentScene.sceneUrl, | ||
| continueLine: WebGAL.sceneManager.sceneData.currentSentenceId, | ||
| }); | ||
| WebGAL.sceneManager.pushFrame(locals, writeReturnTo); |
There was a problem hiding this comment.
Roll back the frame when scene loading fails
When sceneFetcher rejects—for example because a referenced scene is missing or not a .txt file—pushFrame has already replaced currentLocals, but the catch handler only logs the error. The caller therefore resumes with the callee's arguments as its locals, which can shadow its variables and be persisted in later saves; pop the newly pushed frame in the failure path before unlocking.
Useful? React with 👍 / 👎.
| if (entry.writeReturnTo) { | ||
| setGameVar({ key: entry.writeReturnTo, value: returnValue }); |
There was a problem hiding this comment.
Write returns into an existing caller-local target
When a nested caller already has a local with the writeReturnTo name, popFrame() restores that local and this line writes the result only to the stage variable map. Since variable lookup prioritizes currentLocals, subsequent {name} reads still return the caller's old local value and the returned result is inaccessible; update the restored local when that target exists, falling back to normal setVar storage otherwise.
Useful? React with 👍 / 👎.
| const _Merge = { stage, userData }; // 不要直接合并到一起,防止可能的键冲突 | ||
| if (stage.GameVar.hasOwnProperty(key)) { | ||
| // 查找链:当前帧局部变量 -> 舞台变量 -> 全局变量 | ||
| if (locals.hasOwnProperty(key)) { |
There was a problem hiding this comment.
Check local ownership without invoking user data
Because every argument name is accepted as a local, a call such as callScene:x.txt -hasOwnProperty=value replaces this method on the plain locals object. Any subsequent interpolation or expression lookup then executes the string as a function and throws; use Object.prototype.hasOwnProperty.call(locals, key) or create a null-prototype map so argument names cannot break lookup.
Useful? React with 👍 / 👎.
Deploying webgal-dev with
|
| Latest commit: |
30bce07
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://35d3859b.webgal-dev.pages.dev |
| Branch Preview URL: | https://callscene.webgal-dev.pages.dev |
背景
为
callScene增加参数传递与返回值,使被调用的场景成为真正的子过程。相关 issue: #421用法
-xxx=yyy原样成为被调用场景的局部变量,没有前缀、没有保留字-writeReturnTo=b指定返回值写回调用方的哪个变量,b是普通游戏变量,用法同setVarreturn:expr;立即返回并携带返回值;没有return而自然结束时返回空值设计
引入调用帧的概念:一帧 = 一次
callScene,持有局部变量、返回地址、返回值去向。关键在于帧的归属:
changeScene/choose不是帧操作,它们只是把当前帧正在读的文本换掉,栈和局部变量都不动。所以在被调用的场景里changeScene之后,参数仍然有效,结束时也仍然返回原调用方。这两个命令不支持参数。实现
帧结构:
ISceneEntry增加locals/writeReturnTo,ISceneData增加currentLocals(栈顶之上的当前帧,因为sceneName/sceneUrl/currentSentenceId本来就是摊平存放的当前帧)。场景栈和
currentLocals是同一个栈的两截,全部经由SceneManager.pushFrame()/popFrame()变更,避免两者脱节。场景末尾原本手写的 pop + restore 也收敛到returnFromScene(),与return语句共用一条路径。参数求值:不引入任何表达式引擎。
argsParser本来就会把-a=1解析成数字、-a=true解析成布尔;只有经过{}插值的参数会退化成字符串,在提取参数时按同一套规则还原类型即可。引用变量写{b},与其他所有参数一致。存档:场景栈本来就整体
cloneDeep进存档,多层嵌套的局部变量随帧免费持久化;只有当前帧的currentLocals需要单独搬运(saveGame / backlog / loadGame / jumpFromBacklog 各一行)。新字段全部可选,旧存档三处?? {}兜底,可直接读取。其他:
commandType.return追加在枚举末尾——旧存档里PerformList[].script.command是持久化的数值,插在中间会错位callScene无限递归saveGame/fastSaveGame此前没有lockSceneWrite判定,场景写入期间存档会拿到撕裂状态(栈已变更但场景尚未切换)。加了return之后这会导致读档重复返回,本 PR 一并加上判定注意
commandType有改动,webgal 包的类型检查依赖 parser 的构建产物,需要先构建 parser 再检查 webgal。验证
两个包
tsc --noEmit、eslint、parser 既有测试全部通过。callScene参数解析与return的四种写法(return;/return:5;/return:;/return:{x}+1;)已实测。