1137 lines
37 KiB
GDScript
1137 lines
37 KiB
GDScript
class_name InspectorPanel
|
|
extends VBoxContainer
|
|
## Property and behavior editor for the selected object (docs/architecture/editor.md
|
|
## §7.2 `InspectorPanel`).
|
|
##
|
|
## Visual Property & Behavior Inspector (Creator Studio):
|
|
## - Position, yaw, scale, and delete operations.
|
|
## - Visual rule builder for ScriptBehavior rules:
|
|
## Triggers: on_interact, on_touch, on_enter, on_timer
|
|
## Actions: give_item (Buffalo Chips / items), rotate, move, play_sound, spawn_from_catalog
|
|
## - Pure UI: every property & behavior edit calls back into `EditorCore`
|
|
## (`move_object`, `rotate_object`, `scale_object`, `set_object_behavior`, `delete_object`),
|
|
## fully supporting undo and redo.
|
|
|
|
signal delete_requested(object_id: String)
|
|
|
|
var _core: EditorCore = null
|
|
var _object_id: String = ""
|
|
var _updating := false # guards against feedback loops while refreshing fields
|
|
|
|
var _empty_label: Label
|
|
var _scroll: ScrollContainer
|
|
var _fields_box: VBoxContainer
|
|
|
|
# Property fields
|
|
var _id_label: Label
|
|
var _type_label: Label
|
|
var _asset_label: Label
|
|
var _pos_x: SpinBox
|
|
var _pos_y: SpinBox
|
|
var _pos_z: SpinBox
|
|
var _yaw: SpinBox
|
|
var _scale_x: SpinBox
|
|
var _scale_y: SpinBox
|
|
var _scale_z: SpinBox
|
|
var _delete_button: Button
|
|
|
|
# Animation & Motion fields
|
|
var _anim_clip_option: OptionButton
|
|
var _anim_autoplay_check: CheckBox
|
|
var _anim_loop_check: CheckBox
|
|
var _anim_speed_spin: SpinBox
|
|
|
|
var _motion_type_option: OptionButton
|
|
var _motion_trigger_option: OptionButton
|
|
var _motion_rotate_box: VBoxContainer
|
|
var _motion_speed_spin: SpinBox
|
|
var _motion_bob_box: VBoxContainer
|
|
var _motion_bob_amp_spin: SpinBox
|
|
var _motion_bob_freq_spin: SpinBox
|
|
var _motion_waypoint_box: VBoxContainer
|
|
var _motion_wp_speed_spin: SpinBox
|
|
var _motion_wp_pause_spin: SpinBox
|
|
var _motion_wp_count_label: Label
|
|
var _motion_add_wp_btn: Button
|
|
var _motion_clear_wp_btn: Button
|
|
var _current_waypoints: Array[Vector3] = []
|
|
|
|
# Behavior & Rules fields
|
|
var _rules_vbox: VBoxContainer
|
|
var _add_rule_btn: Button
|
|
var _rule_form_box: VBoxContainer
|
|
var _trigger_option: OptionButton
|
|
var _timer_row: HBoxContainer
|
|
var _timer_interval_spin: SpinBox
|
|
var _action_option: OptionButton
|
|
|
|
# Dynamic Action Parameter boxes
|
|
var _action_item_box: VBoxContainer
|
|
var _item_id_edit: LineEdit
|
|
var _item_count_spin: SpinBox
|
|
|
|
var _action_rotate_box: VBoxContainer
|
|
var _rotate_axis_option: OptionButton
|
|
var _rotate_degrees_spin: SpinBox
|
|
var _rotate_duration_spin: SpinBox
|
|
|
|
var _action_move_box: VBoxContainer
|
|
var _move_x_spin: SpinBox
|
|
var _move_y_spin: SpinBox
|
|
var _move_z_spin: SpinBox
|
|
var _move_duration_spin: SpinBox
|
|
|
|
var _action_sound_box: VBoxContainer
|
|
var _sound_id_edit: LineEdit
|
|
|
|
var _action_spawn_box: VBoxContainer
|
|
var _spawn_catalog_id_edit: LineEdit
|
|
var _spawn_x_spin: SpinBox
|
|
var _spawn_y_spin: SpinBox
|
|
var _spawn_z_spin: SpinBox
|
|
|
|
|
|
var _is_built := false
|
|
|
|
|
|
func _init() -> void:
|
|
name = "InspectorPanel"
|
|
size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
|
|
|
|
func _ready() -> void:
|
|
if not _is_built:
|
|
_build_ui()
|
|
if _object_id != "":
|
|
show_object(_object_id)
|
|
|
|
|
|
func _build_ui() -> void:
|
|
if _is_built:
|
|
return
|
|
_is_built = true
|
|
|
|
var header := Label.new()
|
|
header.text = "Inspector"
|
|
header.add_theme_font_size_override("font_size", 15)
|
|
header.add_theme_color_override("font_color", Color(1, 0.95, 0.4))
|
|
add_child(header)
|
|
|
|
_empty_label = Label.new()
|
|
_empty_label.text = "(nothing selected)"
|
|
add_child(_empty_label)
|
|
|
|
_scroll = ScrollContainer.new()
|
|
_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
|
add_child(_scroll)
|
|
|
|
_fields_box = VBoxContainer.new()
|
|
_fields_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_fields_box.visible = false
|
|
_scroll.add_child(_fields_box)
|
|
|
|
_id_label = _add_readonly_row("id")
|
|
_type_label = _add_readonly_row("type")
|
|
_asset_label = _add_readonly_row("asset")
|
|
|
|
_pos_x = _add_spinbox_row("pos x")
|
|
_pos_y = _add_spinbox_row("pos y")
|
|
_pos_z = _add_spinbox_row("pos z (height)")
|
|
_pos_x.value_changed.connect(_on_position_changed)
|
|
_pos_y.value_changed.connect(_on_position_changed)
|
|
_pos_z.value_changed.connect(_on_position_changed)
|
|
|
|
_yaw = _add_spinbox_row("rotation (deg)")
|
|
_yaw.min_value = -360.0
|
|
_yaw.max_value = 360.0
|
|
_yaw.step = 1.0
|
|
_yaw.value_changed.connect(_on_yaw_changed)
|
|
|
|
_scale_x = _add_spinbox_row("scale x")
|
|
_scale_y = _add_spinbox_row("scale y")
|
|
_scale_z = _add_spinbox_row("scale z")
|
|
_scale_x.value_changed.connect(_on_scale_changed)
|
|
_scale_y.value_changed.connect(_on_scale_changed)
|
|
_scale_z.value_changed.connect(_on_scale_changed)
|
|
|
|
_delete_button = Button.new()
|
|
_delete_button.text = "Delete Object"
|
|
_delete_button.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4))
|
|
_delete_button.pressed.connect(_on_delete_pressed)
|
|
_fields_box.add_child(_delete_button)
|
|
|
|
_setup_animation_and_motion_ui()
|
|
_setup_behavior_ui()
|
|
|
|
|
|
func _setup_animation_and_motion_ui() -> void:
|
|
var sep := HSeparator.new()
|
|
sep.custom_minimum_size = Vector2(0, 16)
|
|
_fields_box.add_child(sep)
|
|
|
|
var header := Label.new()
|
|
header.text = "Animation & Movement"
|
|
header.add_theme_font_size_override("font_size", 14)
|
|
header.add_theme_color_override("font_color", Color(0.4, 0.9, 0.5))
|
|
_fields_box.add_child(header)
|
|
|
|
var desc := Label.new()
|
|
desc.text = "Configure 3D clip playback and procedural motion."
|
|
desc.add_theme_font_size_override("font_size", 11)
|
|
desc.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
|
|
_fields_box.add_child(desc)
|
|
|
|
# --- Animation Section ---
|
|
var anim_panel := PanelContainer.new()
|
|
var anim_vbox := VBoxContainer.new()
|
|
anim_panel.add_child(anim_vbox)
|
|
_fields_box.add_child(anim_panel)
|
|
|
|
var anim_lbl := Label.new()
|
|
anim_lbl.text = "Clip Animation"
|
|
anim_lbl.add_theme_font_size_override("font_size", 12)
|
|
anim_lbl.add_theme_color_override("font_color", Color(1.0, 0.9, 0.4))
|
|
anim_vbox.add_child(anim_lbl)
|
|
|
|
var clip_row := HBoxContainer.new()
|
|
var clip_lbl := Label.new()
|
|
clip_lbl.text = "Clip:"
|
|
clip_lbl.custom_minimum_size = Vector2(60, 0)
|
|
clip_row.add_child(clip_lbl)
|
|
_anim_clip_option = OptionButton.new()
|
|
_anim_clip_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_anim_clip_option.item_selected.connect(_on_animation_changed)
|
|
clip_row.add_child(_anim_clip_option)
|
|
anim_vbox.add_child(clip_row)
|
|
|
|
var anim_opts_row := HBoxContainer.new()
|
|
_anim_autoplay_check = CheckBox.new()
|
|
_anim_autoplay_check.text = "Autoplay"
|
|
_anim_autoplay_check.toggled.connect(func(_val): _on_animation_changed(0))
|
|
anim_opts_row.add_child(_anim_autoplay_check)
|
|
|
|
_anim_loop_check = CheckBox.new()
|
|
_anim_loop_check.text = "Loop"
|
|
_anim_loop_check.toggled.connect(func(_val): _on_animation_changed(0))
|
|
anim_opts_row.add_child(_anim_loop_check)
|
|
anim_vbox.add_child(anim_opts_row)
|
|
|
|
var speed_row := HBoxContainer.new()
|
|
var spd_lbl := Label.new()
|
|
spd_lbl.text = "Speed:"
|
|
spd_lbl.custom_minimum_size = Vector2(60, 0)
|
|
speed_row.add_child(spd_lbl)
|
|
_anim_speed_spin = SpinBox.new()
|
|
_anim_speed_spin.min_value = 0.1
|
|
_anim_speed_spin.max_value = 10.0
|
|
_anim_speed_spin.step = 0.1
|
|
_anim_speed_spin.value = 1.0
|
|
_anim_speed_spin.value_changed.connect(func(_val): _on_animation_changed(0))
|
|
speed_row.add_child(_anim_speed_spin)
|
|
anim_vbox.add_child(speed_row)
|
|
|
|
# --- Motion Section ---
|
|
var motion_panel := PanelContainer.new()
|
|
var motion_vbox := VBoxContainer.new()
|
|
motion_panel.add_child(motion_vbox)
|
|
_fields_box.add_child(motion_panel)
|
|
|
|
var motion_lbl := Label.new()
|
|
motion_lbl.text = "Procedural Motion"
|
|
motion_lbl.add_theme_font_size_override("font_size", 12)
|
|
motion_lbl.add_theme_color_override("font_color", Color(0.35, 0.85, 1.0))
|
|
motion_vbox.add_child(motion_lbl)
|
|
|
|
var type_row := HBoxContainer.new()
|
|
var type_lbl := Label.new()
|
|
type_lbl.text = "Type:"
|
|
type_lbl.custom_minimum_size = Vector2(60, 0)
|
|
type_row.add_child(type_lbl)
|
|
_motion_type_option = OptionButton.new()
|
|
_motion_type_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_motion_type_option.add_item("None (Static)", 0)
|
|
_motion_type_option.set_item_metadata(0, "none")
|
|
_motion_type_option.add_item("Rotate (Spin)", 1)
|
|
_motion_type_option.set_item_metadata(1, "rotate")
|
|
_motion_type_option.add_item("Bob / Float", 2)
|
|
_motion_type_option.set_item_metadata(2, "bob")
|
|
_motion_type_option.add_item("Waypoint Path", 3)
|
|
_motion_type_option.set_item_metadata(3, "waypoint")
|
|
_motion_type_option.item_selected.connect(_on_motion_type_selected)
|
|
type_row.add_child(_motion_type_option)
|
|
motion_vbox.add_child(type_row)
|
|
|
|
var trig_row := HBoxContainer.new()
|
|
var trig_lbl := Label.new()
|
|
trig_lbl.text = "Trigger:"
|
|
trig_lbl.custom_minimum_size = Vector2(60, 0)
|
|
trig_row.add_child(trig_lbl)
|
|
_motion_trigger_option = OptionButton.new()
|
|
_motion_trigger_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_motion_trigger_option.add_item("Always (Active)", 0)
|
|
_motion_trigger_option.set_item_metadata(0, "always")
|
|
_motion_trigger_option.add_item("Proximity (Player near)", 1)
|
|
_motion_trigger_option.set_item_metadata(1, "proximity")
|
|
_motion_trigger_option.add_item("Interact (Click)", 2)
|
|
_motion_trigger_option.set_item_metadata(2, "interact")
|
|
_motion_trigger_option.add_item("Signal (Script)", 3)
|
|
_motion_trigger_option.set_item_metadata(3, "signal")
|
|
_motion_trigger_option.item_selected.connect(func(_idx): _on_motion_param_changed())
|
|
trig_row.add_child(_motion_trigger_option)
|
|
motion_vbox.add_child(trig_row)
|
|
|
|
# Rotate parameters
|
|
_motion_rotate_box = VBoxContainer.new()
|
|
var rot_speed_row := HBoxContainer.new()
|
|
var rot_spd_lbl := Label.new()
|
|
rot_spd_lbl.text = "Speed (deg/s):"
|
|
rot_spd_lbl.custom_minimum_size = Vector2(100, 0)
|
|
rot_speed_row.add_child(rot_spd_lbl)
|
|
_motion_speed_spin = SpinBox.new()
|
|
_motion_speed_spin.min_value = -720.0
|
|
_motion_speed_spin.max_value = 720.0
|
|
_motion_speed_spin.value = 45.0
|
|
_motion_speed_spin.value_changed.connect(func(_v): _on_motion_param_changed())
|
|
rot_speed_row.add_child(_motion_speed_spin)
|
|
_motion_rotate_box.add_child(rot_speed_row)
|
|
motion_vbox.add_child(_motion_rotate_box)
|
|
|
|
# Bob parameters
|
|
_motion_bob_box = VBoxContainer.new()
|
|
var bob_amp_row := HBoxContainer.new()
|
|
var bob_amp_lbl := Label.new()
|
|
bob_amp_lbl.text = "Amplitude:"
|
|
bob_amp_lbl.custom_minimum_size = Vector2(80, 0)
|
|
bob_amp_row.add_child(bob_amp_lbl)
|
|
_motion_bob_amp_spin = SpinBox.new()
|
|
_motion_bob_amp_spin.min_value = 0.05
|
|
_motion_bob_amp_spin.max_value = 20.0
|
|
_motion_bob_amp_spin.step = 0.1
|
|
_motion_bob_amp_spin.value = 0.5
|
|
_motion_bob_amp_spin.value_changed.connect(func(_v): _on_motion_param_changed())
|
|
bob_amp_row.add_child(_motion_bob_amp_spin)
|
|
_motion_bob_box.add_child(bob_amp_row)
|
|
|
|
var bob_freq_row := HBoxContainer.new()
|
|
var bob_freq_lbl := Label.new()
|
|
bob_freq_lbl.text = "Frequency:"
|
|
bob_freq_lbl.custom_minimum_size = Vector2(80, 0)
|
|
bob_freq_row.add_child(bob_freq_lbl)
|
|
_motion_bob_freq_spin = SpinBox.new()
|
|
_motion_bob_freq_spin.min_value = 0.1
|
|
_motion_bob_freq_spin.max_value = 10.0
|
|
_motion_bob_freq_spin.step = 0.1
|
|
_motion_bob_freq_spin.value = 1.0
|
|
_motion_bob_freq_spin.value_changed.connect(func(_v): _on_motion_param_changed())
|
|
bob_freq_row.add_child(_motion_bob_freq_spin)
|
|
_motion_bob_box.add_child(bob_freq_row)
|
|
motion_vbox.add_child(_motion_bob_box)
|
|
|
|
# Waypoint parameters
|
|
_motion_waypoint_box = VBoxContainer.new()
|
|
var wp_spd_row := HBoxContainer.new()
|
|
var wp_spd_lbl := Label.new()
|
|
wp_spd_lbl.text = "Move Speed:"
|
|
wp_spd_lbl.custom_minimum_size = Vector2(80, 0)
|
|
wp_spd_row.add_child(wp_spd_lbl)
|
|
_motion_wp_speed_spin = SpinBox.new()
|
|
_motion_wp_speed_spin.min_value = 0.1
|
|
_motion_wp_speed_spin.max_value = 50.0
|
|
_motion_wp_speed_spin.value = 2.0
|
|
_motion_wp_speed_spin.value_changed.connect(func(_v): _on_motion_param_changed())
|
|
wp_spd_row.add_child(_motion_wp_speed_spin)
|
|
_motion_waypoint_box.add_child(wp_spd_row)
|
|
|
|
var wp_pause_row := HBoxContainer.new()
|
|
var wp_pause_lbl := Label.new()
|
|
wp_pause_lbl.text = "Pause (s):"
|
|
wp_pause_lbl.custom_minimum_size = Vector2(80, 0)
|
|
wp_pause_row.add_child(wp_pause_lbl)
|
|
_motion_wp_pause_spin = SpinBox.new()
|
|
_motion_wp_pause_spin.min_value = 0.0
|
|
_motion_wp_pause_spin.max_value = 60.0
|
|
_motion_wp_pause_spin.value = 0.0
|
|
_motion_wp_pause_spin.value_changed.connect(func(_v): _on_motion_param_changed())
|
|
wp_pause_row.add_child(_motion_wp_pause_spin)
|
|
_motion_waypoint_box.add_child(wp_pause_row)
|
|
|
|
_motion_wp_count_label = Label.new()
|
|
_motion_wp_count_label.text = "Waypoints: 0"
|
|
_motion_waypoint_box.add_child(_motion_wp_count_label)
|
|
|
|
var wp_btn_row := HBoxContainer.new()
|
|
_motion_add_wp_btn = Button.new()
|
|
_motion_add_wp_btn.text = "+ Add Current Pos"
|
|
_motion_add_wp_btn.pressed.connect(_on_add_current_pos_waypoint)
|
|
wp_btn_row.add_child(_motion_add_wp_btn)
|
|
|
|
_motion_clear_wp_btn = Button.new()
|
|
_motion_clear_wp_btn.text = "Clear"
|
|
_motion_clear_wp_btn.pressed.connect(_on_clear_waypoints)
|
|
wp_btn_row.add_child(_motion_clear_wp_btn)
|
|
_motion_waypoint_box.add_child(wp_btn_row)
|
|
|
|
motion_vbox.add_child(_motion_waypoint_box)
|
|
|
|
|
|
func _setup_behavior_ui() -> void:
|
|
var sep := HSeparator.new()
|
|
sep.custom_minimum_size = Vector2(0, 16)
|
|
_fields_box.add_child(sep)
|
|
|
|
var b_header := Label.new()
|
|
b_header.text = "Behavior & Rules"
|
|
b_header.add_theme_font_size_override("font_size", 14)
|
|
b_header.add_theme_color_override("font_color", Color(0.35, 0.85, 1.0))
|
|
_fields_box.add_child(b_header)
|
|
|
|
var b_desc := Label.new()
|
|
b_desc.text = "Attach interactive logic and triggers."
|
|
b_desc.add_theme_font_size_override("font_size", 11)
|
|
b_desc.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
|
|
_fields_box.add_child(b_desc)
|
|
|
|
_rules_vbox = VBoxContainer.new()
|
|
_rules_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_fields_box.add_child(_rules_vbox)
|
|
|
|
_add_rule_btn = Button.new()
|
|
_add_rule_btn.text = "+ Add Behavior Rule"
|
|
_add_rule_btn.pressed.connect(_on_add_rule_btn_pressed)
|
|
_fields_box.add_child(_add_rule_btn)
|
|
|
|
_setup_rule_form()
|
|
|
|
|
|
func _setup_rule_form() -> void:
|
|
_rule_form_box = VBoxContainer.new()
|
|
_rule_form_box.visible = false
|
|
_rule_form_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_fields_box.add_child(_rule_form_box)
|
|
|
|
var form_panel := PanelContainer.new()
|
|
var form_box := VBoxContainer.new()
|
|
form_panel.add_child(form_box)
|
|
_rule_form_box.add_child(form_panel)
|
|
|
|
var form_title := Label.new()
|
|
form_title.text = "New Behavior Rule"
|
|
form_title.add_theme_font_size_override("font_size", 13)
|
|
form_title.add_theme_color_override("font_color", Color(1.0, 0.9, 0.3))
|
|
form_box.add_child(form_title)
|
|
|
|
# Trigger Selector
|
|
var trig_row := HBoxContainer.new()
|
|
var trig_lbl := Label.new()
|
|
trig_lbl.text = "Trigger:"
|
|
trig_lbl.custom_minimum_size = Vector2(70, 0)
|
|
trig_row.add_child(trig_lbl)
|
|
_trigger_option = OptionButton.new()
|
|
_trigger_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_trigger_option.add_item("on_interact (Click/Interact)", 0)
|
|
_trigger_option.set_item_metadata(0, "on_interact")
|
|
_trigger_option.add_item("on_touch (Physical contact)", 1)
|
|
_trigger_option.set_item_metadata(1, "on_touch")
|
|
_trigger_option.add_item("on_enter (Enters zone)", 2)
|
|
_trigger_option.set_item_metadata(2, "on_enter")
|
|
_trigger_option.add_item("on_timer (Repeats every X sec)", 3)
|
|
_trigger_option.set_item_metadata(3, "on_timer")
|
|
_trigger_option.item_selected.connect(_on_trigger_changed)
|
|
trig_row.add_child(_trigger_option)
|
|
form_box.add_child(trig_row)
|
|
|
|
# Timer interval row (only visible when on_timer is selected)
|
|
_timer_row = HBoxContainer.new()
|
|
_timer_row.visible = false
|
|
var timer_lbl := Label.new()
|
|
timer_lbl.text = "Interval (s):"
|
|
timer_lbl.custom_minimum_size = Vector2(70, 0)
|
|
_timer_row.add_child(timer_lbl)
|
|
_timer_interval_spin = SpinBox.new()
|
|
_timer_interval_spin.min_value = 0.1
|
|
_timer_interval_spin.max_value = 3600.0
|
|
_timer_interval_spin.step = 0.5
|
|
_timer_interval_spin.value = 2.0
|
|
_timer_row.add_child(_timer_interval_spin)
|
|
form_box.add_child(_timer_row)
|
|
|
|
# Action Selector
|
|
var act_row := HBoxContainer.new()
|
|
var act_lbl := Label.new()
|
|
act_lbl.text = "Action:"
|
|
act_lbl.custom_minimum_size = Vector2(70, 0)
|
|
act_row.add_child(act_lbl)
|
|
_action_option = OptionButton.new()
|
|
_action_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
_action_option.add_item("give_item (Reward Chips/Items)", 0)
|
|
_action_option.set_item_metadata(0, "give_item")
|
|
_action_option.add_item("rotate (Spin Object)", 1)
|
|
_action_option.set_item_metadata(1, "rotate")
|
|
_action_option.add_item("move (Move / Elevate)", 2)
|
|
_action_option.set_item_metadata(2, "move")
|
|
_action_option.add_item("play_sound (Audio FX)", 3)
|
|
_action_option.set_item_metadata(3, "play_sound")
|
|
_action_option.add_item("spawn_from_catalog (Spawn)", 4)
|
|
_action_option.set_item_metadata(4, "spawn_from_catalog")
|
|
_action_option.item_selected.connect(_on_action_changed)
|
|
act_row.add_child(_action_option)
|
|
form_box.add_child(act_row)
|
|
|
|
# Dynamic Parameter Boxes
|
|
# 1. give_item
|
|
_action_item_box = VBoxContainer.new()
|
|
var item_id_row := HBoxContainer.new()
|
|
var item_id_lbl := Label.new()
|
|
item_id_lbl.text = "Item ID:"
|
|
item_id_lbl.custom_minimum_size = Vector2(70, 0)
|
|
item_id_row.add_child(item_id_lbl)
|
|
_item_id_edit = LineEdit.new()
|
|
_item_id_edit.text = "buffalo_chips"
|
|
_item_id_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
item_id_row.add_child(_item_id_edit)
|
|
_action_item_box.add_child(item_id_row)
|
|
|
|
var item_cnt_row := HBoxContainer.new()
|
|
var item_cnt_lbl := Label.new()
|
|
item_cnt_lbl.text = "Amount:"
|
|
item_cnt_lbl.custom_minimum_size = Vector2(70, 0)
|
|
item_cnt_row.add_child(item_cnt_lbl)
|
|
_item_count_spin = SpinBox.new()
|
|
_item_count_spin.min_value = 1
|
|
_item_count_spin.max_value = 10000
|
|
_item_count_spin.value = 5
|
|
item_cnt_row.add_child(_item_count_spin)
|
|
_action_item_box.add_child(item_cnt_row)
|
|
form_box.add_child(_action_item_box)
|
|
|
|
# 2. rotate
|
|
_action_rotate_box = VBoxContainer.new()
|
|
_action_rotate_box.visible = false
|
|
var rot_axis_row := HBoxContainer.new()
|
|
var rot_axis_lbl := Label.new()
|
|
rot_axis_lbl.text = "Axis:"
|
|
rot_axis_lbl.custom_minimum_size = Vector2(70, 0)
|
|
rot_axis_row.add_child(rot_axis_lbl)
|
|
_rotate_axis_option = OptionButton.new()
|
|
_rotate_axis_option.add_item("y (Yaw)")
|
|
_rotate_axis_option.set_item_metadata(0, "y")
|
|
_rotate_axis_option.add_item("x (Pitch)")
|
|
_rotate_axis_option.set_item_metadata(1, "x")
|
|
_rotate_axis_option.add_item("z (Roll)")
|
|
_rotate_axis_option.set_item_metadata(2, "z")
|
|
rot_axis_row.add_child(_rotate_axis_option)
|
|
_action_rotate_box.add_child(rot_axis_row)
|
|
|
|
var rot_deg_row := HBoxContainer.new()
|
|
var rot_deg_lbl := Label.new()
|
|
rot_deg_lbl.text = "Degrees:"
|
|
rot_deg_lbl.custom_minimum_size = Vector2(70, 0)
|
|
rot_deg_row.add_child(rot_deg_lbl)
|
|
_rotate_degrees_spin = SpinBox.new()
|
|
_rotate_degrees_spin.min_value = -360.0
|
|
_rotate_degrees_spin.max_value = 360.0
|
|
_rotate_degrees_spin.step = 15.0
|
|
_rotate_degrees_spin.value = 90.0
|
|
rot_deg_row.add_child(_rotate_degrees_spin)
|
|
_action_rotate_box.add_child(rot_deg_row)
|
|
|
|
var rot_dur_row := HBoxContainer.new()
|
|
var rot_dur_lbl := Label.new()
|
|
rot_dur_lbl.text = "Duration (s):"
|
|
rot_dur_lbl.custom_minimum_size = Vector2(70, 0)
|
|
rot_dur_row.add_child(rot_dur_lbl)
|
|
_rotate_duration_spin = SpinBox.new()
|
|
_rotate_duration_spin.min_value = 0.05
|
|
_rotate_duration_spin.max_value = 5.0
|
|
_rotate_duration_spin.step = 0.1
|
|
_rotate_duration_spin.value = 0.4
|
|
rot_dur_row.add_child(_rotate_duration_spin)
|
|
_action_rotate_box.add_child(rot_dur_row)
|
|
form_box.add_child(_action_rotate_box)
|
|
|
|
# 3. move
|
|
_action_move_box = VBoxContainer.new()
|
|
_action_move_box.visible = false
|
|
var move_lbl := Label.new()
|
|
move_lbl.text = "Offset (X, Y, Z):"
|
|
_action_move_box.add_child(move_lbl)
|
|
var move_coords := HBoxContainer.new()
|
|
_move_x_spin = SpinBox.new()
|
|
_move_x_spin.min_value = -100.0
|
|
_move_x_spin.max_value = 100.0
|
|
_move_x_spin.value = 0.0
|
|
move_coords.add_child(_move_x_spin)
|
|
_move_y_spin = SpinBox.new()
|
|
_move_y_spin.min_value = -100.0
|
|
_move_y_spin.max_value = 100.0
|
|
_move_y_spin.value = 2.0
|
|
move_coords.add_child(_move_y_spin)
|
|
_move_z_spin = SpinBox.new()
|
|
_move_z_spin.min_value = -100.0
|
|
_move_z_spin.max_value = 100.0
|
|
_move_z_spin.value = 0.0
|
|
move_coords.add_child(_move_z_spin)
|
|
_action_move_box.add_child(move_coords)
|
|
|
|
var move_dur_row := HBoxContainer.new()
|
|
var move_dur_lbl := Label.new()
|
|
move_dur_lbl.text = "Duration (s):"
|
|
move_dur_lbl.custom_minimum_size = Vector2(70, 0)
|
|
move_dur_row.add_child(move_dur_lbl)
|
|
_move_duration_spin = SpinBox.new()
|
|
_move_duration_spin.min_value = 0.05
|
|
_move_duration_spin.max_value = 5.0
|
|
_move_duration_spin.step = 0.1
|
|
_move_duration_spin.value = 0.4
|
|
move_dur_row.add_child(_move_duration_spin)
|
|
_action_move_box.add_child(move_dur_row)
|
|
form_box.add_child(_action_move_box)
|
|
|
|
# 4. play_sound
|
|
_action_sound_box = VBoxContainer.new()
|
|
_action_sound_box.visible = false
|
|
|
|
var snd_preset_row := HBoxContainer.new()
|
|
var snd_preset_lbl := Label.new()
|
|
snd_preset_lbl.text = "Preset:"
|
|
snd_preset_lbl.custom_minimum_size = Vector2(70, 0)
|
|
snd_preset_row.add_child(snd_preset_lbl)
|
|
|
|
var snd_option := OptionButton.new()
|
|
snd_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
snd_option.add_item("🔔 Bell Chime (bell)", 0)
|
|
snd_option.set_item_metadata(0, "bell")
|
|
snd_option.add_item("🪙 Coin Pickup (coin)", 1)
|
|
snd_option.set_item_metadata(1, "coin")
|
|
snd_option.add_item("🔘 Button Click (click)", 2)
|
|
snd_option.set_item_metadata(2, "click")
|
|
snd_option.add_item("✨ Magic Chime (chime)", 3)
|
|
snd_option.set_item_metadata(3, "chime")
|
|
snd_option.add_item("🎺 Victory Fanfare (fanfare)", 4)
|
|
snd_option.set_item_metadata(4, "fanfare")
|
|
snd_option.add_item("🚪 Door Open (door_open)", 5)
|
|
snd_option.set_item_metadata(5, "door_open")
|
|
snd_preset_row.add_child(snd_option)
|
|
|
|
var preview_btn := Button.new()
|
|
preview_btn.text = "▶ Preview"
|
|
preview_btn.tooltip_text = "Listen to this sound effect"
|
|
preview_btn.pressed.connect(func():
|
|
var snd_id := _sound_id_edit.text.strip_edges()
|
|
var p := "res://assets/audio/%s.wav" % snd_id
|
|
if ResourceLoader.exists(p):
|
|
var st = load(p)
|
|
if st is AudioStream:
|
|
var asp := AudioStreamPlayer.new()
|
|
asp.stream = st
|
|
add_child(asp)
|
|
asp.finished.connect(asp.queue_free)
|
|
asp.play()
|
|
)
|
|
snd_preset_row.add_child(preview_btn)
|
|
_action_sound_box.add_child(snd_preset_row)
|
|
|
|
var snd_row := HBoxContainer.new()
|
|
var snd_lbl := Label.new()
|
|
snd_lbl.text = "Sound ID:"
|
|
snd_lbl.custom_minimum_size = Vector2(70, 0)
|
|
snd_row.add_child(snd_lbl)
|
|
_sound_id_edit = LineEdit.new()
|
|
_sound_id_edit.text = "bell"
|
|
_sound_id_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
snd_row.add_child(_sound_id_edit)
|
|
_action_sound_box.add_child(snd_row)
|
|
|
|
snd_option.item_selected.connect(func(idx: int):
|
|
var meta = snd_option.get_item_metadata(idx)
|
|
if meta != null:
|
|
_sound_id_edit.text = str(meta)
|
|
)
|
|
|
|
form_box.add_child(_action_sound_box)
|
|
|
|
# 5. spawn_from_catalog
|
|
_action_spawn_box = VBoxContainer.new()
|
|
_action_spawn_box.visible = false
|
|
var sp_row := HBoxContainer.new()
|
|
var sp_lbl := Label.new()
|
|
sp_lbl.text = "Catalog ID:"
|
|
sp_lbl.custom_minimum_size = Vector2(70, 0)
|
|
sp_row.add_child(sp_lbl)
|
|
_spawn_catalog_id_edit = LineEdit.new()
|
|
_spawn_catalog_id_edit.text = "cube"
|
|
_spawn_catalog_id_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
sp_row.add_child(_spawn_catalog_id_edit)
|
|
_action_spawn_box.add_child(sp_row)
|
|
|
|
var sp_pos_lbl := Label.new()
|
|
sp_pos_lbl.text = "Spawn Pos (X, Y, Z):"
|
|
_action_spawn_box.add_child(sp_pos_lbl)
|
|
var sp_coords := HBoxContainer.new()
|
|
_spawn_x_spin = SpinBox.new()
|
|
_spawn_x_spin.min_value = -100.0
|
|
_spawn_x_spin.max_value = 100.0
|
|
_spawn_x_spin.value = 0.0
|
|
sp_coords.add_child(_spawn_x_spin)
|
|
_spawn_y_spin = SpinBox.new()
|
|
_spawn_y_spin.min_value = -100.0
|
|
_spawn_y_spin.max_value = 100.0
|
|
_spawn_y_spin.value = 1.0
|
|
sp_coords.add_child(_spawn_y_spin)
|
|
_spawn_z_spin = SpinBox.new()
|
|
_spawn_z_spin.min_value = -100.0
|
|
_spawn_z_spin.max_value = 100.0
|
|
_spawn_z_spin.value = 0.0
|
|
sp_coords.add_child(_spawn_z_spin)
|
|
_action_spawn_box.add_child(sp_coords)
|
|
form_box.add_child(_action_spawn_box)
|
|
|
|
# Form Buttons
|
|
var btn_row := HBoxContainer.new()
|
|
var save_btn := Button.new()
|
|
save_btn.text = "Save Rule"
|
|
save_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
save_btn.pressed.connect(_on_save_rule_pressed)
|
|
btn_row.add_child(save_btn)
|
|
|
|
var cancel_btn := Button.new()
|
|
cancel_btn.text = "Cancel"
|
|
cancel_btn.pressed.connect(_on_cancel_rule_pressed)
|
|
btn_row.add_child(cancel_btn)
|
|
form_box.add_child(btn_row)
|
|
|
|
|
|
func setup(core: EditorCore) -> void:
|
|
_core = core
|
|
|
|
|
|
func show_object(object_id: String) -> void:
|
|
_object_id = object_id
|
|
if not _is_built:
|
|
_build_ui()
|
|
|
|
if object_id == "" or _core == null:
|
|
if _empty_label != null:
|
|
_empty_label.visible = true
|
|
if _fields_box != null:
|
|
_fields_box.visible = false
|
|
return
|
|
|
|
var obj := _core.model.find_object(object_id)
|
|
if obj == null:
|
|
if _empty_label != null:
|
|
_empty_label.visible = true
|
|
if _fields_box != null:
|
|
_fields_box.visible = false
|
|
return
|
|
|
|
if _empty_label != null:
|
|
_empty_label.visible = false
|
|
if _fields_box != null:
|
|
_fields_box.visible = true
|
|
if _rule_form_box != null:
|
|
_rule_form_box.visible = false
|
|
if _add_rule_btn != null:
|
|
_add_rule_btn.visible = true
|
|
|
|
_updating = true
|
|
_id_label.text = "id: %s" % obj.id
|
|
_type_label.text = "type: %s" % obj.type
|
|
_asset_label.text = "asset: %s" % obj.asset_id
|
|
_pos_x.value = obj.position.x
|
|
_pos_y.value = obj.position.y
|
|
_pos_z.value = obj.position.z
|
|
_yaw.value = rad_to_deg(Quaternion(obj.rotation.x, obj.rotation.y, obj.rotation.z, obj.rotation.w).get_euler().y)
|
|
_scale_x.value = obj.scale.x
|
|
_scale_y.value = obj.scale.y
|
|
_scale_z.value = obj.scale.z
|
|
_updating = false
|
|
|
|
_refresh_animation_and_motion()
|
|
_refresh_rules()
|
|
|
|
|
|
func _refresh_animation_and_motion() -> void:
|
|
if _core == null or _object_id == "":
|
|
return
|
|
var obj := _core.model.find_object(_object_id)
|
|
if obj == null:
|
|
return
|
|
|
|
_updating = true
|
|
|
|
# Refresh Animation
|
|
_anim_clip_option.clear()
|
|
_anim_clip_option.add_item("(None)", 0)
|
|
_anim_clip_option.set_item_metadata(0, "")
|
|
var avail: Array = obj.properties.get("available_animations", [])
|
|
var anim_cfg: Dictionary = obj.properties.get("animation", {})
|
|
var current_clip: String = str(anim_cfg.get("clip", obj.properties.get("active_animation", "")))
|
|
|
|
var selected_idx := 0
|
|
for i in range(avail.size()):
|
|
var clip_name := str(avail[i])
|
|
_anim_clip_option.add_item(clip_name, i + 1)
|
|
_anim_clip_option.set_item_metadata(i + 1, clip_name)
|
|
if clip_name == current_clip:
|
|
selected_idx = i + 1
|
|
_anim_clip_option.selected = selected_idx
|
|
_anim_autoplay_check.button_pressed = bool(anim_cfg.get("autoplay", true))
|
|
_anim_loop_check.button_pressed = bool(anim_cfg.get("loop", true))
|
|
_anim_speed_spin.value = float(anim_cfg.get("speed", 1.0))
|
|
|
|
# Refresh Motion
|
|
var motion_cfg: Dictionary = obj.properties.get("motion", {})
|
|
var m_type := str(motion_cfg.get("motion_type", "none")).to_lower()
|
|
match m_type:
|
|
"rotate": _motion_type_option.selected = 1
|
|
"bob", "oscillate": _motion_type_option.selected = 2
|
|
"waypoint", "patrol": _motion_type_option.selected = 3
|
|
_: _motion_type_option.selected = 0
|
|
|
|
var m_trig := str(motion_cfg.get("trigger_mode", "always")).to_lower()
|
|
match m_trig:
|
|
"proximity": _motion_trigger_option.selected = 1
|
|
"interact": _motion_trigger_option.selected = 2
|
|
"signal": _motion_trigger_option.selected = 3
|
|
_: _motion_trigger_option.selected = 0
|
|
|
|
_motion_speed_spin.value = float(motion_cfg.get("rotation_speed", 45.0))
|
|
_motion_bob_amp_spin.value = float(motion_cfg.get("bob_amplitude", 0.5))
|
|
_motion_bob_freq_spin.value = float(motion_cfg.get("bob_frequency", 1.0))
|
|
_motion_wp_speed_spin.value = float(motion_cfg.get("move_speed", 2.0))
|
|
_motion_wp_pause_spin.value = float(motion_cfg.get("pause_time", 0.0))
|
|
|
|
_current_waypoints.clear()
|
|
var raw_wps = motion_cfg.get("waypoints", [])
|
|
if raw_wps is Array:
|
|
for wp in raw_wps:
|
|
if wp is Array and wp.size() >= 3:
|
|
_current_waypoints.append(Vector3(wp[0], wp[1], wp[2]))
|
|
elif wp is Vector3:
|
|
_current_waypoints.append(wp)
|
|
_motion_wp_count_label.text = "Waypoints: %d" % _current_waypoints.size()
|
|
|
|
_on_motion_type_selected(_motion_type_option.selected)
|
|
_updating = false
|
|
|
|
|
|
func _on_animation_changed(_idx: int) -> void:
|
|
if _updating or _core == null or _object_id == "":
|
|
return
|
|
var clip_meta = _anim_clip_option.get_item_metadata(_anim_clip_option.selected)
|
|
var clip_name := str(clip_meta) if clip_meta != null else ""
|
|
var anim_dict := {
|
|
"clip": clip_name,
|
|
"autoplay": _anim_autoplay_check.button_pressed,
|
|
"loop": _anim_loop_check.button_pressed,
|
|
"speed": _anim_speed_spin.value
|
|
}
|
|
_core.set_object_property(_object_id, "animation", anim_dict)
|
|
|
|
|
|
func _on_motion_type_selected(idx: int) -> void:
|
|
var m_type = _motion_type_option.get_item_metadata(idx)
|
|
if m_type == null:
|
|
m_type = "none"
|
|
_motion_rotate_box.visible = (m_type == "rotate")
|
|
_motion_bob_box.visible = (m_type == "bob")
|
|
_motion_waypoint_box.visible = (m_type == "waypoint")
|
|
if not _updating:
|
|
_on_motion_param_changed()
|
|
|
|
|
|
func _on_motion_param_changed() -> void:
|
|
if _updating or _core == null or _object_id == "":
|
|
return
|
|
var type_meta = _motion_type_option.get_item_metadata(_motion_type_option.selected)
|
|
var trig_meta = _motion_trigger_option.get_item_metadata(_motion_trigger_option.selected)
|
|
var m_type := str(type_meta) if type_meta != null else "none"
|
|
var m_trig := str(trig_meta) if trig_meta != null else "always"
|
|
|
|
var wps_ser: Array = []
|
|
for wp in _current_waypoints:
|
|
wps_ser.append([wp.x, wp.y, wp.z])
|
|
|
|
var motion_dict := {
|
|
"motion_type": m_type,
|
|
"trigger_mode": m_trig,
|
|
"rotation_speed": _motion_speed_spin.value,
|
|
"bob_amplitude": _motion_bob_amp_spin.value,
|
|
"bob_frequency": _motion_bob_freq_spin.value,
|
|
"move_speed": _motion_wp_speed_spin.value,
|
|
"pause_time": _motion_wp_pause_spin.value,
|
|
"waypoints": wps_ser
|
|
}
|
|
_core.set_object_property(_object_id, "motion", motion_dict)
|
|
|
|
|
|
func _on_add_current_pos_waypoint() -> void:
|
|
if _core == null or _object_id == "":
|
|
return
|
|
var obj := _core.model.find_object(_object_id)
|
|
if obj != null:
|
|
_current_waypoints.append(obj.position)
|
|
_motion_wp_count_label.text = "Waypoints: %d" % _current_waypoints.size()
|
|
_on_motion_param_changed()
|
|
|
|
|
|
func _on_clear_waypoints() -> void:
|
|
_current_waypoints.clear()
|
|
_motion_wp_count_label.text = "Waypoints: 0"
|
|
_on_motion_param_changed()
|
|
|
|
|
|
func _refresh_rules() -> void:
|
|
for child in _rules_vbox.get_children():
|
|
child.queue_free()
|
|
|
|
if _core == null or _object_id == "":
|
|
return
|
|
|
|
var obj := _core.model.find_object(_object_id)
|
|
if obj == null:
|
|
return
|
|
|
|
var b_dict: Variant = obj.properties.get("script_behavior", obj.properties.get("behavior"))
|
|
var rules_arr: Array = []
|
|
if b_dict is Dictionary and b_dict.has("rules") and b_dict["rules"] is Array:
|
|
rules_arr = b_dict["rules"]
|
|
|
|
if rules_arr.is_empty():
|
|
var empty_lbl := Label.new()
|
|
empty_lbl.text = "(No behavior rules attached)"
|
|
empty_lbl.add_theme_font_size_override("font_size", 11)
|
|
empty_lbl.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6))
|
|
_rules_vbox.add_child(empty_lbl)
|
|
return
|
|
|
|
for i in range(rules_arr.size()):
|
|
var rule: Variant = rules_arr[i]
|
|
if not (rule is Dictionary):
|
|
continue
|
|
var card := _create_rule_card(i, rule as Dictionary)
|
|
_rules_vbox.add_child(card)
|
|
|
|
|
|
func _create_rule_card(rule_index: int, rule: Dictionary) -> Control:
|
|
var panel := PanelContainer.new()
|
|
var row := HBoxContainer.new()
|
|
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
panel.add_child(row)
|
|
|
|
var text_box := VBoxContainer.new()
|
|
text_box.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
row.add_child(text_box)
|
|
|
|
var trigger_dict: Dictionary = rule.get("trigger", {})
|
|
var trig_type: String = str(trigger_dict.get("type", "unknown"))
|
|
var trig_label := Label.new()
|
|
if trig_type == "on_timer":
|
|
trig_label.text = "⏱️ on_timer (every %ss)" % str(trigger_dict.get("interval", 1.0))
|
|
elif trig_type == "on_touch":
|
|
trig_label.text = "💥 on_touch"
|
|
elif trig_type == "on_enter":
|
|
trig_label.text = "🚶 on_enter"
|
|
else:
|
|
trig_label.text = "⚡ on_interact"
|
|
trig_label.add_theme_font_size_override("font_size", 12)
|
|
trig_label.add_theme_color_override("font_color", Color(0.4, 0.9, 1.0))
|
|
text_box.add_child(trig_label)
|
|
|
|
var actions_arr: Array = rule.get("actions", [])
|
|
for act in actions_arr:
|
|
if not (act is Dictionary):
|
|
continue
|
|
var act_dict: Dictionary = act
|
|
var verb: String = str(act_dict.get("verb", ""))
|
|
var act_lbl := Label.new()
|
|
act_lbl.add_theme_font_size_override("font_size", 11)
|
|
match verb:
|
|
"give_item":
|
|
var item_name: String = str(act_dict.get("item_id", "buffalo_chips"))
|
|
if item_name == "buffalo_chips":
|
|
item_name = "Buffalo Chips"
|
|
act_lbl.text = "→ Give %d %s" % [int(act_dict.get("count", 1)), item_name]
|
|
"rotate":
|
|
act_lbl.text = "→ Rotate %.0f° (%s axis)" % [float(act_dict.get("degrees", 90)), str(act_dict.get("axis", "y"))]
|
|
"move":
|
|
var off: Array = act_dict.get("offset", [0, 0, 0])
|
|
act_lbl.text = "→ Move offset %s" % str(off)
|
|
"play_sound":
|
|
act_lbl.text = "→ Play sound '%s'" % str(act_dict.get("sound_id", ""))
|
|
"spawn_from_catalog":
|
|
act_lbl.text = "→ Spawn '%s'" % str(act_dict.get("catalog_id", ""))
|
|
_:
|
|
act_lbl.text = "→ %s" % verb
|
|
text_box.add_child(act_lbl)
|
|
|
|
var del_btn := Button.new()
|
|
del_btn.text = "✕"
|
|
del_btn.tooltip_text = "Delete this rule"
|
|
del_btn.pressed.connect(_on_delete_rule_pressed.bind(rule_index))
|
|
row.add_child(del_btn)
|
|
|
|
return panel
|
|
|
|
|
|
func _on_add_rule_btn_pressed() -> void:
|
|
_rule_form_box.visible = true
|
|
_add_rule_btn.visible = false
|
|
|
|
|
|
func _on_cancel_rule_pressed() -> void:
|
|
_rule_form_box.visible = false
|
|
_add_rule_btn.visible = true
|
|
|
|
|
|
func _on_trigger_changed(index: int) -> void:
|
|
var trig_key: String = _trigger_option.get_item_metadata(index)
|
|
_timer_row.visible = (trig_key == "on_timer")
|
|
|
|
|
|
func _on_action_changed(index: int) -> void:
|
|
var act_key: String = _action_option.get_item_metadata(index)
|
|
_action_item_box.visible = (act_key == "give_item")
|
|
_action_rotate_box.visible = (act_key == "rotate")
|
|
_action_move_box.visible = (act_key == "move")
|
|
_action_sound_box.visible = (act_key == "play_sound")
|
|
_action_spawn_box.visible = (act_key == "spawn_from_catalog")
|
|
|
|
|
|
func _on_save_rule_pressed() -> void:
|
|
if _core == null or _object_id == "":
|
|
return
|
|
|
|
var obj := _core.model.find_object(_object_id)
|
|
if obj == null:
|
|
return
|
|
|
|
var trig_type: String = _trigger_option.get_item_metadata(_trigger_option.selected)
|
|
var trig_dict: Dictionary = {"type": trig_type}
|
|
if trig_type == "on_timer":
|
|
trig_dict["interval"] = _timer_interval_spin.value
|
|
|
|
var act_verb: String = _action_option.get_item_metadata(_action_option.selected)
|
|
var act_dict: Dictionary = {"verb": act_verb}
|
|
match act_verb:
|
|
"give_item":
|
|
var item_id := _item_id_edit.text.strip_edges()
|
|
if item_id.is_empty():
|
|
item_id = "buffalo_chips"
|
|
act_dict["item_id"] = item_id
|
|
act_dict["count"] = int(_item_count_spin.value)
|
|
"rotate":
|
|
var axis_idx := _rotate_axis_option.selected
|
|
act_dict["axis"] = _rotate_axis_option.get_item_metadata(axis_idx)
|
|
act_dict["degrees"] = _rotate_degrees_spin.value
|
|
act_dict["duration"] = _rotate_duration_spin.value
|
|
act_dict["target"] = "self"
|
|
"move":
|
|
act_dict["target"] = "self"
|
|
act_dict["offset"] = [_move_x_spin.value, _move_y_spin.value, _move_z_spin.value]
|
|
act_dict["duration"] = _move_duration_spin.value
|
|
"play_sound":
|
|
var snd := _sound_id_edit.text.strip_edges()
|
|
if snd.is_empty():
|
|
snd = "bell"
|
|
act_dict["sound_id"] = snd
|
|
"spawn_from_catalog":
|
|
var cat_id := _spawn_catalog_id_edit.text.strip_edges()
|
|
if cat_id.is_empty():
|
|
cat_id = "cube"
|
|
act_dict["catalog_id"] = cat_id
|
|
act_dict["position"] = [_spawn_x_spin.value, _spawn_y_spin.value, _spawn_z_spin.value]
|
|
|
|
var new_rule: Dictionary = {
|
|
"trigger": trig_dict,
|
|
"conditions": [],
|
|
"actions": [act_dict]
|
|
}
|
|
|
|
var cur_behavior: Dictionary = {}
|
|
var raw_b = obj.properties.get("script_behavior", obj.properties.get("behavior"))
|
|
if raw_b is Dictionary:
|
|
cur_behavior = (raw_b as Dictionary).duplicate(true)
|
|
|
|
var cur_rules: Array = cur_behavior.get("rules", []).duplicate(true)
|
|
cur_rules.append(new_rule)
|
|
cur_behavior["rules"] = cur_rules
|
|
|
|
_core.set_object_behavior(_object_id, cur_behavior)
|
|
_rule_form_box.visible = false
|
|
_add_rule_btn.visible = true
|
|
_refresh_rules()
|
|
|
|
|
|
func _on_delete_rule_pressed(index: int) -> void:
|
|
if _core == null or _object_id == "":
|
|
return
|
|
|
|
var obj := _core.model.find_object(_object_id)
|
|
if obj == null:
|
|
return
|
|
|
|
var raw_b = obj.properties.get("script_behavior", obj.properties.get("behavior"))
|
|
if not (raw_b is Dictionary):
|
|
return
|
|
|
|
var cur_behavior: Dictionary = (raw_b as Dictionary).duplicate(true)
|
|
var cur_rules: Array = cur_behavior.get("rules", []).duplicate(true)
|
|
if index >= 0 and index < cur_rules.size():
|
|
cur_rules.remove_at(index)
|
|
cur_behavior["rules"] = cur_rules
|
|
_core.set_object_behavior(_object_id, cur_behavior)
|
|
_refresh_rules()
|
|
|
|
|
|
func _on_position_changed(_v: float) -> void:
|
|
if _updating or _core == null or _object_id == "":
|
|
return
|
|
_core.move_object(_object_id, Vector3(_pos_x.value, _pos_y.value, _pos_z.value))
|
|
|
|
|
|
func _on_yaw_changed(v: float) -> void:
|
|
if _updating or _core == null or _object_id == "":
|
|
return
|
|
var quat := Quaternion(Vector3.UP, deg_to_rad(v))
|
|
_core.rotate_object(_object_id, Vector4(quat.x, quat.y, quat.z, quat.w))
|
|
|
|
|
|
func _on_scale_changed(_v: float) -> void:
|
|
if _updating or _core == null or _object_id == "":
|
|
return
|
|
_core.scale_object(_object_id, Vector3(_scale_x.value, _scale_y.value, _scale_z.value))
|
|
|
|
|
|
func _on_delete_pressed() -> void:
|
|
if _object_id == "":
|
|
return
|
|
var id := _object_id
|
|
delete_requested.emit(id)
|
|
|
|
|
|
func _add_readonly_row(label: String) -> Label:
|
|
var l := Label.new()
|
|
l.text = "%s: " % label
|
|
_fields_box.add_child(l)
|
|
return l
|
|
|
|
|
|
func _add_spinbox_row(label_text: String) -> SpinBox:
|
|
var row := HBoxContainer.new()
|
|
var l := Label.new()
|
|
l.text = label_text
|
|
l.custom_minimum_size = Vector2(90, 0)
|
|
row.add_child(l)
|
|
var sb := SpinBox.new()
|
|
sb.min_value = -100000.0
|
|
sb.max_value = 100000.0
|
|
sb.step = 0.1
|
|
sb.allow_greater = true
|
|
sb.allow_lesser = true
|
|
row.add_child(sb)
|
|
_fields_box.add_child(row)
|
|
return sb
|