feat(studio,stacking,animations,collections): M16 ModBison Studio, object stacking, model animations, and poly collections for MVP 2
checkpoint / checkpoint (push) Successful in 6m32s
checkpoint / checkpoint (push) Successful in 6m32s
This commit is contained in:
@@ -25,6 +25,10 @@ var cell_size: float = 24.0
|
||||
var pending_catalog_id: String = ""
|
||||
## Tile id the next "brush" click paints. Cycled with number keys 0-9.
|
||||
var current_tile_id: int = 1
|
||||
## Elevation offset for placement and hotkey nudging (Q / E).
|
||||
var current_elevation_offset: float = 0.0
|
||||
## Whether to align placed object to surface normal slope.
|
||||
var align_surface_normal: bool = false
|
||||
|
||||
var _hover_tx: int = -1
|
||||
var _hover_ty: int = -1
|
||||
@@ -119,6 +123,32 @@ func _draw() -> void:
|
||||
display_name = obj.id
|
||||
draw_string(default_font, center + Vector2(-r * 1.5, -r - 2), display_name.left(6), HORIZONTAL_ALIGNMENT_CENTER, int(r * 3.0), 10, Color(1, 1, 1, 0.9))
|
||||
|
||||
# Draw motion waypoint path & gizmos for selected object
|
||||
var sel_id := core.selection.get_selected()
|
||||
if sel_id != "":
|
||||
var sel_obj := core.model.find_object(sel_id)
|
||||
if sel_obj != null and sel_obj.properties is Dictionary and sel_obj.properties.has("motion"):
|
||||
var m_cfg = sel_obj.properties["motion"]
|
||||
if m_cfg is Dictionary and m_cfg.has("waypoints") and m_cfg["waypoints"] is Array:
|
||||
var wps: Array = m_cfg["waypoints"]
|
||||
if not wps.is_empty():
|
||||
var prev_pt := Vector2(sel_obj.position.x * cell_size, sel_obj.position.y * cell_size)
|
||||
for i in range(wps.size()):
|
||||
var wp_val = wps[i]
|
||||
var wp_pos := Vector2.ZERO
|
||||
if wp_val is Array and wp_val.size() >= 2:
|
||||
wp_pos = Vector2(float(wp_val[0]) * cell_size, float(wp_val[1]) * cell_size)
|
||||
elif wp_val is Vector3:
|
||||
wp_pos = Vector2(wp_val.x * cell_size, wp_val.y * cell_size)
|
||||
|
||||
draw_line(prev_pt, wp_pos, Color(0.2, 0.85, 1.0, 0.8), 2.0)
|
||||
var wr := cell_size * 0.28
|
||||
draw_circle(wp_pos, wr, Color(0.1, 0.6, 0.9, 0.85))
|
||||
draw_arc(wp_pos, wr, 0.0, TAU, 16, Color(1, 1, 1, 0.9), 1.5)
|
||||
draw_string(default_font, wp_pos + Vector2(-4, 4), str(i + 1), HORIZONTAL_ALIGNMENT_CENTER, 8, 10, Color(1, 1, 1))
|
||||
prev_pt = wp_pos
|
||||
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if core == null:
|
||||
@@ -155,6 +185,57 @@ func _gui_input(event: InputEvent) -> void:
|
||||
func _handle_key(key: InputEventKey) -> void:
|
||||
if key.keycode >= KEY_0 and key.keycode <= KEY_9:
|
||||
current_tile_id = key.keycode - KEY_0
|
||||
elif key.keycode == KEY_E:
|
||||
current_elevation_offset += 0.5
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
core.adjust_object_elevation(sel, 0.5)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_Q:
|
||||
current_elevation_offset = maxf(0.0, current_elevation_offset - 0.5)
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
core.adjust_object_elevation(sel, -0.5)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_END:
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
if core.drop_object_to_surface(sel):
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_R:
|
||||
align_surface_normal = not align_surface_normal
|
||||
elif key.keycode == KEY_W:
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "" and _hover_tx >= 0 and _hover_ty >= 0:
|
||||
var obj := core.model.find_object(sel)
|
||||
if obj != null:
|
||||
var motion_dict = obj.properties.get("motion", {"motion_type": "waypoint", "waypoints": []})
|
||||
if not (motion_dict is Dictionary):
|
||||
motion_dict = {"motion_type": "waypoint", "waypoints": []}
|
||||
var wps = motion_dict.get("waypoints", [])
|
||||
wps.append([float(_hover_tx), float(_hover_ty), obj.position.z])
|
||||
motion_dict["motion_type"] = "waypoint"
|
||||
motion_dict["waypoints"] = wps
|
||||
core.set_object_property(sel, "motion", motion_dict)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
elif key.keycode == KEY_BACKSPACE:
|
||||
var sel := core.selection.get_selected()
|
||||
if sel != "":
|
||||
var obj := core.model.find_object(sel)
|
||||
if obj != null and obj.properties is Dictionary and obj.properties.has("motion"):
|
||||
var motion_dict = obj.properties["motion"]
|
||||
if motion_dict is Dictionary and motion_dict.has("waypoints") and motion_dict["waypoints"] is Array:
|
||||
var wps: Array = motion_dict["waypoints"]
|
||||
if not wps.is_empty():
|
||||
wps.pop_back()
|
||||
motion_dict["waypoints"] = wps
|
||||
core.set_object_property(sel, "motion", motion_dict)
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _handle_click(local_pos: Vector2) -> void:
|
||||
@@ -176,7 +257,22 @@ func _handle_click(local_pos: Vector2) -> void:
|
||||
queue_redraw()
|
||||
"object_place":
|
||||
if pending_catalog_id != "":
|
||||
var new_id := core.place_object(pending_catalog_id, Vector3(tx + 0.5, ty + 0.5, 0.0))
|
||||
var existing_hit := _highest_object_at(tx, ty)
|
||||
var new_id: String = ""
|
||||
if existing_hit != "":
|
||||
new_id = core.place_object_stacked(
|
||||
pending_catalog_id,
|
||||
existing_hit,
|
||||
Vector3(tx + 0.5, ty + 0.5, 0.0),
|
||||
Vector3(0, 0, 1),
|
||||
current_elevation_offset,
|
||||
align_surface_normal
|
||||
)
|
||||
else:
|
||||
new_id = core.place_object(
|
||||
pending_catalog_id,
|
||||
Vector3(tx + 0.5, ty + 0.5, current_elevation_offset)
|
||||
)
|
||||
if new_id != "":
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
@@ -191,3 +287,53 @@ func _object_at(tx: int, ty: int) -> String:
|
||||
if int(floor(obj.position.x)) == tx and int(floor(obj.position.y)) == ty:
|
||||
return obj.id
|
||||
return ""
|
||||
|
||||
|
||||
func _highest_object_at(tx: int, ty: int) -> String:
|
||||
var best_id := ""
|
||||
var max_z: float = -1.0
|
||||
for obj in core.model.payload.objects:
|
||||
if int(floor(obj.position.x)) == tx and int(floor(obj.position.y)) == ty:
|
||||
if obj.position.z > max_z:
|
||||
max_z = obj.position.z
|
||||
best_id = obj.id
|
||||
return best_id
|
||||
|
||||
|
||||
func _can_drop_data(_at_position: Vector2, data: Variant) -> bool:
|
||||
if data is Dictionary and data.get("type") == "model_placement":
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _drop_data(at_position: Vector2, data: Variant) -> void:
|
||||
if not _can_drop_data(at_position, data):
|
||||
return
|
||||
if core == null:
|
||||
return
|
||||
var dict: Dictionary = data as Dictionary
|
||||
var cat_id: String = dict.get("catalog_id", "")
|
||||
if cat_id == "":
|
||||
cat_id = dict.get("model_name", "object").to_lower().replace(" ", "_")
|
||||
|
||||
var tx := int(floor(at_position.x / cell_size))
|
||||
var ty := int(floor(at_position.y / cell_size))
|
||||
var existing_hit := _highest_object_at(tx, ty)
|
||||
var new_id := ""
|
||||
if existing_hit != "":
|
||||
new_id = core.place_object_stacked(
|
||||
cat_id,
|
||||
existing_hit,
|
||||
Vector3(tx + 0.5, ty + 0.5, 0.0),
|
||||
Vector3(0, 0, 1),
|
||||
current_elevation_offset,
|
||||
align_surface_normal
|
||||
)
|
||||
else:
|
||||
new_id = core.place_object(
|
||||
cat_id,
|
||||
Vector3(tx + 0.5, ty + 0.5, current_elevation_offset)
|
||||
)
|
||||
if new_id != "":
|
||||
edit_applied.emit()
|
||||
queue_redraw()
|
||||
|
||||
@@ -35,6 +35,27 @@ 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
|
||||
@@ -140,9 +161,220 @@ func _build_ui() -> void:
|
||||
_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)
|
||||
@@ -516,9 +748,141 @@ func show_object(object_id: String) -> void:
|
||||
_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()
|
||||
|
||||
Reference in New Issue
Block a user