Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlockScriptSerialization: Re-generate block definition for object property blocks #316

Merged
merged 5 commits into from
Nov 20, 2024
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
14 changes: 13 additions & 1 deletion addons/block_code/block_code_node/block_code.gd
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ extends Node

const TxUtils := preload("res://addons/block_code/translation/utils.gd")

@export var block_script: BlockScriptSerialization = null
@export var block_script: BlockScriptSerialization = null:
set = _set_block_script


func _init():
Expand Down Expand Up @@ -37,6 +38,17 @@ func _enter_tree():
block_script = new_block_script


func _set_block_script(value):
if value == null:
# Wipe out the bidirectional link between this block code node and the
# block script
if block_script:
block_script.block_code_node = null
else:
value.block_code_node = self
block_script = value


func _update_parent_script():
if Engine.is_editor_hint():
push_error("Updating the parent script must happen in game.")
Expand Down
55 changes: 41 additions & 14 deletions addons/block_code/code_generation/blocks_catalog.gd
Original file line number Diff line number Diff line change
Expand Up @@ -253,25 +253,52 @@ static func add_custom_blocks(
static func get_variable_block_definitions(variables: Array[VariableDefinition]) -> Array[BlockDefinition]:
var block_definitions: Array[BlockDefinition] = []
for variable: VariableDefinition in variables:
var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type]
var block_def: BlockDefinition

# Getter
var block_def = BlockDefinition.new()
block_def.name = "get_var_%s" % variable.var_name
block_def.category = "Variables"
block_def.type = Types.BlockType.VALUE
block_def.variant_type = variable.var_type
block_def.display_template = variable.var_name
block_def.code_template = variable.var_name
block_def = get_variable_getter_block_definition(variable)
block_definitions.append(block_def)

# Setter
block_def = BlockDefinition.new()
block_def.name = "set_var_%s" % variable.var_name
block_def.category = "Variables"
block_def.type = Types.BlockType.STATEMENT
block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string]
block_def.code_template = "%s = {value}" % [variable.var_name]
block_def = get_variable_setter_block_definition(variable)
block_definitions.append(block_def)

return block_definitions


static func get_variable_getter_block_definition(variable: VariableDefinition) -> BlockDefinition:
var block_def := BlockDefinition.new()

block_def.name = "get_var_%s" % variable.var_name
block_def.category = "Variables"
block_def.type = Types.BlockType.VALUE
block_def.variant_type = variable.var_type
block_def.display_template = variable.var_name
block_def.code_template = variable.var_name

return block_def


static func get_variable_setter_block_definition(variable: VariableDefinition) -> BlockDefinition:
var type_string: String = Types.VARIANT_TYPE_TO_STRING[variable.var_type]
var block_def := BlockDefinition.new()

block_def.name = "set_var_%s" % variable.var_name
block_def.category = "Variables"
block_def.type = Types.BlockType.STATEMENT
block_def.display_template = "Set %s to {value: %s}" % [variable.var_name, type_string]
block_def.code_template = "%s = {value}" % variable.var_name

return block_def


static func get_property_getter_block_definition(variable: VariableDefinition) -> BlockDefinition:
var block_def := get_variable_getter_block_definition(variable)
block_def.description = "The %s property" % variable.var_name
return block_def
Copy link
Member

Choose a reason for hiding this comment

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

It's a bit unfortunate that these property blocks will be in the Variables section, but I can't think of a better place at the moment.



static func get_property_setter_block_definition(variable: VariableDefinition) -> BlockDefinition:
var block_def := get_variable_setter_block_definition(variable)
block_def.description = "Set the %s property" % variable.var_name
return block_def
69 changes: 59 additions & 10 deletions addons/block_code/serialization/block_script_serialization.gd
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const SCENE_PER_TYPE = {
@export var generated_script: String
@export var version: int

var block_code_node: BlockCode
Copy link
Member

Choose a reason for hiding this comment

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

A potential future improvement here would be to cache the parent properties when set so that _get_parent_node_property_info didn't have to iterate the entire property list every time. I don't think that's worth it right now, though.


var _available_blocks: Array[BlockDefinition]
var _categories: Array[BlockCategory]

Expand Down Expand Up @@ -81,22 +83,26 @@ func instantiate_block_by_name(block_name: String) -> Block:

func get_block_definition(block_name: String) -> BlockDefinition:
var split := block_name.split(":", true, 1)
var block_definition: BlockDefinition

if len(split) > 1:
return _get_parameter_block_definition(split[0], split[1])

var block_definition = _get_base_block_definition(block_name)
block_definition = _get_base_block_definition(block_name)
if block_definition != null:
return block_definition

if block_definition == null:
# FIXME: This is a workaround for old-style output block references.
# These were generated ahead of time using a block name that has
# a "_" before the parameter name. Now, these parameter blocks
# are generated on demand for any block name containing a ":".
# Please remove this fallback when it is no longer necessary.
split = block_name.rsplit("_", true, 1)
return _get_parameter_block_definition(split[0], split[1])
block_definition = _get_obj_property_block_definition(block_name)
if block_definition != null:
return block_definition

return block_definition
# FIXME: This is a workaround for old-style output block references.
# These were generated ahead of time using a block name that has
# a "_" before the parameter name. Now, these parameter blocks
# are generated on demand for any block name containing a ":".
# Please remove this fallback when it is no longer necessary.
split = block_name.rsplit("_", true, 1)
return _get_parameter_block_definition(split[0], split[1])


func _get_base_block_definition(block_name: String) -> BlockDefinition:
Expand Down Expand Up @@ -133,6 +139,49 @@ func _get_parameter_block_definition(block_name: String, parameter_name: String)
return block_definition


func _get_obj_property_block_definition(block_name: String) -> BlockDefinition:
var block_definition: BlockDefinition
var variable: VariableDefinition
var property_name: String
var is_getter = true

if block_name.begins_with("get_var_"):
property_name = block_name.get_slice("get_var_", 1)
elif block_name.begins_with("set_var_"):
property_name = block_name.get_slice("set_var_", 1)
is_getter = false
else:
return null

# Getter block needs the property's variant type information by visiting the
# block_code_node's parent node because the type is not saved as a key of
# the resource in the scene file
var property_info := _get_parent_node_property_info(property_name)
if not property_info.has("type"):
return null

if is_getter:
variable = VariableDefinition.new(property_name, property_info["type"])
block_definition = BlocksCatalog.get_property_getter_block_definition(variable)
else:
variable = VariableDefinition.new(property_name, property_info["type"])
block_definition = BlocksCatalog.get_property_setter_block_definition(variable)

return block_definition


func _get_parent_node_property_info(property_name: String) -> Dictionary:
if not block_code_node:
return {}

var properties := block_code_node.get_parent().get_property_list()
for property in properties:
if property["name"] == property_name:
return property

return {}


func _update_block_definitions():
_available_blocks.clear()
_available_blocks.append_array(_get_inherited_block_definitions())
Expand Down
44 changes: 12 additions & 32 deletions addons/block_code/ui/block_canvas/block_canvas.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ extends MarginContainer

const ASTList = preload("res://addons/block_code/code_generation/ast_list.gd")
const BlockAST = preload("res://addons/block_code/code_generation/block_ast.gd")
const BlocksCatalog = preload("res://addons/block_code/code_generation/blocks_catalog.gd")
const BlockCodePlugin = preload("res://addons/block_code/block_code_plugin.gd")
const BlockDefinition = preload("res://addons/block_code/code_generation/block_definition.gd")
const BlockTreeUtil = preload("res://addons/block_code/ui/block_tree_util.gd")
const DragManager = preload("res://addons/block_code/drag_manager/drag_manager.gd")
const ScriptGenerator = preload("res://addons/block_code/code_generation/script_generator.gd")
const Types = preload("res://addons/block_code/types/types.gd")
const Util = preload("res://addons/block_code/ui/util.gd")
const VariableDefinition = preload("res://addons/block_code/code_generation/variable_definition.gd")

const EXTEND_MARGIN: float = 800
const BLOCK_AUTO_PLACE_MARGIN: Vector2 = Vector2(25, 8)
Expand Down Expand Up @@ -79,8 +81,10 @@ func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
if typeof(data) != TYPE_DICTIONARY:
return false

# Allow dropping property block
# Allow dropping property block of the block code node's parent node
if data.get("type", "") == "obj_property":
if data["object"] != _context.parent_node:
return false
return true

var nodes: Array = data.get("nodes", [])
Expand Down Expand Up @@ -124,44 +128,20 @@ func _drop_node(at_position: Vector2, data: Variant) -> void:


func _drop_obj_property(at_position: Vector2, data: Variant) -> void:
var object_name = str(data["object"]).get_slice(":", 0)
var property_name = data["property"]
var property_value = data["value"]
var is_getter = !_modifier_ctrl

# Prepare a Variable block to set / get the property's value according to
# the modifier KEY_CTRL pressing.
var variable := VariableDefinition.new(property_name, typeof(property_value))
var block_definition: BlockDefinition
var property_type = typeof(property_value)
if _modifier_ctrl:
var type_string: String = Types.VARIANT_TYPE_TO_STRING[property_type]
block_definition = (
BlockDefinition
. new(
&"%s_set_%s" % [object_name, property_name],
object_name,
"Set the %s property" % property_name,
"Variables",
Types.BlockType.STATEMENT,
property_type,
"set %s to {value: %s}" % [property_name.capitalize().to_lower(), type_string],
"%s = {value}" % property_name,
{"value": property_value},
)
)

if is_getter:
block_definition = BlocksCatalog.get_property_getter_block_definition(variable)
else:
block_definition = (
BlockDefinition
. new(
&"%s_get_%s" % [object_name, property_name],
object_name,
"The %s property" % property_name,
"Variables",
Types.BlockType.VALUE,
property_type,
"%s" % property_name.capitalize().to_lower(),
"%s" % property_name,
)
)
block_definition = BlocksCatalog.get_property_setter_block_definition(variable)
block_definition.defaults = {"value": property_value}

var block = _context.block_script.instantiate_block(block_definition)
add_block(block, at_position)
Expand Down