@tool extends BaseCreatorDialog signal enemy_data_confirmed(enemy_data: Dictionary) # UI elements var _enemy_name_edit: LineEdit var _enemy_key_edit: LineEdit var _prefab_path_edit: LineEdit var _is_3d_check: CheckBox var _icon_sprite_picker: EditorResourcePicker var _icon_sprite_preview: TextureRect var _selected_icon_sprite: Texture2D = null var _max_health_spin: SpinBox var _movement_speed_spin: SpinBox var _player_detection_range_spin: SpinBox var _view_range_spin: SpinBox var _alarm_react_range_spin: SpinBox var _player_disengage_range_spin: SpinBox var _strafe_speed_spin: SpinBox var _max_strafe_distance_spin: SpinBox var _min_strafe_distance_spin: SpinBox var _response_time_spin: SpinBox var _motivation_reward_spin: SpinBox var _predict_player_check: CheckBox var _weapon_picker: EditorResourcePicker var _selected_weapon: Resource = null func _configure_window() -> void: size = settings.get("enemy_dialog_size") if settings else Vector2i(750, 950) transient = false exclusive = false unresizable = false func _update_title() -> void: var action_text = "Duplicate" if not prefill_data.is_empty() else "Create New" title = action_text + " Enemy" func _get_saved_position() -> Vector2i: if settings: var pos = settings.get("enemy_dialog_position") return pos if pos else Vector2i.ZERO return Vector2i.ZERO func _save_dialog_size() -> void: if settings: settings.set("enemy_dialog_size", size) settings.call("save_settings") func _save_dialog_position() -> void: if settings: settings.set("enemy_dialog_position", position) settings.call("save_settings") func _build_content(container: VBoxContainer) -> void: _build_basic_info_section(container) _build_combat_stats_section(container) _build_ai_settings_section(container) _build_graphics_section(container) func _build_basic_info_section(content_vbox: VBoxContainer) -> void: content_vbox.add_child(_create_section_label("Basic Information")) var name_input = _create_input("Enemy Name:") content_vbox.add_child(name_input["container"]) _enemy_name_edit = name_input["edit"] var key_input = _create_input("Enemy Key:") content_vbox.add_child(key_input["container"]) _enemy_key_edit = key_input["edit"] var prefab_input = _create_input("Prefab Path:") content_vbox.add_child(prefab_input["container"]) _prefab_path_edit = prefab_input["edit"] _is_3d_check = CheckBox.new() _is_3d_check.text = "Is 3D" _is_3d_check.button_pressed = true content_vbox.add_child(_is_3d_check) content_vbox.add_child(HSeparator.new()) func _build_combat_stats_section(content_vbox: VBoxContainer) -> void: content_vbox.add_child(_create_section_label("Combat Stats")) var health_spin = _create_spinbox("Max Health:", 6.0, 0.1, 10000.0, 0.5) content_vbox.add_child(health_spin["container"]) _max_health_spin = health_spin["spinbox"] var movement_spin = _create_spinbox("Movement Speed:", 1.5, 0.0, 1000.0, 0.1) content_vbox.add_child(movement_spin["container"]) _movement_speed_spin = movement_spin["spinbox"] var motivation_spin = _create_spinbox("Motivation Reward:", 4.0, 0.0, 1000.0, 0.5) content_vbox.add_child(motivation_spin["container"]) _motivation_reward_spin = motivation_spin["spinbox"] # Weapon picker var weapon_hbox = HBoxContainer.new() content_vbox.add_child(weapon_hbox) var weapon_label = Label.new() weapon_label.text = "Weapon:" weapon_label.custom_minimum_size = Vector2(120, 0) weapon_hbox.add_child(weapon_label) _weapon_picker = EditorResourcePicker.new() _weapon_picker.base_type = "Resource" _weapon_picker.editable = true _weapon_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL _weapon_picker.resource_changed.connect(_on_weapon_resource_changed) weapon_hbox.add_child(_weapon_picker) content_vbox.add_child(HSeparator.new()) func _build_ai_settings_section(content_vbox: VBoxContainer) -> void: content_vbox.add_child(_create_section_label("AI Settings")) var detect_spin = _create_spinbox("Player Detection Range:", 12.0, 0.0, 1000.0, 1.0) content_vbox.add_child(detect_spin["container"]) _player_detection_range_spin = detect_spin["spinbox"] var view_spin = _create_spinbox("View Range:", 8.0, 0.0, 1000.0, 1.0) content_vbox.add_child(view_spin["container"]) _view_range_spin = view_spin["spinbox"] var alarm_spin = _create_spinbox("Alarm React Range:", 20.0, 0.0, 1000.0, 1.0) content_vbox.add_child(alarm_spin["container"]) _alarm_react_range_spin = alarm_spin["spinbox"] var disengage_spin = _create_spinbox("Player Disengage Range:", 15.0, 0.0, 1000.0, 1.0) content_vbox.add_child(disengage_spin["container"]) _player_disengage_range_spin = disengage_spin["spinbox"] var strafe_spin = _create_spinbox("Strafe Speed:", 0.0, 0.0, 1000.0, 0.1) content_vbox.add_child(strafe_spin["container"]) _strafe_speed_spin = strafe_spin["spinbox"] var max_strafe_spin = _create_spinbox("Max Strafe Distance:", 0.0, 0.0, 1000.0, 1.0) content_vbox.add_child(max_strafe_spin["container"]) _max_strafe_distance_spin = max_strafe_spin["spinbox"] var min_strafe_spin = _create_spinbox("Min Strafe Distance:", 0.0, 0.0, 1000.0, 1.0) content_vbox.add_child(min_strafe_spin["container"]) _min_strafe_distance_spin = min_strafe_spin["spinbox"] var response_spin = _create_spinbox("Response Time:", 0.5, 0.0, 10.0, 0.1) content_vbox.add_child(response_spin["container"]) _response_time_spin = response_spin["spinbox"] _predict_player_check = CheckBox.new() _predict_player_check.text = "Predict Player" _predict_player_check.button_pressed = false content_vbox.add_child(_predict_player_check) content_vbox.add_child(HSeparator.new()) func _build_graphics_section(content_vbox: VBoxContainer) -> void: content_vbox.add_child(_create_section_label("Graphics")) var icon_label = Label.new() icon_label.text = "Icon Sprite (IconSprite)" content_vbox.add_child(icon_label) var icon_container = HBoxContainer.new() icon_container.add_theme_constant_override("separation", 8) content_vbox.add_child(icon_container) var left_vbox = VBoxContainer.new() left_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL left_vbox.add_theme_constant_override("separation", 4) icon_container.add_child(left_vbox) var picker_hbox = HBoxContainer.new() picker_hbox.add_theme_constant_override("separation", 4) left_vbox.add_child(picker_hbox) var picker_label = Label.new() picker_label.text = "Icon Sprite:" picker_label.custom_minimum_size = Vector2(120, 0) picker_hbox.add_child(picker_label) _icon_sprite_picker = EditorResourcePicker.new() _icon_sprite_picker.base_type = "Texture2D" _icon_sprite_picker.editable = true _icon_sprite_picker.size_flags_horizontal = Control.SIZE_EXPAND_FILL _icon_sprite_picker.resource_changed.connect(_on_icon_sprite_resource_changed) picker_hbox.add_child(_icon_sprite_picker) var preview_container = PanelContainer.new() preview_container.custom_minimum_size = Vector2(64, 64) icon_container.add_child(preview_container) _icon_sprite_preview = TextureRect.new() _icon_sprite_preview.custom_minimum_size = Vector2(64, 64) _icon_sprite_preview.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL _icon_sprite_preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED preview_container.add_child(_icon_sprite_preview) func _on_icon_sprite_resource_changed(resource: Resource) -> void: _selected_icon_sprite = resource if resource and resource is Texture2D: _icon_sprite_preview.texture = resource else: _icon_sprite_preview.texture = null func _on_weapon_resource_changed(resource: Resource) -> void: _selected_weapon = resource func _set_default_values() -> void: if settings: _prefab_path_edit.text = settings.get("default_enemy_prefab_3d") func _apply_prefill_data() -> void: if prefill_data.is_empty(): _set_default_values() return if prefill_data.has("enemy_name"): _enemy_name_edit.text = str(prefill_data["enemy_name"]) if prefill_data.has("enemy_key"): _enemy_key_edit.text = str(prefill_data["enemy_key"]) if prefill_data.has("prefab_path"): _prefab_path_edit.text = str(prefill_data["prefab_path"]) if prefill_data.has("is_3d"): _is_3d_check.button_pressed = bool(prefill_data["is_3d"]) if prefill_data.has("max_health"): _max_health_spin.value = float(prefill_data["max_health"]) if prefill_data.has("movement_speed"): _movement_speed_spin.value = float(prefill_data["movement_speed"]) if prefill_data.has("motivation_reward"): _motivation_reward_spin.value = float(prefill_data["motivation_reward"]) if prefill_data.has("weapon_resource"): _selected_weapon = prefill_data["weapon_resource"] _weapon_picker.edited_resource = _selected_weapon if prefill_data.has("player_detection_range"): _player_detection_range_spin.value = float(prefill_data["player_detection_range"]) if prefill_data.has("view_range"): _view_range_spin.value = float(prefill_data["view_range"]) if prefill_data.has("alarm_react_range"): _alarm_react_range_spin.value = float(prefill_data["alarm_react_range"]) if prefill_data.has("player_disengage_range"): _player_disengage_range_spin.value = float(prefill_data["player_disengage_range"]) if prefill_data.has("strafe_speed"): _strafe_speed_spin.value = float(prefill_data["strafe_speed"]) if prefill_data.has("max_strafe_distance"): _max_strafe_distance_spin.value = float(prefill_data["max_strafe_distance"]) if prefill_data.has("min_strafe_distance"): _min_strafe_distance_spin.value = float(prefill_data["min_strafe_distance"]) if prefill_data.has("response_time"): _response_time_spin.value = float(prefill_data["response_time"]) if prefill_data.has("predict_player"): _predict_player_check.button_pressed = bool(prefill_data["predict_player"]) if prefill_data.has("icon_sprite") and prefill_data["icon_sprite"] != null: _selected_icon_sprite = prefill_data["icon_sprite"] _icon_sprite_preview.texture = _selected_icon_sprite _icon_sprite_picker.edited_resource = _selected_icon_sprite func _on_create_pressed() -> void: var enemy_name = _enemy_name_edit.text.strip_edges() var enemy_key = _enemy_key_edit.text.strip_edges() var prefab_path = _prefab_path_edit.text.strip_edges() if enemy_name.is_empty(): _show_error("Enemy name cannot be empty!") return if enemy_key.is_empty(): _show_error("Enemy key cannot be empty!") return if prefab_path.is_empty(): _show_error("Prefab path cannot be empty!") return var enemy_data = { "enemy_name": enemy_name, "enemy_key": enemy_key, "prefab_path": prefab_path, "is_3d": _is_3d_check.button_pressed, "max_health": _max_health_spin.value, "movement_speed": _movement_speed_spin.value, "motivation_reward": _motivation_reward_spin.value, "weapon_resource": _selected_weapon, "player_detection_range": _player_detection_range_spin.value, "view_range": _view_range_spin.value, "alarm_react_range": _alarm_react_range_spin.value, "player_disengage_range": _player_disengage_range_spin.value, "strafe_speed": _strafe_speed_spin.value, "max_strafe_distance": _max_strafe_distance_spin.value, "min_strafe_distance": _min_strafe_distance_spin.value, "response_time": _response_time_spin.value, "predict_player": _predict_player_check.button_pressed, "icon_sprite": _selected_icon_sprite } _save_dialog_size_and_position() enemy_data_confirmed.emit(enemy_data) queue_free()