mirror of
https://github.com/litruv/PNGTuber-Plus.git
synced 2026-07-24 02:26:02 +10:00
Compare commits
30 Commits
build-32ed
...
v2025.07.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 56e76c61e6 | |||
| 5dfe68cb81 | |||
| 6e02a67114 | |||
| 32a67a6617 | |||
| fb0c7a9e3b | |||
| f0ce00b23b | |||
| d4a9f5a0b0 | |||
| 085d6a21a0 | |||
| 99d2d3f508 | |||
| dad22ece22 | |||
| 52496df10e | |||
| 29b903fadd | |||
| e8191e2de8 | |||
| b7b7d7cdec | |||
| fc6e4a1028 | |||
| 1dfe21363b | |||
| fde3f23ed5 | |||
| 79e54c28da | |||
| a8f327ae5a | |||
| 1295bcbcba | |||
| 758f2ca885 | |||
| 524920d140 | |||
| f9c72448d9 | |||
|
|
135e63ef52 | ||
|
|
c86e68d383 | ||
| f226c14c00 | |||
| d345e700fc | |||
| bc187cff09 | |||
| fc4cacdc9f | |||
| 848361d19b |
80
.github/workflows/build-windows.yml
vendored
80
.github/workflows/build-windows.yml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
export-windows:
|
||||
name: Windows Export
|
||||
runs-on: self-hosted
|
||||
if: contains(github.event.head_commit.message, '!build')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -99,6 +100,74 @@ jobs:
|
||||
runs-on: self-hosted
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && success()
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Generate Release Info
|
||||
id: release_info
|
||||
run: |
|
||||
# Get current date and time
|
||||
$date = Get-Date -Format "yyyy.MM.dd"
|
||||
$time = Get-Date -Format "HHmm"
|
||||
|
||||
# Get short commit hash (first 7 characters)
|
||||
$shortSha = "${{ github.sha }}".Substring(0, 7)
|
||||
|
||||
# Get commit count since last tag (for build number)
|
||||
$buildNumber = & git rev-list --count HEAD
|
||||
|
||||
# Create version-like tag and release name
|
||||
$tag = "v$date-build$buildNumber"
|
||||
$releaseName = "PNGTuber+ v$date Build $buildNumber"
|
||||
|
||||
Write-Host "Generated tag: $tag"
|
||||
Write-Host "Generated release name: $releaseName"
|
||||
|
||||
# Get the latest release tag to compare against
|
||||
$latestTag = ""
|
||||
try {
|
||||
$latestTag = & git describe --tags --abbrev=0 2>$null
|
||||
Write-Host "Latest tag found: $latestTag"
|
||||
} catch {
|
||||
Write-Host "No previous tags found, showing all commits"
|
||||
}
|
||||
|
||||
# Generate commit history since last tag (or all commits if no tag)
|
||||
$commitRange = if ($latestTag) { "$latestTag..HEAD" } else { "HEAD" }
|
||||
|
||||
# Get commits with format: short hash, subject, author
|
||||
$commits = & git log $commitRange --pretty=format:"%h|%s|%an" --no-merges
|
||||
|
||||
# Build commit history as a single string
|
||||
$commitHistory = ""
|
||||
|
||||
if ($commits) {
|
||||
$commitHistory += "`n`n## Changes Since Last Release`n`n"
|
||||
|
||||
$commits | ForEach-Object {
|
||||
$parts = $_ -split '\|', 3
|
||||
$hash = $parts[0]
|
||||
$subject = $parts[1] -replace '"', '""'
|
||||
$author = $parts[2]
|
||||
$commitUrl = "https://github.com/${{ github.repository }}/commit/$hash"
|
||||
$commitHistory += "- [$hash]($commitUrl) $subject`n"
|
||||
}
|
||||
} else {
|
||||
$commitHistory += "`n`n## Changes Since Last Release`n`nNo new commits since last release.`n"
|
||||
}
|
||||
|
||||
# Set outputs for use in next steps
|
||||
echo "tag=$tag" >> $env:GITHUB_OUTPUT
|
||||
echo "release_name=$releaseName" >> $env:GITHUB_OUTPUT
|
||||
echo "short_sha=$shortSha" >> $env:GITHUB_OUTPUT
|
||||
echo "build_number=$buildNumber" >> $env:GITHUB_OUTPUT
|
||||
|
||||
# Use heredoc syntax for multiline commit history
|
||||
Add-Content -Path $env:GITHUB_OUTPUT -Value "commit_history<<EOF"
|
||||
Add-Content -Path $env:GITHUB_OUTPUT -Value $commitHistory
|
||||
Add-Content -Path $env:GITHUB_OUTPUT -Value "EOF"
|
||||
- name: Download Windows Build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -111,21 +180,18 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: build-${{ github.sha }}
|
||||
release_name: PNGTuber Plus Build ${{ github.sha }}
|
||||
tag_name: ${{ steps.release_info.outputs.tag }}
|
||||
release_name: ${{ steps.release_info.outputs.release_name }}
|
||||
body: |
|
||||
Automated build of PNGTuber Plus
|
||||
|
||||
## Recent Updates by Litruv
|
||||
- **StreamDeck Integration**: Add buttons to enable the streamdeck plugin that was built in
|
||||
- **Rotation Wobble**: New sprite rotation effects with frequency and amplitude controls
|
||||
- **Enhanced Modifier Keys**: Support for complex key combinations (Ctrl+Shift+Alt+Cmd)
|
||||
- **Godot 4.4.1 Support**: Updated for latest Godot stable release
|
||||
${{ steps.release_info.outputs.commit_history }}
|
||||
|
||||
## Installation
|
||||
1. Download PNGTuber-Plus.exe from the assets below
|
||||
2. Run the executable - no installation required!
|
||||
3. For StreamDeck integration, download the plugin from [here](https://github.com/BoyneGames/streamdeck-godot-plugin/releases/tag/1.0.0)
|
||||
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
|
||||
33
.vscode/launch.json
vendored
33
.vscode/launch.json
vendored
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch PNGTuber Plus (Godot)",
|
||||
"type": "godot",
|
||||
"request": "launch",
|
||||
"port": 6007,
|
||||
"address": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"name": "Attach to Running Godot",
|
||||
"type": "godot",
|
||||
"request": "attach",
|
||||
"port": 6007,
|
||||
"address": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"name": "Launch Built Executable",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
"script": "if (Test-Path '${workspaceFolder}\\build\\windows\\PNGTuber-Plus.exe') { Start-Process '${workspaceFolder}\\build\\windows\\PNGTuber-Plus.exe' } else { Write-Host 'Executable not found. Build the project first.' }",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Build and Launch",
|
||||
"type": "PowerShell",
|
||||
"request": "launch",
|
||||
"script": ".\\build-windows.bat; if (Test-Path '.\\build\\windows\\PNGTuber-Plus.exe') { Start-Process '.\\build\\windows\\PNGTuber-Plus.exe' }",
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
116
.vscode/tasks.json
vendored
116
.vscode/tasks.json
vendored
@@ -2,120 +2,10 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build Windows (Local)",
|
||||
"label": "Build PNGTuber-Plus",
|
||||
"type": "shell",
|
||||
"command": ".\\build-windows.bat",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"args": [],
|
||||
"isBackground": false,
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"detail": "Build Windows executable using local build script with template management"
|
||||
},
|
||||
{
|
||||
"label": "Build Windows (CI)",
|
||||
"type": "shell",
|
||||
"command": ".\\build-windows-ci.bat",
|
||||
"group": "build",
|
||||
"args": [],
|
||||
"isBackground": false,
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"detail": "Build Windows executable using CI build script (no template download)"
|
||||
},
|
||||
{
|
||||
"label": "Export Windows (Godot Direct)",
|
||||
"type": "shell",
|
||||
"command": "D:\\Godot\\Godot_v4.4.1-stable_win64.exe",
|
||||
"group": "build",
|
||||
"args": [
|
||||
"--headless",
|
||||
"--export-release",
|
||||
"\"Windows Desktop\"",
|
||||
"\"build/windows/PNGTuber-Plus.exe\""
|
||||
],
|
||||
"isBackground": false,
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"detail": "Export Windows build directly using Godot (requires templates already installed)"
|
||||
},
|
||||
{
|
||||
"label": "Clean Build Directory",
|
||||
"type": "shell",
|
||||
"command": "if (Test-Path 'build') { Remove-Item -Recurse -Force 'build' }; Write-Host 'Build directory cleaned'",
|
||||
"group": "build",
|
||||
"args": [],
|
||||
"isBackground": false,
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"detail": "Clean the build output directory"
|
||||
},
|
||||
{
|
||||
"label": "Open Build Directory",
|
||||
"type": "shell",
|
||||
"command": "if (Test-Path 'build\\windows') { explorer 'build\\windows' } else { Write-Host 'Build directory does not exist. Run a build first.' }",
|
||||
"group": "build",
|
||||
"args": [],
|
||||
"isBackground": false,
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"detail": "Open the Windows build output directory in Explorer"
|
||||
},
|
||||
{
|
||||
"label": "Run Built Executable",
|
||||
"type": "shell",
|
||||
"command": "if (Test-Path 'build\\windows\\PNGTuber-Plus.exe') { Start-Process 'build\\windows\\PNGTuber-Plus.exe' } else { Write-Host 'Executable not found. Build the project first.' }",
|
||||
"group": "test",
|
||||
"args": [],
|
||||
"isBackground": true,
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"detail": "Run the built Windows executable for testing"
|
||||
"command": "cd c:\\DEV\\Sync\\PNGTuber-Plus; .\\build-windows.bat",
|
||||
"group": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
79
README.md
79
README.md
@@ -2,16 +2,73 @@
|
||||
|
||||

|
||||
|
||||
### Recent Updates by Litruv
|
||||
- **GitHub Actions**: Automated Windows builds on every commit
|
||||
- **StreamDeck Integration**: Add buttons to enable the streamdeck plugin that was built in
|
||||
- **Rotation Wobble**: New sprite rotation effects with frequency and amplitude controls
|
||||
- **Enhanced Modifier Keys**: Support for complex key combinations (Ctrl+Shift+Alt+Cmd)
|
||||
- **Godot 4.4.1 Support**: Updated for latest Godot stable release
|
||||
- **Microphone Improvements**: Audio delay fix & more *from [k0ffinz/PNGTuber-Plus](https://github.com/k0ffinz/PNGTuber-Plus)*
|
||||
A fork of [kaiakairos/PNGTuber-Plus](https://github.com/kaiakairos/PNGTuber-Plus) with some quality-of-life upgrades, editor polish, and a few power-user toys.
|
||||
|
||||
## 🙏 Credits
|
||||
---
|
||||
|
||||
- **Original Project**: [PNGTuber Plus by kaiakairos](https://github.com/kaiakairos/PNGTuber-Plus)
|
||||
- **StreamDeck Plugin**: Based on [BoyneGames StreamDeck Godot Plugin](https://github.com/BoyneGames/streamdeck-godot-plugin)
|
||||
- **Microphone Improvements**: k0ffinz - Experimental mic loudness detection, improved audio initialization, and audio delay fixes from [k0ffinz/PNGTuber-Plus](https://github.com/k0ffinz/PNGTuber-Plus)
|
||||
## 🔧 Fork Changes (Litruv Edition)
|
||||
|
||||
### 🎛️ Opacity Controls
|
||||
|
||||
Added a proper opacity slider for sprites, with an "affect children" toggle. Clip masks still behave like they should.
|
||||
|
||||

|
||||
|
||||
|
||||
### 🌀 Rotation Wobble
|
||||
|
||||
Sprites can now do a subtle (or chaotic) rotational wobble. Frequency + amplitude sliders included.
|
||||
|
||||

|
||||
|
||||
|
||||
### ⌨️ Enhanced Modifier Keys
|
||||
|
||||
Supports combos like Ctrl+Shift+Alt+Cmd. For people with more keybinds than fingers.
|
||||
|
||||

|
||||
|
||||
|
||||
### ↗️ Editor Middle Mouse Button Panning
|
||||
|
||||
Hold MMB in the editor to pan around the scene, it'll reset when you go back to live.
|
||||
|
||||

|
||||
|
||||
### ↗️ View Mode MMB Shake
|
||||
|
||||
Hold MMB in view mode to shake your character, Show off them physics!
|
||||
|
||||

|
||||
|
||||
|
||||
🎞️ Delta Time Physics
|
||||
|
||||
Physics are now framerate-independent. Low FPS won’t make your character move in slow motion anymore.
|
||||
Just smooth, consistent jank if that’s your thing. Great for stop-motion vibes.
|
||||
|
||||
|
||||
### 🎤 Microphone Fixes
|
||||
|
||||
Merged in [k0ffinz’s](https://github.com/k0ffinz/PNGTuber-Plus) audio tweaks:
|
||||
|
||||
* Reduced mic startup delay
|
||||
* Better loudness detection
|
||||
* General stability improvements
|
||||
|
||||
|
||||
### 🧱 Engine Update
|
||||
|
||||
Updated to **Godot 4.4.1** — no more editor nags or compatibility weirdness.
|
||||
|
||||
|
||||
### 🎚️ StreamDeck Integration
|
||||
|
||||
StreamDeck plugin was already in, this just wires up the buttons.
|
||||
---
|
||||
|
||||
## 🧾 Credits
|
||||
|
||||
* Original project by [kaiakairos](https://github.com/kaiakairos/PNGTuber-Plus)
|
||||
* StreamDeck plugin from [BoyneGames](https://github.com/BoyneGames/streamdeck-godot-plugin)
|
||||
* Mic improvements by [k0ffinz](https://github.com/k0ffinz/PNGTuber-Plus)
|
||||
|
||||
Binary file not shown.
@@ -106,7 +106,7 @@ func _process(delta):
|
||||
heldSprite.z += 1
|
||||
heldSprite.setZIndex()
|
||||
pushUpdate("Moved sprite layer.")
|
||||
if main.editMode:
|
||||
if main and main.editMode:
|
||||
if Input.is_action_just_pressed("reparent"):
|
||||
reparentMode = !reparentMode
|
||||
Global.chain.enable(reparentMode)
|
||||
@@ -115,7 +115,7 @@ func _process(delta):
|
||||
reparentMode = false
|
||||
Global.chain.enable(reparentMode)
|
||||
|
||||
if main.editMode:
|
||||
if main and main.editMode:
|
||||
if reparentMode:
|
||||
RenderingServer.set_default_clear_color(Color.POWDER_BLUE)
|
||||
else:
|
||||
@@ -126,7 +126,7 @@ func _process(delta):
|
||||
scrollSprites()
|
||||
|
||||
|
||||
if !main.fileSystemOpen:
|
||||
if !main or main.fileSystemOpen:
|
||||
|
||||
if Input.is_action_just_pressed("refresh"):
|
||||
refresh()
|
||||
@@ -222,7 +222,7 @@ func scrollSprites():
|
||||
if Input.is_action_pressed("control"):
|
||||
return
|
||||
|
||||
if !main.editMode:
|
||||
if !main or !main.editMode:
|
||||
return
|
||||
|
||||
if main.fileSystemOpen:
|
||||
|
||||
Binary file not shown.
@@ -47,3 +47,4 @@ if %ERRORLEVEL% EQU 0 (
|
||||
echo.
|
||||
)
|
||||
|
||||
pause
|
||||
|
||||
@@ -4,7 +4,8 @@ extends Node2D
|
||||
@onready var editButton = $Edit/Button
|
||||
|
||||
func _process(delta):
|
||||
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
|
||||
if Rect2(editButton.get_parent().position-Vector2(24,24),editButton.size).has_point(get_local_mouse_position()):
|
||||
editSprite.scale = lerp(editSprite.scale,Vector2(1.2,1.2),0.2)
|
||||
editSprite.scale = lerp(editSprite.scale, Vector2(1.2,1.2), delta * uiAnimSpeed)
|
||||
else:
|
||||
editSprite.scale = lerp(editSprite.scale,Vector2(1.0,1.0),0.2)
|
||||
editSprite.scale = lerp(editSprite.scale, Vector2(1.0,1.0), delta * uiAnimSpeed)
|
||||
|
||||
@@ -30,7 +30,8 @@ func _process(delta):
|
||||
if buttons[b] == null:
|
||||
continue
|
||||
if Rect2(buttons[b].get_parent().position-Vector2(24,24),buttons[b].size).has_point(get_local_mouse_position()):
|
||||
sprites[s].scale = lerp(sprites[s].scale,Vector2(1.2,1.2),0.2)
|
||||
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
|
||||
sprites[s].scale = lerp(sprites[s].scale, Vector2(1.2,1.2), delta * uiAnimSpeed)
|
||||
if Input.is_action_pressed("mouse_left"):
|
||||
sprites[s].scale = Vector2(0.6,0.6)
|
||||
match b:
|
||||
@@ -50,7 +51,8 @@ func _process(delta):
|
||||
Global.mouse.text = "Duplicate sprite"
|
||||
|
||||
else:
|
||||
sprites[s].scale = lerp(sprites[s].scale,Vector2(1.0,1.0),0.2)
|
||||
var uiAnimSpeed = Global.main.physicsTimeMultiplier * 20.0 if Global.main else 120.0
|
||||
sprites[s].scale = lerp(sprites[s].scale, Vector2(1.0,1.0), delta * uiAnimSpeed)
|
||||
s += 1
|
||||
|
||||
var newColor = Color.DARK_SLATE_GRAY if Global.heldSprite == null else Color.WHITE
|
||||
|
||||
@@ -30,13 +30,16 @@ var editMode = true
|
||||
@onready var spriteObject = preload("res://ui_scenes/selectedSprite/spriteObject.tscn")
|
||||
|
||||
var saveLoaded = false
|
||||
var isSelectingMask = false
|
||||
|
||||
#Motion
|
||||
var yVel = 0
|
||||
var bounceSlider = 250
|
||||
var bounceGravity = 1000
|
||||
|
||||
# Physics timing multiplier - adjust this to fine-tune physics speed
|
||||
# 1.0 = very slow, 10.0 = reasonable, 60.0 = old frame-based speed
|
||||
var physicsTimeMultiplier = 6.0
|
||||
|
||||
#Costumes
|
||||
var costume = 1
|
||||
var bounceOnCostumeChange = false
|
||||
@@ -44,6 +47,18 @@ var bounceOnCostumeChange = false
|
||||
#Zooming
|
||||
var scaleOverall = 100
|
||||
|
||||
#Camera Panning
|
||||
var isPanning = false
|
||||
var initialPanPosition = Vector2.ZERO
|
||||
var initialCameraOffset = Vector2.ZERO
|
||||
var cameraOffset = Vector2.ZERO
|
||||
|
||||
#View Mode Panning
|
||||
var viewModePanning = false
|
||||
var initialViewPanPosition = Vector2.ZERO
|
||||
var viewPanOffset = Vector2.ZERO
|
||||
var viewPanLerpSpeed = 3.0
|
||||
|
||||
var bounceChange = 0.0
|
||||
|
||||
#IMPORTANT
|
||||
@@ -63,9 +78,7 @@ func _ready():
|
||||
|
||||
Global.connect("startSpeaking",onSpeak)
|
||||
|
||||
var streamdeck_node = get_node_or_null("/root/ElgatoStreamDeck")
|
||||
if streamdeck_node:
|
||||
streamdeck_node.on_key_down.connect(changeCostumeStreamDeck)
|
||||
ElgatoStreamDeck.on_key_down.connect(changeCostumeStreamDeck)
|
||||
|
||||
if Saving.settings["newUser"]:
|
||||
_on_load_dialog_file_selected("default")
|
||||
@@ -133,43 +146,96 @@ func _ready():
|
||||
|
||||
var s = get_viewport().get_visible_rect().size
|
||||
origin.position = s*0.5
|
||||
camera.position = origin.position
|
||||
updateCameraPosition()
|
||||
|
||||
func _process(delta):
|
||||
var hold = origin.get_parent().position.y
|
||||
|
||||
origin.get_parent().position.y += yVel * 0.0166
|
||||
origin.get_parent().position.y += yVel * delta * physicsTimeMultiplier
|
||||
if origin.get_parent().position.y > 0:
|
||||
origin.get_parent().position.y = 0
|
||||
bounceChange = hold - origin.get_parent().position.y
|
||||
|
||||
yVel += bounceGravity*0.0166
|
||||
yVel += bounceGravity * delta * physicsTimeMultiplier
|
||||
|
||||
# Handle view mode panning lerp back to center
|
||||
if !editMode:
|
||||
if !viewModePanning:
|
||||
# Lerp back to center when not actively panning
|
||||
viewPanOffset = viewPanOffset.lerp(Vector2.ZERO, viewPanLerpSpeed * delta)
|
||||
|
||||
# Apply view pan offset to the root character (affects physics)
|
||||
origin.position = (get_viewport().get_visible_rect().size * 0.5) + viewPanOffset
|
||||
updateCameraPosition()
|
||||
|
||||
if Input.is_action_just_pressed("openFolder"):
|
||||
OS.shell_open(ProjectSettings.globalize_path("user://"))
|
||||
|
||||
moveSpriteMenu(delta)
|
||||
zoomScene()
|
||||
zoomScene(delta)
|
||||
|
||||
fileSystemOpen = isFileSystemOpen()
|
||||
|
||||
followShadow()
|
||||
|
||||
func followShadow():
|
||||
shadow.visible = false # Disabled drop shadow
|
||||
return
|
||||
## Handle input events, especially for camera panning
|
||||
func _input(event):
|
||||
# Handle middle mouse button for camera panning/reset
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_MIDDLE:
|
||||
if editMode:
|
||||
if event.pressed:
|
||||
# Start panning in edit mode
|
||||
isPanning = true
|
||||
initialPanPosition = event.global_position
|
||||
initialCameraOffset = cameraOffset
|
||||
else:
|
||||
# Stop panning
|
||||
isPanning = false
|
||||
else:
|
||||
if event.pressed:
|
||||
# Start view mode panning
|
||||
viewModePanning = true
|
||||
initialViewPanPosition = event.global_position
|
||||
else:
|
||||
# Stop view mode panning
|
||||
viewModePanning = false
|
||||
|
||||
shadow.visible = is_instance_valid(Global.heldSprite)
|
||||
if !shadow.visible:
|
||||
return
|
||||
|
||||
shadow.global_position = Global.heldSprite.sprite.global_position + Vector2(6,6)
|
||||
shadow.global_rotation = Global.heldSprite.sprite.global_rotation
|
||||
shadow.offset = Global.heldSprite.sprite.offset
|
||||
# Handle mouse movement during edit mode panning
|
||||
elif editMode and event is InputEventMouseMotion and isPanning:
|
||||
var totalDelta = event.global_position - initialPanPosition
|
||||
|
||||
shadow.texture = Global.heldSprite.sprite.texture
|
||||
shadow.hframes = Global.heldSprite.sprite.hframes
|
||||
shadow.frame = Global.heldSprite.sprite.frame
|
||||
# Scale pan distance based on zoom level for more intuitive panning
|
||||
# Higher zoom (closer) = less movement, lower zoom (farther) = more movement
|
||||
var zoomScale = 1.0 / camera.zoom.x
|
||||
var scaledDelta = totalDelta * zoomScale
|
||||
|
||||
# Calculate new camera offset from initial position plus scaled movement
|
||||
cameraOffset = initialCameraOffset - scaledDelta
|
||||
|
||||
# Update camera position immediately
|
||||
var s = get_viewport().get_visible_rect().size
|
||||
camera.position = origin.position + cameraOffset
|
||||
|
||||
# Update UI positions
|
||||
controlPanel.position = camera.position + (s/(camera.zoom*2.0))
|
||||
tutorial.position = controlPanel.position
|
||||
editControls.position = camera.position - (s/(camera.zoom*2.0))
|
||||
viewerArrows.position = editControls.position
|
||||
pushUpdates.position.y = controlPanel.position.y
|
||||
pushUpdates.position.x = editControls.position.x
|
||||
|
||||
# Handle mouse movement during view mode panning
|
||||
elif !editMode and event is InputEventMouseMotion and viewModePanning:
|
||||
var totalDelta = event.global_position - initialViewPanPosition
|
||||
|
||||
# Scale pan distance for view mode (less sensitive than edit mode)
|
||||
# Invert the movement so dragging right moves character right (not left)
|
||||
var panSensitivity = 0.5
|
||||
viewPanOffset = -totalDelta * panSensitivity
|
||||
|
||||
func followShadow():
|
||||
# Shadow disabled for selected sprites
|
||||
shadow.visible = false
|
||||
|
||||
|
||||
func isFileSystemOpen():
|
||||
@@ -177,9 +243,6 @@ func isFileSystemOpen():
|
||||
if obj.visible:
|
||||
if obj == replaceDialog:
|
||||
return true
|
||||
# Don't clear held sprite when selecting mask
|
||||
if obj == fileDialog and isSelectingMask:
|
||||
return true
|
||||
Global.heldSprite = null
|
||||
return true
|
||||
return false
|
||||
@@ -207,16 +270,10 @@ func onWindowSizeChange():
|
||||
lines.position = s*0.5
|
||||
lines.drawLine()
|
||||
|
||||
camera.position = origin.position
|
||||
controlPanel.position = camera.position + (s/(camera.zoom*2.0))
|
||||
tutorial.position = controlPanel.position
|
||||
editControls.position = camera.position - (s/(camera.zoom*2.0))
|
||||
viewerArrows.position = editControls.position
|
||||
updateCameraPosition()
|
||||
spriteList.position.x = s.x - 233
|
||||
pushUpdates.position.y = controlPanel.position.y
|
||||
pushUpdates.position.x = editControls.position.x
|
||||
|
||||
func zoomScene():
|
||||
func zoomScene(delta):
|
||||
#Handles Zooming
|
||||
if Input.is_action_pressed("control"):
|
||||
if Input.is_action_just_pressed("scrollUp"):
|
||||
@@ -230,7 +287,7 @@ func zoomScene():
|
||||
scaleOverall -= 10
|
||||
changeZoom()
|
||||
|
||||
$ControlPanel/ZoomLabel.modulate.a = lerp($ControlPanel/ZoomLabel.modulate.a,0.0,0.02)
|
||||
$ControlPanel/ZoomLabel.modulate.a = lerp($ControlPanel/ZoomLabel.modulate.a, 0.0, delta * (physicsTimeMultiplier * 1.2))
|
||||
|
||||
func changeZoom():
|
||||
var newZoom = Vector2(1.0,1.0) / camera.zoom
|
||||
@@ -242,11 +299,45 @@ func changeZoom():
|
||||
pushUpdates.scale = newZoom
|
||||
Global.mouse.scale = newZoom
|
||||
|
||||
# Update UI positions to account for new zoom level
|
||||
# Force update even during panning since zoom affects UI positioning calculations
|
||||
var s = get_viewport().get_visible_rect().size
|
||||
controlPanel.position = camera.position + (s/(camera.zoom*2.0))
|
||||
tutorial.position = controlPanel.position
|
||||
editControls.position = camera.position - (s/(camera.zoom*2.0))
|
||||
viewerArrows.position = editControls.position
|
||||
pushUpdates.position.y = controlPanel.position.y
|
||||
pushUpdates.position.x = editControls.position.x
|
||||
|
||||
$ControlPanel/ZoomLabel.modulate.a = 6.0
|
||||
$ControlPanel/ZoomLabel.text = "Zoom : " + str(scaleOverall) + "%"
|
||||
|
||||
Global.pushUpdate("Set zoom to " + str(scaleOverall) + "%")
|
||||
onWindowSizeChange()
|
||||
|
||||
## Update camera position with current offset
|
||||
func updateCameraPosition():
|
||||
var s = get_viewport().get_visible_rect().size
|
||||
|
||||
# Only update if not currently panning to avoid conflicts
|
||||
if !isPanning:
|
||||
camera.position = origin.position + cameraOffset
|
||||
|
||||
# Update UI element positions relative to new camera position
|
||||
controlPanel.position = camera.position + (s/(camera.zoom*2.0))
|
||||
tutorial.position = controlPanel.position
|
||||
editControls.position = camera.position - (s/(camera.zoom*2.0))
|
||||
viewerArrows.position = editControls.position
|
||||
pushUpdates.position.y = controlPanel.position.y
|
||||
pushUpdates.position.x = editControls.position.x
|
||||
|
||||
## Reset camera position to center
|
||||
func resetCameraPosition():
|
||||
cameraOffset = Vector2.ZERO
|
||||
isPanning = false
|
||||
viewPanOffset = Vector2.ZERO
|
||||
viewModePanning = false
|
||||
updateCameraPosition()
|
||||
Global.pushUpdate("Camera reset to center.")
|
||||
|
||||
#When the user speaks!
|
||||
func onSpeak():
|
||||
@@ -261,6 +352,14 @@ func swapMode():
|
||||
editMode = !editMode
|
||||
Global.pushUpdate("Toggled editing mode.")
|
||||
|
||||
# Reset camera when entering view mode
|
||||
if !editMode:
|
||||
resetCameraPosition()
|
||||
else:
|
||||
# Reset view mode panning when entering edit mode
|
||||
viewPanOffset = Vector2.ZERO
|
||||
viewModePanning = false
|
||||
|
||||
get_viewport().transparent_bg = !editMode
|
||||
if Global.backgroundColor.a != 0.0:
|
||||
get_viewport().transparent_bg = false
|
||||
@@ -297,20 +396,7 @@ func _on_add_button_pressed():
|
||||
|
||||
#Runs when selecting image in File Dialog
|
||||
func _on_file_dialog_file_selected(path):
|
||||
if isSelectingMask:
|
||||
# Handle mask selection
|
||||
if Global.heldSprite != null:
|
||||
Global.heldSprite.setMaskTexture(path)
|
||||
Global.spriteEdit.updateMaskDisplay(path)
|
||||
isSelectingMask = false
|
||||
else:
|
||||
# Handle normal sprite addition
|
||||
add_image(path)
|
||||
|
||||
#Opens File Dialog for mask selection
|
||||
func openMaskSelection():
|
||||
isSelectingMask = true
|
||||
fileDialog.visible = true
|
||||
add_image(path)
|
||||
|
||||
func _on_save_button_pressed():
|
||||
$SaveDialog.visible = true
|
||||
@@ -384,14 +470,10 @@ func _on_load_dialog_file_selected(path):
|
||||
sprite.clipped = data[item]["clipped"]
|
||||
if data[item].has("toggle"):
|
||||
sprite.toggle = data[item]["toggle"]
|
||||
|
||||
# Load mask properties
|
||||
if data[item].has("mask_texture_path"):
|
||||
sprite.mask_texture_path = data[item]["mask_texture_path"]
|
||||
if data[item].has("use_mask"):
|
||||
sprite.use_mask = data[item]["use_mask"]
|
||||
if data[item].has("mask_opacity"):
|
||||
sprite.mask_opacity = data[item]["mask_opacity"]
|
||||
if data[item].has("spriteOpacity"):
|
||||
sprite.spriteOpacity = data[item]["spriteOpacity"]
|
||||
if data[item].has("affectChildrenOpacity"):
|
||||
sprite.affectChildrenOpacity = data[item]["affectChildrenOpacity"]
|
||||
|
||||
origin.add_child(sprite)
|
||||
sprite.position = str_to_var(data[item]["pos"])
|
||||
@@ -400,6 +482,13 @@ func _on_load_dialog_file_selected(path):
|
||||
Saving.settings["lastAvatar"] = path
|
||||
Global.spriteList.updateData()
|
||||
|
||||
# Update opacity shaders for all loaded sprites after they're all created
|
||||
await get_tree().process_frame # Wait for all sprites to be fully initialized
|
||||
var allSprites = get_tree().get_nodes_in_group("saved")
|
||||
for sprite in allSprites:
|
||||
if sprite.has_method("updateOpacity"):
|
||||
sprite.updateOpacity()
|
||||
|
||||
Global.pushUpdate("Loaded avatar at: " + path)
|
||||
|
||||
onWindowSizeChange()
|
||||
@@ -454,10 +543,8 @@ func _on_save_dialog_file_selected(path):
|
||||
|
||||
data[id]["toggle"] = child.toggle
|
||||
|
||||
# Save mask properties
|
||||
data[id]["mask_texture_path"] = child.mask_texture_path
|
||||
data[id]["use_mask"] = child.use_mask
|
||||
data[id]["mask_opacity"] = child.mask_opacity
|
||||
data[id]["spriteOpacity"] = child.spriteOpacity
|
||||
data[id]["affectChildrenOpacity"] = child.affectChildrenOpacity
|
||||
|
||||
id += 1
|
||||
|
||||
@@ -535,9 +622,16 @@ func _on_duplicate_button_pressed():
|
||||
|
||||
sprite.costumeLayers = Global.heldSprite.costumeLayers
|
||||
|
||||
sprite.spriteOpacity = Global.heldSprite.spriteOpacity
|
||||
sprite.affectChildrenOpacity = Global.heldSprite.affectChildrenOpacity
|
||||
|
||||
origin.add_child(sprite)
|
||||
sprite.position = Global.heldSprite.position + Vector2(16,16)
|
||||
|
||||
# Update opacity shader for the duplicated sprite
|
||||
await get_tree().process_frame # Wait for sprite to be fully initialized
|
||||
sprite.updateOpacity()
|
||||
|
||||
Global.heldSprite = sprite
|
||||
|
||||
Global.spriteList.updateData()
|
||||
@@ -582,7 +676,7 @@ func moveSpriteMenu(delta):
|
||||
|
||||
var size = get_viewport().get_visible_rect().size
|
||||
|
||||
var windowLength = 1800
|
||||
var windowLength = 1500
|
||||
|
||||
$ViewerArrows/Arrows.position.y = size.y - 25
|
||||
|
||||
@@ -610,9 +704,9 @@ func moveSpriteMenu(delta):
|
||||
|
||||
|
||||
if $EditControls/MoveMenuUp.overlaps_area(Global.mouse.area):
|
||||
Global.spriteEdit.position.y += (delta*432.0)
|
||||
Global.spriteEdit.position.y += (delta * 432.0 * physicsTimeMultiplier)
|
||||
elif $EditControls/MoveMenuDown.overlaps_area(Global.mouse.area):
|
||||
Global.spriteEdit.position.y -= (delta*432.0)
|
||||
Global.spriteEdit.position.y -= (delta * 432.0 * physicsTimeMultiplier)
|
||||
|
||||
if Global.spriteEdit.position.y > 66:
|
||||
Global.spriteEdit.position.y = 66
|
||||
@@ -639,7 +733,7 @@ func _on_background_input_capture_bg_key_pressed(node, keys_pressed):
|
||||
var has_ctrl = keys_pressed.has(KEY_CTRL) and keys_pressed[KEY_CTRL]
|
||||
var has_shift = keys_pressed.has(KEY_SHIFT) and keys_pressed[KEY_SHIFT]
|
||||
var has_alt = keys_pressed.has(KEY_ALT) and keys_pressed[KEY_ALT]
|
||||
var meta_pressed = keys_pressed.has(KEY_META) and keys_pressed[KEY_META] # Command key on macOS
|
||||
var has_meta = keys_pressed.has(KEY_META) and keys_pressed[KEY_META] # Command key on macOS
|
||||
|
||||
# Build modifier prefix once
|
||||
if has_ctrl:
|
||||
@@ -648,7 +742,7 @@ func _on_background_input_capture_bg_key_pressed(node, keys_pressed):
|
||||
modifiers.append("Shift")
|
||||
if has_alt:
|
||||
modifiers.append("Alt")
|
||||
if meta_pressed:
|
||||
if has_meta:
|
||||
modifiers.append("Cmd") # Command key on macOS
|
||||
|
||||
var modifier_prefix = ""
|
||||
@@ -662,47 +756,49 @@ func _on_background_input_capture_bg_key_pressed(node, keys_pressed):
|
||||
if i == KEY_CTRL or i == KEY_SHIFT or i == KEY_ALT or i == KEY_META:
|
||||
continue
|
||||
|
||||
var key_name = OS.get_keycode_string(i) if !OS.get_keycode_string(i).strip_edges().is_empty() else "Keycode" + str(i)
|
||||
keyStrings.append(modifier_prefix + key_name)
|
||||
# Skip mouse button keycodes (common source of phantom inputs)
|
||||
if i == MOUSE_BUTTON_LEFT or i == MOUSE_BUTTON_RIGHT or i == MOUSE_BUTTON_MIDDLE:
|
||||
continue
|
||||
|
||||
# Skip phantom keypad inputs if no actual keypad connected
|
||||
var key_name = OS.get_keycode_string(i)
|
||||
if key_name.begins_with("kp ") or key_name.begins_with("Kp "):
|
||||
# Skip keypad inputs unless they're from actual key presses
|
||||
continue
|
||||
|
||||
if !key_name.strip_edges().is_empty():
|
||||
keyStrings.append(modifier_prefix + key_name)
|
||||
else:
|
||||
keyStrings.append(modifier_prefix + "Keycode" + str(i))
|
||||
|
||||
if fileSystemOpen:
|
||||
return
|
||||
|
||||
if keyStrings.size() <= 0:
|
||||
emit_signal("emptiedCapture")
|
||||
emit_signal("fatfuckingballs")
|
||||
return
|
||||
|
||||
if settingsMenu.awaitingCostumeInput >= 0:
|
||||
|
||||
if keyStrings[0] == "Keycode1":
|
||||
if !settingsMenu.hasMouse:
|
||||
emit_signal("pressedKey")
|
||||
return
|
||||
# Cancel key assignment if user clicks outside the settings menu
|
||||
if keyStrings[0] == "Keycode1" and !settingsMenu.hasMouse:
|
||||
# Don't assign the key, just complete the flow
|
||||
emit_signal("pressedKey")
|
||||
return
|
||||
|
||||
var currentButton = costumeKeys[settingsMenu.awaitingCostumeInput]
|
||||
costumeKeys[settingsMenu.awaitingCostumeInput] = keyStrings[0]
|
||||
Saving.settings["costumeKeys"] = costumeKeys
|
||||
Global.pushUpdate("Changed costume " + str(settingsMenu.awaitingCostumeInput+1) + " hotkey from \"" + currentButton + "\" to \"" + keyStrings[0] + "\"")
|
||||
emit_signal("pressedKey")
|
||||
return
|
||||
|
||||
# Handle sprite visibility toggles if not waiting for costume input
|
||||
spriteVisToggles.emit(keyStrings)
|
||||
|
||||
for key in keyStrings:
|
||||
var i = costumeKeys.find(key)
|
||||
if i >= 0:
|
||||
changeCostume(i+1)
|
||||
|
||||
|
||||
|
||||
func bgInputSprite(node, keys_pressed):
|
||||
if fileSystemOpen:
|
||||
return
|
||||
var keyStrings = []
|
||||
|
||||
for i in keys_pressed:
|
||||
if keys_pressed[i]:
|
||||
keyStrings.append(OS.get_keycode_string(i) if !OS.get_keycode_string(i).strip_edges().is_empty() else "Keycode" + str(i))
|
||||
|
||||
if keyStrings.size() <= 0:
|
||||
emit_signal("fatfuckingballs")
|
||||
return
|
||||
|
||||
spriteVisToggles.emit(keyStrings)
|
||||
|
||||
@@ -14358,6 +14358,7 @@ text = "Zoom : 100%"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="VersionLabels" type="Node2D" parent="ControlPanel"]
|
||||
position = Vector2(-284, 59)
|
||||
|
||||
[node name="Label" type="Label" parent="ControlPanel/VersionLabels"]
|
||||
offset_left = -713.0
|
||||
@@ -14377,13 +14378,21 @@ label_settings = SubResource("LabelSettings_qg0do")
|
||||
|
||||
[node name="versionNo" type="Label" parent="ControlPanel/VersionLabels/Label2"]
|
||||
layout_mode = 0
|
||||
offset_left = 144.0
|
||||
offset_top = 40.0
|
||||
offset_right = 282.0
|
||||
offset_bottom = 74.0
|
||||
offset_left = 104.0
|
||||
offset_top = 11.0
|
||||
offset_right = 242.0
|
||||
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
|
||||
text = "Litruv Edition"
|
||||
label_settings = SubResource("LabelSettings_qg0do")
|
||||
|
||||
[node name="EditControls" type="Node2D" parent="."]
|
||||
z_index = 4090
|
||||
script = ExtResource("17_bf1ic")
|
||||
@@ -14745,4 +14754,3 @@ z_index = -4096
|
||||
[connection signal="file_selected" from="SaveDialog" to="." method="_on_save_dialog_file_selected"]
|
||||
[connection signal="file_selected" from="LoadDialog" to="." method="_on_load_dialog_file_selected"]
|
||||
[connection signal="bg_key_pressed" from="BackgroundInputCapture" to="." method="_on_background_input_capture_bg_key_pressed"]
|
||||
[connection signal="bg_key_pressed" from="BackgroundInputCapture" to="." method="bgInputSprite"]
|
||||
|
||||
@@ -136,6 +136,11 @@ control={
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
mouse_middle={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":3,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
saveImages={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":76,"key_label":0,"unicode":108,"location":0,"echo":false,"script":null)
|
||||
@@ -148,6 +153,4 @@ textures/canvas_textures/default_texture_filter=0
|
||||
textures/canvas_textures/default_texture_repeat=1
|
||||
renderer/rendering_method="gl_compatibility"
|
||||
renderer/rendering_method.mobile="gl_compatibility"
|
||||
shading/overrides/force_vertex_shading=true
|
||||
shading/overrides/force_lambert_over_burley=true
|
||||
environment/defaults/default_clear_color=Color(0.376471, 0.376471, 0.376471, 1)
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform sampler2D noise_texture: source_color, repeat_enable;
|
||||
uniform float distortion_strengh: hint_range(0, 0.1) = 0.01;
|
||||
uniform sampler2D noise_texture:repeat_enable;
|
||||
uniform float distortion_strengh: hint_range(0, 0.1) = 1.0;
|
||||
uniform float speed: hint_range(0.1, 10) = 1.0;
|
||||
uniform bool use_noise_texture: hint_default_true = false;
|
||||
|
||||
void fragment() {
|
||||
vec2 uv_offset = vec2(0.0);
|
||||
|
||||
if (use_noise_texture) {
|
||||
vec4 noise_pixel = texture(noise_texture, UV + floor(TIME * speed) / 3.0);
|
||||
uv_offset = (noise_pixel.rg * 2.0 - 1.0) * distortion_strengh;
|
||||
} else {
|
||||
// Fallback procedural noise when no texture is provided
|
||||
float noise = sin(UV.x * 20.0 + TIME * speed) * cos(UV.y * 20.0 + TIME * speed * 0.7);
|
||||
uv_offset = vec2(noise) * distortion_strengh;
|
||||
}
|
||||
|
||||
vec4 noise_pixel = texture(noise_texture, UV + floor(TIME*speed)/3.0);
|
||||
vec2 uv_offset = (noise_pixel.rg * 2.0 - 1.0) * distortion_strengh;
|
||||
COLOR = texture(TEXTURE, UV + uv_offset);
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
uid://4a1uhohwy43l
|
||||
uid://b342og0l0k1p
|
||||
|
||||
@@ -9,7 +9,7 @@ func _ready():
|
||||
Global.mouse = self
|
||||
|
||||
func _process(delta):
|
||||
if Global.main.editMode:
|
||||
if Global.main and Global.main.editMode:
|
||||
if text != "":
|
||||
label.text = text
|
||||
visible = true
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform sampler2D mask_texture : source_color;
|
||||
uniform bool use_mask = false;
|
||||
uniform float mask_opacity : hint_range(0.0, 1.0) = 1.0;
|
||||
|
||||
void fragment() {
|
||||
// Get the original sprite color
|
||||
vec4 sprite_color = texture(TEXTURE, UV);
|
||||
|
||||
if (use_mask) {
|
||||
// Sample the mask texture
|
||||
vec4 mask_sample = texture(mask_texture, UV);
|
||||
|
||||
// Use the mask's alpha channel to determine visibility
|
||||
float mask_alpha = mask_sample.a * mask_opacity;
|
||||
|
||||
// Apply the mask by multiplying the sprite's alpha with the mask alpha
|
||||
sprite_color.a *= mask_alpha;
|
||||
}
|
||||
|
||||
COLOR = sprite_color;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://btd72itp0cy6b
|
||||
8
ui_scenes/selectedSprite/opacity.gdshader
Normal file
8
ui_scenes/selectedSprite/opacity.gdshader
Normal file
@@ -0,0 +1,8 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform float opacity : hint_range(0.0, 1.0) = 1.0;
|
||||
|
||||
void fragment() {
|
||||
COLOR = texture(TEXTURE, UV);
|
||||
COLOR.a *= opacity;
|
||||
}
|
||||
1
ui_scenes/selectedSprite/opacity.gdshader.uid
Normal file
1
ui_scenes/selectedSprite/opacity.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://byxs44vljsg2f
|
||||
@@ -1,8 +1,17 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform vec4 outline_color : source_color = vec4(1.0, 0.0, 0.0, 0.3);
|
||||
uniform float speed;
|
||||
|
||||
uniform sampler2D backImg: repeat_enable;
|
||||
|
||||
uniform sampler2D palette;
|
||||
uniform float palette_speed = 0.1;
|
||||
|
||||
void fragment(){
|
||||
vec4 tex = texture(TEXTURE, UV);
|
||||
COLOR = vec4(outline_color.rgb, tex.a * outline_color.a);
|
||||
|
||||
vec2 scroll = vec2(1.0,0.0) * TIME * speed;
|
||||
vec4 tex = texture(TEXTURE, (UV));
|
||||
float palette_swap = mod(tex.r - TIME * palette_speed, 1.0);
|
||||
COLOR = vec4(texture(palette, vec2(palette_swap, 0)).rgb, tex.a);
|
||||
|
||||
}
|
||||
@@ -28,6 +28,9 @@ var imageSize = Vector2.ZERO
|
||||
@onready var wob = $WobbleOrigin
|
||||
|
||||
@onready var outlineScene = preload("res://ui_scenes/selectedSprite/outline.tscn")
|
||||
@onready var opacityShader = preload("res://ui_scenes/selectedSprite/opacity.gdshader")
|
||||
|
||||
var opacityMaterial = null
|
||||
|
||||
#Visuals
|
||||
var mouseOffset = Vector2.ZERO
|
||||
@@ -76,18 +79,17 @@ var ignoreBounce = false
|
||||
#Animation
|
||||
var frames = 1
|
||||
var animSpeed = 0
|
||||
|
||||
#Masking
|
||||
var mask_texture_path = ""
|
||||
var mask_texture = null
|
||||
var use_mask = false
|
||||
var mask_opacity = 1.0
|
||||
var animTime = 0.0 # Time accumulator for animations
|
||||
|
||||
var remadePolygon = false
|
||||
|
||||
var clipped = false
|
||||
|
||||
var tick = 0
|
||||
var time = 0.0 # Time accumulator for physics calculations
|
||||
|
||||
#Opacity
|
||||
var spriteOpacity = 1.0
|
||||
var affectChildrenOpacity = false
|
||||
|
||||
#Vis toggle
|
||||
var toggle = "null"
|
||||
@@ -125,9 +127,6 @@ func _ready():
|
||||
|
||||
sprite.texture = tex
|
||||
|
||||
# Initialize masking
|
||||
setupMasking()
|
||||
|
||||
var bitmap = BitMap.new()
|
||||
bitmap.create_from_image_alpha(imageData)
|
||||
|
||||
@@ -226,7 +225,7 @@ func replaceSprite(pathNew):
|
||||
remakePolygon()
|
||||
|
||||
func _process(delta):
|
||||
tick += 1
|
||||
time += delta
|
||||
if Global.heldSprite == self:
|
||||
|
||||
grabArea.visible = true
|
||||
@@ -253,13 +252,17 @@ func _process(delta):
|
||||
|
||||
talkBlink()
|
||||
|
||||
animation()
|
||||
animation(delta)
|
||||
|
||||
func animation():
|
||||
|
||||
var speed = max(float(animSpeed),Engine.max_fps*6.0)
|
||||
func animation(delta):
|
||||
if animSpeed > 0 and frames > 1:
|
||||
if Global.animationTick % int((speed)/float(animSpeed)) == 0:
|
||||
animTime += delta
|
||||
# Convert animSpeed to frames per second - no additional scaling needed since this controls frame rate
|
||||
var frameRate = animSpeed / 60.0 # Normalize from old frame-based system
|
||||
var frameInterval = 1.0 / frameRate
|
||||
|
||||
if animTime >= frameInterval:
|
||||
animTime -= frameInterval
|
||||
if sprite.frame == frames - 1:
|
||||
sprite.frame = 0
|
||||
else:
|
||||
@@ -271,10 +274,27 @@ func setZIndex():
|
||||
sprite.z_index = z
|
||||
|
||||
func talkBlink():
|
||||
var faded = 0.2 * int(Global.main.editMode)
|
||||
var faded = 0.2 * int(Global.main and Global.main.editMode)
|
||||
var value = (showOnTalk + (showOnBlink*3)) + (int(Global.speaking)*10) + (int(Global.blink)*20)
|
||||
var yes = [0,10,20,30,1,21,12,32,3,13,4,15,26,36,27,38].has(int(value))
|
||||
sprite.self_modulate.a = max(int(yes),faded)
|
||||
var baseAlpha = max(int(yes), faded)
|
||||
|
||||
# Only handle talk/blink/edit visibility with modulate
|
||||
# Opacity slider effects are handled by shader
|
||||
sprite.self_modulate.a = 1.0 # Always keep original texture alpha for clipping
|
||||
sprite.modulate.a = baseAlpha # Only apply talk/blink/edit visibility
|
||||
|
||||
func calculateParentOpacityMultiplier():
|
||||
var multiplier = 1.0
|
||||
var currentParent = parentSprite
|
||||
|
||||
# Traverse up the entire parent hierarchy
|
||||
while currentParent != null:
|
||||
if currentParent.affectChildrenOpacity:
|
||||
multiplier *= currentParent.spriteOpacity
|
||||
currentParent = currentParent.parentSprite
|
||||
|
||||
return multiplier
|
||||
|
||||
func delete():
|
||||
queue_free()
|
||||
@@ -333,31 +353,46 @@ func drag(delta):
|
||||
if dragSpeed == 0:
|
||||
dragger.global_position = wob.global_position
|
||||
else:
|
||||
dragger.global_position = lerp(dragger.global_position,wob.global_position,1/dragSpeed)
|
||||
# Convert dragSpeed to a proper delta-time lerp factor
|
||||
# Higher dragSpeed = slower movement, so we invert it
|
||||
# Use Global.main.physicsTimeMultiplier for consistent physics timing
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var lerpFactor = clamp(delta * (physicsMultiplier * 10.0 / max(dragSpeed, 0.1)), 0.0, 1.0)
|
||||
dragger.global_position = lerp(dragger.global_position, wob.global_position, lerpFactor)
|
||||
dragOrigin.global_position = dragger.global_position
|
||||
|
||||
func wobble():
|
||||
wob.position.x = sin(tick*xFrq)*xAmp
|
||||
wob.position.y = sin(tick*yFrq)*yAmp
|
||||
# Use Global.main.physicsTimeMultiplier for consistent physics timing
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var scaledTime = time * physicsMultiplier
|
||||
wob.position.x = sin(scaledTime * xFrq) * xAmp
|
||||
wob.position.y = sin(scaledTime * yFrq) * yAmp
|
||||
|
||||
func rotationalDrag(length,delta):
|
||||
func rotationalDrag(length, delta):
|
||||
var yvel = (length * rdragStr)
|
||||
|
||||
#Calculate Max angle
|
||||
yvel = clamp(yvel,rLimitMin,rLimitMax)
|
||||
yvel = clamp(yvel, rLimitMin, rLimitMax)
|
||||
|
||||
# Add rotation wobble as percentage of rotational limits range
|
||||
var rotationRange = rLimitMax - rLimitMin
|
||||
var rotationWobble = sin(tick*rFrq) * (rotationRange * rAmp * 0.01)
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var scaledTime = time * physicsMultiplier
|
||||
var rotationWobble = sin(scaledTime * rFrq) * (rotationRange * rAmp * 0.01)
|
||||
var totalRotation = yvel + rotationWobble
|
||||
|
||||
sprite.rotation = lerp_angle(sprite.rotation,deg_to_rad(totalRotation),0.25)
|
||||
# Use delta-based lerp for smooth rotation with slower speed
|
||||
var lerpFactor = clamp(delta * (physicsMultiplier * 1.0), 0.0, 1.0) # Reduced from 15.0 to 4.0
|
||||
sprite.rotation = lerp_angle(sprite.rotation, deg_to_rad(totalRotation), lerpFactor)
|
||||
|
||||
func stretch(length,delta):
|
||||
func stretch(length, delta):
|
||||
var yvel = (length * stretchAmount * 0.01)
|
||||
var target = Vector2(1.0-yvel,1.0+yvel)
|
||||
var target = Vector2(1.0 - yvel, 1.0 + yvel)
|
||||
|
||||
sprite.scale = lerp(sprite.scale,target,0.5)
|
||||
# Use delta-based lerp for smooth stretching
|
||||
var physicsMultiplier = Global.main.physicsTimeMultiplier if Global.main else 10.0
|
||||
var lerpFactor = clamp(delta * (physicsMultiplier * 30.0), 0.0, 1.0)
|
||||
sprite.scale = lerp(sprite.scale, target, lerpFactor)
|
||||
|
||||
func changeCollision(enable):
|
||||
grabArea.monitorable = enable
|
||||
@@ -412,59 +447,42 @@ func getAllLinkedSprites():
|
||||
linkedSprites.append(node)
|
||||
return linkedSprites
|
||||
|
||||
func updateOpacity():
|
||||
# Force a refresh of opacity for this sprite and its children
|
||||
talkBlink()
|
||||
updateShaderOpacity()
|
||||
|
||||
# Update all child sprites in the entire hierarchy if this sprite affects children
|
||||
if affectChildrenOpacity:
|
||||
updateChildrenOpacityRecursively()
|
||||
|
||||
func updateShaderOpacity():
|
||||
# Calculate total opacity from user settings and parent hierarchy
|
||||
var totalOpacity = spriteOpacity * calculateParentOpacityMultiplier()
|
||||
|
||||
if totalOpacity >= 1.0:
|
||||
# No shader needed for 100% opacity
|
||||
sprite.material = null
|
||||
else:
|
||||
# Create or update shader material for opacity less than 100%
|
||||
if opacityMaterial == null:
|
||||
opacityMaterial = ShaderMaterial.new()
|
||||
opacityMaterial.shader = opacityShader
|
||||
|
||||
opacityMaterial.set_shader_parameter("opacity", totalOpacity)
|
||||
sprite.material = opacityMaterial
|
||||
|
||||
func updateChildrenOpacityRecursively():
|
||||
var linkedSprites = getAllLinkedSprites()
|
||||
for child in linkedSprites:
|
||||
child.talkBlink()
|
||||
child.updateShaderOpacity()
|
||||
# Always update the entire hierarchy below this child
|
||||
child.updateChildrenOpacityRecursively()
|
||||
|
||||
func visToggle(keys):
|
||||
if keys.has(toggle):
|
||||
$WobbleOrigin/DragOrigin.visible = !$WobbleOrigin/DragOrigin.visible
|
||||
|
||||
func makeVis():
|
||||
$WobbleOrigin/DragOrigin.visible = true
|
||||
|
||||
## Setup masking system
|
||||
func setupMasking():
|
||||
if use_mask and mask_texture_path != "":
|
||||
loadMaskTexture()
|
||||
updateMaskShader()
|
||||
|
||||
## Load mask texture from file path
|
||||
func loadMaskTexture():
|
||||
if mask_texture_path == "":
|
||||
return
|
||||
|
||||
var img = Image.new()
|
||||
var err = img.load(mask_texture_path)
|
||||
if err != OK:
|
||||
print_debug("Failed to load mask texture: " + mask_texture_path)
|
||||
return
|
||||
|
||||
mask_texture = ImageTexture.create_from_image(img)
|
||||
|
||||
## Apply or remove mask shader
|
||||
func updateMaskShader():
|
||||
if use_mask and mask_texture != null:
|
||||
# Create mask material with shader
|
||||
var mask_shader = load("res://ui_scenes/selectedSprite/mask.gdshader")
|
||||
var mask_material = ShaderMaterial.new()
|
||||
mask_material.shader = mask_shader
|
||||
mask_material.set_shader_parameter("mask_texture", mask_texture)
|
||||
mask_material.set_shader_parameter("use_mask", true)
|
||||
mask_material.set_shader_parameter("mask_opacity", mask_opacity)
|
||||
|
||||
sprite.material = mask_material
|
||||
else:
|
||||
# Remove mask material
|
||||
sprite.material = null
|
||||
|
||||
## Set mask texture from external source
|
||||
func setMaskTexture(texture_path: String, opacity: float = 1.0):
|
||||
mask_texture_path = texture_path
|
||||
mask_opacity = opacity
|
||||
use_mask = true
|
||||
loadMaskTexture()
|
||||
updateMaskShader()
|
||||
|
||||
## Remove masking
|
||||
func removeMask():
|
||||
use_mask = false
|
||||
mask_texture = null
|
||||
mask_texture_path = ""
|
||||
updateMaskShader()
|
||||
|
||||
@@ -138,13 +138,10 @@ func _on_bounce_gravity_value_changed(value):
|
||||
|
||||
func costumeButtonsPressed(label,id):
|
||||
label.text = "AWAITING INPUT"
|
||||
await Global.main.emptiedCapture
|
||||
awaitingCostumeInput = id - 1
|
||||
|
||||
|
||||
await Global.main.pressedKey
|
||||
label.text = "costume " + str(id) + " key: \"" + Global.main.costumeKeys[id - 1] + "\""
|
||||
await Global.main.emptiedCapture
|
||||
awaitingCostumeInput = -1
|
||||
|
||||
func _on_costume_button_1_pressed():
|
||||
@@ -204,8 +201,8 @@ func _on_costume_check_toggled(button_pressed):
|
||||
## @param button_pressed: bool - Whether StreamDeck should be enabled
|
||||
func _on_stream_deck_check_toggled(button_pressed):
|
||||
Saving.settings["useStreamDeck"] = button_pressed
|
||||
if get_node_or_null("/root/ElgatoStreamDeck"):
|
||||
get_node("/root/ElgatoStreamDeck").refresh_connection()
|
||||
if ElgatoStreamDeck:
|
||||
ElgatoStreamDeck.refresh_connection()
|
||||
Global.pushUpdate("StreamDeck integration " + ("enabled" if button_pressed else "disabled") + ".")
|
||||
|
||||
## Handle experimental microphone loudness toggle
|
||||
|
||||
@@ -73,16 +73,11 @@ func setImage():
|
||||
$Animation/animFramesLabel.text = "sprite frames: " + str(Global.heldSprite.frames)
|
||||
$Animation/animFrames.value = Global.heldSprite.frames
|
||||
|
||||
$VisToggle/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
$Opacity/opacityLabel.text = "opacity: " + str(int(Global.heldSprite.spriteOpacity * 100)) + "%"
|
||||
$Opacity/opacitySlider.value = Global.heldSprite.spriteOpacity
|
||||
$Opacity/affectChildrenCheck.button_pressed = Global.heldSprite.affectChildrenOpacity
|
||||
|
||||
# Update masking controls
|
||||
$Masking/useMaskCheckbox.button_pressed = Global.heldSprite.use_mask
|
||||
$Masking/maskOpacityLabel.text = "mask opacity: " + str(Global.heldSprite.mask_opacity)
|
||||
$Masking/maskOpacity.value = Global.heldSprite.mask_opacity
|
||||
if Global.heldSprite.mask_texture_path.is_empty():
|
||||
$Masking/maskPathLabel.text = "mask: (none)"
|
||||
else:
|
||||
$Masking/maskPathLabel.text = "mask: " + Global.heldSprite.mask_texture_path.get_file()
|
||||
$VisToggle/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
|
||||
changeRotLimit()
|
||||
|
||||
@@ -373,25 +368,17 @@ func _on_delete_pressed():
|
||||
func _on_set_toggle_pressed():
|
||||
$VisToggle/setToggle/Label.text = "toggle: AWAITING INPUT"
|
||||
await Global.main.fatfuckingballs
|
||||
|
||||
var keys = await Global.main.spriteVisToggles
|
||||
var key = keys[0]
|
||||
Global.heldSprite.toggle = key
|
||||
$VisToggle/setToggle/Label.text = "toggle: \"" + Global.heldSprite.toggle + "\""
|
||||
|
||||
## Masking signal handlers
|
||||
func _on_use_mask_checkbox_toggled(button_pressed):
|
||||
Global.heldSprite.use_mask = button_pressed
|
||||
Global.heldSprite.updateMaskShader()
|
||||
func _on_opacity_slider_value_changed(value):
|
||||
$Opacity/opacityLabel.text = "opacity: " + str(int(value * 100)) + "%"
|
||||
Global.heldSprite.spriteOpacity = value
|
||||
Global.heldSprite.updateOpacity()
|
||||
|
||||
func _on_mask_opacity_value_changed(value):
|
||||
Global.heldSprite.mask_opacity = value
|
||||
$Masking/maskOpacityLabel.text = "mask opacity: " + str(value)
|
||||
Global.heldSprite.updateMaskShader()
|
||||
|
||||
func _on_select_mask_pressed():
|
||||
Global.main.openMaskSelection()
|
||||
|
||||
func updateMaskDisplay(path: String):
|
||||
$Masking/maskPathLabel.text = "mask: " + path.get_file()
|
||||
$Masking/useMaskCheckbox.button_pressed = true
|
||||
|
||||
func _on_clear_mask_pressed():
|
||||
Global.heldSprite.removeMask()
|
||||
$Masking/maskPathLabel.text = "mask: (none)"
|
||||
$Masking/useMaskCheckbox.button_pressed = false
|
||||
func _on_affect_children_check_toggled(button_pressed):
|
||||
Global.heldSprite.affectChildrenOpacity = button_pressed
|
||||
Global.heldSprite.updateOpacity()
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
[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, 1596.5)
|
||||
size = Vector2(245, 1455)
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_hxmah"]
|
||||
shader = ExtResource("3_ky0lu")
|
||||
@@ -14285,11 +14285,12 @@ script = ExtResource("1_hp0e3")
|
||||
z_index = -2
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
|
||||
position = Vector2(122.5, 725)
|
||||
position = Vector2(122.5, 727.5)
|
||||
shape = SubResource("RectangleShape2D_21kva")
|
||||
|
||||
[node name="Border" type="Sprite2D" parent="."]
|
||||
scale = Vector2(1, 1.16)
|
||||
position = Vector2(0, 2.22901)
|
||||
scale = Vector2(1, 1.37547)
|
||||
texture = ExtResource("1_b54o8")
|
||||
centered = false
|
||||
offset = Vector2(-9, -10.3448)
|
||||
@@ -14349,13 +14350,13 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3.362)
|
||||
fov = 36.5
|
||||
|
||||
[node name="Position" type="Node2D" parent="."]
|
||||
position = Vector2(0, 10)
|
||||
position = Vector2(0, 20)
|
||||
|
||||
[node name="fileTitle" type="Label" parent="Position"]
|
||||
offset_left = 10.0
|
||||
offset_top = 134.0
|
||||
offset_top = 127.0
|
||||
offset_right = 236.0
|
||||
offset_bottom = 154.0
|
||||
offset_bottom = 147.0
|
||||
text = "user://greenThing/head1.png"
|
||||
label_settings = SubResource("LabelSettings_6favr")
|
||||
|
||||
@@ -14381,7 +14382,7 @@ offset_bottom = 227.0
|
||||
text = "layer : 0"
|
||||
|
||||
[node name="Buttons" type="Node2D" parent="."]
|
||||
position = Vector2(0, 27)
|
||||
position = Vector2(0, 141)
|
||||
|
||||
[node name="Speaking" type="Sprite2D" parent="Buttons"]
|
||||
position = Vector2(42, 523)
|
||||
@@ -14468,7 +14469,7 @@ scrollable = false
|
||||
|
||||
[node name="WobbleControl" type="Node2D" parent="."]
|
||||
z_index = 1
|
||||
position = Vector2(-3, 378)
|
||||
position = Vector2(-3, 486)
|
||||
|
||||
[node name="animationBox" type="Sprite2D" parent="WobbleControl"]
|
||||
position = Vector2(125, 384.5)
|
||||
@@ -14612,7 +14613,7 @@ scrollable = false
|
||||
|
||||
[node name="RotationalLimits" type="Node2D" parent="."]
|
||||
z_index = -1
|
||||
position = Vector2(0, 325)
|
||||
position = Vector2(0, 435)
|
||||
|
||||
[node name="RotBack" type="Sprite2D" parent="RotationalLimits"]
|
||||
clip_children = 2
|
||||
@@ -14725,8 +14726,43 @@ offset_bottom = 1068.0
|
||||
theme = SubResource("Theme_mrbyn")
|
||||
scrollable = false
|
||||
|
||||
[node name="Opacity" type="Node2D" parent="."]
|
||||
position = Vector2(-2, -611)
|
||||
|
||||
[node name="opacityLabel" type="Label" parent="Opacity"]
|
||||
offset_left = 11.0
|
||||
offset_top = 1090.0
|
||||
offset_right = 159.0
|
||||
offset_bottom = 1116.0
|
||||
text = "opacity: 100%"
|
||||
|
||||
[node name="opacitySlider" type="HSlider" parent="Opacity"]
|
||||
offset_left = 10.0
|
||||
offset_top = 1111.0
|
||||
offset_right = 233.0
|
||||
offset_bottom = 1131.0
|
||||
theme = SubResource("Theme_mrbyn")
|
||||
max_value = 1.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
scrollable = false
|
||||
|
||||
[node name="affectChildrenLabel" type="Label" parent="Opacity"]
|
||||
offset_left = 11.0
|
||||
offset_top = 1140.0
|
||||
offset_right = 159.0
|
||||
offset_bottom = 1166.0
|
||||
text = "affect children:"
|
||||
|
||||
[node name="affectChildrenCheck" type="CheckBox" parent="Opacity"]
|
||||
offset_left = 140.0
|
||||
offset_top = 1140.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 1166.0
|
||||
theme = SubResource("Theme_mrbyn")
|
||||
|
||||
[node name="Layers" type="Node2D" parent="."]
|
||||
position = Vector2(0, 327)
|
||||
position = Vector2(0, 443)
|
||||
|
||||
[node name="Layer1" type="Sprite2D" parent="Layers"]
|
||||
position = Vector2(23, 878)
|
||||
@@ -14863,7 +14899,7 @@ position = Vector2(23, 878)
|
||||
texture = ExtResource("20_px0dt")
|
||||
|
||||
[node name="VisToggle" type="Node2D" parent="."]
|
||||
position = Vector2(11, 1289)
|
||||
position = Vector2(11, 1404)
|
||||
|
||||
[node name="setToggle" type="Button" parent="VisToggle"]
|
||||
custom_minimum_size = Vector2(0, 37.125)
|
||||
@@ -14910,66 +14946,6 @@ position = Vector2(-2, -2)
|
||||
texture = ExtResource("27_fgu6g")
|
||||
centered = false
|
||||
|
||||
[node name="Masking" type="Node2D" parent="."]
|
||||
position = Vector2(0, 1360.83)
|
||||
|
||||
[node name="animationBox" type="Sprite2D" parent="Masking"]
|
||||
position = Vector2(125, 92.4052)
|
||||
scale = Vector2(1, 0.905768)
|
||||
texture = ExtResource("15_wqi8b")
|
||||
|
||||
[node name="maskingLabel" type="Label" parent="Masking"]
|
||||
offset_left = 20.0
|
||||
offset_right = 120.0
|
||||
offset_bottom = 26.0
|
||||
text = "MASKING"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="useMaskCheckbox" type="CheckBox" parent="Masking"]
|
||||
offset_left = 20.0
|
||||
offset_top = 30.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 54.0
|
||||
text = "Use Mask"
|
||||
|
||||
[node name="maskOpacityLabel" type="Label" parent="Masking"]
|
||||
offset_left = 20.0
|
||||
offset_top = 60.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 86.0
|
||||
text = "mask opacity: 1.0"
|
||||
|
||||
[node name="maskOpacity" type="HSlider" parent="Masking"]
|
||||
offset_left = 20.0
|
||||
offset_top = 90.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 106.0
|
||||
max_value = 1.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
|
||||
[node name="maskPathLabel" type="Label" parent="Masking"]
|
||||
offset_left = 20.0
|
||||
offset_top = 120.0
|
||||
offset_right = 400.0
|
||||
offset_bottom = 146.0
|
||||
text = "mask: (none)"
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="selectMask" type="Button" parent="Masking"]
|
||||
offset_left = 20.0
|
||||
offset_top = 150.0
|
||||
offset_right = 120.0
|
||||
offset_bottom = 181.0
|
||||
text = "Select Mask"
|
||||
|
||||
[node name="clearMask" type="Button" parent="Masking"]
|
||||
offset_left = 130.0
|
||||
offset_top = 150.0
|
||||
offset_right = 220.0
|
||||
offset_bottom = 181.0
|
||||
text = "Clear Mask"
|
||||
|
||||
[connection signal="pressed" from="Buttons/Speaking/speaking" to="." method="_on_speaking_pressed"]
|
||||
[connection signal="pressed" from="Buttons/Blinking/blinking" to="." method="_on_blinking_pressed"]
|
||||
[connection signal="pressed" from="Buttons/Trash/trash" to="." method="_on_trash_pressed"]
|
||||
@@ -14989,6 +14965,8 @@ text = "Clear Mask"
|
||||
[connection signal="value_changed" from="RotationalLimits/rotLimitMax" to="." method="_on_rot_limit_max_value_changed"]
|
||||
[connection signal="value_changed" from="Animation/animFrames" to="." method="_on_anim_frames_value_changed"]
|
||||
[connection signal="value_changed" from="Animation/animSpeed" to="." method="_on_anim_speed_value_changed"]
|
||||
[connection signal="value_changed" from="Opacity/opacitySlider" to="." method="_on_opacity_slider_value_changed"]
|
||||
[connection signal="toggled" from="Opacity/affectChildrenCheck" to="." method="_on_affect_children_check_toggled"]
|
||||
[connection signal="pressed" from="Layers/Layer1/layerButton1" to="." method="_on_layer_button_1_pressed"]
|
||||
[connection signal="pressed" from="Layers/Layer2/layerButton2" to="." method="_on_layer_button_2_pressed"]
|
||||
[connection signal="pressed" from="Layers/Layer3/layerButton3" to="." method="_on_layer_button_3_pressed"]
|
||||
@@ -15001,7 +14979,3 @@ text = "Clear Mask"
|
||||
[connection signal="pressed" from="Layers/Layer10/layerButton10" to="." method="_on_layer_button_10_pressed"]
|
||||
[connection signal="pressed" from="VisToggle/setToggle" to="." method="_on_set_toggle_pressed"]
|
||||
[connection signal="pressed" from="VisToggle/setToggle/delete" to="." method="_on_delete_pressed"]
|
||||
[connection signal="toggled" from="Masking/useMaskCheckbox" to="." method="_on_use_mask_checkbox_toggled"]
|
||||
[connection signal="value_changed" from="Masking/maskOpacity" to="." method="_on_mask_opacity_value_changed"]
|
||||
[connection signal="pressed" from="Masking/selectMask" to="." method="_on_select_mask_pressed"]
|
||||
[connection signal="pressed" from="Masking/clearMask" to="." method="_on_clear_mask_pressed"]
|
||||
|
||||
Reference in New Issue
Block a user