mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 06:55:34 +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
54
OldExport.ps1
Normal file
54
OldExport.ps1
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Define paths
|
||||
$gitVersionPath = "dotnet-gitversion.exe"
|
||||
$godotPath = $env:GODOT
|
||||
$buildDir = ".\build"
|
||||
$configFile = "export_presets.cfg"
|
||||
$zipOutputDir = ".\release"
|
||||
$fileName = "Cirno_No_Reason"
|
||||
|
||||
# Step 1: Clear the build directory
|
||||
if (Test-Path $buildDir) {
|
||||
Remove-Item "$buildDir\*" -Recurse -Force
|
||||
} else {
|
||||
New-Item -ItemType Directory -Path $buildDir | Out-Null
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $buildDir\win | Out-Null
|
||||
New-Item -ItemType Directory -Path $buildDir\linux | Out-Null
|
||||
|
||||
# Step 2: Get version data from GitVersion
|
||||
$gitVersionOutput = & $gitVersionPath | ConvertFrom-Json
|
||||
$assemblySemFileVer = $gitVersionOutput.AssemblySemFileVer
|
||||
$assemblySemVer = $gitVersionOutput.AssemblySemVer
|
||||
$fullSemVer = $gitVersionOutput.FullSemVer
|
||||
|
||||
# Step 3: Set environment variables for use in the build process
|
||||
[System.Environment]::SetEnvironmentVariable("GIT_ASSEMBLY_SEM_FILE_VER", $assemblySemFileVer, [System.EnvironmentVariableTarget]::Process)
|
||||
[System.Environment]::SetEnvironmentVariable("GIT_ASSEMBLY_SEM_VER", $assemblySemVer, [System.EnvironmentVariableTarget]::Process)
|
||||
|
||||
Write-Host "Set environment variables:"
|
||||
Write-Host " GIT_ASSEMBLY_SEM_FILE_VER = $assemblySemFileVer"
|
||||
Write-Host " GIT_ASSEMBLY_SEM_VER = $assemblySemVer"
|
||||
|
||||
# Step 3: Update export_presets.cfg
|
||||
if (Test-Path $configFile) {
|
||||
(Get-Content $configFile) `
|
||||
-replace 'application/file_version="[^"]*"', "application/file_version=`"$assemblySemFileVer`"" `
|
||||
-replace 'application/product_version="[^"]*"', "application/product_version=`"$assemblySemVer`"" `
|
||||
| Set-Content $configFile
|
||||
} else {
|
||||
Write-Host "Warning: $configFile not found!"
|
||||
}
|
||||
|
||||
# Step 4: Export the build
|
||||
#& $godotPath --headless --export-release "Windows Desktop" "$buildDir\win\$fileName.exe"
|
||||
|
||||
#& $godotPath --headless --export-release "Linux" "$buildDir\linux\$fileName.x86_64"
|
||||
|
||||
# Step 4: Export the Windows build via CMD in the same window
|
||||
$winExportCmd = "`"$godotPath`" --headless --export-release `"Windows Desktop`" `"$buildDir\win\$fileName.exe`""
|
||||
& cmd.exe /c $winExportCmd
|
||||
|
||||
# Step 5: Export the Linux build via CMD in the same window
|
||||
$linuxExportCmd = "`"$godotPath`" --headless --export-release `"Linux`" `"$buildDir\linux\$fileName.x86_64`""
|
||||
& cmd.exe /c $linuxExportCmd
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="BossPhase" load_steps=18 format=3 uid="uid://bu3a6hv5i1qam"]
|
||||
[gd_resource type="Resource" script_class="BossPhase" format=3 uid="uid://bu3a6hv5i1qam"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cdd6q2h0t1hhq" path="res://Scripts/Resources/BossPhase.cs" id="1_4trvi"]
|
||||
[ext_resource type="Resource" uid="uid://djjp4nyufqxlp" path="res://Resources/Bullets/Boss/Reimu/Reimu_Card_Bullet_Red.tres" id="1_x3pjh"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="BossPhase" load_steps=42 format=3 uid="uid://ddb5dqocmk6x7"]
|
||||
[gd_resource type="Resource" script_class="BossPhase" format=3 uid="uid://ddb5dqocmk6x7"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dh81snen2f6bf" path="res://Scenes/Weapons/Bullets/enemyBullet_rice_blue_small.tscn" id="1_as8n2"]
|
||||
[ext_resource type="Script" uid="uid://cdd6q2h0t1hhq" path="res://Scripts/Resources/BossPhase.cs" id="1_fdlxv"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="BossPhase" load_steps=24 format=3 uid="uid://ccj0cqbveey8c"]
|
||||
[gd_resource type="Resource" script_class="BossPhase" format=3 uid="uid://ccj0cqbveey8c"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bhc7rbcico4kp" path="res://Scripts/Resources/SimpleMovementPattern.cs" id="1_lw82i"]
|
||||
[ext_resource type="PackedScene" uid="uid://dohakkayqj4w2" path="res://Scenes/Weapons/Bullets/enemyBullet_green.tscn" id="1_o4um1"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="BossPhase" load_steps=10 format=3 uid="uid://ks6fypeil6gk"]
|
||||
[gd_resource type="Resource" script_class="BossPhase" format=3 uid="uid://ks6fypeil6gk"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cdd6q2h0t1hhq" path="res://Scripts/Resources/BossPhase.cs" id="1_0cgch"]
|
||||
[ext_resource type="Script" uid="uid://bhc7rbcico4kp" path="res://Scripts/Resources/SimpleMovementPattern.cs" id="1_xksf5"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="BulletResource" load_steps=7 format=3 uid="uid://bifeoxb6nfykq"]
|
||||
[gd_resource type="Resource" script_class="BulletResource" format=3 uid="uid://bifeoxb6nfykq"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bi3f14klscvlw" path="res://Scenes/Weapons/Bullets/enemyBullet_mid_red.tscn" id="1_y6dig"]
|
||||
[ext_resource type="Script" uid="uid://b6h8slfcd5suh" path="res://Scripts/Resources/DecreasingSpeedModifier.cs" id="2_cxo3j"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=17 format=3 uid="uid://cocl3qontm3be"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://cocl3qontm3be"]
|
||||
|
||||
[ext_resource type="SpriteFrames" uid="uid://chjsokgyo0e33" path="res://Resources/Sprites/base_fairy.tres" id="1_ppsgt"]
|
||||
[ext_resource type="Resource" uid="uid://ct1fa2huvy34n" path="res://Resources/Items/Ammo1.tres" id="1_q1ekm"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=16 format=3 uid="uid://cqfyuurvqb8m6"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://cqfyuurvqb8m6"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://callpd48wwnlw" path="res://Sprites/Actors/Fairy_Special.png" id="1_kekqr"]
|
||||
[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="1_tf7s2"]
|
||||
|
|
@ -53,15 +53,9 @@ MaxHealth = 12.0
|
|||
MovementSpeed = 30.0
|
||||
Weapon = ExtResource("7_tf7s2")
|
||||
LootDrops = Array[ExtResource("1_tf7s2")]([SubResource("Resource_c8nix"), SubResource("Resource_gs2l3"), SubResource("Resource_sqnvg"), SubResource("Resource_5tyar"), SubResource("Resource_48xq6")])
|
||||
MotivationReward = 4.0
|
||||
PredictPlayer = false
|
||||
PlayerDetectionRange = 90.0
|
||||
ViewRange = 120.0
|
||||
AlarmReactRange = 150.0
|
||||
PlayerDisengageRange = 200.0
|
||||
StrafeSpeed = 25.0
|
||||
MaxStrafeDistance = 16.0
|
||||
MinStrafeDistance = 8.0
|
||||
ResponseTime = 0.5
|
||||
IconSprite = SubResource("AtlasTexture_c0hok")
|
||||
metadata/_custom_type_script = "uid://cd5o0ceb50jki"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=22 format=3 uid="uid://c48vva6qw2h12"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://c48vva6qw2h12"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://callpd48wwnlw" path="res://Sprites/Actors/Fairy_Special.png" id="1_0afqr"]
|
||||
[ext_resource type="AudioStream" uid="uid://rh8w0qte7wup" path="res://SFX/fairy_stop.wav" id="1_6ilmv"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=6 format=3 uid="uid://ng3lpe8ifbsn"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://ng3lpe8ifbsn"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://p6cs703f5m3n" path="res://ExternalMaterial/RoboBoss/RoboBoss.png" id="1_bmkgj"]
|
||||
[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="2_3teuq"]
|
||||
|
|
@ -17,16 +17,9 @@ PrefabPath = &"uid://clyrne78j3f5a"
|
|||
MaxHealth = 100.0
|
||||
MovementSpeed = 40.0
|
||||
Weapon = ExtResource("8_w06jt")
|
||||
LootDrops = Array[ExtResource("2_3teuq")]([])
|
||||
MotivationReward = 100.0
|
||||
PredictPlayer = false
|
||||
PlayerDetectionRange = 90.0
|
||||
ViewRange = 120.0
|
||||
AlarmReactRange = 150.0
|
||||
PlayerDisengageRange = 500.0
|
||||
StrafeSpeed = 25.0
|
||||
MaxStrafeDistance = 32.0
|
||||
MinStrafeDistance = 16.0
|
||||
ResponseTime = 0.5
|
||||
IconSprite = SubResource("AtlasTexture_n54y5")
|
||||
metadata/_custom_type_script = "uid://cd5o0ceb50jki"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=17 format=3 uid="uid://qbo6avc7x64b"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://qbo6avc7x64b"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://xhwfgbv0fjbr" path="res://Sprites/Actors/FairyGuard.png" id="1_ivudp"]
|
||||
[ext_resource type="SpriteFrames" uid="uid://ch2ll1on8im2p" path="res://Resources/Sprites/FairyGuard.tres" id="1_n54y5"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=8 format=3 uid="uid://bqdvyxs8sj3qa"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://bqdvyxs8sj3qa"]
|
||||
|
||||
[ext_resource type="Resource" uid="uid://dlhe7by67eu4v" path="res://Resources/BossPhases/Reimu/Reimu_Boss_Script.tres" id="1_amg33"]
|
||||
[ext_resource type="SpriteFrames" uid="uid://dpaxpwb2himus" path="res://Resources/Sprites/Reimu_Boss.tres" id="1_gjra5"]
|
||||
|
|
@ -18,7 +18,6 @@ EnemyKey = &"REIMU"
|
|||
PrefabPath = &"uid://d2xmlov4ee2fc"
|
||||
MaxHealth = 500.0
|
||||
Weapon = ExtResource("2_amg33")
|
||||
LootDrops = Array[ExtResource("4_h8i14")]([])
|
||||
IconSprite = SubResource("AtlasTexture_gdgx5")
|
||||
AnimationFrames = ExtResource("1_gjra5")
|
||||
BossScript = ExtResource("1_amg33")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=6 format=3 uid="uid://ij73f6irjcjg"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://ij73f6irjcjg"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="1_76vce"]
|
||||
[ext_resource type="Resource" uid="uid://cdfmedtgp2rcn" path="res://Resources/Weapons/EnemyWeapon.tres" id="1_bpaea"]
|
||||
|
|
@ -15,17 +15,10 @@ EnemyName = &"Robot 2"
|
|||
EnemyKey = &"ROBOT_2"
|
||||
PrefabPath = &"res://Scenes/Actors/RoamingSusan.tscn"
|
||||
MaxHealth = 10.0
|
||||
MovementSpeed = 20.0
|
||||
Weapon = ExtResource("1_bpaea")
|
||||
LootDrops = Array[ExtResource("1_76vce")]([])
|
||||
MotivationReward = 4.0
|
||||
PlayerDetectionRange = 90.0
|
||||
ViewRange = 120.0
|
||||
AlarmReactRange = 200.0
|
||||
PlayerDisengageRange = 500.0
|
||||
StrafeSpeed = 25.0
|
||||
MaxStrafeDistance = 16.0
|
||||
MinStrafeDistance = 8.0
|
||||
ResponseTime = 0.5
|
||||
IconSprite = SubResource("AtlasTexture_x043l")
|
||||
metadata/_custom_type_script = "uid://cd5o0ceb50jki"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=10 format=3 uid="uid://1jtogtbjk2r5"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://1jtogtbjk2r5"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bbdrws471xslc" path="res://Sprites/Actors/Rumia-world.png" id="1_6mall"]
|
||||
[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="1_ibtcs"]
|
||||
|
|
@ -41,19 +41,7 @@ EnemyName = &"Rumia"
|
|||
EnemyKey = &"RUMIA"
|
||||
PrefabPath = &"uid://d2xmlov4ee2fc"
|
||||
MaxHealth = 500.0
|
||||
MovementSpeed = 20.0
|
||||
Weapon = ExtResource("2_xjekt")
|
||||
LootDrops = Array[ExtResource("1_ibtcs")]([])
|
||||
MotivationReward = 4.0
|
||||
PredictPlayer = false
|
||||
PlayerDetectionRange = 90.0
|
||||
ViewRange = 120.0
|
||||
AlarmReactRange = 0.0
|
||||
PlayerDisengageRange = 0.0
|
||||
StrafeSpeed = 25.0
|
||||
MaxStrafeDistance = 64.0
|
||||
MinStrafeDistance = 16.0
|
||||
ResponseTime = 0.5
|
||||
AnimationFrames = SubResource("SpriteFrames_v6t43")
|
||||
BossScript = ExtResource("1_xjekt")
|
||||
metadata/_custom_type_script = "uid://cd5o0ceb50jki"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=6 format=3 uid="uid://cfdvg162u65sr"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://cfdvg162u65sr"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://hukxr2e63gky" path="res://Sprites/Actors/Robot3.png" id="1_bdpjc"]
|
||||
[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="1_f3huq"]
|
||||
|
|
@ -17,7 +17,6 @@ PrefabPath = &"uid://dky13otbks8cm"
|
|||
MaxHealth = 16.0
|
||||
MovementSpeed = 38.0
|
||||
Weapon = ExtResource("2_f3huq")
|
||||
LootDrops = Array[ExtResource("1_f3huq")]([])
|
||||
AlarmReactRange = 200.0
|
||||
PlayerDisengageRange = 500.0
|
||||
MaxStrafeDistance = 0.0
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=6 format=3 uid="uid://ysd6wl2gmdhn"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://ysd6wl2gmdhn"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://c2seclmhd5yt3" path="res://Sprites/Actors/Cannon3D.png" id="1_gkmvt"]
|
||||
[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="1_sma76"]
|
||||
|
|
@ -17,7 +17,6 @@ PrefabPath = &"res://Scenes/Actors/Turret360.tscn"
|
|||
MaxHealth = 20.0
|
||||
MovementSpeed = 0.0
|
||||
Weapon = ExtResource("1_yap8t")
|
||||
LootDrops = Array[ExtResource("1_sma76")]([])
|
||||
AlarmReactRange = 200.0
|
||||
PlayerDisengageRange = 500.0
|
||||
StrafeSpeed = 0.0
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="EnemyResource" load_steps=6 format=3 uid="uid://ddx2b6ymqu3eo"]
|
||||
[gd_resource type="Resource" script_class="EnemyResource" format=3 uid="uid://ddx2b6ymqu3eo"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://b58oxkofm64o7" path="res://ExternalMaterial/WallTurret/WallTurret_Small.png" id="1_1l6xq"]
|
||||
[ext_resource type="Script" uid="uid://cq65aed620ijo" path="res://Scripts/Resources/Loot/LootDrop.cs" id="2_mc6lv"]
|
||||
|
|
@ -17,16 +17,10 @@ PrefabPath = &"uid://8ab7omgqeodu"
|
|||
MaxHealth = 4.0
|
||||
MovementSpeed = 0.0
|
||||
Weapon = ExtResource("8_ylifv")
|
||||
LootDrops = Array[ExtResource("2_mc6lv")]([])
|
||||
MotivationReward = 4.0
|
||||
PredictPlayer = false
|
||||
PlayerDetectionRange = 90.0
|
||||
ViewRange = 120.0
|
||||
AlarmReactRange = 150.0
|
||||
PlayerDisengageRange = 200.0
|
||||
StrafeSpeed = 0.0
|
||||
MaxStrafeDistance = 0.0
|
||||
MinStrafeDistance = 0.0
|
||||
ResponseTime = 0.5
|
||||
IconSprite = SubResource("AtlasTexture_mc6lv")
|
||||
metadata/_custom_type_script = "uid://cd5o0ceb50jki"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="Resource" script_class="PatternGroup" load_steps=13 format=3 uid="uid://du2kuv125vbrx"]
|
||||
[gd_resource type="Resource" script_class="PatternGroup" format=3 uid="uid://du2kuv125vbrx"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dre2wvw4pa3hc" path="res://Scenes/Weapons/Bullets/enemyBullet_mid_blue.tscn" id="1_4xpdn"]
|
||||
[ext_resource type="Script" uid="uid://c0ndqalsc4jve" path="res://Scripts/AttackPatterns/SpiralPattern.cs" id="2_ee42k"]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="SpriteFrames" load_steps=7 format=3 uid="uid://dpaxpwb2himus"]
|
||||
[gd_resource type="SpriteFrames" format=3 uid="uid://dpaxpwb2himus"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://bqpjq0po68bgn" path="res://Sprites/Actors/Reimu_World.png" id="1_2wot2"]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
[gd_resource type="SpriteFrames" load_steps=10 format=3 uid="uid://cnl6ju3qlr2bj"]
|
||||
[gd_resource type="SpriteFrames" format=3 uid="uid://cnl6ju3qlr2bj"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://callpd48wwnlw" path="res://Sprites/Actors/Fairy_Special.png" id="2_ucisl"]
|
||||
|
||||
|
|
|
|||
176
addons/weapon_creator/BaseViewer.gd
Normal file
176
addons/weapon_creator/BaseViewer.gd
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
@tool
|
||||
class_name BaseViewer
|
||||
extends PanelContainer
|
||||
|
||||
# Base class for all viewer panels (WeaponViewer, BulletViewer, ItemViewer, EnemyViewer)
|
||||
# Provides common functionality for resource loading, filtering, and UI building
|
||||
|
||||
var _editor_interface: EditorInterface
|
||||
var _grid_container: HFlowContainer
|
||||
var _dock: PanelContainer
|
||||
var _show_2d_checkbox: CheckBox
|
||||
var _show_3d_checkbox: CheckBox
|
||||
|
||||
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void:
|
||||
_editor_interface = editor_interface
|
||||
_dock = dock
|
||||
_load_filter_settings()
|
||||
call_deferred("refresh")
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
call_deferred("refresh")
|
||||
|
||||
func _build_ui() -> void:
|
||||
var margin = MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 8)
|
||||
margin.add_theme_constant_override("margin_top", 8)
|
||||
margin.add_theme_constant_override("margin_right", 8)
|
||||
margin.add_theme_constant_override("margin_bottom", 8)
|
||||
add_child(margin)
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(vbox)
|
||||
|
||||
# Header with create buttons and filters
|
||||
var header_hbox = HBoxContainer.new()
|
||||
vbox.add_child(header_hbox)
|
||||
|
||||
_build_header_buttons(header_hbox)
|
||||
|
||||
var spacer = Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_hbox.add_child(spacer)
|
||||
|
||||
if _supports_2d_3d_filter():
|
||||
_show_2d_checkbox = CheckBox.new()
|
||||
_show_2d_checkbox.text = "2D"
|
||||
_show_2d_checkbox.button_pressed = true
|
||||
_show_2d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_2d_checkbox)
|
||||
|
||||
_show_3d_checkbox = CheckBox.new()
|
||||
_show_3d_checkbox.text = "3D"
|
||||
_show_3d_checkbox.button_pressed = true
|
||||
_show_3d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_3d_checkbox)
|
||||
|
||||
var refresh_button = Button.new()
|
||||
refresh_button.text = "Refresh"
|
||||
refresh_button.pressed.connect(refresh)
|
||||
header_hbox.add_child(refresh_button)
|
||||
|
||||
vbox.add_child(HSeparator.new())
|
||||
|
||||
# Scroll container for grid
|
||||
var scroll = ScrollContainer.new()
|
||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
vbox.add_child(scroll)
|
||||
|
||||
_grid_container = HFlowContainer.new()
|
||||
_grid_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_grid_container.add_theme_constant_override("h_separation", 8)
|
||||
_grid_container.add_theme_constant_override("v_separation", 8)
|
||||
scroll.add_child(_grid_container)
|
||||
|
||||
# Override in derived classes to add create buttons
|
||||
func _build_header_buttons(header_hbox: HBoxContainer) -> void:
|
||||
pass
|
||||
|
||||
# Override in derived classes if they support 2D/3D filtering
|
||||
func _supports_2d_3d_filter() -> bool:
|
||||
return false
|
||||
|
||||
# Override in derived classes to implement refresh logic
|
||||
func refresh() -> void:
|
||||
if not _grid_container:
|
||||
return
|
||||
_clear_grid()
|
||||
|
||||
func _clear_grid() -> void:
|
||||
if not _grid_container:
|
||||
return
|
||||
for child in _grid_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
func _on_filter_changed(_toggled: bool) -> void:
|
||||
_save_filter_settings()
|
||||
refresh()
|
||||
|
||||
# Override these in derived classes for specific filter settings
|
||||
func _get_filter_setting_2d_key() -> String:
|
||||
return ""
|
||||
|
||||
func _get_filter_setting_3d_key() -> String:
|
||||
return ""
|
||||
|
||||
func _load_filter_settings() -> void:
|
||||
if not _supports_2d_3d_filter():
|
||||
return
|
||||
|
||||
if _show_2d_checkbox:
|
||||
_show_2d_checkbox.button_pressed = _load_filter_setting(_get_filter_setting_2d_key(), true)
|
||||
if _show_3d_checkbox:
|
||||
_show_3d_checkbox.button_pressed = _load_filter_setting(_get_filter_setting_3d_key(), true)
|
||||
|
||||
func _save_filter_settings() -> void:
|
||||
if not _supports_2d_3d_filter():
|
||||
return
|
||||
|
||||
if not _editor_interface:
|
||||
return
|
||||
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings:
|
||||
if _show_2d_checkbox:
|
||||
editor_settings.set_setting(_get_filter_setting_2d_key(), _show_2d_checkbox.button_pressed)
|
||||
if _show_3d_checkbox:
|
||||
editor_settings.set_setting(_get_filter_setting_3d_key(), _show_3d_checkbox.button_pressed)
|
||||
|
||||
func _load_filter_setting(key: String, default_value: bool) -> bool:
|
||||
if not _editor_interface or key.is_empty():
|
||||
return default_value
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings and editor_settings.has_setting(key):
|
||||
return editor_settings.get_setting(key)
|
||||
return default_value
|
||||
|
||||
# Utility functions for adding labels to the grid
|
||||
func _add_error_label(text: String) -> void:
|
||||
var label = Label.new()
|
||||
label.text = text
|
||||
label.add_theme_color_override("font_color", Color.RED)
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
if _grid_container:
|
||||
_grid_container.add_child(label)
|
||||
|
||||
func _add_info_label(text: String) -> void:
|
||||
var label = Label.new()
|
||||
label.text = text
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
if _grid_container:
|
||||
_grid_container.add_child(label)
|
||||
|
||||
func _add_warning_label(text: String) -> void:
|
||||
var label = Label.new()
|
||||
label.text = text
|
||||
label.add_theme_color_override("font_color", Color.ORANGE)
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
if _grid_container:
|
||||
_grid_container.add_child(label)
|
||||
|
||||
# Check if 2D should be shown based on filter
|
||||
func _should_show_2d() -> bool:
|
||||
return _show_2d_checkbox == null or _show_2d_checkbox.button_pressed
|
||||
|
||||
# Check if 3D should be shown based on filter
|
||||
func _should_show_3d() -> bool:
|
||||
return _show_3d_checkbox == null or _show_3d_checkbox.button_pressed
|
||||
|
||||
# Utility to log to dock
|
||||
func _log_to_dock(message: String, color: Color = Color.WHITE) -> void:
|
||||
if _dock and _dock.has_method("add_log"):
|
||||
_dock.call("add_log", message, color)
|
||||
|
||||
1
addons/weapon_creator/BaseViewer.gd.uid
Normal file
1
addons/weapon_creator/BaseViewer.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://c2j8r2gndgolf
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
extends BaseViewer
|
||||
|
||||
# Displays all bullets from the Resources/Bullets folders in a grid format
|
||||
# Shows sprite and name, with tooltip showing resource path
|
||||
|
|
@ -9,44 +9,19 @@ signal duplicate_bullet_requested(bullet_data: Dictionary)
|
|||
signal bullet_deleted(bullet_name: String, bullet_path: String)
|
||||
signal bullet_duplication_started(bullet_name: String, is_3d: bool)
|
||||
|
||||
var _editor_interface: EditorInterface
|
||||
var _grid_container: HFlowContainer
|
||||
var _show_2d_checkbox: CheckBox
|
||||
var _show_3d_checkbox: CheckBox
|
||||
var _dock: PanelContainer
|
||||
|
||||
const SETTING_SHOW_2D = "weapon_creator/bullet_filter_show_2d"
|
||||
const SETTING_SHOW_3D = "weapon_creator/bullet_filter_show_3d"
|
||||
|
||||
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void:
|
||||
_editor_interface = editor_interface
|
||||
_dock = dock
|
||||
if _show_2d_checkbox:
|
||||
_show_2d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_2D, true)
|
||||
if _show_3d_checkbox:
|
||||
_show_3d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_3D, true)
|
||||
refresh_bullets()
|
||||
func _supports_2d_3d_filter() -> bool:
|
||||
return true
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
refresh_bullets()
|
||||
func _get_filter_setting_2d_key() -> String:
|
||||
return SETTING_SHOW_2D
|
||||
|
||||
func _build_ui() -> void:
|
||||
var margin = MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 8)
|
||||
margin.add_theme_constant_override("margin_top", 8)
|
||||
margin.add_theme_constant_override("margin_right", 8)
|
||||
margin.add_theme_constant_override("margin_bottom", 8)
|
||||
add_child(margin)
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(vbox)
|
||||
|
||||
# Header with title, filters, and buttons
|
||||
var header_hbox = HBoxContainer.new()
|
||||
vbox.add_child(header_hbox)
|
||||
func _get_filter_setting_3d_key() -> String:
|
||||
return SETTING_SHOW_3D
|
||||
|
||||
func _build_header_buttons(header_hbox: HBoxContainer) -> void:
|
||||
var create_2d_button = Button.new()
|
||||
create_2d_button.text = "Create (2D)"
|
||||
create_2d_button.pressed.connect(_on_create_bullet_pressed.bind(false))
|
||||
|
|
@ -57,52 +32,11 @@ func _build_ui() -> void:
|
|||
create_3d_button.pressed.connect(_on_create_bullet_pressed.bind(true))
|
||||
header_hbox.add_child(create_3d_button)
|
||||
|
||||
# Spacer
|
||||
var spacer = Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_hbox.add_child(spacer)
|
||||
func refresh() -> void:
|
||||
_clear_grid()
|
||||
|
||||
_show_2d_checkbox = CheckBox.new()
|
||||
_show_2d_checkbox.text = "2D"
|
||||
_show_2d_checkbox.button_pressed = true
|
||||
_show_2d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_2d_checkbox)
|
||||
|
||||
_show_3d_checkbox = CheckBox.new()
|
||||
_show_3d_checkbox.text = "3D"
|
||||
_show_3d_checkbox.button_pressed = true
|
||||
_show_3d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_3d_checkbox)
|
||||
|
||||
var refresh_button = Button.new()
|
||||
refresh_button.text = "Refresh"
|
||||
refresh_button.pressed.connect(refresh_bullets)
|
||||
header_hbox.add_child(refresh_button)
|
||||
|
||||
vbox.add_child(HSeparator.new())
|
||||
|
||||
# Scroll container for grid
|
||||
var scroll = ScrollContainer.new()
|
||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
vbox.add_child(scroll)
|
||||
|
||||
_grid_container = HFlowContainer.new()
|
||||
_grid_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_grid_container.add_theme_constant_override("h_separation", 8)
|
||||
_grid_container.add_theme_constant_override("v_separation", 8)
|
||||
scroll.add_child(_grid_container)
|
||||
|
||||
func refresh_bullets() -> void:
|
||||
if not _grid_container:
|
||||
return
|
||||
|
||||
# Clear existing items
|
||||
for child in _grid_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
var show_2d = _show_2d_checkbox == null or _show_2d_checkbox.button_pressed
|
||||
var show_3d = _show_3d_checkbox == null or _show_3d_checkbox.button_pressed
|
||||
var show_2d = _should_show_2d()
|
||||
var show_3d = _should_show_3d()
|
||||
|
||||
var bullet_count = 0
|
||||
|
||||
|
|
@ -302,9 +236,8 @@ func _duplicate_bullet(bullet_resource: Resource, is_3d: bool) -> void:
|
|||
var bullet_name = bullet_resource.resource_path.get_file().get_basename()
|
||||
var dimension = "3D" if is_3d else "2D"
|
||||
|
||||
if _dock:
|
||||
_dock.call("add_log", "=== Duplicating Bullet (" + dimension + ") ===", Color.CYAN)
|
||||
_dock.call("add_log", "Source: " + bullet_name, Color.CYAN)
|
||||
_log_to_dock("=== Duplicating Bullet (" + dimension + ") ===", Color.CYAN)
|
||||
_log_to_dock("Source: " + bullet_name, Color.CYAN)
|
||||
|
||||
bullet_duplication_started.emit(bullet_name, is_3d)
|
||||
|
||||
|
|
@ -325,7 +258,7 @@ func _duplicate_bullet(bullet_resource: Resource, is_3d: bool) -> void:
|
|||
|
||||
func _on_duplicate_bullet_confirmed(bullet_data: Dictionary) -> void:
|
||||
duplicate_bullet_requested.emit(bullet_data)
|
||||
get_tree().create_timer(0.5).timeout.connect(refresh_bullets)
|
||||
get_tree().create_timer(0.5).timeout.connect(refresh)
|
||||
|
||||
func _copy_bullet_resource_path(bullet_resource: Resource) -> void:
|
||||
if bullet_resource and bullet_resource.resource_path:
|
||||
|
|
@ -378,12 +311,11 @@ func _perform_delete(bullet_resource: Resource, dialog: ConfirmationDialog) -> v
|
|||
if deleted_success:
|
||||
bullet_deleted.emit(bullet_name, bullet_path)
|
||||
|
||||
if _dock:
|
||||
_dock.call("add_log", "=== Bullet Deleted ===", Color.ORANGE)
|
||||
_dock.call("add_log", "Bullet: " + bullet_name, Color.ORANGE)
|
||||
_dock.call("add_log", "✓ Deleted BulletResource: " + bullet_path, Color.GREEN)
|
||||
_log_to_dock("=== Bullet Deleted ===", Color.ORANGE)
|
||||
_log_to_dock("Bullet: " + bullet_name, Color.ORANGE)
|
||||
_log_to_dock("✓ Deleted BulletResource: " + bullet_path, Color.GREEN)
|
||||
|
||||
refresh_bullets()
|
||||
refresh()
|
||||
|
||||
func _on_create_bullet_pressed(is_3d: bool) -> void:
|
||||
_open_bullet_dialog(is_3d, {})
|
||||
|
|
@ -399,30 +331,3 @@ func _open_bullet_dialog(is_3d: bool, prefill_data: Dictionary = {}) -> void:
|
|||
dialog.call_deferred("connect", "bullet_data_confirmed", _on_duplicate_bullet_confirmed)
|
||||
dialog.call_deferred("popup_centered")
|
||||
|
||||
func _add_error_label(error_message: String) -> void:
|
||||
var label = Label.new()
|
||||
label.text = error_message
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.modulate = Color.ORANGE_RED
|
||||
_grid_container.add_child(label)
|
||||
|
||||
func _on_filter_changed(_toggled: bool) -> void:
|
||||
_save_filter_settings()
|
||||
refresh_bullets()
|
||||
|
||||
func _load_filter_setting(key: String, default_value: bool) -> bool:
|
||||
if not _editor_interface:
|
||||
return default_value
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings and editor_settings.has_setting(key):
|
||||
return editor_settings.get_setting(key)
|
||||
return default_value
|
||||
|
||||
func _save_filter_settings() -> void:
|
||||
if not _editor_interface:
|
||||
return
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings:
|
||||
editor_settings.set_setting(SETTING_SHOW_2D, _show_2d_checkbox.button_pressed)
|
||||
editor_settings.set_setting(SETTING_SHOW_3D, _show_3d_checkbox.button_pressed)
|
||||
|
||||
|
|
|
|||
308
addons/weapon_creator/EnemyCreatorDialog.gd
Normal file
308
addons/weapon_creator/EnemyCreatorDialog.gd
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
@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()
|
||||
|
||||
|
||||
|
||||
1
addons/weapon_creator/EnemyCreatorDialog.gd.uid
Normal file
1
addons/weapon_creator/EnemyCreatorDialog.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dmp7lxlnmtc7k
|
||||
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)
|
||||
)
|
||||
|
||||
|
||||
1
addons/weapon_creator/EnemyViewer.gd.uid
Normal file
1
addons/weapon_creator/EnemyViewer.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://ca7c420864dfm
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
extends BaseViewer
|
||||
|
||||
# Displays all items from the ItemsDatabase in a grid format
|
||||
# Shows sprite, name, and item type badge
|
||||
|
|
@ -10,13 +10,8 @@ signal duplicate_item_requested(item_data: Dictionary)
|
|||
signal item_deleted(item_name: String, item_path: String)
|
||||
signal item_duplication_started(item_name: String)
|
||||
|
||||
var _editor_interface: EditorInterface
|
||||
var _grid_container: HFlowContainer
|
||||
var _items_database_path := "res://Resources/ItemsDatabase.tres"
|
||||
var _show_2d_checkbox: CheckBox
|
||||
var _show_3d_checkbox: CheckBox
|
||||
var _show_neither_checkbox: CheckBox
|
||||
var _dock: PanelContainer
|
||||
|
||||
const SETTING_SHOW_2D = "weapon_creator/item_filter_show_2d"
|
||||
const SETTING_SHOW_3D = "weapon_creator/item_filter_show_3d"
|
||||
|
|
@ -57,90 +52,49 @@ const ITEM_TYPE_NAMES = [
|
|||
"KeyItem"
|
||||
]
|
||||
|
||||
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void:
|
||||
_editor_interface = editor_interface
|
||||
_dock = dock
|
||||
func _supports_2d_3d_filter() -> bool:
|
||||
return true
|
||||
|
||||
if _show_2d_checkbox:
|
||||
_show_2d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_2D, true)
|
||||
if _show_3d_checkbox:
|
||||
_show_3d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_3D, true)
|
||||
if _show_neither_checkbox:
|
||||
_show_neither_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_NEITHER, true)
|
||||
func _get_filter_setting_2d_key() -> String:
|
||||
return SETTING_SHOW_2D
|
||||
|
||||
refresh_items()
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
refresh_items()
|
||||
func _get_filter_setting_3d_key() -> String:
|
||||
return SETTING_SHOW_3D
|
||||
|
||||
# Override to add the "Neither" checkbox
|
||||
func _build_ui() -> void:
|
||||
var margin = MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 8)
|
||||
margin.add_theme_constant_override("margin_top", 8)
|
||||
margin.add_theme_constant_override("margin_right", 8)
|
||||
margin.add_theme_constant_override("margin_bottom", 8)
|
||||
add_child(margin)
|
||||
super._build_ui()
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(vbox)
|
||||
# Find the header_hbox and add the Neither checkbox after 3D checkbox
|
||||
var margin = get_child(0) as MarginContainer
|
||||
var vbox = margin.get_child(0) as VBoxContainer
|
||||
var header_hbox = vbox.get_child(0) as HBoxContainer
|
||||
|
||||
var header_hbox = HBoxContainer.new()
|
||||
vbox.add_child(header_hbox)
|
||||
# The refresh button is the last child, so insert before it
|
||||
var refresh_button = header_hbox.get_child(header_hbox.get_child_count() - 1)
|
||||
|
||||
_show_neither_checkbox = CheckBox.new()
|
||||
_show_neither_checkbox.text = "Neither"
|
||||
_show_neither_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_NEITHER, true)
|
||||
_show_neither_checkbox.toggled.connect(_on_filter_changed)
|
||||
_show_neither_checkbox.tooltip_text = "Show items that don't have 2D or 3D properties set"
|
||||
header_hbox.add_child(_show_neither_checkbox)
|
||||
header_hbox.move_child(_show_neither_checkbox, header_hbox.get_child_count() - 2)
|
||||
|
||||
func _build_header_buttons(header_hbox: HBoxContainer) -> void:
|
||||
var create_button = Button.new()
|
||||
create_button.text = "Create Item"
|
||||
create_button.pressed.connect(_on_create_item_pressed)
|
||||
header_hbox.add_child(create_button)
|
||||
|
||||
var spacer = Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_hbox.add_child(spacer)
|
||||
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void:
|
||||
super.setup(editor_interface, dock)
|
||||
|
||||
_show_2d_checkbox = CheckBox.new()
|
||||
_show_2d_checkbox.text = "2D"
|
||||
_show_2d_checkbox.button_pressed = true
|
||||
_show_2d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_2d_checkbox)
|
||||
if _show_neither_checkbox:
|
||||
_show_neither_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_NEITHER, true)
|
||||
|
||||
_show_3d_checkbox = CheckBox.new()
|
||||
_show_3d_checkbox.text = "3D"
|
||||
_show_3d_checkbox.button_pressed = true
|
||||
_show_3d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_3d_checkbox)
|
||||
|
||||
_show_neither_checkbox = CheckBox.new()
|
||||
_show_neither_checkbox.text = "Neither"
|
||||
_show_neither_checkbox.button_pressed = true
|
||||
_show_neither_checkbox.toggled.connect(_on_filter_changed)
|
||||
_show_neither_checkbox.tooltip_text = "Show items that don't have 2D or 3D properties set"
|
||||
header_hbox.add_child(_show_neither_checkbox)
|
||||
|
||||
var refresh_button = Button.new()
|
||||
refresh_button.text = "Refresh"
|
||||
refresh_button.pressed.connect(refresh_items)
|
||||
header_hbox.add_child(refresh_button)
|
||||
|
||||
vbox.add_child(HSeparator.new())
|
||||
|
||||
var scroll = ScrollContainer.new()
|
||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
vbox.add_child(scroll)
|
||||
|
||||
_grid_container = HFlowContainer.new()
|
||||
_grid_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_grid_container.add_theme_constant_override("h_separation", 8)
|
||||
_grid_container.add_theme_constant_override("v_separation", 8)
|
||||
scroll.add_child(_grid_container)
|
||||
|
||||
func refresh_items() -> void:
|
||||
if not _grid_container:
|
||||
return
|
||||
|
||||
for child in _grid_container.get_children():
|
||||
child.queue_free()
|
||||
func refresh() -> void:
|
||||
_clear_grid()
|
||||
|
||||
if not ResourceLoader.exists(_items_database_path):
|
||||
_add_error_label("ItemsDatabase not found at: " + _items_database_path)
|
||||
|
|
@ -156,8 +110,8 @@ func refresh_items() -> void:
|
|||
_add_error_label("No LootItems found in database")
|
||||
return
|
||||
|
||||
var show_2d = _show_2d_checkbox == null or _show_2d_checkbox.button_pressed
|
||||
var show_3d = _show_3d_checkbox == null or _show_3d_checkbox.button_pressed
|
||||
var show_2d = _should_show_2d()
|
||||
var show_3d = _should_show_3d()
|
||||
var show_neither = _show_neither_checkbox == null or _show_neither_checkbox.button_pressed
|
||||
|
||||
var item_count = 0
|
||||
|
|
@ -180,7 +134,7 @@ func refresh_items() -> void:
|
|||
item_count += 1
|
||||
|
||||
if item_count == 0:
|
||||
_add_error_label("No items match the current filter")
|
||||
_add_info_label("No items match current filters")
|
||||
|
||||
func _item_has_2d(loot_item: Resource) -> bool:
|
||||
var weapon_data = loot_item.get("WeaponData")
|
||||
|
|
@ -192,25 +146,14 @@ func _item_has_3d(loot_item: Resource) -> bool:
|
|||
var drop_scene_3d = loot_item.get("DropScenePath3D")
|
||||
return weapon_data_3d != null or (drop_scene_3d != null and drop_scene_3d != "")
|
||||
|
||||
func _on_filter_changed(_toggled: bool) -> void:
|
||||
_save_filter_settings()
|
||||
refresh_items()
|
||||
|
||||
func _load_filter_setting(key: String, default_value: bool) -> bool:
|
||||
if not _editor_interface:
|
||||
return default_value
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings and editor_settings.has_setting(key):
|
||||
return editor_settings.get_setting(key)
|
||||
return default_value
|
||||
|
||||
# Override to save the Neither checkbox setting
|
||||
func _save_filter_settings() -> void:
|
||||
super._save_filter_settings()
|
||||
|
||||
if not _editor_interface:
|
||||
return
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings:
|
||||
editor_settings.set_setting(SETTING_SHOW_2D, _show_2d_checkbox.button_pressed)
|
||||
editor_settings.set_setting(SETTING_SHOW_3D, _show_3d_checkbox.button_pressed)
|
||||
if editor_settings and _show_neither_checkbox:
|
||||
editor_settings.set_setting(SETTING_SHOW_NEITHER, _show_neither_checkbox.button_pressed)
|
||||
|
||||
func _create_item_tile(loot_item: Resource, has_2d: bool, has_3d: bool) -> void:
|
||||
|
|
@ -416,10 +359,9 @@ func _duplicate_item(loot_item: Resource) -> void:
|
|||
|
||||
var item_name = loot_item.get("ItemName") if loot_item else "Unknown"
|
||||
|
||||
if _dock:
|
||||
_dock.call("add_log", "=== Duplicating Item ===", Color.CYAN)
|
||||
_dock.call("add_log", "Source: " + item_name, Color.CYAN)
|
||||
_dock.call("add_log", "Opening creation dialog with prefilled data...", Color.CYAN)
|
||||
_log_to_dock("=== Duplicating Item ===", Color.CYAN)
|
||||
_log_to_dock("Source: " + item_name, Color.CYAN)
|
||||
_log_to_dock("Opening creation dialog with prefilled data...", Color.CYAN)
|
||||
|
||||
print("Duplicating item: ", item_name)
|
||||
|
||||
|
|
@ -454,7 +396,7 @@ func _duplicate_item(loot_item: Resource) -> void:
|
|||
|
||||
func _on_duplicate_item_confirmed(item_data: Dictionary) -> void:
|
||||
duplicate_item_requested.emit(item_data)
|
||||
get_tree().create_timer(0.5).timeout.connect(refresh_items)
|
||||
get_tree().create_timer(0.5).timeout.connect(refresh)
|
||||
|
||||
func _copy_item_resource_path(loot_item: Resource) -> void:
|
||||
if loot_item and loot_item.resource_path:
|
||||
|
|
@ -540,14 +482,7 @@ func _perform_delete(loot_item: Resource, dialog: ConfirmationDialog) -> void:
|
|||
_dock.call("add_log", "✓ Removed from ItemsDatabase", Color.GREEN)
|
||||
_dock.call("add_log", "Note: Linked assets were NOT deleted", Color.YELLOW)
|
||||
|
||||
refresh_items()
|
||||
|
||||
func _add_error_label(error_message: String) -> void:
|
||||
var label = Label.new()
|
||||
label.text = error_message
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.modulate = Color.ORANGE_RED
|
||||
_grid_container.add_child(label)
|
||||
refresh()
|
||||
|
||||
func _on_create_item_pressed() -> void:
|
||||
if not _editor_interface:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const SETTINGS_PATH = "user://weapon_creator_settings.tres"
|
|||
signal create_weapon_requested(weapon_data: Dictionary)
|
||||
signal create_bullet_requested(bullet_data: Dictionary)
|
||||
signal create_item_requested(item_data: Dictionary)
|
||||
signal create_enemy_requested(enemy_data: Dictionary)
|
||||
|
||||
# UI elements
|
||||
var clear_button: Button
|
||||
|
|
@ -18,6 +19,7 @@ var log_output: RichTextLabel
|
|||
var weapon_viewer: PanelContainer
|
||||
var bullet_viewer: PanelContainer
|
||||
var item_viewer: PanelContainer
|
||||
var enemy_viewer: PanelContainer
|
||||
var tab_container: TabContainer
|
||||
|
||||
var _is_creating: bool = false
|
||||
|
|
@ -123,11 +125,22 @@ func _build_ui() -> void:
|
|||
item_viewer.item_duplication_started.connect(_on_item_duplication_started)
|
||||
tab_container.add_child(item_viewer)
|
||||
|
||||
# Enemy Viewer Tab
|
||||
var enemy_viewer_script = load("res://addons/weapon_creator/EnemyViewer.gd")
|
||||
enemy_viewer = PanelContainer.new()
|
||||
enemy_viewer.set_script(enemy_viewer_script)
|
||||
enemy_viewer.name = "Enemies"
|
||||
enemy_viewer.duplicate_enemy_requested.connect(_on_enemy_data_confirmed)
|
||||
enemy_viewer.enemy_deleted.connect(_on_enemy_deleted)
|
||||
enemy_viewer.enemy_duplication_started.connect(_on_enemy_duplication_started)
|
||||
tab_container.add_child(enemy_viewer)
|
||||
|
||||
# Setup after adding to scene tree
|
||||
if _editor_interface:
|
||||
weapon_viewer.setup(_editor_interface, self)
|
||||
bullet_viewer.setup(_editor_interface, self)
|
||||
item_viewer.setup(_editor_interface, self)
|
||||
enemy_viewer.setup(_editor_interface, self)
|
||||
|
||||
# Log header with label and clear button
|
||||
var log_header_hbox = HBoxContainer.new()
|
||||
|
|
@ -184,11 +197,13 @@ func _on_options_pressed() -> void:
|
|||
settings = WeaponCreatorSettings.new()
|
||||
# Refresh all viewers to pick up new settings
|
||||
if weapon_viewer:
|
||||
weapon_viewer.call("refresh_weapons")
|
||||
weapon_viewer.call("refresh")
|
||||
if bullet_viewer:
|
||||
bullet_viewer.call("refresh_bullets")
|
||||
bullet_viewer.call("refresh")
|
||||
if item_viewer:
|
||||
item_viewer.call("refresh_items")
|
||||
item_viewer.call("refresh")
|
||||
if enemy_viewer:
|
||||
enemy_viewer.call("refresh")
|
||||
)
|
||||
|
||||
func _on_weapon_deleted(weapon_name: String, weapon_path: String, item_path: String) -> void:
|
||||
|
|
@ -228,6 +243,21 @@ func _on_item_deleted(item_name: String, item_path: String) -> void:
|
|||
func _on_item_duplication_started(item_name: String) -> void:
|
||||
pass
|
||||
|
||||
func _on_enemy_data_confirmed(enemy_data: Dictionary) -> void:
|
||||
if _is_creating:
|
||||
return
|
||||
|
||||
_is_creating = true
|
||||
log_output.clear()
|
||||
|
||||
create_enemy_requested.emit(enemy_data)
|
||||
|
||||
func _on_enemy_deleted(enemy_name: String, enemy_path: String) -> void:
|
||||
pass
|
||||
|
||||
func _on_enemy_duplication_started(enemy_name: String, is_3d: bool) -> void:
|
||||
pass
|
||||
|
||||
func add_log(message: String, color: Color = Color.WHITE) -> void:
|
||||
# Add colored message to log output
|
||||
log_output.append_text("[color=#" + color.to_html(false) + "]" + message + "[/color]\n")
|
||||
|
|
@ -236,8 +266,10 @@ func set_creation_complete() -> void:
|
|||
_is_creating = false
|
||||
|
||||
if weapon_viewer:
|
||||
weapon_viewer.call("refresh_weapons")
|
||||
weapon_viewer.call("refresh")
|
||||
if bullet_viewer:
|
||||
bullet_viewer.call("refresh_bullets")
|
||||
bullet_viewer.call("refresh")
|
||||
if item_viewer:
|
||||
item_viewer.call("refresh_items")
|
||||
item_viewer.call("refresh")
|
||||
if enemy_viewer:
|
||||
enemy_viewer.call("refresh")
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ func _enter_tree() -> void:
|
|||
dock_instance.create_bullet_requested.connect(_on_create_bullet_requested)
|
||||
if dock_instance.has_signal("create_item_requested"):
|
||||
dock_instance.create_item_requested.connect(_on_create_item_requested)
|
||||
if dock_instance.has_signal("create_enemy_requested"):
|
||||
dock_instance.create_enemy_requested.connect(_on_create_enemy_requested)
|
||||
|
||||
# Add the dock to the editor (bottom dock area)
|
||||
add_control_to_bottom_panel(dock_instance, "Weapon Creator")
|
||||
|
|
@ -428,3 +430,85 @@ func _create_bullet_resource(path: String, bullet_data: Dictionary) -> bool:
|
|||
return false
|
||||
|
||||
return true
|
||||
|
||||
func _on_create_enemy_requested(enemy_data: Dictionary) -> void:
|
||||
var settings: Resource = null
|
||||
if ResourceLoader.exists(SETTINGS_PATH):
|
||||
settings = ResourceLoader.load(SETTINGS_PATH)
|
||||
if not settings:
|
||||
settings = WeaponCreatorSettings.new()
|
||||
|
||||
var enemy_name: String = enemy_data.get("enemy_name", "New Enemy")
|
||||
var enemy_key: String = enemy_data.get("enemy_key", "NEW_ENEMY")
|
||||
var is_3d: bool = enemy_data.get("is_3d", true)
|
||||
|
||||
var enemies_dir = settings.get("enemies_dir")
|
||||
var dimension_suffix = "_3D" if is_3d else "_2D"
|
||||
var enemy_resource_path: String = enemies_dir + enemy_key + dimension_suffix + ".tres"
|
||||
|
||||
dock_instance.call("add_log", "=== Starting Enemy Creation ===")
|
||||
dock_instance.call("add_log", "Enemy Name: " + enemy_name)
|
||||
dock_instance.call("add_log", "Enemy Key: " + enemy_key)
|
||||
dock_instance.call("add_log", "Mode: " + ("3D" if is_3d else "2D"))
|
||||
|
||||
if _create_enemy_resource(enemy_resource_path, enemy_data):
|
||||
dock_instance.call("add_log", "✓ Created EnemyResource at: " + enemy_resource_path, Color.GREEN)
|
||||
else:
|
||||
dock_instance.call("add_log", "✗ Failed to create EnemyResource", Color.RED)
|
||||
dock_instance.call("set_creation_complete")
|
||||
return
|
||||
|
||||
dock_instance.call("add_log", "=== Enemy Creation Complete ===", Color.CYAN)
|
||||
dock_instance.call("add_log", "Next steps:")
|
||||
dock_instance.call("add_log", "1. Add animation frames and sounds if needed")
|
||||
dock_instance.call("add_log", "2. Configure loot drops")
|
||||
dock_instance.call("add_log", "3. Test the enemy in-game")
|
||||
dock_instance.call("set_creation_complete")
|
||||
|
||||
get_editor_interface().get_resource_filesystem().scan()
|
||||
|
||||
func _create_enemy_resource(path: String, enemy_data: Dictionary) -> bool:
|
||||
if ResourceLoader.exists(path):
|
||||
dock_instance.call("add_log", "Warning: EnemyResource already exists at " + path, Color.YELLOW)
|
||||
return false
|
||||
|
||||
var enemy_script = load("res://Scripts/Resources/EnemyResource.cs")
|
||||
if enemy_script == null:
|
||||
dock_instance.call("add_log", "Error: Could not load EnemyResource.cs script", Color.RED)
|
||||
return false
|
||||
|
||||
var enemy_resource = Resource.new()
|
||||
enemy_resource.set_script(enemy_script)
|
||||
|
||||
enemy_resource.set("EnemyName", enemy_data.get("enemy_name", "New Enemy"))
|
||||
enemy_resource.set("EnemyKey", enemy_data.get("enemy_key", "NEW_ENEMY"))
|
||||
enemy_resource.set("PrefabPath", enemy_data.get("prefab_path", "res://Scenes/Actors/Generic_Enemy_FSM_3D.tscn"))
|
||||
enemy_resource.set("MaxHealth", enemy_data.get("max_health", 6.0))
|
||||
enemy_resource.set("MovementSpeed", enemy_data.get("movement_speed", 1.5))
|
||||
enemy_resource.set("MotivationReward", enemy_data.get("motivation_reward", 4.0))
|
||||
|
||||
var weapon_resource = enemy_data.get("weapon_resource", null)
|
||||
if weapon_resource:
|
||||
enemy_resource.set("Weapon", weapon_resource)
|
||||
|
||||
enemy_resource.set("PlayerDetectionRange", enemy_data.get("player_detection_range", 12.0))
|
||||
enemy_resource.set("ViewRange", enemy_data.get("view_range", 8.0))
|
||||
enemy_resource.set("AlarmReactRange", enemy_data.get("alarm_react_range", 20.0))
|
||||
enemy_resource.set("PlayerDisengageRange", enemy_data.get("player_disengage_range", 15.0))
|
||||
enemy_resource.set("StrafeSpeed", enemy_data.get("strafe_speed", 0.0))
|
||||
enemy_resource.set("MaxStrafeDistance", enemy_data.get("max_strafe_distance", 0.0))
|
||||
enemy_resource.set("MinStrafeDistance", enemy_data.get("min_strafe_distance", 0.0))
|
||||
enemy_resource.set("ResponseTime", enemy_data.get("response_time", 0.5))
|
||||
enemy_resource.set("PredictPlayer", enemy_data.get("predict_player", false))
|
||||
|
||||
var icon_sprite = enemy_data.get("icon_sprite", null)
|
||||
if icon_sprite:
|
||||
enemy_resource.set("IconSprite", icon_sprite)
|
||||
|
||||
var err = ResourceSaver.save(enemy_resource, path)
|
||||
if err != OK:
|
||||
dock_instance.call("add_log", "Error saving EnemyResource: " + str(err), Color.RED)
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
|
|
|||
|
|
@ -12,28 +12,35 @@ const SETTINGS_PATH = "user://weapon_creator_settings.tres"
|
|||
@export var items_dir: String = "res://Resources/Items/"
|
||||
@export var bullets_dir: String = "res://Resources/Bullets/"
|
||||
@export var bullets_3d_dir: String = "res://Resources/Bullets/3D/"
|
||||
@export var enemies_dir: String = "res://Resources/Enemies/"
|
||||
@export var items_database_path: String = "res://Resources/ItemsDatabase.tres"
|
||||
|
||||
# Default bullet paths
|
||||
@export var default_bullet_2d: String = "res://Resources/Bullets/simple_ice_bullet.tres"
|
||||
@export var default_bullet_3d: String = "res://Resources/Bullets/3D/icicle_repeater_bullets_3D.tres"
|
||||
|
||||
# Default enemy paths
|
||||
@export var default_enemy_prefab_3d: String = "res://Scenes/Actors/Generic_Enemy_FSM_3D.tscn"
|
||||
|
||||
# Dialog sizes (saved automatically)
|
||||
@export var weapon_dialog_size: Vector2i = Vector2i(750, 950)
|
||||
@export var bullet_dialog_size: Vector2i = Vector2i(750, 850)
|
||||
@export var item_dialog_size: Vector2i = Vector2i(750, 850)
|
||||
@export var enemy_dialog_size: Vector2i = Vector2i(750, 950)
|
||||
@export var settings_dialog_size: Vector2i = Vector2i(600, 600)
|
||||
|
||||
# Dialog positions (saved automatically)
|
||||
@export var weapon_dialog_position: Vector2i = Vector2i.ZERO
|
||||
@export var bullet_dialog_position: Vector2i = Vector2i.ZERO
|
||||
@export var item_dialog_position: Vector2i = Vector2i.ZERO
|
||||
@export var enemy_dialog_position: Vector2i = Vector2i.ZERO
|
||||
@export var settings_dialog_position: Vector2i = Vector2i.ZERO
|
||||
|
||||
# Viewer filters (saved automatically)
|
||||
@export var weapon_viewer_filter: String = ""
|
||||
@export var bullet_viewer_filter: String = ""
|
||||
@export var item_viewer_filter: String = ""
|
||||
@export var enemy_viewer_filter: String = ""
|
||||
|
||||
static func load_settings():
|
||||
if ResourceLoader.exists(SETTINGS_PATH):
|
||||
|
|
@ -53,20 +60,25 @@ func reset_to_defaults() -> void:
|
|||
items_dir = "res://Resources/Items/"
|
||||
bullets_dir = "res://Resources/Bullets/"
|
||||
bullets_3d_dir = "res://Resources/Bullets/3D/"
|
||||
enemies_dir = "res://Resources/Enemies/"
|
||||
items_database_path = "res://Resources/ItemsDatabase.tres"
|
||||
default_bullet_2d = "res://Resources/Bullets/simple_ice_bullet.tres"
|
||||
default_bullet_3d = "res://Resources/Bullets/3D/icicle_repeater_bullets_3D.tres"
|
||||
default_enemy_prefab_3d = "res://Scenes/Actors/Generic_Enemy_FSM_3D.tscn"
|
||||
weapon_dialog_size = Vector2i(750, 950)
|
||||
bullet_dialog_size = Vector2i(750, 850)
|
||||
item_dialog_size = Vector2i(750, 850)
|
||||
enemy_dialog_size = Vector2i(750, 950)
|
||||
settings_dialog_size = Vector2i(600, 600)
|
||||
weapon_dialog_position = Vector2i.ZERO
|
||||
bullet_dialog_position = Vector2i.ZERO
|
||||
item_dialog_position = Vector2i.ZERO
|
||||
enemy_dialog_position = Vector2i.ZERO
|
||||
settings_dialog_position = Vector2i.ZERO
|
||||
weapon_viewer_filter = ""
|
||||
bullet_viewer_filter = ""
|
||||
item_viewer_filter = ""
|
||||
enemy_viewer_filter = ""
|
||||
save_settings()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@tool
|
||||
extends PanelContainer
|
||||
extends BaseViewer
|
||||
|
||||
# Displays all weapons from the ItemsDatabase in a grid format
|
||||
# Shows sprite and name, with tooltip showing resource path
|
||||
|
|
@ -10,49 +10,21 @@ signal duplicate_weapon_requested(weapon_data: Dictionary)
|
|||
signal weapon_deleted(weapon_name: String, weapon_path: String, item_path: String)
|
||||
signal weapon_duplication_started(weapon_name: String, is_3d: bool)
|
||||
|
||||
var _editor_interface: EditorInterface
|
||||
var _grid_container: HFlowContainer
|
||||
var _items_database_path := "res://Resources/ItemsDatabase.tres"
|
||||
var _show_2d_checkbox: CheckBox
|
||||
var _show_3d_checkbox: CheckBox
|
||||
var _dock: PanelContainer # Reference to the dock for logging
|
||||
|
||||
const SETTING_SHOW_2D = "weapon_creator/filter_show_2d"
|
||||
const SETTING_SHOW_3D = "weapon_creator/filter_show_3d"
|
||||
|
||||
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void:
|
||||
_editor_interface = editor_interface
|
||||
_dock = dock
|
||||
# Load saved filter settings after editor interface is available
|
||||
if _show_2d_checkbox:
|
||||
_show_2d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_2D, true)
|
||||
if _show_3d_checkbox:
|
||||
_show_3d_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_3D, true)
|
||||
# Refresh to apply loaded filter settings
|
||||
refresh_weapons()
|
||||
func _supports_2d_3d_filter() -> bool:
|
||||
return true
|
||||
|
||||
func _ready() -> void:
|
||||
_build_ui()
|
||||
refresh_weapons()
|
||||
func _get_filter_setting_2d_key() -> String:
|
||||
return SETTING_SHOW_2D
|
||||
|
||||
func _build_ui() -> void:
|
||||
# Main margin container
|
||||
var margin = MarginContainer.new()
|
||||
margin.add_theme_constant_override("margin_left", 8)
|
||||
margin.add_theme_constant_override("margin_top", 8)
|
||||
margin.add_theme_constant_override("margin_right", 8)
|
||||
margin.add_theme_constant_override("margin_bottom", 8)
|
||||
add_child(margin)
|
||||
|
||||
# Main vbox
|
||||
var vbox = VBoxContainer.new()
|
||||
vbox.add_theme_constant_override("separation", 8)
|
||||
margin.add_child(vbox)
|
||||
|
||||
# Header with title and refresh button
|
||||
var header_hbox = HBoxContainer.new()
|
||||
vbox.add_child(header_hbox)
|
||||
func _get_filter_setting_3d_key() -> String:
|
||||
return SETTING_SHOW_3D
|
||||
|
||||
func _build_header_buttons(header_hbox: HBoxContainer) -> void:
|
||||
var create_2d_button = Button.new()
|
||||
create_2d_button.text = "Create (2D)"
|
||||
create_2d_button.pressed.connect(_on_create_weapon_pressed.bind(false))
|
||||
|
|
@ -63,50 +35,8 @@ func _build_ui() -> void:
|
|||
create_3d_button.pressed.connect(_on_create_weapon_pressed.bind(true))
|
||||
header_hbox.add_child(create_3d_button)
|
||||
|
||||
# Spacer
|
||||
var spacer = Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
header_hbox.add_child(spacer)
|
||||
|
||||
_show_2d_checkbox = CheckBox.new()
|
||||
_show_2d_checkbox.text = "2D"
|
||||
_show_2d_checkbox.button_pressed = true
|
||||
_show_2d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_2d_checkbox)
|
||||
|
||||
_show_3d_checkbox = CheckBox.new()
|
||||
_show_3d_checkbox.text = "3D"
|
||||
_show_3d_checkbox.button_pressed = true
|
||||
_show_3d_checkbox.toggled.connect(_on_filter_changed)
|
||||
header_hbox.add_child(_show_3d_checkbox)
|
||||
|
||||
var refresh_button = Button.new()
|
||||
refresh_button.text = "Refresh"
|
||||
refresh_button.pressed.connect(refresh_weapons)
|
||||
header_hbox.add_child(refresh_button)
|
||||
|
||||
vbox.add_child(HSeparator.new())
|
||||
|
||||
# Scroll container for grid
|
||||
var scroll = ScrollContainer.new()
|
||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
vbox.add_child(scroll)
|
||||
|
||||
# Flow container for responsive wrapping
|
||||
_grid_container = HFlowContainer.new()
|
||||
_grid_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_grid_container.add_theme_constant_override("h_separation", 8)
|
||||
_grid_container.add_theme_constant_override("v_separation", 8)
|
||||
scroll.add_child(_grid_container)
|
||||
|
||||
func refresh_weapons() -> void:
|
||||
if not _grid_container:
|
||||
return
|
||||
|
||||
# Clear existing items
|
||||
for child in _grid_container.get_children():
|
||||
child.queue_free()
|
||||
func refresh() -> void:
|
||||
_clear_grid()
|
||||
|
||||
# Load ItemsDatabase
|
||||
if not ResourceLoader.exists(_items_database_path):
|
||||
|
|
@ -124,8 +54,8 @@ func refresh_weapons() -> void:
|
|||
return
|
||||
|
||||
# Filter for weapons only (items with WeaponData3D or WeaponData)
|
||||
var show_2d = _show_2d_checkbox == null or _show_2d_checkbox.button_pressed
|
||||
var show_3d = _show_3d_checkbox == null or _show_3d_checkbox.button_pressed
|
||||
var show_2d = _should_show_2d()
|
||||
var show_3d = _should_show_3d()
|
||||
|
||||
var weapon_count = 0
|
||||
for loot_item in loot_items:
|
||||
|
|
@ -143,25 +73,6 @@ func refresh_weapons() -> void:
|
|||
if weapon_count == 0:
|
||||
_add_error_label("No weapons found in database")
|
||||
|
||||
func _on_filter_changed(_toggled: bool) -> void:
|
||||
_save_filter_settings()
|
||||
refresh_weapons()
|
||||
|
||||
func _load_filter_setting(key: String, default_value: bool) -> bool:
|
||||
if not _editor_interface:
|
||||
return default_value
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings and editor_settings.has_setting(key):
|
||||
return editor_settings.get_setting(key)
|
||||
return default_value
|
||||
|
||||
func _save_filter_settings() -> void:
|
||||
if not _editor_interface:
|
||||
return
|
||||
var editor_settings = _editor_interface.get_editor_settings()
|
||||
if editor_settings:
|
||||
editor_settings.set_setting(SETTING_SHOW_2D, _show_2d_checkbox.button_pressed)
|
||||
editor_settings.set_setting(SETTING_SHOW_3D, _show_3d_checkbox.button_pressed)
|
||||
|
||||
func _create_weapon_tile(loot_item: Resource, weapon_data: Resource, is_3d: bool) -> void:
|
||||
var panel = PanelContainer.new()
|
||||
|
|
@ -336,10 +247,9 @@ func _duplicate_weapon(loot_item: Resource, weapon_data: Resource, is_3d: bool)
|
|||
var dimension = "3D" if is_3d else "2D"
|
||||
|
||||
# Log to dock
|
||||
if _dock:
|
||||
_dock.call("add_log", "=== Duplicating Weapon (" + dimension + ") ===", Color.CYAN)
|
||||
_dock.call("add_log", "Source: " + weapon_name, Color.CYAN)
|
||||
_dock.call("add_log", "Opening creation dialog with prefilled data...", Color.CYAN)
|
||||
_log_to_dock("=== Duplicating Weapon (" + dimension + ") ===", Color.CYAN)
|
||||
_log_to_dock("Source: " + weapon_name, Color.CYAN)
|
||||
_log_to_dock("Opening creation dialog with prefilled data...", Color.CYAN)
|
||||
|
||||
# Also log to Godot console
|
||||
print("Duplicating weapon (", dimension, "): ", weapon_name)
|
||||
|
|
@ -397,7 +307,7 @@ func _on_duplicate_weapon_confirmed(weapon_data: Dictionary) -> void:
|
|||
# Forward the weapon data to be created
|
||||
duplicate_weapon_requested.emit(weapon_data)
|
||||
# Refresh after a short delay to allow file system to update
|
||||
get_tree().create_timer(0.5).timeout.connect(refresh_weapons)
|
||||
get_tree().create_timer(0.5).timeout.connect(refresh)
|
||||
|
||||
func _copy_weapon_resource_path(weapon_data: Resource) -> void:
|
||||
if weapon_data and weapon_data.resource_path:
|
||||
|
|
@ -494,21 +404,13 @@ func _perform_delete(loot_item: Resource, weapon_data: Resource, dialog: Confirm
|
|||
weapon_deleted.emit(weapon_name, weapon_path, loot_item_path)
|
||||
|
||||
# Log to dock
|
||||
if _dock:
|
||||
_dock.call("add_log", "=== Weapon Deleted ===", Color.ORANGE)
|
||||
_dock.call("add_log", "Weapon: " + weapon_name, Color.ORANGE)
|
||||
_dock.call("add_log", "✓ Deleted WeaponResource: " + weapon_path, Color.GREEN)
|
||||
_dock.call("add_log", "✓ Deleted LootItem: " + loot_item_path, Color.GREEN)
|
||||
_dock.call("add_log", "✓ Removed from ItemsDatabase", Color.GREEN)
|
||||
_log_to_dock("=== Weapon Deleted ===", Color.ORANGE)
|
||||
_log_to_dock("Weapon: " + weapon_name, Color.ORANGE)
|
||||
_log_to_dock("✓ Deleted WeaponResource: " + weapon_path, Color.GREEN)
|
||||
_log_to_dock("✓ Deleted LootItem: " + loot_item_path, Color.GREEN)
|
||||
_log_to_dock("✓ Removed from ItemsDatabase", Color.GREEN)
|
||||
|
||||
refresh_weapons()
|
||||
|
||||
func _add_error_label(error_message: String) -> void:
|
||||
var label = Label.new()
|
||||
label.text = error_message
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.modulate = Color.ORANGE_RED
|
||||
_grid_container.add_child(label)
|
||||
refresh()
|
||||
|
||||
func _on_create_weapon_pressed(is_3d: bool) -> void:
|
||||
if not _editor_interface:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue