Files
2025-08-01 16:06:32 -07:00

288 lines
7.9 KiB
GDScript

@tool
class_name BaseGraphNode
extends GraphNode
# Base class for all cutscene graph nodes
# Signals
signal node_selected2(node)
signal node_deleted(node)
signal parameter_changed(node, parameter_name, new_value)
# Node states
enum NodeState {
IDLE,
ACTIVE,
COMPLETED,
ERROR,
PAUSED
}
# Properties
var node_type: String = "base"
var node_id: String
var action_parameters: Dictionary = {} # Stores parameter values
var current_state: int = NodeState.IDLE
var error_message: String = ""
var property_editors: Dictionary = {} # Map of property names to editors
# Visual feedback elements
var state_border: ColorRect
var overlay_icon: TextureRect
var checkmark_texture: Texture2D
var error_texture: Texture2D
var pause_texture: Texture2D
# Called when the node is ready
func _ready() -> void:
# Set up common node properties
connect("dragged", _on_node_dragged)
connect("selected", _on_node_selected)
# Add close butto
# Set up visual feedback elements
_setup_visual_feedback()
# Add parameter fields based on node type
_setup_parameter_fields()
# Set up visual feedback elements
func _setup_visual_feedback() -> void:
# Create state border
state_border = ColorRect.new()
state_border.name = "StateBorder"
state_border.anchor_right = 1
state_border.anchor_bottom = 1
state_border.margin_left = -2
state_border.margin_top = -2
state_border.margin_right = 2
state_border.margin_bottom = 2
state_border.color = Color(0, 0, 0, 0) # Transparent by default
state_border.z_index = -1 # Behind the node
add_child(state_border)
# Create overlay icon container
var overlay_container = CenterContainer.new()
overlay_container.name = "OverlayContainer"
overlay_container.anchor_right = 1
overlay_container.anchor_bottom = 1
overlay_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(overlay_container)
# Create overlay icon
overlay_icon = TextureRect.new()
overlay_icon.name = "OverlayIcon"
overlay_icon.expand = true
overlay_icon.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
overlay_icon.size_flags_vertical = Control.SIZE_SHRINK_CENTER
overlay_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
overlay_container.add_child(overlay_icon)
# Load textures (these would be actual textures in a real implementation)
# checkmark_texture = preload("res://addons/cutscene_editor/icons/checkmark.png")
# error_texture = preload("res://addons/cutscene_editor/icons/error.png")
# pause_texture = preload("res://addons/cutscene_editor/icons/pause.png")
# Set up parameter fields based on node type
func _setup_parameter_fields() -> void:
# This method should be overridden by subclasses
pass
# Set node state and update visual feedback
func set_state(new_state: int, error_msg: String = "") -> void:
current_state = new_state
error_message = error_msg
_update_visual_feedback()
# Update visual feedback based on current state
func _update_visual_feedback() -> void:
match current_state:
NodeState.IDLE:
_set_idle_state()
NodeState.ACTIVE:
_set_active_state()
NodeState.COMPLETED:
_set_completed_state()
NodeState.ERROR:
_set_error_state()
NodeState.PAUSED:
_set_paused_state()
# Set idle state visuals
func _set_idle_state() -> void:
# Reset to default appearance
state_border.color = Color(0, 0, 0, 0)
overlay_icon.texture = null
modulate = Color(1, 1, 1, 1)
scale = Vector2(1, 1)
# Stop any animations
_stop_animations()
# Set active state visuals
func _set_active_state() -> void:
# White border highlight
state_border.color = Color(1, 1, 1, 1)
state_border.size = Vector2(2, 2)
# Clear overlay
overlay_icon.texture = null
# Start pulsing animation
_start_pulsing_animation()
# Slight scale increase
scale = Vector2(1.05, 1.05)
# Set completed state visuals
func _set_completed_state() -> void:
# Green border
state_border.color = Color(0, 1, 0, 0.5)
state_border.size = Vector2(2, 2)
# Checkmark overlay
overlay_icon.texture = checkmark_texture
overlay_icon.modulate = Color(0, 1, 0, 0.7)
# Slight transparency
modulate = Color(1, 1, 1, 0.8)
# Stop animations
_stop_animations()
# Set error state visuals
func _set_error_state() -> void:
# Red border
state_border.color = Color(1, 0, 0, 1)
state_border.size = Vector2(3, 3)
# Error icon overlay
overlay_icon.texture = error_texture
overlay_icon.modulate = Color(1, 0, 0, 1)
# Red tint
modulate = Color(1, 0.7, 0.7, 1)
# Start shake animation
_start_shake_animation()
# Set paused state visuals
func _set_paused_state() -> void:
# Yellow border
state_border.color = Color(1, 1, 0, 1)
state_border.size = Vector2(2, 2)
# Pause icon overlay
overlay_icon.texture = pause_texture
overlay_icon.modulate = Color(1, 1, 0, 1)
# Stop animations
_stop_animations()
# Animation functions
func _start_pulsing_animation() -> void:
# Create animation player for pulsing effect
var anim_player = AnimationPlayer.new()
anim_player.name = "StateAnimationPlayer"
# Remove existing animation player if present
var existing_player = get_node_or_null("StateAnimationPlayer")
if existing_player:
remove_child(existing_player)
existing_player.queue_free()
add_child(anim_player)
# Create pulsing animation
var animation = Animation.new()
animation.name = "pulse"
animation.length = 1.0
animation.loop_mode = Animation.LOOP_LINEAR
# Scale property track
var scale_track = animation.add_track(Animation.TYPE_VALUE)
animation.track_set_path(scale_track, ".:scale")
animation.track_insert_key(scale_track, 0.0, Vector2(1.05, 1.05))
animation.track_insert_key(scale_track, 0.5, Vector2(1.1, 1.1))
animation.track_insert_key(scale_track, 1.0, Vector2(1.05, 1.05))
anim_player.add_animation("pulse", animation)
anim_player.play("pulse")
func _start_shake_animation() -> void:
# Create animation player for shake effect
var anim_player = AnimationPlayer.new()
anim_player.name = "StateAnimationPlayer"
# Remove existing animation player if present
var existing_player = get_node_or_null("StateAnimationPlayer")
if existing_player:
remove_child(existing_player)
existing_player.queue_free()
add_child(anim_player)
# Create shake animation
var animation = Animation.new()
animation.name = "shake"
animation.length = 0.5
animation.loop_mode = Animation.LOOP_LINEAR
# Position property track
var position_track = animation.add_track(Animation.TYPE_VALUE)
animation.track_set_path(position_track, ".:position_offset")
animation.track_insert_key(position_track, 0.0, position_offset)
animation.track_insert_key(position_track, 0.1, position_offset + Vector2(2, 0))
animation.track_insert_key(position_track, 0.2, position_offset + Vector2(-2, 0))
animation.track_insert_key(position_track, 0.3, position_offset + Vector2(0, 2))
animation.track_insert_key(position_track, 0.4, position_offset + Vector2(0, -2))
animation.track_insert_key(position_track, 0.5, position_offset)
anim_player.add_animation("shake", animation)
anim_player.play("shake")
func _stop_animations() -> void:
var anim_player = get_node_or_null("StateAnimationPlayer")
if anim_player:
anim_player.stop()
# Update parameter value
func set_parameter(parameter_name: String, value) -> void:
action_parameters[parameter_name] = value
# Update editor if it exists
if property_editors.has(parameter_name):
var editor = property_editors[parameter_name]
editor.set_value(value)
emit_signal("parameter_changed", self, parameter_name, value)
# Get parameter value
func get_parameter(parameter_name: String):
return action_parameters.get(parameter_name, null)
# Handle node dragging
func _on_node_dragged(from: Vector2, to: Vector2) -> void:
# Update position
position_offset = to
# Handle node selection
func _on_node_selected() -> void:
emit_signal("node_selected2", self)
# Clear parameter fields
func _clear_parameter_fields() -> void:
# Remove existing editors
for param_name in property_editors:
var editor = property_editors[param_name]
if editor.get_parent() == self:
remove_child(editor)
editor.queue_free()
property_editors.clear()
func _parameters_to_view() -> void:
pass