Compare commits

..

4 Commits

23 changed files with 355 additions and 411 deletions

View File

@@ -15,7 +15,7 @@ Added a proper opacity slider for sprites, with an "affect children" toggle. Cli
![Opacity Demo](https://github.com/user-attachments/assets/d5a05508-1cb1-46e0-8c90-d0aac73398db)
### <EFBFBD> Blend Modes
### 🎨 Blend Modes
Photoshop-style blend modes for sprites! Choose from Normal, Multiply, Screen, Overlay, Soft Light, Hard Light, Color Dodge, Color Burn, Darken, Lighten, Add, and Subtract. Perfect for lighting effects, shadows, or artistic styling.
@@ -23,7 +23,7 @@ Photoshop-style blend modes for sprites! Choose from Normal, Multiply, Screen, O
* Works alongside opacity controls
### <EFBFBD>🌀 Rotation Wobble
### 🌀 Rotation Wobble
Sprites can now do a subtle (or chaotic) rotational wobble. Frequency + amplitude sliders included.

View File

@@ -280,7 +280,6 @@ func blinking():
blinkTick = -12
func epicFail(err):
print(fail)
if fail == null:
return

View File

@@ -12,10 +12,8 @@ signal sprite_joined_group(sprite_id: int, group_name: String)
signal sprite_left_group(sprite_id: int, group_name: String)
func _ready():
print("DEBUG: WobbleSyncManager._ready() called")
print("DEBUG: WobbleSyncManager autoload initialized successfully")
print("DEBUG: Initial syncGroups: ", syncGroups)
print("DEBUG: Initial spriteGroupMembership: ", spriteGroupMembership)
# Manager initialization complete
pass
class WobbleSyncGroup:
var name: String
@@ -32,8 +30,6 @@ class WobbleSyncGroup:
## Set wobble parameters for the entire group
func setWobbleParameters(x_frq: float, x_amp: float, y_frq: float, y_amp: float, r_frq: float, r_amp: float, manager_ref):
print("DEBUG: WobbleSyncGroup.setWobbleParameters called for group '", name, "' with ", memberSprites.size(), " members")
print("DEBUG: Member sprite IDs: ", memberSprites)
xFrq = x_frq
xAmp = x_amp
yFrq = y_frq
@@ -43,13 +39,10 @@ class WobbleSyncGroup:
# Update all member sprites using the manager reference
var sprites = manager_ref.get_tree().get_nodes_in_group("saved")
print("DEBUG: Found ", sprites.size(), " total sprites in 'saved' group")
var updated_count = 0
for sprite in sprites:
print("DEBUG: Checking sprite ID ", sprite.id, " (type: ", typeof(sprite.id), ") against member list")
if sprite.id in memberSprites:
print("DEBUG: Updating sprite ID ", sprite.id, " with new wobble values (no scaling - values stored as-is)")
# Preserve the sprite's group membership
var current_group = sprite.wobbleSyncGroup
sprite.xFrq = xFrq
@@ -61,19 +54,13 @@ class WobbleSyncGroup:
# Restore group membership in case it got cleared
sprite.wobbleSyncGroup = current_group
updated_count += 1
else:
print("DEBUG: Sprite ID ", sprite.id, " not in member list")
print("DEBUG: Updated ", updated_count, " sprites in group '", name, "'")
## Add sprite to this group
func addSprite(sprite_id): # Remove type constraint to handle float IDs
print("DEBUG: WobbleSyncGroup.addSprite called with sprite_id: ", sprite_id, " (type: ", typeof(sprite_id), ")")
if sprite_id not in memberSprites:
memberSprites.append(sprite_id)
print("DEBUG: Added sprite to memberSprites. Current members: ", memberSprites)
else:
print("DEBUG: Sprite already in memberSprites")
pass # Sprite already in group
## Remove sprite from this group
func removeSprite(sprite_id): # Remove type constraint
@@ -144,10 +131,8 @@ func deleteGroup(group_name: String):
## Add sprite to a group
func addSpriteToGroup(sprite_id, group_name: String) -> bool: # Remove type constraint
print("DEBUG: addSpriteToGroup called with sprite_id: ", sprite_id, " (type: ", typeof(sprite_id), "), group: '", group_name, "'")
if not syncGroups.has(group_name):
print("DEBUG: Group '", group_name, "' not found")
return false
# Remove sprite from any existing group first
@@ -156,12 +141,10 @@ func addSpriteToGroup(sprite_id, group_name: String) -> bool: # Remove type con
var group = syncGroups[group_name]
group.addSprite(sprite_id)
spriteGroupMembership[sprite_id] = group_name
print("DEBUG: Added sprite ", sprite_id, " to group '", group_name, "'. Group now has ", group.memberSprites.size(), " members")
# Sync the sprite's wobble parameters to the group
var sprite = getSpriteById(sprite_id)
if sprite:
print("DEBUG: Found sprite object, syncing wobble parameters to group values")
sprite.wobbleSyncGroup = group_name
sprite.xFrq = group.xFrq
sprite.xAmp = group.xAmp
@@ -176,24 +159,19 @@ func addSpriteToGroup(sprite_id, group_name: String) -> bool: # Remove type con
## Remove sprite from a specific group
func removeSpriteFromGroup(sprite_id, group_name: String): # Remove type constraint
print("DEBUG: removeSpriteFromGroup called for sprite ID: ", sprite_id, " (type: ", typeof(sprite_id), "), group: '", group_name, "'")
if not syncGroups.has(group_name):
print("DEBUG: Group '", group_name, "' not found")
return
var group = syncGroups[group_name]
print("DEBUG: Group before removal has ", group.memberSprites.size(), " members: ", group.memberSprites)
group.removeSprite(sprite_id)
spriteGroupMembership.erase(sprite_id)
print("DEBUG: Group after removal has ", group.memberSprites.size(), " members: ", group.memberSprites)
# Clear the sprite's sync group reference
var sprite = getSpriteById(sprite_id)
if sprite:
print("DEBUG: Clearing sprite's wobbleSyncGroup property")
sprite.wobbleSyncGroup = ""
else:
print("DEBUG: Could not find sprite object for ID: ", sprite_id)
pass # Sprite not found
sprite_left_group.emit(sprite_id, group_name)
@@ -205,27 +183,19 @@ func removeSpriteFromGroup(sprite_id, group_name: String): # Remove type constr
## Remove sprite from all groups
func removeSpriteFromAllGroups(sprite_id): # Remove type constraint
print("DEBUG: removeSpriteFromAllGroups called for sprite ID: ", sprite_id)
if spriteGroupMembership.has(sprite_id):
var group_name = spriteGroupMembership[sprite_id]
print("DEBUG: Found sprite in group '", group_name, "', removing...")
removeSpriteFromGroup(sprite_id, group_name)
else:
print("DEBUG: Sprite not found in any group membership")
pass # Sprite not in any group
## Update wobble parameters for a group
func updateGroupWobble(group_name: String, x_frq: float, x_amp: float, y_frq: float, y_amp: float, r_frq: float, r_amp: float):
print("DEBUG: updateGroupWobble called for group '", group_name, "' with values - xFrq:", x_frq, " xAmp:", x_amp, " yFrq:", y_frq, " yAmp:", y_amp, " rFrq:", r_frq, " rAmp:", r_amp)
if not syncGroups.has(group_name):
print("DEBUG: Group '", group_name, "' not found in syncGroups!")
print("DEBUG: Available groups: ", syncGroups.keys())
print("DEBUG: This suggests the avatar's wobble sync groups were never loaded from the save file!")
print("DEBUG: Either the save file doesn't contain wobbleSyncGroups, or the loading process didn't run.")
return
var group = syncGroups[group_name]
print("DEBUG: Found group, calling setWobbleParameters on group with ", group.memberSprites.size(), " members")
group.setWobbleParameters(x_frq, x_amp, y_frq, y_amp, r_frq, r_amp, self)
## Get sprite's current group name
@@ -234,14 +204,10 @@ func getSpriteGroupName(sprite_id: int) -> String:
## Get all group names
func getGroupNames() -> Array:
print("DEBUG: getGroupNames called. syncGroups has ", syncGroups.size(), " groups: ", syncGroups.keys())
print("DEBUG: getGroupNames - syncGroups reference: ", syncGroups)
print("DEBUG: getGroupNames - self reference: ", self)
return syncGroups.keys()
## Test function to manually load wobble sync groups
func testManualLoad():
print("DEBUG: testManualLoad called - creating test data...")
var test_data = {
"ears": {
"name": "ears",
@@ -254,9 +220,7 @@ func testManualLoad():
"memberSprites": [3291286625.0]
}
}
print("DEBUG: Calling loadSaveData with test data...")
await loadSaveData(test_data)
print("DEBUG: testManualLoad completed. syncGroups now has: ", syncGroups.size(), " groups")
## Check if a group exists
func hasGroup(group_name: String) -> bool:
@@ -305,91 +269,52 @@ func getSaveData() -> Dictionary:
## Load save data for all groups
func loadSaveData(save_data: Dictionary):
print("DEBUG: ========================")
print("DEBUG: WobbleSyncManager.loadSaveData called with save_data: ", save_data)
print("DEBUG: save_data type: ", typeof(save_data))
print("DEBUG: save_data.keys(): ", save_data.keys())
print("DEBUG: save_data.size(): ", save_data.size())
print("DEBUG: self reference: ", self)
print("DEBUG: syncGroups before clear: ", syncGroups)
# Check if save_data is valid
if save_data == null or typeof(save_data) != TYPE_DICTIONARY:
print("DEBUG: ERROR - Invalid save_data provided")
return
if save_data.size() == 0:
print("DEBUG: WARNING - Empty save_data provided")
return
# Clear existing groups
print("DEBUG: Clearing existing syncGroups (had ", syncGroups.size(), " groups)")
syncGroups.clear()
spriteGroupMembership.clear()
print("DEBUG: Cleared existing data. syncGroups now has ", syncGroups.size(), " groups")
# Load groups from save data
var loaded_groups = 0
print("DEBUG: Starting group loading loop...")
for group_name in save_data.keys():
print("DEBUG: ---- Processing group '", group_name, "' (attempt ", loaded_groups + 1, ") ----")
var group_data = save_data[group_name]
print("DEBUG: Group data type: ", typeof(group_data))
print("DEBUG: Group data: ", group_data)
if typeof(group_data) == TYPE_DICTIONARY:
print("DEBUG: Creating WobbleSyncGroup instance...")
var group = WobbleSyncGroup.new(group_name)
print("DEBUG: Created new WobbleSyncGroup instance for '", group_name, "'")
print("DEBUG: Calling fromSaveData...")
group.fromSaveData(group_data)
print("DEBUG: Called fromSaveData on group '", group_name, "'")
print("DEBUG: Adding to syncGroups dictionary...")
syncGroups[group_name] = group
loaded_groups += 1
print("DEBUG: Added group '", group_name, "' to syncGroups. Total groups now: ", syncGroups.size())
print("DEBUG: syncGroups.keys() now: ", syncGroups.keys())
print("DEBUG: Group memberSprites: ", group.memberSprites)
# Update sprite group membership
print("DEBUG: Updating sprite group membership...")
for sprite_id in group.memberSprites:
spriteGroupMembership[sprite_id] = group_name
print("DEBUG: Added sprite ", sprite_id, " to spriteGroupMembership for group '", group_name, "'")
print("DEBUG: ---- Finished processing group '", group_name, "' ----")
else:
print("DEBUG: ERROR - Group data for '", group_name, "' is not a Dictionary, skipping")
pass # Invalid group data
print("DEBUG: ========================")
print("DEBUG: GROUP LOADING COMPLETE")
print("DEBUG: Loaded ", loaded_groups, " groups total")
print("DEBUG: Final syncGroups after loading: ", syncGroups.keys())
print("DEBUG: Final syncGroups size: ", syncGroups.size())
print("DEBUG: Final syncGroups contents: ", syncGroups)
print("DEBUG: Final spriteGroupMembership: ", spriteGroupMembership)
print("DEBUG: ========================")
# If no groups were loaded, clear any orphaned group references on sprites
if syncGroups.size() == 0:
print("DEBUG: No groups loaded, clearing orphaned sprite group references")
var sprites = get_tree().get_nodes_in_group("saved")
for sprite in sprites:
if sprite.wobbleSyncGroup != "":
print("DEBUG: Clearing orphaned group reference '", sprite.wobbleSyncGroup, "' from sprite ID ", sprite.id)
sprite.wobbleSyncGroup = ""
else:
print("DEBUG: Groups loaded successfully, syncing sprite parameters...")
# Apply wobble parameters to sprites with better timing
await get_tree().process_frame # Wait for sprites to be loaded
await get_tree().process_frame # Extra frame to ensure everything is ready
for group_name in syncGroups.keys():
var group = syncGroups[group_name]
print("DEBUG: Syncing parameters for group '", group_name, "' with ", group.memberSprites.size(), " members")
for sprite_id in group.memberSprites:
var sprite = getSpriteById(sprite_id)
if sprite:
print("DEBUG: Syncing sprite ID ", sprite_id, " to group '", group_name, "'")
sprite.wobbleSyncGroup = group_name
sprite.xFrq = group.xFrq
sprite.xAmp = group.xAmp
@@ -403,50 +328,9 @@ func loadSaveData(save_data: Dictionary):
if sprite.has_method("updateShaderOpacity"):
sprite.updateShaderOpacity()
else:
print("DEBUG: ERROR - Could not find sprite with ID ", sprite_id)
pass # Sprite not found
print("DEBUG: ========================")
print("DEBUG: loadSaveData FUNCTION COMPLETE")
print("DEBUG: Final verification - syncGroups has ", syncGroups.size(), " groups: ", syncGroups.keys())
print("DEBUG: ========================")
## Called when a sprite is deleted to clean up references
func onSpriteDeleted(sprite_id): # Remove type constraint
removeSpriteFromAllGroups(sprite_id)
## Debug function to check data consistency
func checkDataConsistency():
print("=== WOBBLE SYNC DATA CONSISTENCY CHECK ===")
# Check each group's member sprites
for group_name in syncGroups.keys():
var group = syncGroups[group_name]
print("Group '", group_name, "' has ", group.memberSprites.size(), " members: ", group.memberSprites)
for sprite_id in group.memberSprites:
# Check if sprite exists
var sprite = getSpriteById(sprite_id)
if sprite == null:
print(" ERROR: Sprite ID ", sprite_id, " in group but sprite object not found!")
else:
# Check if sprite's wobbleSyncGroup matches
if sprite.wobbleSyncGroup != group_name:
print(" ERROR: Sprite ID ", sprite_id, " in group '", group_name, "' but sprite.wobbleSyncGroup = '", sprite.wobbleSyncGroup, "'")
else:
print(" OK: Sprite ID ", sprite_id, " consistent")
# Check if membership mapping is correct
if not spriteGroupMembership.has(sprite_id) or spriteGroupMembership[sprite_id] != group_name:
print(" ERROR: Sprite ID ", sprite_id, " in group but spriteGroupMembership is wrong!")
# Check membership mapping for orphaned entries
for sprite_id in spriteGroupMembership.keys():
var group_name = spriteGroupMembership[sprite_id]
if not syncGroups.has(group_name):
print("ERROR: Sprite ID ", sprite_id, " mapped to non-existent group '", group_name, "'")
else:
var group = syncGroups[group_name]
if sprite_id not in group.memberSprites:
print("ERROR: Sprite ID ", sprite_id, " mapped to group '", group_name, "' but not in group's member list")
print("=== END CONSISTENCY CHECK ===")

View File

@@ -75,6 +75,7 @@ func _ready():
Global.main = self
Global.fail = $Failed
var newUser = Saving.settings["newUser"]
Global.connect("startSpeaking",onSpeak)
@@ -83,22 +84,14 @@ func _ready():
if streamdeck_node != null:
streamdeck_node.on_key_down.connect(changeCostumeStreamDeck)
if Saving.settings["newUser"]:
print("DEBUG: MAIN._ready() - New user detected, loading default avatar")
_on_load_dialog_file_selected("default")
Saving.settings["newUser"] = false
saveLoaded = true
if newUser:
loadDefaultAvatar()
else:
print("DEBUG: MAIN._ready() - Existing user, loading last avatar")
var lastAvatar = Saving.settings["lastAvatar"]
print("DEBUG: lastAvatar setting: ", lastAvatar, " (type: ", typeof(lastAvatar), ")")
# Ensure lastAvatar is a string, not a Dictionary
if typeof(lastAvatar) == TYPE_STRING and lastAvatar != "":
print("DEBUG: Loading avatar from: ", lastAvatar)
_on_load_dialog_file_selected(lastAvatar)
var lastAvatar = Saving.settings.get("lastAvatar", "")
if lastAvatar != null and lastAvatar != "" and typeof(lastAvatar) == TYPE_STRING and FileAccess.file_exists(lastAvatar):
loadAvatar(lastAvatar)
else:
print("WARNING: lastAvatar setting is invalid, loading default instead")
_on_load_dialog_file_selected("default")
loadDefaultAvatar()
$ControlPanel/volumeSlider.value = Saving.settings["volume"]
$ControlPanel/sensitiveSlider.value = Saving.settings["sense"]
@@ -425,54 +418,35 @@ func _on_load_button_pressed():
#LOAD AVATAR
func _on_load_dialog_file_selected(path):
print("DEBUG: _on_load_dialog_file_selected called with path: ", path, " (type: ", typeof(path), ")")
# Handle case where path might be a Dictionary instead of string
if typeof(path) == TYPE_DICTIONARY:
print("ERROR: Expected string path but got Dictionary: ", path)
push_error("Expected string path but got Dictionary: " + str(path))
return
print("DEBUG: About to read save data from: ", path)
var data = Saving.read_save(path)
print("DEBUG: Save data read. Data type: ", typeof(data), ", null check: ", data == null)
if data == null:
print("DEBUG: Save data is null, returning")
return
print("DEBUG: Save data keys: ", data.keys() if typeof(data) == TYPE_DICTIONARY else "not a dictionary")
print("DEBUG: Save data size: ", data.size() if typeof(data) == TYPE_DICTIONARY else "not a dictionary")
# Check the structure of the first few items
if typeof(data) == TYPE_DICTIONARY:
var count = 0
for key in data.keys():
if count < 3: # Just show first 3 items for debugging
print("DEBUG: data[", key, "] = ", data[key])
print("DEBUG: data[", key, "] type = ", typeof(data[key]))
if typeof(data[key]) == TYPE_DICTIONARY and data[key].has("path"):
print("DEBUG: data[", key, "][\"path\"] = ", data[key]["path"])
else:
print("DEBUG: data[", key, "] doesn't have 'path' key or isn't a dictionary")
count += 1
origin.queue_free()
var new = Node2D.new()
$OriginMotion.add_child(new)
origin = new
for item in data:
print("DEBUG: Processing item: ", item, ", data type: ", typeof(data[item]))
if typeof(data[item]) != TYPE_DICTIONARY:
print("DEBUG: Skipping item ", item, " - not a dictionary")
continue
if not data[item].has("path"):
print("DEBUG: Skipping item ", item, " - no 'path' key. Keys: ", data[item].keys())
continue
var sprite = spriteObject.instantiate()
sprite.path = data[item]["path"]
# Load display name (with backward compatibility)
if data[item].has("displayName"):
sprite.displayName = data[item]["displayName"]
sprite.id = data[item]["identification"]
sprite.parentId = data[item]["parentId"]
@@ -503,9 +477,9 @@ func _on_load_dialog_file_selected(path):
if data[item].has("costumeLayers"):
sprite.costumeLayers = str_to_var(data[item]["costumeLayers"]).duplicate()
if sprite.costumeLayers.size() < 8:
for i in range(5):
sprite.costumeLayers.append(1)
# Ensure we have exactly 10 costume layers
while sprite.costumeLayers.size() < 10:
sprite.costumeLayers.append(1)
if data[item].has("stretchAmount"):
sprite.stretchAmount = data[item]["stretchAmount"]
@@ -542,23 +516,8 @@ func _on_load_dialog_file_selected(path):
Global.spriteList.updateData()
# Load wobble sync groups
print("DEBUG: About to check for wobble sync groups...")
print("DEBUG: data type: ", typeof(data), ", data.keys(): ", data.keys() if typeof(data) == TYPE_DICTIONARY else "not a dictionary")
print("DEBUG: data.has('wobbleSyncGroups'): ", data.has("wobbleSyncGroups"))
print("DEBUG: WobbleSyncManager exists: ", WobbleSyncManager != null)
if WobbleSyncManager != null:
print("DEBUG: WobbleSyncManager type: ", typeof(WobbleSyncManager))
# Show save data content related to wobble sync
if data.has("wobbleSyncGroups"):
print("DEBUG: wobbleSyncGroups data found: ", data["wobbleSyncGroups"])
else:
print("DEBUG: No wobbleSyncGroups found in save data")
if data.has("wobbleSyncGroups") and WobbleSyncManager:
print("DEBUG: Loading wobble sync groups. Data: ", data["wobbleSyncGroups"])
await WobbleSyncManager.loadSaveData(data["wobbleSyncGroups"])
print("DEBUG: Finished loading wobble sync groups")
# Refresh wobble sync UI after groups are loaded
if Global.spriteEdit and Global.spriteEdit.wobbleSyncControl:
Global.spriteEdit.wobbleSyncControl.updateUI()
@@ -568,14 +527,6 @@ func _on_load_dialog_file_selected(path):
# Force refresh in case timing issues
await get_tree().process_frame
Global.spriteEdit.wobbleSyncControl.forceRefresh()
else:
print("DEBUG: Not loading wobble sync groups - missing data or manager")
# Final debug check
if WobbleSyncManager:
print("DEBUG: Final check - WobbleSyncManager has ", WobbleSyncManager.getGroupNames().size(), " groups: ", WobbleSyncManager.getGroupNames())
else:
print("DEBUG: No wobble sync groups to load. has key: ", data.has("wobbleSyncGroups"), ", WobbleSyncManager: ", WobbleSyncManager != null)
# Update opacity shaders for all loaded sprites after wobble sync data is applied
await get_tree().process_frame # Wait for all sprites to be fully initialized
@@ -587,7 +538,15 @@ func _on_load_dialog_file_selected(path):
Global.pushUpdate("Loaded avatar at: " + path)
onWindowSizeChange()
## Load the default avatar data
func loadDefaultAvatar():
_on_load_dialog_file_selected("default")
## Load avatar from a specific file path
func loadAvatar(path: String):
_on_load_dialog_file_selected(path)
#SAVE AVATAR
func _on_save_dialog_file_selected(path):
var data = {}
@@ -599,6 +558,7 @@ func _on_save_dialog_file_selected(path):
data[id] = {}
data[id]["type"] = "sprite"
data[id]["path"] = child.path
data[id]["displayName"] = child.displayName
data[id]["imageData"] = Marshalls.raw_to_base64(child.imageData.save_png_to_buffer())
data[id]["identification"] = child.id
data[id]["parentId"] = child.parentId
@@ -758,9 +718,11 @@ func changeCostumeStreamDeck(id: String):
"9":changeCostume(9)
"10":changeCostume(10)
func changeCostume(newCostume):
func changeCostume(newCostume, preserve_selection: bool = false):
costume = newCostume
Global.heldSprite = null
# Only clear heldSprite for actual costume changes, not layer updates
if not preserve_selection:
Global.heldSprite = null
var nodes = get_tree().get_nodes_in_group("saved")
for sprite in nodes:
if sprite.costumeLayers[newCostume-1] == 1:
@@ -783,7 +745,7 @@ func moveSpriteMenu(delta):
var size = get_viewport().get_visible_rect().size
var windowLength = 1800
var windowLength = 2000
$ViewerArrows/Arrows.position.y = size.y - 25

View File

@@ -14357,26 +14357,38 @@ offset_bottom = -51.0
text = "Zoom : 100%"
horizontal_alignment = 2
[node name="VersionLabels" type="Node2D" parent="ControlPanel"]
position = Vector2(-284, 59)
[node name="VersionLabels" type="Control" parent="ControlPanel"]
layout_mode = 3
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = -870.0
offset_top = -107.0
offset_right = -610.0
offset_bottom = -7.0
grow_vertical = 0
[node name="Label" type="Label" parent="ControlPanel/VersionLabels"]
offset_left = -713.0
offset_top = -94.0
offset_right = -575.0
offset_bottom = -60.0
[node name="VBoxContainer" type="VBoxContainer" parent="ControlPanel/VersionLabels"]
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -100.0
offset_right = 138.0
offset_bottom = 1.0
grow_vertical = 0
[node name="Label" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
layout_mode = 2
text = "PNGTuber Plus"
label_settings = SubResource("LabelSettings_w0f1f")
[node name="Label2" type="Label" parent="ControlPanel/VersionLabels"]
offset_left = -713.0
offset_top = -123.0
offset_right = -575.0
offset_bottom = -89.0
[node name="Label2" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
layout_mode = 2
text = "kaiakairos'"
label_settings = SubResource("LabelSettings_qg0do")
[node name="versionNo" type="Label" parent="ControlPanel/VersionLabels/Label2"]
[node name="versionNo" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer/Label2"]
layout_mode = 0
offset_left = 104.0
offset_top = 11.0
@@ -14385,15 +14397,13 @@ offset_bottom = 54.0
text = "v1.4.5"
label_settings = SubResource("LabelSettings_xvf50")
[node name="Label3" type="Label" parent="ControlPanel/VersionLabels"]
offset_left = -566.0
offset_top = -93.0
offset_right = -428.0
offset_bottom = -59.0
[node name="Label3" type="Label" parent="ControlPanel/VersionLabels/VBoxContainer"]
layout_mode = 2
text = "Litruv Edition"
label_settings = SubResource("LabelSettings_qg0do")
[node name="EditControls" type="Node2D" parent="."]
visible = false
z_index = 4090
script = ExtResource("17_bf1ic")

View File

@@ -50,11 +50,6 @@ window/vsync/vsync_mode=0
project/assembly_name="PNGTuberPlus"
[editor]
version_control/plugin_name=""
version_control/autoload_on_startup=false
[editor_plugins]
enabled=PackedStringArray("res://addons/godot-streamdeck-addon/plugin.cfg")

View File

@@ -1,30 +0,0 @@
extends Node
## Test script for wobble sync groups - run this to verify the system is working
func _ready():
# Wait a frame for everything to initialize
await get_tree().process_frame
print("=== Wobble Sync Groups Test ===")
# Test creating a group
var success = WobbleSyncManager.createGroup("TestGroup")
print("Create group 'TestGroup': ", success)
# Test getting group names
var groups = WobbleSyncManager.getGroupNames()
print("Available groups: ", groups)
# Test group existence
print("Group 'TestGroup' exists: ", WobbleSyncManager.hasGroup("TestGroup"))
print("Group 'NonExistent' exists: ", WobbleSyncManager.hasGroup("NonExistent"))
# Clean up test group
WobbleSyncManager.deleteGroup("TestGroup")
print("Deleted test group")
groups = WobbleSyncManager.getGroupNames()
print("Groups after deletion: ", groups)
print("=== Test Complete ===")

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://41u8egcau00i"
path="res://.godot/imported/checkmark.png-7bf604eba3017c173b17bad2b166951a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui_scenes/button sprites/checkmark.png"
dest_files=["res://.godot/imported/checkmark.png-7bf604eba3017c173b17bad2b166951a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -1,82 +0,0 @@
## Example implementation showing how to replace legacy wobble controls
## with the new WobbleControlPanel component
extends Node2D
## This demonstrates the integration approach for the sprite viewer
## Legacy slider references (to be replaced)
@onready var legacy_wobble_control = $LegacyWobbleControl
## New component system
@onready var wobble_panel: WobbleControlPanel = $WobbleControlPanel
var slider_manager: SliderManager
func _ready():
# Initialize slider management
slider_manager = SliderManager.new(self)
# Set up the wobble panel
if wobble_panel:
# The panel will automatically configure its sliders
print("WobbleControlPanel initialized")
# Hide legacy controls when using new system
if legacy_wobble_control:
legacy_wobble_control.visible = false
## Updated setImage function using new components
func setImage_new_system():
if Global.heldSprite == null:
return
print("Setting image with new component system")
# Update wobble panel with current sprite
if wobble_panel:
wobble_panel.set_target_sprite(Global.heldSprite)
wobble_panel.update_from_sprite()
# Update sync feedback
updateWobbleControlStates_new_system()
## Updated wobble control states using new system
func updateWobbleControlStates_new_system():
if Global.heldSprite == null:
return
if wobble_panel:
wobble_panel.update_sync_feedback()
## Migration helper - gradually replace sections
func migrate_to_new_system():
print("Migrating wobble controls to new component system...")
# Step 1: Hide old controls
if has_node("WobbleControl"):
$WobbleControl.visible = false
# Step 2: Show new panel
if wobble_panel:
wobble_panel.visible = true
# Step 3: Update with current sprite
if Global.heldSprite:
setImage_new_system()
print("Migration complete!")
## Demonstration of component benefits
func demonstrate_consistency():
print("=== Component System Benefits ===")
if wobble_panel:
print("All wobble sliders have consistent:")
print("- Visual styling")
print("- Value formatting")
print("- Label positioning")
print("- Signal handling")
print("- Sync state feedback")
print("Components are easily reusable across scenes")
print("Centralized management through SliderManager")
print("Type-safe with proper class inheritance")

View File

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

View File

@@ -1,35 +0,0 @@
[gd_scene load_steps=3 format=4 uid="uid://cm4n7yap2k8q1"]
[ext_resource type="Script" path="res://ui_scenes/components/ComponentMigrationExample.gd" id="1_migration_script"]
[ext_resource type="PackedScene" uid="uid://bp2q8r4l6k9m3" path="res://ui_scenes/components/WobbleControlPanel.tscn" id="2_wobble_panel"]
[node name="ComponentMigrationExample" type="Node2D"]
script = ExtResource("1_migration_script")
[node name="Background" type="ColorRect" parent="."]
offset_right = 300.0
offset_bottom = 400.0
color = Color(0.2, 0.2, 0.2, 1)
[node name="Title" type="Label" parent="."]
offset_left = 10.0
offset_top = 10.0
offset_right = 290.0
offset_bottom = 35.0
text = "New Component System Demo"
horizontal_alignment = 1
[node name="WobbleControlPanel" parent="." instance=ExtResource("2_wobble_panel")]
offset_left = 10.0
offset_top = 50.0
offset_right = 290.0
offset_bottom = 350.0
[node name="Instructions" type="Label" parent="."]
offset_left = 10.0
offset_top = 360.0
offset_right = 290.0
offset_bottom = 390.0
text = "These sliders automatically sync with sprites"
horizontal_alignment = 1
vertical_alignment = 1

View File

@@ -61,7 +61,6 @@ func _on_parameter_value_changed(new_value: float):
if not target_sprite or sprite_property == "":
return
print("DEBUG: ParameterSlider ", sprite_property, " changed to: ", new_value)
target_sprite.set(sprite_property, new_value)

View File

@@ -45,7 +45,6 @@ func _on_wobble_value_changed(new_value: float):
if not target_sprite or parameter_name == "":
return
print("DEBUG: WobbleSlider ", parameter_name, " changed to: ", new_value)
target_sprite.updateWobbleParameter(parameter_name, new_value)
# Update visual feedback for sync status

View File

@@ -6,6 +6,8 @@ var type = "sprite"
var imageData = null
var tex = null
@export var path = ""
## Custom display name for the sprite (separate from file path)
var displayName = ""
var loadedImageData = null
@@ -319,8 +321,6 @@ func calculateParentOpacityMultiplier():
## Update wobble parameters and sync to group if needed
func updateWobbleParameter(parameter: String, value: float):
print("DEBUG: updateWobbleParameter called - parameter: ", parameter, ", value: ", value, ", sprite ID: ", id)
print("DEBUG: Current wobbleSyncGroup: '", wobbleSyncGroup, "'")
# Update this sprite's parameter first
match parameter:
@@ -331,17 +331,15 @@ func updateWobbleParameter(parameter: String, value: float):
"rFrq": rFrq = value
"rAmp": rAmp = value
print("DEBUG: Parameter updated on sprite. New values - xFrq:", xFrq, " xAmp:", xAmp, " yFrq:", yFrq, " yAmp:", yAmp, " rFrq:", rFrq, " rAmp:", rAmp)
# If this sprite is in a sync group, update the entire group
if wobbleSyncGroup != "" and WobbleSyncManager:
print("DEBUG: Sprite is in sync group '", wobbleSyncGroup, "' - updating group wobble parameters")
WobbleSyncManager.updateGroupWobble(wobbleSyncGroup, xFrq, xAmp, yFrq, yAmp, rFrq, rAmp)
# Don't refresh UI immediately - let the sync process complete first
# The wobble sync control will handle UI updates when needed
else:
print("DEBUG: Sprite not in sync group or WobbleSyncManager not available")
pass # Individual sprite update - no sync needed
## Check if this sprite is synced to a group
func isSynced() -> bool:

View File

@@ -211,7 +211,6 @@ func _on_stream_deck_check_toggled(button_pressed):
func _on_experimental_mic_loudness_toggle(checked):
Global.experimentalMicLoudness = checked
Saving.settings["experimentalMicLoudness"] = checked
print("Experimental mic loudness: ", checked)
func _process(delta):

View File

@@ -24,8 +24,6 @@ func _ready():
# Find the wobble sync control node
wobbleSyncControl = find_child("WobbleSyncControl")
if wobbleSyncControl == null:
print("Warning: WobbleSyncControl node not found in sprite viewer")
# Set up blend mode dropdown
setupBlendModeDropdown()
@@ -355,8 +353,11 @@ func changeRotLimit():
$VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay.rotation_degrees = Global.heldSprite.rLimitMin
$VBoxContainer/RotationalLimitsSection/RotBack/RotLineDisplay2.rotation_degrees = Global.heldSprite.rLimitMax
func setLayerButtons():
if Global.heldSprite == null:
return
var a = Global.heldSprite.costumeLayers.duplicate()
$VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1.frame = 1-a[0]
@@ -370,6 +371,10 @@ func setLayerButtons():
$VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.frame = 1-a[8]
$VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10.frame = 1-a[9]
# Update checkmarks to show current costume layer
updateLayerCheckmarks()
# Update sprite visibility for all sprites based on current costume
var nodes = get_tree().get_nodes_in_group("saved")
for sprite in nodes:
if sprite.costumeLayers[Global.main.costume - 1] == 1:
@@ -378,7 +383,45 @@ func setLayerButtons():
else:
sprite.visible = false
sprite.changeCollision(false)
# Update the sprite list to reflect visibility changes
Global.spriteList.updateAllVisible()
func updateLayerCheckmarks():
# Hide all checkmarks first
$VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1/Checkmark1.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2/Checkmark2.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3/Checkmark3.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4/Checkmark4.visible = false
$VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5/Checkmark5.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6/Checkmark6.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7/Checkmark7.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8/Checkmark8.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9/Checkmark9.visible = false
$VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10/Checkmark10.visible = false
# Show checkmark for current costume layer
match Global.main.costume:
1:
$VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1/Checkmark1.visible = true
2:
$VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2/Checkmark2.visible = true
3:
$VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3/Checkmark3.visible = true
4:
$VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4/Checkmark4.visible = true
5:
$VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5/Checkmark5.visible = true
6:
$VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6/Checkmark6.visible = true
7:
$VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7/Checkmark7.visible = true
8:
$VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8/Checkmark8.visible = true
9:
$VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9/Checkmark9.visible = true
10:
$VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10/Checkmark10.visible = true
func _on_layer_button_1_pressed():
@@ -478,6 +521,11 @@ func layerSelected():
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9.position
10:
newPos = $VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10.position
# Update selection indicator position if it exists
var select_node = get_node_or_null("VBoxContainer/LayersSection/Select")
if select_node:
select_node.position = newPos
func _on_clip_linked_toggled(button_pressed):
@@ -494,7 +542,7 @@ func _on_delete_pressed():
Global.heldSprite.makeVis()
func _on_set_toggle_pressed():
$VBoxContainer/BoxContainer/setToggle/Labelbel.text = "toggle: AWAITING INPUT"
$VBoxContainer/BoxContainer/setToggle/Label.text = "toggle: AWAITING INPUT"
await Global.main.fatfuckingballs
var keys = await Global.main.spriteVisToggles

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=77 format=4 uid="uid://d3anahesvdgfh"]
[gd_scene load_steps=78 format=4 uid="uid://d3anahesvdgfh"]
[ext_resource type="Script" uid="uid://bgoaqt86bc73p" path="res://ui_scenes/spriteEditMenu/sprite_viewer.gd" id="1_hp0e3"]
[ext_resource type="FontFile" uid="uid://ukj8gv8ucqsg" path="res://font/goober_pixel.ttf" id="2_da8lk"]
@@ -27,11 +27,12 @@
[ext_resource type="Texture2D" uid="uid://drcl08v4k3laj" path="res://ui_scenes/spriteEditMenu/layerButtons/8.png" id="22_041y0"]
[ext_resource type="Texture2D" uid="uid://byyf5t56u3fq8" path="res://ui_scenes/spriteEditMenu/layerButtons/9.png" id="23_wja1u"]
[ext_resource type="Texture2D" uid="uid://dxxj6jvajo37s" path="res://ui_scenes/spriteEditMenu/layerButtons/10.png" id="24_1m26d"]
[ext_resource type="Texture2D" uid="uid://41u8egcau00i" path="res://ui_scenes/button sprites/checkmark.png" id="25_checkmark"]
[ext_resource type="Texture2D" uid="uid://23rqddatjku3" path="res://ui_scenes/mouse/tooltipBox.png" id="26_8cfad"]
[ext_resource type="Texture2D" uid="uid://gflmeocxfnrq" path="res://ui_scenes/settings/deleteHotkey.png" id="27_fgu6g"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_21kva"]
size = Vector2(245, 1597)
size = Vector2(261, 1794)
[sub_resource type="ShaderMaterial" id="ShaderMaterial_hxmah"]
shader = ExtResource("3_ky0lu")
@@ -14290,7 +14291,7 @@ script = ExtResource("1_hp0e3")
z_index = -2
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
position = Vector2(122.5, 798.5)
position = Vector2(130.5, 899)
shape = SubResource("RectangleShape2D_21kva")
[node name="NinePatchRect" type="NinePatchRect" parent="."]
@@ -14783,7 +14784,6 @@ size_flags_horizontal = 0
size_flags_vertical = 0
[node name="Layer1" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer1Container"]
z_as_relative = false
position = Vector2(22, 22)
texture = ExtResource("15_scank")
hframes = 2
@@ -14796,6 +14796,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark1" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer2Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14815,6 +14820,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark2" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer3Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14834,6 +14844,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark3" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer4Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14853,6 +14868,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark4" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer5Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14872,6 +14892,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark5" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/LayersSection"]
layout_mode = 2
theme_override_constants/separation = 5
@@ -14895,6 +14920,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark6" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer7Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14914,6 +14944,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark7" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer8Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14933,6 +14968,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark8" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer9Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14952,6 +14992,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark9" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="Layer10Container" type="Control" parent="VBoxContainer/LayersSection/HBoxContainer2"]
custom_minimum_size = Vector2(44, 44)
layout_mode = 2
@@ -14971,6 +15016,11 @@ offset_bottom = 22.0
theme = SubResource("Theme_ig6qj")
flat = true
[node name="Checkmark10" type="Sprite2D" parent="VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10"]
visible = false
position = Vector2(14, 12)
texture = ExtResource("25_checkmark")
[node name="BoxContainer" type="BoxContainer" parent="VBoxContainer"]
layout_mode = 2
@@ -15024,5 +15074,15 @@ centered = false
[connection signal="pressed" from="VBoxContainer/Buttons/TrashWrapper/Trash/trash" to="." method="_on_trash_pressed"]
[connection signal="toggled" from="VBoxContainer/RenderingSection/ClipLinked" to="." method="_on_clip_linked_toggled"]
[connection signal="toggled" from="VBoxContainer/PhysicsSection/BounceVelocity" to="." method="_on_check_box_toggled"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer1Container/Layer1/layerButton1" to="." method="_on_layer_button_1_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer2Container/Layer2/layerButton2" to="." method="_on_layer_button_2_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer3Container/Layer3/layerButton3" to="." method="_on_layer_button_3_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer4Container/Layer4/layerButton4" to="." method="_on_layer_button_4_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer/Layer5Container/Layer5/layerButton5" to="." method="_on_layer_button_5_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer6Container/Layer6/layerButton6" to="." method="_on_layer_button_6_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer7Container/Layer7/layerButton7" to="." method="_on_layer_button_7_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer8Container/Layer8/layerButton8" to="." method="_on_layer_button_8_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer9Container/Layer9/layerButton9" to="." method="_on_layer_button_9_pressed"]
[connection signal="pressed" from="VBoxContainer/LayersSection/HBoxContainer2/Layer10Container/Layer10/layerButton10" to="." method="_on_layer_button_10_pressed"]
[connection signal="pressed" from="VBoxContainer/BoxContainer/setToggle" to="." method="_on_set_toggle_pressed"]
[connection signal="pressed" from="VBoxContainer/BoxContainer/setToggle/delete" to="." method="_on_delete_pressed"]

View File

@@ -55,11 +55,10 @@ func updateGroupDropdown():
# Add existing groups
if WobbleSyncManager:
var groups = WobbleSyncManager.getGroupNames()
print("DEBUG: updateGroupDropdown - available groups: ", groups)
for group_name in groups:
groupDropdown.add_item(group_name)
else:
print("DEBUG: updateGroupDropdown - WobbleSyncManager not available")
pass # WobbleSyncManager not available
# Add "Create New..." option
groupDropdown.add_separator()
@@ -67,7 +66,6 @@ func updateGroupDropdown():
# Select current group if sprite is synced
if currentSprite != null and currentSprite.wobbleSyncGroup != "":
print("DEBUG: updateGroupDropdown - sprite has group '", currentSprite.wobbleSyncGroup, "'")
var group_index = -1
for i in range(groupDropdown.get_item_count()):
if groupDropdown.get_item_text(i) == currentSprite.wobbleSyncGroup:
@@ -75,13 +73,10 @@ func updateGroupDropdown():
break
if group_index != -1:
print("DEBUG: updateGroupDropdown - selecting group at index ", group_index)
groupDropdown.selected = group_index
else:
print("DEBUG: updateGroupDropdown - group '", currentSprite.wobbleSyncGroup, "' not found in dropdown")
groupDropdown.selected = 0 # Select "None"
else:
print("DEBUG: updateGroupDropdown - sprite has no group, selecting 'None'")
groupDropdown.selected = 0 # Select "None"
updating_dropdown = false
@@ -100,11 +95,9 @@ func updateSyncIndicator():
## Set the current sprite to manage
func setSprite(sprite):
print("DEBUG: WobbleSyncControl.setSprite called with sprite ID: ", sprite.id if sprite else "null")
currentSprite = sprite
if currentSprite:
print("DEBUG: Current sprite wobbleSyncGroup: '", currentSprite.wobbleSyncGroup, "'")
print("DEBUG: Current sprite isSynced(): ", currentSprite.isSynced())
pass # Sprite is valid
updateUI()
## Get available groups for the dropdown (excluding "None" and "Create New...")
@@ -120,18 +113,15 @@ func _on_group_dropdown_selected(index: int):
return
var selected_text = groupDropdown.get_item_text(index)
print("DEBUG: Dropdown selected: '", selected_text, "' at index ", index)
match selected_text:
"None":
# Remove sprite from any group
if currentSprite.wobbleSyncGroup != "" and WobbleSyncManager:
print("DEBUG: Removing sprite ID ", currentSprite.id, " from group '", currentSprite.wobbleSyncGroup, "'")
WobbleSyncManager.removeSpriteFromAllGroups(currentSprite.id)
Global.pushUpdate("Removed sprite from wobble sync group")
"Create New...":
print("DEBUG: User selected 'Create New...' from dropdown")
# Show create group dialog
groupNameInput.text = ""
groupNameInput.placeholder_text = "Enter group name..."
@@ -143,45 +133,39 @@ func _on_group_dropdown_selected(index: int):
_:
# Join selected group
if WobbleSyncManager and WobbleSyncManager.hasGroup(selected_text):
print("DEBUG: Adding sprite ID ", currentSprite.id, " to group '", selected_text, "'")
WobbleSyncManager.addSpriteToGroup(currentSprite.id, selected_text)
# Refresh sprite editor to show updated wobble values
if Global.spriteEdit:
Global.spriteEdit.setImage()
else:
print("DEBUG: Group '", selected_text, "' not found or WobbleSyncManager not available")
pass # Group doesn't exist
updateSyncIndicator()
## Handle create group dialog confirmation
func _on_create_group_confirmed():
var group_name = groupNameInput.text.strip_edges()
print("DEBUG: Create group confirmed with name: '", group_name, "'")
if group_name == "":
print("DEBUG: Group name is empty")
Global.pushUpdate("Group name cannot be empty")
return
if WobbleSyncManager and WobbleSyncManager.hasGroup(group_name):
print("DEBUG: Group '", group_name, "' already exists")
Global.pushUpdate("Group \"" + group_name + "\" already exists")
return
# Create group with current sprite
if currentSprite != null and WobbleSyncManager:
print("DEBUG: Creating group '", group_name, "' with sprite ID ", currentSprite.id)
if WobbleSyncManager.createGroup(group_name, currentSprite.id):
print("DEBUG: Group created successfully")
createGroupDialog.hide()
updateUI()
# Refresh sprite editor to show sync status
if Global.spriteEdit:
Global.spriteEdit.setImage()
else:
print("DEBUG: Failed to create group")
pass # Failed to create group
else:
print("DEBUG: No current sprite to add to group")
pass # No sprite or WobbleSyncManager not available
## Handle create group dialog cancellation
func _on_create_group_cancelled():
@@ -203,5 +187,4 @@ func canSync() -> bool:
## Force refresh the UI - useful after loading saves
func forceRefresh():
print("DEBUG: WobbleSyncControl.forceRefresh called")
updateUI()

View File

@@ -2,9 +2,9 @@ extends NinePatchRect
@onready var spritePreview = $SpritePreview/Sprite2D
@onready var outline = $Selected
@onready var fade = $Fade
@onready var button = $Button
@onready var label = $Label
var sprite = null
var parent = null
@@ -14,9 +14,23 @@ var indent = 0
var childrenTags = []
var parentTag = null
## Context menu for right-click actions
var context_menu: PopupMenu = null
## Line edit for renaming
var rename_edit: LineEdit = null
## Track if we're currently in rename mode
var is_renaming: bool = false
func _ready():
var count = spritePath.get_slice_count("/") - 1
$Label.text = spritePath.get_slice("/",count)
# Use displayName if available, otherwise fallback to path
var display_text = ""
if sprite.displayName != "":
display_text = sprite.displayName
else:
var count = spritePath.get_slice_count("/") - 1
display_text = spritePath.get_slice("/",count)
label.text = display_text
$Line2D.visible = false
spritePreview.texture = sprite.sprite.texture
@@ -25,6 +39,115 @@ func _ready():
spritePreview.scale = Vector2(1,1) * (60.0/displaySize)
spritePreview.offset = sprite.sprite.offset
# Set up right-click detection
button.gui_input.connect(_on_button_gui_input)
# Create context menu
_setup_context_menu()
## Set up the right-click context menu
func _setup_context_menu():
context_menu = PopupMenu.new()
add_child(context_menu)
# Add rename option
context_menu.add_item("Rename", 0)
context_menu.id_pressed.connect(_on_context_menu_selected)
# Wait for menu to be ready, then adjust size
context_menu.ready.connect(_adjust_menu_size)
## Adjust the context menu size to be more compact
func _adjust_menu_size():
if context_menu:
# Let the menu calculate its natural size first
await get_tree().process_frame
context_menu.reset_size() # Reset to content-based size
## Handle GUI input events on the button
func _on_button_gui_input(event: InputEvent):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
# Show context menu at cursor position
var global_pos = get_global_mouse_position()
context_menu.position = Vector2i(global_pos)
context_menu.popup()
## Handle context menu selections
func _on_context_menu_selected(id: int):
match id:
0: # Rename
_start_rename()
## Start the rename process
func _start_rename():
if is_renaming:
return
is_renaming = true
# Create line edit for renaming
rename_edit = LineEdit.new()
add_child(rename_edit)
# Position and size the line edit over the label
rename_edit.position = label.position
rename_edit.size = label.size
rename_edit.text = label.text
# Hide the original label
label.visible = false
# Focus the line edit and select all text
rename_edit.grab_focus()
rename_edit.select_all()
# Connect signals
rename_edit.text_submitted.connect(_on_rename_submitted)
rename_edit.focus_exited.connect(_on_rename_focus_lost)
## Handle rename submission (Enter key)
func _on_rename_submitted(new_name: String):
if rename_edit != null:
_finish_rename(new_name)
## Handle losing focus (clicking elsewhere)
func _on_rename_focus_lost():
if rename_edit != null:
_finish_rename(rename_edit.text)
## Finish the rename process
func _finish_rename(new_name: String):
if not is_renaming:
return
is_renaming = false
# Clean up the line edit
if rename_edit:
rename_edit.queue_free()
rename_edit = null
# Show the label again
label.visible = true
# Validate and apply the new name
new_name = new_name.strip_edges()
if new_name != "" and new_name != label.text:
_apply_rename(new_name)
## Apply the rename to the sprite
func _apply_rename(new_name: String):
# Update the display name (without file extension)
label.text = new_name
sprite.displayName = new_name # Store display name separately
# Update the global sprite list
Global.spriteList.updateData()
# Show feedback to user
Global.pushUpdate("Renamed sprite to: " + new_name)
func updateChildren():
for child in childrenTags: