Add enemy creation and viewer functionality with filtering options

This commit is contained in:
MaddoScientisto 2026-02-26 21:58:48 +01:00
commit 18683c0680
32 changed files with 1009 additions and 411 deletions

54
OldExport.ps1 Normal file
View 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

View file

@ -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="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"] [ext_resource type="Resource" uid="uid://djjp4nyufqxlp" path="res://Resources/Bullets/Boss/Reimu/Reimu_Card_Bullet_Red.tres" id="1_x3pjh"]

View file

@ -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="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"] [ext_resource type="Script" uid="uid://cdd6q2h0t1hhq" path="res://Scripts/Resources/BossPhase.cs" id="1_fdlxv"]

View file

@ -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="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"] [ext_resource type="PackedScene" uid="uid://dohakkayqj4w2" path="res://Scenes/Weapons/Bullets/enemyBullet_green.tscn" id="1_o4um1"]

View file

@ -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://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"] [ext_resource type="Script" uid="uid://bhc7rbcico4kp" path="res://Scripts/Resources/SimpleMovementPattern.cs" id="1_xksf5"]

View file

@ -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="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"] [ext_resource type="Script" uid="uid://b6h8slfcd5suh" path="res://Scripts/Resources/DecreasingSpeedModifier.cs" id="2_cxo3j"]

View file

@ -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="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"] [ext_resource type="Resource" uid="uid://ct1fa2huvy34n" path="res://Resources/Items/Ammo1.tres" id="1_q1ekm"]

View file

@ -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="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"] [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 MovementSpeed = 30.0
Weapon = ExtResource("7_tf7s2") Weapon = ExtResource("7_tf7s2")
LootDrops = Array[ExtResource("1_tf7s2")]([SubResource("Resource_c8nix"), SubResource("Resource_gs2l3"), SubResource("Resource_sqnvg"), SubResource("Resource_5tyar"), SubResource("Resource_48xq6")]) 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 AlarmReactRange = 150.0
PlayerDisengageRange = 200.0 PlayerDisengageRange = 200.0
StrafeSpeed = 25.0
MaxStrafeDistance = 16.0 MaxStrafeDistance = 16.0
MinStrafeDistance = 8.0 MinStrafeDistance = 8.0
ResponseTime = 0.5
IconSprite = SubResource("AtlasTexture_c0hok") IconSprite = SubResource("AtlasTexture_c0hok")
metadata/_custom_type_script = "uid://cd5o0ceb50jki" metadata/_custom_type_script = "uid://cd5o0ceb50jki"

View file

@ -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="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"] [ext_resource type="AudioStream" uid="uid://rh8w0qte7wup" path="res://SFX/fairy_stop.wav" id="1_6ilmv"]

View file

@ -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="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"] [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 MaxHealth = 100.0
MovementSpeed = 40.0 MovementSpeed = 40.0
Weapon = ExtResource("8_w06jt") Weapon = ExtResource("8_w06jt")
LootDrops = Array[ExtResource("2_3teuq")]([])
MotivationReward = 100.0 MotivationReward = 100.0
PredictPlayer = false
PlayerDetectionRange = 90.0
ViewRange = 120.0
AlarmReactRange = 150.0 AlarmReactRange = 150.0
PlayerDisengageRange = 500.0 PlayerDisengageRange = 500.0
StrafeSpeed = 25.0
MaxStrafeDistance = 32.0 MaxStrafeDistance = 32.0
MinStrafeDistance = 16.0
ResponseTime = 0.5
IconSprite = SubResource("AtlasTexture_n54y5") IconSprite = SubResource("AtlasTexture_n54y5")
metadata/_custom_type_script = "uid://cd5o0ceb50jki" metadata/_custom_type_script = "uid://cd5o0ceb50jki"

View file

@ -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="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"] [ext_resource type="SpriteFrames" uid="uid://ch2ll1on8im2p" path="res://Resources/Sprites/FairyGuard.tres" id="1_n54y5"]

View file

@ -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="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"] [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" PrefabPath = &"uid://d2xmlov4ee2fc"
MaxHealth = 500.0 MaxHealth = 500.0
Weapon = ExtResource("2_amg33") Weapon = ExtResource("2_amg33")
LootDrops = Array[ExtResource("4_h8i14")]([])
IconSprite = SubResource("AtlasTexture_gdgx5") IconSprite = SubResource("AtlasTexture_gdgx5")
AnimationFrames = ExtResource("1_gjra5") AnimationFrames = ExtResource("1_gjra5")
BossScript = ExtResource("1_amg33") BossScript = ExtResource("1_amg33")

View file

@ -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="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"] [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" EnemyKey = &"ROBOT_2"
PrefabPath = &"res://Scenes/Actors/RoamingSusan.tscn" PrefabPath = &"res://Scenes/Actors/RoamingSusan.tscn"
MaxHealth = 10.0 MaxHealth = 10.0
MovementSpeed = 20.0
Weapon = ExtResource("1_bpaea") Weapon = ExtResource("1_bpaea")
LootDrops = Array[ExtResource("1_76vce")]([])
MotivationReward = 4.0
PlayerDetectionRange = 90.0
ViewRange = 120.0
AlarmReactRange = 200.0 AlarmReactRange = 200.0
PlayerDisengageRange = 500.0 PlayerDisengageRange = 500.0
StrafeSpeed = 25.0
MaxStrafeDistance = 16.0 MaxStrafeDistance = 16.0
MinStrafeDistance = 8.0 MinStrafeDistance = 8.0
ResponseTime = 0.5
IconSprite = SubResource("AtlasTexture_x043l") IconSprite = SubResource("AtlasTexture_x043l")
metadata/_custom_type_script = "uid://cd5o0ceb50jki" metadata/_custom_type_script = "uid://cd5o0ceb50jki"

View file

@ -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="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"] [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" EnemyKey = &"RUMIA"
PrefabPath = &"uid://d2xmlov4ee2fc" PrefabPath = &"uid://d2xmlov4ee2fc"
MaxHealth = 500.0 MaxHealth = 500.0
MovementSpeed = 20.0
Weapon = ExtResource("2_xjekt") 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") AnimationFrames = SubResource("SpriteFrames_v6t43")
BossScript = ExtResource("1_xjekt") BossScript = ExtResource("1_xjekt")
metadata/_custom_type_script = "uid://cd5o0ceb50jki" metadata/_custom_type_script = "uid://cd5o0ceb50jki"

View file

@ -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="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"] [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 MaxHealth = 16.0
MovementSpeed = 38.0 MovementSpeed = 38.0
Weapon = ExtResource("2_f3huq") Weapon = ExtResource("2_f3huq")
LootDrops = Array[ExtResource("1_f3huq")]([])
AlarmReactRange = 200.0 AlarmReactRange = 200.0
PlayerDisengageRange = 500.0 PlayerDisengageRange = 500.0
MaxStrafeDistance = 0.0 MaxStrafeDistance = 0.0

View file

@ -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="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"] [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 MaxHealth = 20.0
MovementSpeed = 0.0 MovementSpeed = 0.0
Weapon = ExtResource("1_yap8t") Weapon = ExtResource("1_yap8t")
LootDrops = Array[ExtResource("1_sma76")]([])
AlarmReactRange = 200.0 AlarmReactRange = 200.0
PlayerDisengageRange = 500.0 PlayerDisengageRange = 500.0
StrafeSpeed = 0.0 StrafeSpeed = 0.0

View file

@ -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="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"] [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 MaxHealth = 4.0
MovementSpeed = 0.0 MovementSpeed = 0.0
Weapon = ExtResource("8_ylifv") Weapon = ExtResource("8_ylifv")
LootDrops = Array[ExtResource("2_mc6lv")]([])
MotivationReward = 4.0
PredictPlayer = false
PlayerDetectionRange = 90.0
ViewRange = 120.0
AlarmReactRange = 150.0 AlarmReactRange = 150.0
PlayerDisengageRange = 200.0 PlayerDisengageRange = 200.0
StrafeSpeed = 0.0 StrafeSpeed = 0.0
MaxStrafeDistance = 0.0 MaxStrafeDistance = 0.0
MinStrafeDistance = 0.0 MinStrafeDistance = 0.0
ResponseTime = 0.5
IconSprite = SubResource("AtlasTexture_mc6lv") IconSprite = SubResource("AtlasTexture_mc6lv")
metadata/_custom_type_script = "uid://cd5o0ceb50jki" metadata/_custom_type_script = "uid://cd5o0ceb50jki"

View file

@ -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="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"] [ext_resource type="Script" uid="uid://c0ndqalsc4jve" path="res://Scripts/AttackPatterns/SpiralPattern.cs" id="2_ee42k"]

View file

@ -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"] [ext_resource type="Texture2D" uid="uid://bqpjq0po68bgn" path="res://Sprites/Actors/Reimu_World.png" id="1_2wot2"]

View file

@ -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"] [ext_resource type="Texture2D" uid="uid://callpd48wwnlw" path="res://Sprites/Actors/Fairy_Special.png" id="2_ucisl"]

View 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)

View file

@ -0,0 +1 @@
uid://c2j8r2gndgolf

View file

@ -1,5 +1,5 @@
@tool @tool
extends PanelContainer extends BaseViewer
# Displays all bullets from the Resources/Bullets folders in a grid format # Displays all bullets from the Resources/Bullets folders in a grid format
# Shows sprite and name, with tooltip showing resource path # 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_deleted(bullet_name: String, bullet_path: String)
signal bullet_duplication_started(bullet_name: String, is_3d: bool) 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_2D = "weapon_creator/bullet_filter_show_2d"
const SETTING_SHOW_3D = "weapon_creator/bullet_filter_show_3d" const SETTING_SHOW_3D = "weapon_creator/bullet_filter_show_3d"
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void: func _supports_2d_3d_filter() -> bool:
_editor_interface = editor_interface return true
_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 _ready() -> void: func _get_filter_setting_2d_key() -> String:
_build_ui() return SETTING_SHOW_2D
refresh_bullets()
func _build_ui() -> void: func _get_filter_setting_3d_key() -> String:
var margin = MarginContainer.new() return SETTING_SHOW_3D
margin.add_theme_constant_override("margin_left", 8)
margin.add_theme_constant_override("margin_top", 8) func _build_header_buttons(header_hbox: HBoxContainer) -> void:
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)
var create_2d_button = Button.new() var create_2d_button = Button.new()
create_2d_button.text = "Create (2D)" create_2d_button.text = "Create (2D)"
create_2d_button.pressed.connect(_on_create_bullet_pressed.bind(false)) 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.text = "Create (3D)"
create_3d_button.pressed.connect(_on_create_bullet_pressed.bind(true)) create_3d_button.pressed.connect(_on_create_bullet_pressed.bind(true))
header_hbox.add_child(create_3d_button) 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: func refresh() -> void:
if not _grid_container: _clear_grid()
return
# Clear existing items var show_2d = _should_show_2d()
for child in _grid_container.get_children(): var show_3d = _should_show_3d()
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 bullet_count = 0 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 bullet_name = bullet_resource.resource_path.get_file().get_basename()
var dimension = "3D" if is_3d else "2D" var dimension = "3D" if is_3d else "2D"
if _dock: _log_to_dock("=== Duplicating Bullet (" + dimension + ") ===", Color.CYAN)
_dock.call("add_log", "=== Duplicating Bullet (" + dimension + ") ===", Color.CYAN) _log_to_dock("Source: " + bullet_name, Color.CYAN)
_dock.call("add_log", "Source: " + bullet_name, Color.CYAN)
bullet_duplication_started.emit(bullet_name, is_3d) 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: func _on_duplicate_bullet_confirmed(bullet_data: Dictionary) -> void:
duplicate_bullet_requested.emit(bullet_data) 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: func _copy_bullet_resource_path(bullet_resource: Resource) -> void:
if bullet_resource and bullet_resource.resource_path: if bullet_resource and bullet_resource.resource_path:
@ -378,12 +311,11 @@ func _perform_delete(bullet_resource: Resource, dialog: ConfirmationDialog) -> v
if deleted_success: if deleted_success:
bullet_deleted.emit(bullet_name, bullet_path) bullet_deleted.emit(bullet_name, bullet_path)
if _dock: _log_to_dock("=== Bullet Deleted ===", Color.ORANGE)
_dock.call("add_log", "=== Bullet Deleted ===", Color.ORANGE) _log_to_dock("Bullet: " + bullet_name, Color.ORANGE)
_dock.call("add_log", "Bullet: " + bullet_name, Color.ORANGE) _log_to_dock("✓ Deleted BulletResource: " + bullet_path, Color.GREEN)
_dock.call("add_log", "✓ Deleted BulletResource: " + bullet_path, Color.GREEN)
refresh_bullets() refresh()
func _on_create_bullet_pressed(is_3d: bool) -> void: func _on_create_bullet_pressed(is_3d: bool) -> void:
_open_bullet_dialog(is_3d, {}) _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("connect", "bullet_data_confirmed", _on_duplicate_bullet_confirmed)
dialog.call_deferred("popup_centered") 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)

View 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()

View file

@ -0,0 +1 @@
uid://dmp7lxlnmtc7k

View 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)
)

View file

@ -0,0 +1 @@
uid://ca7c420864dfm

View file

@ -1,5 +1,5 @@
@tool @tool
extends PanelContainer extends BaseViewer
# Displays all items from the ItemsDatabase in a grid format # Displays all items from the ItemsDatabase in a grid format
# Shows sprite, name, and item type badge # 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_deleted(item_name: String, item_path: String)
signal item_duplication_started(item_name: 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 _items_database_path := "res://Resources/ItemsDatabase.tres"
var _show_2d_checkbox: CheckBox
var _show_3d_checkbox: CheckBox
var _show_neither_checkbox: CheckBox var _show_neither_checkbox: CheckBox
var _dock: PanelContainer
const SETTING_SHOW_2D = "weapon_creator/item_filter_show_2d" const SETTING_SHOW_2D = "weapon_creator/item_filter_show_2d"
const SETTING_SHOW_3D = "weapon_creator/item_filter_show_3d" const SETTING_SHOW_3D = "weapon_creator/item_filter_show_3d"
@ -57,90 +52,49 @@ const ITEM_TYPE_NAMES = [
"KeyItem" "KeyItem"
] ]
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void: func _supports_2d_3d_filter() -> bool:
_editor_interface = editor_interface return true
_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 _ready() -> void: func _get_filter_setting_2d_key() -> String:
_build_ui() return SETTING_SHOW_2D
refresh_items()
func _get_filter_setting_3d_key() -> String:
return SETTING_SHOW_3D
# Override to add the "Neither" checkbox
func _build_ui() -> void: func _build_ui() -> void:
var margin = MarginContainer.new() super._build_ui()
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() # Find the header_hbox and add the Neither checkbox after 3D checkbox
vbox.add_theme_constant_override("separation", 8) var margin = get_child(0) as MarginContainer
margin.add_child(vbox) var vbox = margin.get_child(0) as VBoxContainer
var header_hbox = vbox.get_child(0) as HBoxContainer
var header_hbox = HBoxContainer.new() # The refresh button is the last child, so insert before it
vbox.add_child(header_hbox) 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() var create_button = Button.new()
create_button.text = "Create Item" create_button.text = "Create Item"
create_button.pressed.connect(_on_create_item_pressed) create_button.pressed.connect(_on_create_item_pressed)
header_hbox.add_child(create_button) 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: func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void:
if not _grid_container: super.setup(editor_interface, dock)
return
for child in _grid_container.get_children(): if _show_neither_checkbox:
child.queue_free() _show_neither_checkbox.button_pressed = _load_filter_setting(SETTING_SHOW_NEITHER, true)
func refresh() -> void:
_clear_grid()
if not ResourceLoader.exists(_items_database_path): if not ResourceLoader.exists(_items_database_path):
_add_error_label("ItemsDatabase not found at: " + _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") _add_error_label("No LootItems found in database")
return return
var show_2d = _show_2d_checkbox == null or _show_2d_checkbox.button_pressed var show_2d = _should_show_2d()
var show_3d = _show_3d_checkbox == null or _show_3d_checkbox.button_pressed var show_3d = _should_show_3d()
var show_neither = _show_neither_checkbox == null or _show_neither_checkbox.button_pressed var show_neither = _show_neither_checkbox == null or _show_neither_checkbox.button_pressed
var item_count = 0 var item_count = 0
@ -180,7 +134,7 @@ func refresh_items() -> void:
item_count += 1 item_count += 1
if item_count == 0: 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: func _item_has_2d(loot_item: Resource) -> bool:
var weapon_data = loot_item.get("WeaponData") 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") var drop_scene_3d = loot_item.get("DropScenePath3D")
return weapon_data_3d != null or (drop_scene_3d != null and drop_scene_3d != "") return weapon_data_3d != null or (drop_scene_3d != null and drop_scene_3d != "")
func _on_filter_changed(_toggled: bool) -> void: # Override to save the Neither checkbox setting
_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
func _save_filter_settings() -> void: func _save_filter_settings() -> void:
super._save_filter_settings()
if not _editor_interface: if not _editor_interface:
return return
var editor_settings = _editor_interface.get_editor_settings() var editor_settings = _editor_interface.get_editor_settings()
if editor_settings: if editor_settings and _show_neither_checkbox:
editor_settings.set_setting(SETTING_SHOW_2D, _show_2d_checkbox.button_pressed)
editor_settings.set_setting(SETTING_SHOW_3D, _show_3d_checkbox.button_pressed)
editor_settings.set_setting(SETTING_SHOW_NEITHER, _show_neither_checkbox.button_pressed) 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: 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" var item_name = loot_item.get("ItemName") if loot_item else "Unknown"
if _dock: _log_to_dock("=== Duplicating Item ===", Color.CYAN)
_dock.call("add_log", "=== Duplicating Item ===", Color.CYAN) _log_to_dock("Source: " + item_name, Color.CYAN)
_dock.call("add_log", "Source: " + item_name, Color.CYAN) _log_to_dock("Opening creation dialog with prefilled data...", Color.CYAN)
_dock.call("add_log", "Opening creation dialog with prefilled data...", Color.CYAN)
print("Duplicating item: ", item_name) 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: func _on_duplicate_item_confirmed(item_data: Dictionary) -> void:
duplicate_item_requested.emit(item_data) 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: func _copy_item_resource_path(loot_item: Resource) -> void:
if loot_item and loot_item.resource_path: 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", "✓ Removed from ItemsDatabase", Color.GREEN)
_dock.call("add_log", "Note: Linked assets were NOT deleted", Color.YELLOW) _dock.call("add_log", "Note: Linked assets were NOT deleted", Color.YELLOW)
refresh_items() refresh()
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_create_item_pressed() -> void: func _on_create_item_pressed() -> void:
if not _editor_interface: if not _editor_interface:

View file

@ -10,6 +10,7 @@ const SETTINGS_PATH = "user://weapon_creator_settings.tres"
signal create_weapon_requested(weapon_data: Dictionary) signal create_weapon_requested(weapon_data: Dictionary)
signal create_bullet_requested(bullet_data: Dictionary) signal create_bullet_requested(bullet_data: Dictionary)
signal create_item_requested(item_data: Dictionary) signal create_item_requested(item_data: Dictionary)
signal create_enemy_requested(enemy_data: Dictionary)
# UI elements # UI elements
var clear_button: Button var clear_button: Button
@ -18,6 +19,7 @@ var log_output: RichTextLabel
var weapon_viewer: PanelContainer var weapon_viewer: PanelContainer
var bullet_viewer: PanelContainer var bullet_viewer: PanelContainer
var item_viewer: PanelContainer var item_viewer: PanelContainer
var enemy_viewer: PanelContainer
var tab_container: TabContainer var tab_container: TabContainer
var _is_creating: bool = false var _is_creating: bool = false
@ -123,11 +125,22 @@ func _build_ui() -> void:
item_viewer.item_duplication_started.connect(_on_item_duplication_started) item_viewer.item_duplication_started.connect(_on_item_duplication_started)
tab_container.add_child(item_viewer) 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 # Setup after adding to scene tree
if _editor_interface: if _editor_interface:
weapon_viewer.setup(_editor_interface, self) weapon_viewer.setup(_editor_interface, self)
bullet_viewer.setup(_editor_interface, self) bullet_viewer.setup(_editor_interface, self)
item_viewer.setup(_editor_interface, self) item_viewer.setup(_editor_interface, self)
enemy_viewer.setup(_editor_interface, self)
# Log header with label and clear button # Log header with label and clear button
var log_header_hbox = HBoxContainer.new() var log_header_hbox = HBoxContainer.new()
@ -184,11 +197,13 @@ func _on_options_pressed() -> void:
settings = WeaponCreatorSettings.new() settings = WeaponCreatorSettings.new()
# Refresh all viewers to pick up new settings # Refresh all viewers to pick up new settings
if weapon_viewer: if weapon_viewer:
weapon_viewer.call("refresh_weapons") weapon_viewer.call("refresh")
if bullet_viewer: if bullet_viewer:
bullet_viewer.call("refresh_bullets") bullet_viewer.call("refresh")
if item_viewer: 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: 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: func _on_item_duplication_started(item_name: String) -> void:
pass 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: func add_log(message: String, color: Color = Color.WHITE) -> void:
# Add colored message to log output # Add colored message to log output
log_output.append_text("[color=#" + color.to_html(false) + "]" + message + "[/color]\n") log_output.append_text("[color=#" + color.to_html(false) + "]" + message + "[/color]\n")
@ -236,8 +266,10 @@ func set_creation_complete() -> void:
_is_creating = false _is_creating = false
if weapon_viewer: if weapon_viewer:
weapon_viewer.call("refresh_weapons") weapon_viewer.call("refresh")
if bullet_viewer: if bullet_viewer:
bullet_viewer.call("refresh_bullets") bullet_viewer.call("refresh")
if item_viewer: if item_viewer:
item_viewer.call("refresh_items") item_viewer.call("refresh")
if enemy_viewer:
enemy_viewer.call("refresh")

View file

@ -25,6 +25,8 @@ func _enter_tree() -> void:
dock_instance.create_bullet_requested.connect(_on_create_bullet_requested) dock_instance.create_bullet_requested.connect(_on_create_bullet_requested)
if dock_instance.has_signal("create_item_requested"): if dock_instance.has_signal("create_item_requested"):
dock_instance.create_item_requested.connect(_on_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 the dock to the editor (bottom dock area)
add_control_to_bottom_panel(dock_instance, "Weapon Creator") 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 false
return true 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

View file

@ -12,28 +12,35 @@ const SETTINGS_PATH = "user://weapon_creator_settings.tres"
@export var items_dir: String = "res://Resources/Items/" @export var items_dir: String = "res://Resources/Items/"
@export var bullets_dir: String = "res://Resources/Bullets/" @export var bullets_dir: String = "res://Resources/Bullets/"
@export var bullets_3d_dir: String = "res://Resources/Bullets/3D/" @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" @export var items_database_path: String = "res://Resources/ItemsDatabase.tres"
# Default bullet paths # Default bullet paths
@export var default_bullet_2d: String = "res://Resources/Bullets/simple_ice_bullet.tres" @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" @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) # Dialog sizes (saved automatically)
@export var weapon_dialog_size: Vector2i = Vector2i(750, 950) @export var weapon_dialog_size: Vector2i = Vector2i(750, 950)
@export var bullet_dialog_size: Vector2i = Vector2i(750, 850) @export var bullet_dialog_size: Vector2i = Vector2i(750, 850)
@export var item_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) @export var settings_dialog_size: Vector2i = Vector2i(600, 600)
# Dialog positions (saved automatically) # Dialog positions (saved automatically)
@export var weapon_dialog_position: Vector2i = Vector2i.ZERO @export var weapon_dialog_position: Vector2i = Vector2i.ZERO
@export var bullet_dialog_position: Vector2i = Vector2i.ZERO @export var bullet_dialog_position: Vector2i = Vector2i.ZERO
@export var item_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 @export var settings_dialog_position: Vector2i = Vector2i.ZERO
# Viewer filters (saved automatically) # Viewer filters (saved automatically)
@export var weapon_viewer_filter: String = "" @export var weapon_viewer_filter: String = ""
@export var bullet_viewer_filter: String = "" @export var bullet_viewer_filter: String = ""
@export var item_viewer_filter: String = "" @export var item_viewer_filter: String = ""
@export var enemy_viewer_filter: String = ""
static func load_settings(): static func load_settings():
if ResourceLoader.exists(SETTINGS_PATH): if ResourceLoader.exists(SETTINGS_PATH):
@ -53,20 +60,25 @@ func reset_to_defaults() -> void:
items_dir = "res://Resources/Items/" items_dir = "res://Resources/Items/"
bullets_dir = "res://Resources/Bullets/" bullets_dir = "res://Resources/Bullets/"
bullets_3d_dir = "res://Resources/Bullets/3D/" bullets_3d_dir = "res://Resources/Bullets/3D/"
enemies_dir = "res://Resources/Enemies/"
items_database_path = "res://Resources/ItemsDatabase.tres" items_database_path = "res://Resources/ItemsDatabase.tres"
default_bullet_2d = "res://Resources/Bullets/simple_ice_bullet.tres" default_bullet_2d = "res://Resources/Bullets/simple_ice_bullet.tres"
default_bullet_3d = "res://Resources/Bullets/3D/icicle_repeater_bullets_3D.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) weapon_dialog_size = Vector2i(750, 950)
bullet_dialog_size = Vector2i(750, 850) bullet_dialog_size = Vector2i(750, 850)
item_dialog_size = Vector2i(750, 850) item_dialog_size = Vector2i(750, 850)
enemy_dialog_size = Vector2i(750, 950)
settings_dialog_size = Vector2i(600, 600) settings_dialog_size = Vector2i(600, 600)
weapon_dialog_position = Vector2i.ZERO weapon_dialog_position = Vector2i.ZERO
bullet_dialog_position = Vector2i.ZERO bullet_dialog_position = Vector2i.ZERO
item_dialog_position = Vector2i.ZERO item_dialog_position = Vector2i.ZERO
enemy_dialog_position = Vector2i.ZERO
settings_dialog_position = Vector2i.ZERO settings_dialog_position = Vector2i.ZERO
weapon_viewer_filter = "" weapon_viewer_filter = ""
bullet_viewer_filter = "" bullet_viewer_filter = ""
item_viewer_filter = "" item_viewer_filter = ""
enemy_viewer_filter = ""
save_settings() save_settings()

View file

@ -1,5 +1,5 @@
@tool @tool
extends PanelContainer extends BaseViewer
# Displays all weapons from the ItemsDatabase in a grid format # Displays all weapons from the ItemsDatabase in a grid format
# Shows sprite and name, with tooltip showing resource path # 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_deleted(weapon_name: String, weapon_path: String, item_path: String)
signal weapon_duplication_started(weapon_name: String, is_3d: bool) 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 _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_2D = "weapon_creator/filter_show_2d"
const SETTING_SHOW_3D = "weapon_creator/filter_show_3d" const SETTING_SHOW_3D = "weapon_creator/filter_show_3d"
func setup(editor_interface: EditorInterface, dock: PanelContainer = null) -> void: func _supports_2d_3d_filter() -> bool:
_editor_interface = editor_interface return true
_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 _ready() -> void: func _get_filter_setting_2d_key() -> String:
_build_ui() return SETTING_SHOW_2D
refresh_weapons()
func _build_ui() -> void: func _get_filter_setting_3d_key() -> String:
# Main margin container return SETTING_SHOW_3D
var margin = MarginContainer.new()
margin.add_theme_constant_override("margin_left", 8) func _build_header_buttons(header_hbox: HBoxContainer) -> void:
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)
var create_2d_button = Button.new() var create_2d_button = Button.new()
create_2d_button.text = "Create (2D)" create_2d_button.text = "Create (2D)"
create_2d_button.pressed.connect(_on_create_weapon_pressed.bind(false)) 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.text = "Create (3D)"
create_3d_button.pressed.connect(_on_create_weapon_pressed.bind(true)) create_3d_button.pressed.connect(_on_create_weapon_pressed.bind(true))
header_hbox.add_child(create_3d_button) 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: func refresh() -> void:
if not _grid_container: _clear_grid()
return
# Clear existing items
for child in _grid_container.get_children():
child.queue_free()
# Load ItemsDatabase # Load ItemsDatabase
if not ResourceLoader.exists(_items_database_path): if not ResourceLoader.exists(_items_database_path):
@ -124,8 +54,8 @@ func refresh_weapons() -> void:
return return
# Filter for weapons only (items with WeaponData3D or WeaponData) # Filter for weapons only (items with WeaponData3D or WeaponData)
var show_2d = _show_2d_checkbox == null or _show_2d_checkbox.button_pressed var show_2d = _should_show_2d()
var show_3d = _show_3d_checkbox == null or _show_3d_checkbox.button_pressed var show_3d = _should_show_3d()
var weapon_count = 0 var weapon_count = 0
for loot_item in loot_items: for loot_item in loot_items:
@ -143,25 +73,6 @@ func refresh_weapons() -> void:
if weapon_count == 0: if weapon_count == 0:
_add_error_label("No weapons found in database") _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: func _create_weapon_tile(loot_item: Resource, weapon_data: Resource, is_3d: bool) -> void:
var panel = PanelContainer.new() 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" var dimension = "3D" if is_3d else "2D"
# Log to dock # Log to dock
if _dock: _log_to_dock("=== Duplicating Weapon (" + dimension + ") ===", Color.CYAN)
_dock.call("add_log", "=== Duplicating Weapon (" + dimension + ") ===", Color.CYAN) _log_to_dock("Source: " + weapon_name, Color.CYAN)
_dock.call("add_log", "Source: " + weapon_name, Color.CYAN) _log_to_dock("Opening creation dialog with prefilled data...", Color.CYAN)
_dock.call("add_log", "Opening creation dialog with prefilled data...", Color.CYAN)
# Also log to Godot console # Also log to Godot console
print("Duplicating weapon (", dimension, "): ", weapon_name) 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 # Forward the weapon data to be created
duplicate_weapon_requested.emit(weapon_data) duplicate_weapon_requested.emit(weapon_data)
# Refresh after a short delay to allow file system to update # 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: func _copy_weapon_resource_path(weapon_data: Resource) -> void:
if weapon_data and weapon_data.resource_path: 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) weapon_deleted.emit(weapon_name, weapon_path, loot_item_path)
# Log to dock # Log to dock
if _dock: _log_to_dock("=== Weapon Deleted ===", Color.ORANGE)
_dock.call("add_log", "=== Weapon Deleted ===", Color.ORANGE) _log_to_dock("Weapon: " + weapon_name, Color.ORANGE)
_dock.call("add_log", "Weapon: " + weapon_name, Color.ORANGE) _log_to_dock("✓ Deleted WeaponResource: " + weapon_path, Color.GREEN)
_dock.call("add_log", "✓ Deleted WeaponResource: " + weapon_path, Color.GREEN) _log_to_dock("✓ Deleted LootItem: " + loot_item_path, Color.GREEN)
_dock.call("add_log", "✓ Deleted LootItem: " + loot_item_path, Color.GREEN) _log_to_dock("✓ Removed from ItemsDatabase", Color.GREEN)
_dock.call("add_log", "✓ Removed from ItemsDatabase", Color.GREEN)
refresh_weapons() refresh()
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_create_weapon_pressed(is_3d: bool) -> void: func _on_create_weapon_pressed(is_3d: bool) -> void:
if not _editor_interface: if not _editor_interface: