Update utils/block_relation_builder.py
Browse files- utils/block_relation_builder.py +137 -85
utils/block_relation_builder.py
CHANGED
|
@@ -2508,8 +2508,8 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
|
| 2508 |
|
| 2509 |
# Initialize dictionaries to store and reuse generated unique IDs
|
| 2510 |
# This prevents creating multiple unique IDs for the same variable/broadcast across different blocks
|
| 2511 |
-
variable_id_map = defaultdict(lambda: generate_secure_token())
|
| 2512 |
-
broadcast_id_map = defaultdict(lambda: generate_secure_token())
|
| 2513 |
|
| 2514 |
# Define the mapping for input field names to their required integer types for shadows
|
| 2515 |
input_type_mapping = {
|
|
@@ -2534,6 +2534,31 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
|
| 2534 |
"VALUE_STRING": 10 # Used internally for VALUE when it should be type 10
|
| 2535 |
}
|
| 2536 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2537 |
for block_id, gen_block_data in generated_output_json.items():
|
| 2538 |
processed_block = {}
|
| 2539 |
all_gen_block_data = all_generated_blocks.get(block_id, {})
|
|
@@ -2549,13 +2574,14 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
|
| 2549 |
if "mutation" in all_gen_block_data:
|
| 2550 |
processed_block["mutation"] = all_gen_block_data["mutation"]
|
| 2551 |
|
|
|
|
|
|
|
| 2552 |
# Process inputs
|
| 2553 |
if "inputs" in all_gen_block_data:
|
| 2554 |
for input_name, input_data in all_gen_block_data["inputs"].items():
|
|
|
|
| 2555 |
if input_name == "BROADCAST_INPUT":
|
| 2556 |
-
# Special handling for BROADCAST_INPUT (type 11)
|
| 2557 |
if isinstance(input_data, dict) and input_data.get("kind") == "value":
|
| 2558 |
-
# This is the new logic to handle a direct value for broadcast
|
| 2559 |
menu_option = input_data.get("value", "message1")
|
| 2560 |
broadcast_id = broadcast_id_map[menu_option]
|
| 2561 |
processed_block["inputs"][input_name] = [
|
|
@@ -2567,23 +2593,26 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
|
| 2567 |
]
|
| 2568 |
]
|
| 2569 |
elif isinstance(input_data, list) and len(input_data) == 2 and isinstance(input_data[1], list) and len(input_data[1]) == 3 and input_data[1][0] == 11:
|
| 2570 |
-
# Preserve
|
| 2571 |
processed_block["inputs"][input_name] = input_data
|
| 2572 |
else:
|
| 2573 |
-
# Fallback
|
| 2574 |
-
|
| 2575 |
-
|
| 2576 |
-
|
| 2577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2578 |
# Determine the correct shadow type based on the input_name and opcode
|
| 2579 |
-
shadow_type = input_type_mapping.get(input_name, 4)
|
| 2580 |
-
|
| 2581 |
-
# Specific
|
| 2582 |
-
if input_name == "NUM" and
|
| 2583 |
shadow_type = 7
|
| 2584 |
-
|
| 2585 |
-
# Specific override for "VALUE" in "data_setvariableto"
|
| 2586 |
-
if input_name == "VALUE" and processed_block["opcode"] == "data_setvariableto":
|
| 2587 |
shadow_type = 10
|
| 2588 |
|
| 2589 |
processed_block["inputs"][input_name] = [
|
|
@@ -2593,119 +2622,142 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
|
| 2593 |
str(input_data.get("value", ""))
|
| 2594 |
]
|
| 2595 |
]
|
| 2596 |
-
|
| 2597 |
-
|
|
|
|
|
|
|
| 2598 |
nested_block_id = input_data.get("block", "")
|
| 2599 |
-
|
| 2600 |
if nested_block_id in all_generated_blocks:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2601 |
nested_block_opcode = all_generated_blocks[nested_block_id].get("op_code")
|
| 2602 |
-
|
| 2603 |
if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
|
| 2604 |
-
# These are control flow blocks, should always be type 2
|
| 2605 |
processed_block["inputs"][input_name] = [2, nested_block_id]
|
| 2606 |
elif nested_block_opcode in ["data_variable", "data_listcontents"]:
|
| 2607 |
-
#
|
| 2608 |
variable_name = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[0]
|
| 2609 |
variable_id = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[1]
|
| 2610 |
if not variable_id:
|
| 2611 |
variable_id = variable_id_map[variable_name]
|
| 2612 |
processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
|
| 2613 |
elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
|
| 2614 |
-
#
|
| 2615 |
-
# and should be type 1, referring to a shadow block.
|
| 2616 |
processed_block["inputs"][input_name] = [1, nested_block_id]
|
| 2617 |
else:
|
| 2618 |
-
#
|
| 2619 |
-
|
| 2620 |
-
|
| 2621 |
-
processed_block["inputs"][input_name] = [3, nested_block_id, [shadow_type_for_nested, ""]] # Empty shadow value
|
| 2622 |
else:
|
| 2623 |
-
#
|
| 2624 |
-
|
| 2625 |
-
|
| 2626 |
-
|
| 2627 |
-
|
| 2628 |
-
#
|
| 2629 |
-
menu_option = input_data.get("option", "")
|
| 2630 |
-
# These should typically be type 1 referring to a shadow block, or a direct value if no shadow block
|
| 2631 |
-
# For simplicity, we'll try to get the block ID if it's a menu block.
|
| 2632 |
if "block" in input_data:
|
| 2633 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2634 |
else:
|
| 2635 |
-
#
|
| 2636 |
-
|
| 2637 |
-
processed_block["inputs"][input_name] = [1, [10, menu_option]]
|
| 2638 |
-
|
| 2639 |
-
|
| 2640 |
-
elif isinstance(input_data, list):
|
| 2641 |
-
# This branch handles existing list structures from all_gen_block_data (or generated_output_json if no all_gen_block_data)
|
| 2642 |
-
# It needs to decide if it's a direct value, a block reference, or a special type (11, 12)
|
| 2643 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2644 |
if len(input_data) == 2:
|
| 2645 |
first_val = input_data[0]
|
| 2646 |
second_val = input_data[1]
|
| 2647 |
|
|
|
|
| 2648 |
if isinstance(second_val, str) and second_val in all_generated_blocks:
|
| 2649 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2650 |
nested_block_opcode = all_generated_blocks[second_val].get("op_code")
|
| 2651 |
|
| 2652 |
if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
|
| 2653 |
-
# These are control flow blocks, should always be type 2
|
| 2654 |
processed_block["inputs"][input_name] = [2, second_val]
|
| 2655 |
elif nested_block_opcode in ["data_variable", "data_listcontents"]:
|
| 2656 |
-
# It's a variable/list reporter, should be type 12 inside type 1
|
| 2657 |
variable_name = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[0]
|
| 2658 |
variable_id = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[1]
|
| 2659 |
if not variable_id:
|
| 2660 |
variable_id = variable_id_map[variable_name]
|
| 2661 |
processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
|
| 2662 |
elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
|
| 2663 |
-
# These are menu/dropdown inputs that refer to another block/menu,
|
| 2664 |
-
# and should be type 1, referring to a shadow block.
|
| 2665 |
processed_block["inputs"][input_name] = [1, second_val]
|
| 2666 |
else:
|
| 2667 |
-
# For other reporter blocks (like motion_xposition, operators),
|
| 2668 |
-
# they should be type 3 when nested. Correct the first_val if it's not 3.
|
| 2669 |
shadow_type_for_nested = input_type_mapping.get(input_name, 10)
|
|
|
|
| 2670 |
processed_block["inputs"][input_name] = [3, second_val, [shadow_type_for_nested, ""]]
|
|
|
|
| 2671 |
|
| 2672 |
-
|
| 2673 |
-
|
| 2674 |
-
shadow_type = input_type_mapping.get(input_name, 4)
|
| 2675 |
-
if input_name == "NUM" and
|
| 2676 |
shadow_type = 7
|
| 2677 |
-
if input_name == "VALUE" and
|
| 2678 |
shadow_type = 10
|
| 2679 |
processed_block["inputs"][input_name] = [1, [shadow_type, str(second_val)]]
|
| 2680 |
-
|
| 2681 |
-
|
| 2682 |
-
|
|
|
|
|
|
|
| 2683 |
|
| 2684 |
elif len(input_data) == 3 and isinstance(input_data[1], str) and isinstance(input_data[2], list):
|
| 2685 |
-
#
|
| 2686 |
-
#
|
| 2687 |
-
|
| 2688 |
-
if
|
| 2689 |
-
#
|
| 2690 |
-
|
| 2691 |
-
|
| 2692 |
-
|
| 2693 |
-
|
| 2694 |
-
|
| 2695 |
-
|
| 2696 |
-
|
| 2697 |
-
|
| 2698 |
-
|
| 2699 |
-
|
| 2700 |
-
|
| 2701 |
-
|
| 2702 |
-
|
| 2703 |
-
|
| 2704 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2705 |
else:
|
| 2706 |
-
#
|
| 2707 |
processed_block["inputs"][input_name] = input_data
|
|
|
|
| 2708 |
|
|
|
|
|
|
|
| 2709 |
|
| 2710 |
# Process fields
|
| 2711 |
if "fields" in all_gen_block_data:
|
|
@@ -2751,7 +2803,7 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
|
| 2751 |
|
| 2752 |
# Check if TOUCHINGOBJECTMENU input exists and is a block reference
|
| 2753 |
referenced_block_id = None
|
| 2754 |
-
if "TOUCHINGOBJECTMENU" in all_gen_block_data
|
| 2755 |
input_val = all_gen_block_data["inputs"]["TOUCHINGOBJECTMENU"]
|
| 2756 |
if isinstance(input_val, list) and len(input_val) > 1 and isinstance(input_val[1], str):
|
| 2757 |
referenced_block_id = input_val[1]
|
|
@@ -2769,7 +2821,7 @@ def process_scratch_blocks(all_generated_blocks, generated_output_json):
|
|
| 2769 |
else:
|
| 2770 |
processed_block["fields"][field_name] = field_value
|
| 2771 |
|
| 2772 |
-
# Remove unwanted keys from the processed block
|
| 2773 |
keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"]
|
| 2774 |
for key in keys_to_remove:
|
| 2775 |
if key in processed_block:
|
|
|
|
| 2508 |
|
| 2509 |
# Initialize dictionaries to store and reuse generated unique IDs
|
| 2510 |
# This prevents creating multiple unique IDs for the same variable/broadcast across different blocks
|
| 2511 |
+
variable_id_map = defaultdict(lambda: generate_secure_token(20))
|
| 2512 |
+
broadcast_id_map = defaultdict(lambda: generate_secure_token(20))
|
| 2513 |
|
| 2514 |
# Define the mapping for input field names to their required integer types for shadows
|
| 2515 |
input_type_mapping = {
|
|
|
|
| 2534 |
"VALUE_STRING": 10 # Used internally for VALUE when it should be type 10
|
| 2535 |
}
|
| 2536 |
|
| 2537 |
+
# Explicit mapping of opcodes -> expected menu inputs that MUST be simple menu/shadow links ([1, block_id])
|
| 2538 |
+
explicit_menu_links = {
|
| 2539 |
+
"motion_goto": [("TO", "motion_goto_menu")],
|
| 2540 |
+
"motion_glideto": [("TO", "motion_glideto_menu")],
|
| 2541 |
+
"motion_pointtowards": [("TOWARDS", "motion_pointtowards_menu")],
|
| 2542 |
+
"sensing_keypressed": [("KEY_OPTION", "sensing_keyoptions")],
|
| 2543 |
+
"sensing_of": [("OBJECT", "sensing_of_object_menu")],
|
| 2544 |
+
"sensing_touchingobject": [("TOUCHINGOBJECTMENU", "sensing_touchingobjectmenu")],
|
| 2545 |
+
"control_create_clone_of": [("CLONE_OPTION", "control_create_clone_of_menu")],
|
| 2546 |
+
"sound_play": [("SOUND_MENU", "sound_sounds_menu")],
|
| 2547 |
+
"sound_playuntildone": [("SOUND_MENU", "sound_sounds_menu")],
|
| 2548 |
+
"looks_switchcostumeto": [("COSTUME", "looks_costume")],
|
| 2549 |
+
"looks_switchbackdropto": [("BACKDROP", "looks_backdrops")],
|
| 2550 |
+
}
|
| 2551 |
+
|
| 2552 |
+
# helper to check if this opcode+input_name is an explicit menu link
|
| 2553 |
+
def is_explicit_menu_opcode_input(opcode, input_name):
|
| 2554 |
+
if not opcode:
|
| 2555 |
+
return False
|
| 2556 |
+
pairs = explicit_menu_links.get(opcode, [])
|
| 2557 |
+
for inp, _menu in pairs:
|
| 2558 |
+
if inp == input_name:
|
| 2559 |
+
return True
|
| 2560 |
+
return False
|
| 2561 |
+
|
| 2562 |
for block_id, gen_block_data in generated_output_json.items():
|
| 2563 |
processed_block = {}
|
| 2564 |
all_gen_block_data = all_generated_blocks.get(block_id, {})
|
|
|
|
| 2574 |
if "mutation" in all_gen_block_data:
|
| 2575 |
processed_block["mutation"] = all_gen_block_data["mutation"]
|
| 2576 |
|
| 2577 |
+
opcode = processed_block["opcode"] # convenience
|
| 2578 |
+
|
| 2579 |
# Process inputs
|
| 2580 |
if "inputs" in all_gen_block_data:
|
| 2581 |
for input_name, input_data in all_gen_block_data["inputs"].items():
|
| 2582 |
+
# --- BROADCAST INPUT special handling (type 11) ---
|
| 2583 |
if input_name == "BROADCAST_INPUT":
|
|
|
|
| 2584 |
if isinstance(input_data, dict) and input_data.get("kind") == "value":
|
|
|
|
| 2585 |
menu_option = input_data.get("value", "message1")
|
| 2586 |
broadcast_id = broadcast_id_map[menu_option]
|
| 2587 |
processed_block["inputs"][input_name] = [
|
|
|
|
| 2593 |
]
|
| 2594 |
]
|
| 2595 |
elif isinstance(input_data, list) and len(input_data) == 2 and isinstance(input_data[1], list) and len(input_data[1]) == 3 and input_data[1][0] == 11:
|
| 2596 |
+
# Preserve well-formed existing type 11 structure
|
| 2597 |
processed_block["inputs"][input_name] = input_data
|
| 2598 |
else:
|
| 2599 |
+
# Fallback: try original generated_output_json value if present, else synthesize
|
| 2600 |
+
fallback = gen_block_data.get("inputs", {}).get(input_name,
|
| 2601 |
+
[1, [11, "message1", generate_secure_token(20)]])
|
| 2602 |
+
processed_block["inputs"][input_name] = fallback
|
| 2603 |
+
continue
|
| 2604 |
+
|
| 2605 |
+
# --- Input described as a dict (value, block, menu) ---
|
| 2606 |
+
if isinstance(input_data, dict):
|
| 2607 |
+
kind = input_data.get("kind")
|
| 2608 |
+
if kind == "value":
|
| 2609 |
# Determine the correct shadow type based on the input_name and opcode
|
| 2610 |
+
shadow_type = input_type_mapping.get(input_name, 4) # default 4
|
| 2611 |
+
|
| 2612 |
+
# Specific overrides
|
| 2613 |
+
if input_name == "NUM" and opcode == "looks_goforwardbackwardlayers":
|
| 2614 |
shadow_type = 7
|
| 2615 |
+
if input_name == "VALUE" and opcode == "data_setvariableto":
|
|
|
|
|
|
|
| 2616 |
shadow_type = 10
|
| 2617 |
|
| 2618 |
processed_block["inputs"][input_name] = [
|
|
|
|
| 2622 |
str(input_data.get("value", ""))
|
| 2623 |
]
|
| 2624 |
]
|
| 2625 |
+
continue
|
| 2626 |
+
|
| 2627 |
+
if kind == "block":
|
| 2628 |
+
# nested block reference via dict
|
| 2629 |
nested_block_id = input_data.get("block", "")
|
|
|
|
| 2630 |
if nested_block_id in all_generated_blocks:
|
| 2631 |
+
# If this opcode+input_name is explicitly a menu -> force [1, nested_block_id]
|
| 2632 |
+
if is_explicit_menu_opcode_input(opcode, input_name):
|
| 2633 |
+
processed_block["inputs"][input_name] = [1, nested_block_id]
|
| 2634 |
+
continue
|
| 2635 |
+
|
| 2636 |
nested_block_opcode = all_generated_blocks[nested_block_id].get("op_code")
|
| 2637 |
+
|
| 2638 |
if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
|
|
|
|
| 2639 |
processed_block["inputs"][input_name] = [2, nested_block_id]
|
| 2640 |
elif nested_block_opcode in ["data_variable", "data_listcontents"]:
|
| 2641 |
+
# variable/list reporter inside a reporter -> [1, [12, name, id]]
|
| 2642 |
variable_name = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[0]
|
| 2643 |
variable_id = all_generated_blocks[nested_block_id].get("fields", {}).get("VARIABLE", ["", ""])[1]
|
| 2644 |
if not variable_id:
|
| 2645 |
variable_id = variable_id_map[variable_name]
|
| 2646 |
processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
|
| 2647 |
elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
|
| 2648 |
+
# menu/dropdown inputs that refer to another block/menu -> type 1, refer to block id
|
|
|
|
| 2649 |
processed_block["inputs"][input_name] = [1, nested_block_id]
|
| 2650 |
else:
|
| 2651 |
+
# other reporter blocks (like motion_xposition, operators) -> nested type 3
|
| 2652 |
+
shadow_type_for_nested = input_type_mapping.get(input_name, 10)
|
| 2653 |
+
processed_block["inputs"][input_name] = [3, nested_block_id, [shadow_type_for_nested, ""]]
|
|
|
|
| 2654 |
else:
|
| 2655 |
+
# referenced block missing: fallback to original input format if available, else a safe fallback
|
| 2656 |
+
processed_block["inputs"][input_name] = gen_block_data.get("inputs", {}).get(input_name, [3, nested_block_id, [10, ""]])
|
| 2657 |
+
continue
|
| 2658 |
+
|
| 2659 |
+
if kind == "menu":
|
| 2660 |
+
# menu kind: either references a block or a direct value
|
|
|
|
|
|
|
|
|
|
| 2661 |
if "block" in input_data:
|
| 2662 |
+
# If explicit menu, ensure we store as [1, block_id]
|
| 2663 |
+
if is_explicit_menu_opcode_input(opcode, input_name):
|
| 2664 |
+
processed_block["inputs"][input_name] = [1, input_data["block"]]
|
| 2665 |
+
else:
|
| 2666 |
+
processed_block["inputs"][input_name] = [1, input_data["block"]]
|
| 2667 |
else:
|
| 2668 |
+
# direct value in menu -> type 1 with a string shadow (type 10)
|
| 2669 |
+
menu_option = input_data.get("option", "")
|
| 2670 |
+
processed_block["inputs"][input_name] = [1, [10, menu_option]]
|
| 2671 |
+
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2672 |
|
| 2673 |
+
# --- Input is a list already (various formats) ---
|
| 2674 |
+
if isinstance(input_data, list):
|
| 2675 |
+
# Common shapes:
|
| 2676 |
+
# [1, <value shadow>], [1, <block_id>], [3, <block_id>, [shadow_type, value]], etc.
|
| 2677 |
if len(input_data) == 2:
|
| 2678 |
first_val = input_data[0]
|
| 2679 |
second_val = input_data[1]
|
| 2680 |
|
| 2681 |
+
# If the second element is a block id that exists
|
| 2682 |
if isinstance(second_val, str) and second_val in all_generated_blocks:
|
| 2683 |
+
# If explicit menu mapping -> force [1, block_id]
|
| 2684 |
+
if is_explicit_menu_opcode_input(opcode, input_name):
|
| 2685 |
+
processed_block["inputs"][input_name] = [1, second_val]
|
| 2686 |
+
continue
|
| 2687 |
+
|
| 2688 |
nested_block_opcode = all_generated_blocks[second_val].get("op_code")
|
| 2689 |
|
| 2690 |
if input_name in ["SUBSTACK", "CONDITION", "SUBSTACK2"]:
|
|
|
|
| 2691 |
processed_block["inputs"][input_name] = [2, second_val]
|
| 2692 |
elif nested_block_opcode in ["data_variable", "data_listcontents"]:
|
|
|
|
| 2693 |
variable_name = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[0]
|
| 2694 |
variable_id = all_generated_blocks[second_val].get("fields", {}).get("VARIABLE", ["", ""])[1]
|
| 2695 |
if not variable_id:
|
| 2696 |
variable_id = variable_id_map[variable_name]
|
| 2697 |
processed_block["inputs"][input_name] = [1, [12, variable_name, variable_id]]
|
| 2698 |
elif input_name in ["TOUCHINGOBJECTMENU", "TO", "TOWARDS", "CLONE_OPTION", "SOUND_MENU", "KEY_OPTION", "OBJECT"]:
|
|
|
|
|
|
|
| 2699 |
processed_block["inputs"][input_name] = [1, second_val]
|
| 2700 |
else:
|
|
|
|
|
|
|
| 2701 |
shadow_type_for_nested = input_type_mapping.get(input_name, 10)
|
| 2702 |
+
# Ensure nested reporters use type 3
|
| 2703 |
processed_block["inputs"][input_name] = [3, second_val, [shadow_type_for_nested, ""]]
|
| 2704 |
+
continue
|
| 2705 |
|
| 2706 |
+
# If the second element is a string literal value -> wrap with appropriate shadow type
|
| 2707 |
+
if isinstance(second_val, str):
|
| 2708 |
+
shadow_type = input_type_mapping.get(input_name, 4) # default 4
|
| 2709 |
+
if input_name == "NUM" and opcode == "looks_goforwardbackwardlayers":
|
| 2710 |
shadow_type = 7
|
| 2711 |
+
if input_name == "VALUE" and opcode == "data_setvariableto":
|
| 2712 |
shadow_type = 10
|
| 2713 |
processed_block["inputs"][input_name] = [1, [shadow_type, str(second_val)]]
|
| 2714 |
+
continue
|
| 2715 |
+
|
| 2716 |
+
# fallback: preserve the input as-is
|
| 2717 |
+
processed_block["inputs"][input_name] = input_data
|
| 2718 |
+
continue
|
| 2719 |
|
| 2720 |
elif len(input_data) == 3 and isinstance(input_data[1], str) and isinstance(input_data[2], list):
|
| 2721 |
+
# case: [3, "block_id", [shadow_type, shadow_value]] or
|
| 2722 |
+
# [1, "block_id", [shadow_type, shadow_value]] (rare)
|
| 2723 |
+
block_ref = input_data[1]
|
| 2724 |
+
if block_ref in all_generated_blocks:
|
| 2725 |
+
# If explicit menu mapping -> convert to [1, block_id]
|
| 2726 |
+
if is_explicit_menu_opcode_input(opcode, input_name):
|
| 2727 |
+
processed_block["inputs"][input_name] = [1, block_ref]
|
| 2728 |
+
continue
|
| 2729 |
+
|
| 2730 |
+
# keep already well-formed nested reporter [3, ...]
|
| 2731 |
+
if input_data[0] == 3:
|
| 2732 |
+
processed_block["inputs"][input_name] = input_data
|
| 2733 |
+
continue
|
| 2734 |
+
|
| 2735 |
+
# If it's a type 1 with a type shadow like [1, [11,...]] or [1, [12,...]] -> preserve
|
| 2736 |
+
if input_data[0] == 1 and isinstance(input_data[1], list) and input_data[1][0] in [11, 12]:
|
| 2737 |
+
processed_block["inputs"][input_name] = input_data
|
| 2738 |
+
continue
|
| 2739 |
+
|
| 2740 |
+
# if it's [1, [shadow_type, value]] then re-evaluate shadow type
|
| 2741 |
+
if input_data[0] == 1 and isinstance(input_data[1], list) and len(input_data[1]) == 2:
|
| 2742 |
+
shadow_type = input_type_mapping.get(input_name, 4)
|
| 2743 |
+
if input_name == "NUM" and opcode == "looks_goforwardbackwardlayers":
|
| 2744 |
+
shadow_type = 7
|
| 2745 |
+
if input_name == "VALUE" and opcode == "data_setvariableto":
|
| 2746 |
+
shadow_type = 10
|
| 2747 |
+
processed_block["inputs"][input_name] = [input_data[0], [shadow_type, str(input_data[1][1])]]
|
| 2748 |
+
continue
|
| 2749 |
+
|
| 2750 |
+
# fallback: preserve the list as-is
|
| 2751 |
+
processed_block["inputs"][input_name] = input_data
|
| 2752 |
+
continue
|
| 2753 |
+
|
| 2754 |
else:
|
| 2755 |
+
# other unexpected list shapes -> preserve for now
|
| 2756 |
processed_block["inputs"][input_name] = input_data
|
| 2757 |
+
continue
|
| 2758 |
|
| 2759 |
+
# If input_data didn't match any of the above -> try to fallback to original generated value if available
|
| 2760 |
+
processed_block["inputs"][input_name] = gen_block_data.get("inputs", {}).get(input_name, input_data)
|
| 2761 |
|
| 2762 |
# Process fields
|
| 2763 |
if "fields" in all_gen_block_data:
|
|
|
|
| 2803 |
|
| 2804 |
# Check if TOUCHINGOBJECTMENU input exists and is a block reference
|
| 2805 |
referenced_block_id = None
|
| 2806 |
+
if "TOUCHINGOBJECTMENU" in all_gen_block_data.get("inputs", {}):
|
| 2807 |
input_val = all_gen_block_data["inputs"]["TOUCHINGOBJECTMENU"]
|
| 2808 |
if isinstance(input_val, list) and len(input_val) > 1 and isinstance(input_val[1], str):
|
| 2809 |
referenced_block_id = input_val[1]
|
|
|
|
| 2821 |
else:
|
| 2822 |
processed_block["fields"][field_name] = field_value
|
| 2823 |
|
| 2824 |
+
# Remove unwanted keys from the processed block (if somehow present)
|
| 2825 |
keys_to_remove = ["functionality", "block_shape", "id", "block_name", "block_type"]
|
| 2826 |
for key in keys_to_remove:
|
| 2827 |
if key in processed_block:
|