mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-21 02:43:48 +00:00
Add enemy creation and viewer functionality with filtering options
This commit is contained in:
parent
e735060f93
commit
18683c0680
32 changed files with 1009 additions and 411 deletions
228
addons/weapon_creator/EnemyViewer.gd
Normal file
228
addons/weapon_creator/EnemyViewer.gd
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
@tool
|
||||
extends BaseViewer
|
||||
|
||||
# Displays all enemies from directory structure or database
|
||||
# Shows icon sprite, name, and 2D/3D badge
|
||||
|
||||
signal enemy_selected(enemy_resource_path: String)
|
||||
signal duplicate_enemy_requested(enemy_data: Dictionary)
|
||||
signal enemy_deleted(enemy_name: String, enemy_path: String)
|
||||
signal enemy_duplication_started(enemy_name: String, is_3d: bool)
|
||||
|
||||
var _enemies_dir := "res://Resources/Enemies/"
|
||||
|
||||
const SETTING_SHOW_2D = "weapon_creator/enemy_filter_show_2d"
|
||||
const SETTING_SHOW_3D = "weapon_creator/enemy_filter_show_3d"
|
||||
|
||||
func _supports_2d_3d_filter() -> bool:
|
||||
return true
|
||||
|
||||
func _get_filter_setting_2d_key() -> String:
|
||||
return SETTING_SHOW_2D
|
||||
|
||||
func _get_filter_setting_3d_key() -> String:
|
||||
return SETTING_SHOW_3D
|
||||
|
||||
func _build_header_buttons(header_hbox: HBoxContainer) -> void:
|
||||
var create_button = Button.new()
|
||||
create_button.text = "Create Enemy"
|
||||
create_button.pressed.connect(_on_create_enemy_pressed)
|
||||
header_hbox.add_child(create_button)
|
||||
|
||||
func refresh() -> void:
|
||||
_clear_grid()
|
||||
|
||||
if not DirAccess.dir_exists_absolute(_enemies_dir):
|
||||
_add_error_label("Enemies directory not found: " + _enemies_dir)
|
||||
return
|
||||
|
||||
var show_2d = _should_show_2d()
|
||||
var show_3d = _should_show_3d()
|
||||
|
||||
var enemy_count = 0
|
||||
|
||||
# Load 2D enemies
|
||||
if show_2d:
|
||||
enemy_count += _load_enemies_from_directory(_enemies_dir, false)
|
||||
|
||||
# Load 3D enemies
|
||||
if show_3d:
|
||||
enemy_count += _load_enemies_from_directory(_enemies_dir, true)
|
||||
|
||||
if enemy_count == 0:
|
||||
_add_info_label("No enemies found")
|
||||
|
||||
func _load_enemies_from_directory(dir_path: String, is_3d: bool) -> int:
|
||||
var dir = DirAccess.open(dir_path)
|
||||
if dir == null:
|
||||
return 0
|
||||
|
||||
var count = 0
|
||||
dir.list_dir_begin()
|
||||
var file_name = dir.get_next()
|
||||
|
||||
while file_name != "":
|
||||
if not dir.current_is_dir() and (file_name.ends_with(".tres") or file_name.ends_with(".res")):
|
||||
var enemy_path = dir_path + file_name
|
||||
|
||||
# Check if this matches the 2D/3D filter based on filename
|
||||
var file_is_3d = "_3D" in file_name
|
||||
if file_is_3d != is_3d:
|
||||
file_name = dir.get_next()
|
||||
continue
|
||||
|
||||
var enemy_resource = load(enemy_path)
|
||||
|
||||
if enemy_resource and enemy_resource.get_script():
|
||||
var script_path = enemy_resource.get_script().resource_path
|
||||
if script_path and "EnemyResource.cs" in script_path:
|
||||
_create_enemy_card(enemy_resource, enemy_path, is_3d)
|
||||
count += 1
|
||||
|
||||
file_name = dir.get_next()
|
||||
|
||||
dir.list_dir_end()
|
||||
return count
|
||||
|
||||
func _create_enemy_card(enemy_resource: Resource, enemy_path: String, is_3d: bool) -> void:
|
||||
var card = PanelContainer.new()
|
||||
card.custom_minimum_size = Vector2(120, 140)
|
||||
card.tooltip_text = enemy_path
|
||||
_grid_container.add_child(card)
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 4)
|
||||
card.add_child(vbox)
|
||||
|
||||
var preview_container = CenterContainer.new()
|
||||
preview_container.custom_minimum_size = Vector2(0, 64)
|
||||
vbox.add_child(preview_container)
|
||||
|
||||
var icon_sprite = enemy_resource.get("IconSprite")
|
||||
if icon_sprite:
|
||||
var texture_rect = TextureRect.new()
|
||||
texture_rect.texture = icon_sprite
|
||||
texture_rect.custom_minimum_size = Vector2(64, 64)
|
||||
texture_rect.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL
|
||||
texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
preview_container.add_child(texture_rect)
|
||||
else:
|
||||
var placeholder = Label.new()
|
||||
placeholder.text = "No Icon"
|
||||
placeholder.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
preview_container.add_child(placeholder)
|
||||
|
||||
var enemy_name = enemy_resource.get("EnemyName")
|
||||
var name_label = Label.new()
|
||||
name_label.text = str(enemy_name) if enemy_name else "Unnamed"
|
||||
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
name_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
vbox.add_child(name_label)
|
||||
|
||||
var mode_badge = Label.new()
|
||||
mode_badge.text = "3D" if is_3d else "2D"
|
||||
mode_badge.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
mode_badge.add_theme_color_override("font_color", Color.CYAN if is_3d else Color.LIGHT_CORAL)
|
||||
vbox.add_child(mode_badge)
|
||||
|
||||
var button_hbox = HBoxContainer.new()
|
||||
button_hbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
button_hbox.add_theme_constant_override("separation", 4)
|
||||
vbox.add_child(button_hbox)
|
||||
|
||||
var view_button = Button.new()
|
||||
view_button.text = "View"
|
||||
view_button.pressed.connect(_on_view_enemy_pressed.bind(enemy_path))
|
||||
button_hbox.add_child(view_button)
|
||||
|
||||
var duplicate_button = Button.new()
|
||||
duplicate_button.text = "Duplicate"
|
||||
duplicate_button.pressed.connect(_on_duplicate_enemy_pressed.bind(enemy_resource, enemy_path, is_3d))
|
||||
button_hbox.add_child(duplicate_button)
|
||||
|
||||
var delete_button = Button.new()
|
||||
delete_button.text = "Delete"
|
||||
delete_button.pressed.connect(_on_delete_enemy_pressed.bind(enemy_resource, enemy_path))
|
||||
button_hbox.add_child(delete_button)
|
||||
|
||||
func _on_view_enemy_pressed(enemy_path: String) -> void:
|
||||
if _editor_interface:
|
||||
_editor_interface.edit_resource(load(enemy_path))
|
||||
enemy_selected.emit(enemy_path)
|
||||
|
||||
func _on_duplicate_enemy_pressed(enemy_resource: Resource, enemy_path: String, is_3d: bool) -> void:
|
||||
var enemy_name = enemy_resource.get("EnemyName")
|
||||
enemy_duplication_started.emit(str(enemy_name), is_3d)
|
||||
|
||||
var enemy_data = {
|
||||
"enemy_name": str(enemy_resource.get("EnemyName")),
|
||||
"enemy_key": str(enemy_resource.get("EnemyKey")) + "_COPY",
|
||||
"prefab_path": str(enemy_resource.get("PrefabPath")),
|
||||
"is_3d": is_3d,
|
||||
"max_health": float(enemy_resource.get("MaxHealth")),
|
||||
"movement_speed": float(enemy_resource.get("MovementSpeed")),
|
||||
"motivation_reward": float(enemy_resource.get("MotivationReward")),
|
||||
"weapon_resource": enemy_resource.get("Weapon"),
|
||||
"player_detection_range": float(enemy_resource.get("PlayerDetectionRange")),
|
||||
"view_range": float(enemy_resource.get("ViewRange")),
|
||||
"alarm_react_range": float(enemy_resource.get("AlarmReactRange")),
|
||||
"player_disengage_range": float(enemy_resource.get("PlayerDisengageRange")),
|
||||
"strafe_speed": float(enemy_resource.get("StrafeSpeed")),
|
||||
"max_strafe_distance": float(enemy_resource.get("MaxStrafeDistance")),
|
||||
"min_strafe_distance": float(enemy_resource.get("MinStrafeDistance")),
|
||||
"response_time": float(enemy_resource.get("ResponseTime")),
|
||||
"predict_player": bool(enemy_resource.get("PredictPlayer")),
|
||||
"icon_sprite": enemy_resource.get("IconSprite")
|
||||
}
|
||||
|
||||
var dialog_script = load("res://addons/weapon_creator/EnemyCreatorDialog.gd")
|
||||
var dialog = Window.new()
|
||||
dialog.set_script(dialog_script)
|
||||
dialog.setup(_editor_interface, enemy_data)
|
||||
add_child(dialog)
|
||||
dialog.popup_centered()
|
||||
|
||||
dialog.enemy_data_confirmed.connect(func(data):
|
||||
duplicate_enemy_requested.emit(data)
|
||||
)
|
||||
|
||||
func _on_delete_enemy_pressed(enemy_resource: Resource, enemy_path: String) -> void:
|
||||
var enemy_name = enemy_resource.get("EnemyName")
|
||||
var confirm_dialog = ConfirmationDialog.new()
|
||||
confirm_dialog.dialog_text = "Are you sure you want to delete enemy '" + str(enemy_name) + "'?\nThis cannot be undone."
|
||||
confirm_dialog.title = "Confirm Deletion"
|
||||
add_child(confirm_dialog)
|
||||
confirm_dialog.popup_centered()
|
||||
|
||||
confirm_dialog.confirmed.connect(func():
|
||||
_delete_enemy_file(enemy_path)
|
||||
enemy_deleted.emit(str(enemy_name), enemy_path)
|
||||
refresh()
|
||||
confirm_dialog.queue_free()
|
||||
)
|
||||
|
||||
confirm_dialog.canceled.connect(func():
|
||||
confirm_dialog.queue_free()
|
||||
)
|
||||
|
||||
func _delete_enemy_file(enemy_path: String) -> void:
|
||||
var err = DirAccess.remove_absolute(enemy_path)
|
||||
if err != OK:
|
||||
push_error("Failed to delete enemy file: " + enemy_path)
|
||||
else:
|
||||
if _editor_interface:
|
||||
_editor_interface.get_resource_filesystem().scan()
|
||||
|
||||
func _on_create_enemy_pressed() -> void:
|
||||
var dialog_script = load("res://addons/weapon_creator/EnemyCreatorDialog.gd")
|
||||
var dialog = Window.new()
|
||||
dialog.set_script(dialog_script)
|
||||
dialog.setup(_editor_interface)
|
||||
add_child(dialog)
|
||||
dialog.popup_centered()
|
||||
|
||||
dialog.enemy_data_confirmed.connect(func(enemy_data):
|
||||
duplicate_enemy_requested.emit(enemy_data)
|
||||
)
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue