From 18683c068052e28f93c71c7501250c2be4eed752 Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Thu, 26 Feb 2026 21:58:48 +0100 Subject: [PATCH] Add enemy creation and viewer functionality with filtering options --- OldExport.ps1 | 54 +++ Resources/BossPhases/Reimu/Reimu_NS1.tres | 2 +- Resources/BossPhases/Rumia_NS2.tres | 2 +- Resources/BossPhases/Rumia_SP1.tres | 2 +- Resources/BossPhases/TestBoss1.tres | 2 +- .../Boss/Rumia/Rumia_Red_Chase_Bullet.tres | 2 +- Resources/Enemies/Base_Fairy.tres | 2 +- Resources/Enemies/Base_Fairy_Special.tres | 8 +- Resources/Enemies/Base_Fairy_Special_3D.tres | 2 +- Resources/Enemies/Boss_1.tres | 9 +- Resources/Enemies/Fairy_Guard.tres | 2 +- Resources/Enemies/Reimu_Boss.tres | 3 +- Resources/Enemies/Roaming_Susan.tres | 9 +- Resources/Enemies/Rumia_Boss.tres | 14 +- Resources/Enemies/Thermathron.tres | 3 +- Resources/Enemies/Turret360.tres | 3 +- Resources/Enemies/Wall_Turret.tres | 8 +- Resources/Patterns/rumia_ns_2.tres | 2 +- Resources/Sprites/Reimu_Boss.tres | 2 +- Resources/Sprites/base_fairy_special.tres | 2 +- addons/weapon_creator/BaseViewer.gd | 176 ++++++++++ addons/weapon_creator/BaseViewer.gd.uid | 1 + addons/weapon_creator/BulletViewer.gd | 135 ++------ addons/weapon_creator/EnemyCreatorDialog.gd | 308 ++++++++++++++++++ .../weapon_creator/EnemyCreatorDialog.gd.uid | 1 + addons/weapon_creator/EnemyViewer.gd | 228 +++++++++++++ addons/weapon_creator/EnemyViewer.gd.uid | 1 + addons/weapon_creator/ItemViewer.gd | 153 +++------ addons/weapon_creator/WeaponCreatorDock.gd | 44 ++- addons/weapon_creator/WeaponCreatorPlugin.gd | 84 +++++ .../weapon_creator/WeaponCreatorSettings.gd | 12 + addons/weapon_creator/WeaponViewer.gd | 144 ++------ 32 files changed, 1009 insertions(+), 411 deletions(-) create mode 100644 OldExport.ps1 create mode 100644 addons/weapon_creator/BaseViewer.gd create mode 100644 addons/weapon_creator/BaseViewer.gd.uid create mode 100644 addons/weapon_creator/EnemyCreatorDialog.gd create mode 100644 addons/weapon_creator/EnemyCreatorDialog.gd.uid create mode 100644 addons/weapon_creator/EnemyViewer.gd create mode 100644 addons/weapon_creator/EnemyViewer.gd.uid diff --git a/OldExport.ps1 b/OldExport.ps1 new file mode 100644 index 00000000..92ee738d --- /dev/null +++ b/OldExport.ps1 @@ -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 \ No newline at end of file diff --git a/Resources/BossPhases/Reimu/Reimu_NS1.tres b/Resources/BossPhases/Reimu/Reimu_NS1.tres index 8fcc0a46..54f156c0 100644 --- a/Resources/BossPhases/Reimu/Reimu_NS1.tres +++ b/Resources/BossPhases/Reimu/Reimu_NS1.tres @@ -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"] diff --git a/Resources/BossPhases/Rumia_NS2.tres b/Resources/BossPhases/Rumia_NS2.tres index b8330bb9..570fe078 100644 --- a/Resources/BossPhases/Rumia_NS2.tres +++ b/Resources/BossPhases/Rumia_NS2.tres @@ -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"] diff --git a/Resources/BossPhases/Rumia_SP1.tres b/Resources/BossPhases/Rumia_SP1.tres index 4be1b5d3..a9bee88b 100644 --- a/Resources/BossPhases/Rumia_SP1.tres +++ b/Resources/BossPhases/Rumia_SP1.tres @@ -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"] diff --git a/Resources/BossPhases/TestBoss1.tres b/Resources/BossPhases/TestBoss1.tres index c1798e88..314f204c 100644 --- a/Resources/BossPhases/TestBoss1.tres +++ b/Resources/BossPhases/TestBoss1.tres @@ -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"] diff --git a/Resources/Bullets/Boss/Rumia/Rumia_Red_Chase_Bullet.tres b/Resources/Bullets/Boss/Rumia/Rumia_Red_Chase_Bullet.tres index 9c70bb72..66df19d6 100644 --- a/Resources/Bullets/Boss/Rumia/Rumia_Red_Chase_Bullet.tres +++ b/Resources/Bullets/Boss/Rumia/Rumia_Red_Chase_Bullet.tres @@ -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"] diff --git a/Resources/Enemies/Base_Fairy.tres b/Resources/Enemies/Base_Fairy.tres index 3fa50905..f1b496f3 100644 --- a/Resources/Enemies/Base_Fairy.tres +++ b/Resources/Enemies/Base_Fairy.tres @@ -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"] diff --git a/Resources/Enemies/Base_Fairy_Special.tres b/Resources/Enemies/Base_Fairy_Special.tres index 0e417042..78e3777e 100644 --- a/Resources/Enemies/Base_Fairy_Special.tres +++ b/Resources/Enemies/Base_Fairy_Special.tres @@ -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" diff --git a/Resources/Enemies/Base_Fairy_Special_3D.tres b/Resources/Enemies/Base_Fairy_Special_3D.tres index a630f6cd..2677f1d9 100644 --- a/Resources/Enemies/Base_Fairy_Special_3D.tres +++ b/Resources/Enemies/Base_Fairy_Special_3D.tres @@ -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"] diff --git a/Resources/Enemies/Boss_1.tres b/Resources/Enemies/Boss_1.tres index 779d947e..7bf6e5e4 100644 --- a/Resources/Enemies/Boss_1.tres +++ b/Resources/Enemies/Boss_1.tres @@ -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" diff --git a/Resources/Enemies/Fairy_Guard.tres b/Resources/Enemies/Fairy_Guard.tres index 096e4104..c73334de 100644 --- a/Resources/Enemies/Fairy_Guard.tres +++ b/Resources/Enemies/Fairy_Guard.tres @@ -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"] diff --git a/Resources/Enemies/Reimu_Boss.tres b/Resources/Enemies/Reimu_Boss.tres index 6f0d12ff..14695d15 100644 --- a/Resources/Enemies/Reimu_Boss.tres +++ b/Resources/Enemies/Reimu_Boss.tres @@ -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") diff --git a/Resources/Enemies/Roaming_Susan.tres b/Resources/Enemies/Roaming_Susan.tres index 839ee1de..3b9853e0 100644 --- a/Resources/Enemies/Roaming_Susan.tres +++ b/Resources/Enemies/Roaming_Susan.tres @@ -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" diff --git a/Resources/Enemies/Rumia_Boss.tres b/Resources/Enemies/Rumia_Boss.tres index 80c3ce90..7a469b1b 100644 --- a/Resources/Enemies/Rumia_Boss.tres +++ b/Resources/Enemies/Rumia_Boss.tres @@ -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" diff --git a/Resources/Enemies/Thermathron.tres b/Resources/Enemies/Thermathron.tres index c01e7dbd..8e106829 100644 --- a/Resources/Enemies/Thermathron.tres +++ b/Resources/Enemies/Thermathron.tres @@ -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 diff --git a/Resources/Enemies/Turret360.tres b/Resources/Enemies/Turret360.tres index 90cf32d1..32924f1d 100644 --- a/Resources/Enemies/Turret360.tres +++ b/Resources/Enemies/Turret360.tres @@ -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 diff --git a/Resources/Enemies/Wall_Turret.tres b/Resources/Enemies/Wall_Turret.tres index 79b65a0c..2a7790d6 100644 --- a/Resources/Enemies/Wall_Turret.tres +++ b/Resources/Enemies/Wall_Turret.tres @@ -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" diff --git a/Resources/Patterns/rumia_ns_2.tres b/Resources/Patterns/rumia_ns_2.tres index d0143be8..fa1054e1 100644 --- a/Resources/Patterns/rumia_ns_2.tres +++ b/Resources/Patterns/rumia_ns_2.tres @@ -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"] diff --git a/Resources/Sprites/Reimu_Boss.tres b/Resources/Sprites/Reimu_Boss.tres index 30353c2d..7fe52ce5 100644 --- a/Resources/Sprites/Reimu_Boss.tres +++ b/Resources/Sprites/Reimu_Boss.tres @@ -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"] diff --git a/Resources/Sprites/base_fairy_special.tres b/Resources/Sprites/base_fairy_special.tres index afc4f8d5..e9234cb9 100644 --- a/Resources/Sprites/base_fairy_special.tres +++ b/Resources/Sprites/base_fairy_special.tres @@ -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"] diff --git a/addons/weapon_creator/BaseViewer.gd b/addons/weapon_creator/BaseViewer.gd new file mode 100644 index 00000000..0c988de6 --- /dev/null +++ b/addons/weapon_creator/BaseViewer.gd @@ -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) + diff --git a/addons/weapon_creator/BaseViewer.gd.uid b/addons/weapon_creator/BaseViewer.gd.uid new file mode 100644 index 00000000..ee5542f5 --- /dev/null +++ b/addons/weapon_creator/BaseViewer.gd.uid @@ -0,0 +1 @@ +uid://c2j8r2gndgolf diff --git a/addons/weapon_creator/BulletViewer.gd b/addons/weapon_creator/BulletViewer.gd index 5d77e58a..38829d4e 100644 --- a/addons/weapon_creator/BulletViewer.gd +++ b/addons/weapon_creator/BulletViewer.gd @@ -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)) @@ -56,53 +31,12 @@ func _build_ui() -> void: create_3d_button.text = "Create (3D)" 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) - - _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 +func refresh() -> void: + _clear_grid() - # 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) - diff --git a/addons/weapon_creator/EnemyCreatorDialog.gd b/addons/weapon_creator/EnemyCreatorDialog.gd new file mode 100644 index 00000000..9a9a4802 --- /dev/null +++ b/addons/weapon_creator/EnemyCreatorDialog.gd @@ -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() + + + diff --git a/addons/weapon_creator/EnemyCreatorDialog.gd.uid b/addons/weapon_creator/EnemyCreatorDialog.gd.uid new file mode 100644 index 00000000..12ee2c99 --- /dev/null +++ b/addons/weapon_creator/EnemyCreatorDialog.gd.uid @@ -0,0 +1 @@ +uid://dmp7lxlnmtc7k diff --git a/addons/weapon_creator/EnemyViewer.gd b/addons/weapon_creator/EnemyViewer.gd new file mode 100644 index 00000000..bf43e2eb --- /dev/null +++ b/addons/weapon_creator/EnemyViewer.gd @@ -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) + ) + + diff --git a/addons/weapon_creator/EnemyViewer.gd.uid b/addons/weapon_creator/EnemyViewer.gd.uid new file mode 100644 index 00000000..61bf5411 --- /dev/null +++ b/addons/weapon_creator/EnemyViewer.gd.uid @@ -0,0 +1 @@ +uid://ca7c420864dfm diff --git a/addons/weapon_creator/ItemViewer.gd b/addons/weapon_creator/ItemViewer.gd index c94cebdd..58ad156a 100644 --- a/addons/weapon_creator/ItemViewer.gd +++ b/addons/weapon_creator/ItemViewer.gd @@ -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 - - 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) - - refresh_items() +func _supports_2d_3d_filter() -> bool: + return true -func _ready() -> void: - _build_ui() - refresh_items() +func _get_filter_setting_2d_key() -> String: + return SETTING_SHOW_2D +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) - - _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) - - _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 +func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void: + super.setup(editor_interface, dock) - for child in _grid_container.get_children(): - child.queue_free() + if _show_neither_checkbox: + _show_neither_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_NEITHER, true) + +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: diff --git a/addons/weapon_creator/WeaponCreatorDock.gd b/addons/weapon_creator/WeaponCreatorDock.gd index 8f707f83..6c7c225e 100644 --- a/addons/weapon_creator/WeaponCreatorDock.gd +++ b/addons/weapon_creator/WeaponCreatorDock.gd @@ -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") diff --git a/addons/weapon_creator/WeaponCreatorPlugin.gd b/addons/weapon_creator/WeaponCreatorPlugin.gd index ebed119c..8ce90dda 100644 --- a/addons/weapon_creator/WeaponCreatorPlugin.gd +++ b/addons/weapon_creator/WeaponCreatorPlugin.gd @@ -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 + diff --git a/addons/weapon_creator/WeaponCreatorSettings.gd b/addons/weapon_creator/WeaponCreatorSettings.gd index 1eb18dd2..6afe5c90 100644 --- a/addons/weapon_creator/WeaponCreatorSettings.gd +++ b/addons/weapon_creator/WeaponCreatorSettings.gd @@ -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() diff --git a/addons/weapon_creator/WeaponViewer.gd b/addons/weapon_creator/WeaponViewer.gd index bd0e74d2..c0c9af54 100644 --- a/addons/weapon_creator/WeaponViewer.gd +++ b/addons/weapon_creator/WeaponViewer.gd @@ -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)) @@ -62,51 +34,9 @@ func _build_ui() -> void: create_3d_button.text = "Create (3D)" 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: