From cf5e85995224ffe15c38c665103e7055b447f7b1 Mon Sep 17 00:00:00 2001 From: SimonShiki Date: Tue, 11 Aug 2026 16:11:48 +0800 Subject: [PATCH] :bug: fix(vm): keep obcursed menu dropdown shadow Signed-off-by: SimonShiki --- packages/vm/src/serialization/sb2.js | 1 + .../fixtures/obscured-dropdown-shadow.json | 26 +++++++++++ packages/vm/test/unit/engine_adapter.js | 44 +++++++++++++++++++ packages/vm/test/unit/serialization_sb2.js | 28 ++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 packages/vm/test/fixtures/obscured-dropdown-shadow.json diff --git a/packages/vm/src/serialization/sb2.js b/packages/vm/src/serialization/sb2.js index c6c4c10b..3deefef7 100644 --- a/packages/vm/src/serialization/sb2.js +++ b/packages/vm/src/serialization/sb2.js @@ -952,6 +952,7 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension inputs: {}, // Inputs to this block and the blocks they point to. fields: {}, // Fields on this block and their values. next: null, // Next block. + topLevel: false, // Nested blocks are not script roots. shadow: false, // No shadow blocks in an SB2 by default. children: [] // Store any generated children, flattened in `flatten`. }; diff --git a/packages/vm/test/fixtures/obscured-dropdown-shadow.json b/packages/vm/test/fixtures/obscured-dropdown-shadow.json new file mode 100644 index 00000000..2b990c15 --- /dev/null +++ b/packages/vm/test/fixtures/obscured-dropdown-shadow.json @@ -0,0 +1,26 @@ +{ + "projectVersion": 2, + "objName": "Stage", + "variables": [ + { + "name": "costume name", + "value": "costume1" + } + ], + "lists": [], + "scripts": [ + [ + 80, + 60, + [ + [ + "lookLike:", + ["getVar:", "costume name"] + ] + ] + ] + ], + "costumes": [], + "sounds": [], + "children": [] +} diff --git a/packages/vm/test/unit/engine_adapter.js b/packages/vm/test/unit/engine_adapter.js index ec42613c..84016376 100644 --- a/packages/vm/test/unit/engine_adapter.js +++ b/packages/vm/test/unit/engine_adapter.js @@ -170,6 +170,50 @@ test('create with obscured shadow', t => { t.end(); }); +test('create from state keeps an obscured shadow nested', t => { + const result = adapter({ + json: [{ + id: 'switch-costume', + type: 'looks_switchcostumeto', + inputs: { + COSTUME: { + block: { + id: 'variable-reporter', + type: 'data_variable', + fields: { + VARIABLE: { + id: 'costume-name', + name: 'costume name' + } + } + }, + shadow: { + id: 'costume-shadow', + type: 'looks_costume', + fields: { + COSTUME: 'COSTUME1' + } + } + } + } + }] + }); + + t.equal(result.length, 3); + t.equal(result.filter(block => block.topLevel).length, 1); + const parent = result.find(block => block.id === 'switch-costume'); + const variableReporter = result.find(block => block.id === 'variable-reporter'); + const shadow = result.find(block => block.id === 'costume-shadow'); + t.equal(parent.topLevel, true); + t.equal(parent.inputs.COSTUME.block, variableReporter.id); + t.equal(parent.inputs.COSTUME.shadow, shadow.id); + t.equal(variableReporter.shadow, false); + t.equal(variableReporter.topLevel, false); + t.equal(shadow.shadow, true); + t.equal(shadow.topLevel, false); + t.end(); +}); + test('create variable with entity in name', t => { const result = adapter(events.createvariablewithentity); diff --git a/packages/vm/test/unit/serialization_sb2.js b/packages/vm/test/unit/serialization_sb2.js index 5552b759..e5c07f42 100644 --- a/packages/vm/test/unit/serialization_sb2.js +++ b/packages/vm/test/unit/serialization_sb2.js @@ -4,6 +4,7 @@ import {extractProjectJson} from '../fixtures/readProjectFile.js'; import RenderedTarget from '../../src/sprites/rendered-target'; import Runtime from '../../src/engine/runtime'; import {deserialize} from '../../src/serialization/sb2.js'; +import obscuredDropdownShadow from '../fixtures/obscured-dropdown-shadow.json'; test('spec', t => { t.type(deserialize, 'function'); @@ -100,3 +101,30 @@ test('Ordering', t => { t.end(); }); }); + +test('obscured dropdown shadow keeps the variable reporter as the input block', t => { + const runtime = new Runtime(); + + return deserialize(obscuredDropdownShadow, runtime).then(({targets}) => { + const blocks = targets[0].blocks; + const switchCostume = blocks.getBlock(blocks.getScripts()[0]); + const input = switchCostume.inputs.COSTUME; + const variableReporter = blocks.getBlock(input.block); + const shadow = blocks.getBlock(input.shadow); + + t.equal(switchCostume.opcode, 'looks_switchcostumeto'); + t.equal(variableReporter.opcode, 'data_variable'); + t.equal(variableReporter.shadow, false); + t.equal(variableReporter.topLevel, false); + t.equal(shadow.opcode, 'looks_costume'); + t.equal(shadow.shadow, true); + t.equal(shadow.topLevel, false); + t.ok(input.block !== input.shadow); + + const state = blocks.toState(); + const stateInput = state[0].inputs.COSTUME; + t.equal(stateInput.block.type, 'data_variable'); + t.equal(stateInput.shadow.type, 'looks_costume'); + t.end(); + }); +});