mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-20 12:33:14 +00:00
Merge pull request #143 from azagaya/master
Added 2 rotation algorithms and popup for rotating current layer
This commit is contained in:
commit
c4229e1759
5 changed files with 359 additions and 9 deletions
29
Main.tscn
29
Main.tscn
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=64 format=2]
|
||||
[gd_scene load_steps=65 format=2]
|
||||
|
||||
[ext_resource path="res://Themes & Styles/Dark Theme/Dark Theme.tres" type="Theme" id=1]
|
||||
[ext_resource path="res://Scripts/Main.gd" type="Script" id=2]
|
||||
|
@ -53,6 +53,7 @@
|
|||
[ext_resource path="res://Prefabs/EditPalettePopup.tscn" type="PackedScene" id=51]
|
||||
[ext_resource path="res://Prefabs/NewPaletteDialog.tscn" type="PackedScene" id=52]
|
||||
[ext_resource path="res://Prefabs/PaletteImportFileDialog.tscn" type="PackedScene" id=53]
|
||||
[ext_resource path="res://Prefabs/Dialogs/RotateImage.tscn" type="PackedScene" id=54]
|
||||
|
||||
[sub_resource type="InputEventKey" id=1]
|
||||
scancode = 88
|
||||
|
@ -1401,17 +1402,18 @@ columns = 5
|
|||
[node name="SplashDialog" parent="." instance=ExtResource( 43 )]
|
||||
|
||||
[node name="CreateNewImage" parent="." instance=ExtResource( 44 )]
|
||||
window_title = "Confirmá, por favor..."
|
||||
|
||||
[node name="OpenSprite" type="FileDialog" parent="."]
|
||||
margin_right = 515.0
|
||||
margin_bottom = 348.0
|
||||
window_title = "Open a File"
|
||||
window_title = "Abrir un Archivo"
|
||||
resizable = true
|
||||
mode = 0
|
||||
access = 2
|
||||
filters = PoolStringArray( "*.pxo ; Pixelorama Project" )
|
||||
current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama"
|
||||
current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/"
|
||||
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
|
||||
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
|
||||
|
||||
[node name="SaveSprite" type="FileDialog" parent="."]
|
||||
anchor_left = 0.5
|
||||
|
@ -1426,12 +1428,17 @@ window_title = "Save Sprite as .pxo"
|
|||
resizable = true
|
||||
access = 2
|
||||
filters = PoolStringArray( "*.pxo ; Pixelorama Project" )
|
||||
current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama"
|
||||
current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/"
|
||||
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
|
||||
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
|
||||
|
||||
[node name="ImportSprites" parent="." instance=ExtResource( 45 )]
|
||||
window_title = "Abrir Archivo(s)"
|
||||
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
|
||||
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
|
||||
|
||||
[node name="ExportSprites" parent="." instance=ExtResource( 46 )]
|
||||
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
|
||||
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
|
||||
|
||||
[node name="ScaleImage" parent="." instance=ExtResource( 47 )]
|
||||
|
||||
|
@ -1439,12 +1446,14 @@ current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρι
|
|||
|
||||
[node name="OutlineDialog" parent="." instance=ExtResource( 49 )]
|
||||
visible = false
|
||||
window_title = "Confirmá, por favor..."
|
||||
|
||||
[node name="AboutDialog" parent="." instance=ExtResource( 50 )]
|
||||
|
||||
[node name="QuitDialog" type="ConfirmationDialog" parent="."]
|
||||
margin_right = 200.0
|
||||
margin_bottom = 70.0
|
||||
window_title = "Confirmá, por favor..."
|
||||
resizable = true
|
||||
dialog_text = "QUIT_LABEL"
|
||||
|
||||
|
@ -1460,15 +1469,19 @@ visible = false
|
|||
[node name="NewPaletteDialog" parent="." instance=ExtResource( 52 )]
|
||||
|
||||
[node name="PaletteImportFileDialog" parent="." instance=ExtResource( 53 )]
|
||||
window_title = "Abrir un Archivo"
|
||||
filters = PoolStringArray( "*.json ; JavaScript Object Notation", "*.gpl ; Gimp Palette Library" )
|
||||
current_dir = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama"
|
||||
current_path = "C:/Users/Overloaded/Dropbox/Orama Founding Members/εταιρικα αρχεια/Godot Projects/Pixelorama/"
|
||||
current_dir = "/home/azagaya/Documentos/VJ/Pixelorama"
|
||||
current_path = "/home/azagaya/Documentos/VJ/Pixelorama/"
|
||||
|
||||
[node name="LeftCursor" type="Sprite" parent="."]
|
||||
visible = false
|
||||
|
||||
[node name="RightCursor" type="Sprite" parent="."]
|
||||
visible = false
|
||||
|
||||
[node name="RotateImage" parent="." instance=ExtResource( 54 )]
|
||||
visible = false
|
||||
[connection signal="pressed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorSwitchCenter/ColorSwitch" to="." method="_on_ColorSwitch_pressed"]
|
||||
[connection signal="color_changed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorPickersCenter/ColorPickersHorizontal/LeftColorPickerButton" to="." method="_on_LeftColorPickerButton_color_changed"]
|
||||
[connection signal="popup_closed" from="MenuAndUI/UI/ToolPanel/Tools/ColorAndToolOptions/ColorButtonsVertical/ColorPickersCenter/ColorPickersHorizontal/LeftColorPickerButton" to="." method="_can_draw_true"]
|
||||
|
|
56
Prefabs/Dialogs/RotateImage.gd
Normal file
56
Prefabs/Dialogs/RotateImage.gd
Normal file
|
@ -0,0 +1,56 @@
|
|||
extends ConfirmationDialog
|
||||
|
||||
var texture : ImageTexture
|
||||
var aux_img : Image
|
||||
var layer : Image
|
||||
|
||||
func _ready():
|
||||
texture = ImageTexture.new()
|
||||
texture.flags = 0
|
||||
aux_img = Image.new()
|
||||
$VBoxContainer/HBoxContainer2/OptionButton.add_item("Rotxel")
|
||||
$VBoxContainer/HBoxContainer2/OptionButton.add_item("Nearest neighbour")
|
||||
|
||||
func set_sprite(sprite : Image):
|
||||
aux_img.copy_from(sprite)
|
||||
layer = sprite
|
||||
texture.create_from_image(aux_img, 0)
|
||||
$VBoxContainer/TextureRect.texture = texture
|
||||
|
||||
|
||||
func _on_HSlider_value_changed(value):
|
||||
rotate()
|
||||
$VBoxContainer/HBoxContainer/SpinBox.value = $VBoxContainer/HBoxContainer/HSlider.value
|
||||
|
||||
|
||||
func _on_SpinBox_value_changed(value):
|
||||
$VBoxContainer/HBoxContainer/HSlider.value = $VBoxContainer/HBoxContainer/SpinBox.value
|
||||
|
||||
|
||||
func _on_RotateImage_confirmed():
|
||||
Global.canvas.handle_undo("Draw")
|
||||
match $VBoxContainer/HBoxContainer2/OptionButton.text:
|
||||
"Rotxel":
|
||||
Global.rotxel(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
"Nearest neighbour":
|
||||
Global.nn_rotate(layer,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
Global.canvas.handle_redo("Draw")
|
||||
$VBoxContainer/HBoxContainer/HSlider.value = 0
|
||||
|
||||
func rotate():
|
||||
var sprite : Image = Image.new()
|
||||
sprite.copy_from(aux_img)
|
||||
match $VBoxContainer/HBoxContainer2/OptionButton.text:
|
||||
"Rotxel":
|
||||
Global.rotxel(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
"Nearest neighbour":
|
||||
Global.nn_rotate(sprite,$VBoxContainer/HBoxContainer/HSlider.value*PI/180)
|
||||
texture.create_from_image(sprite, 0)
|
||||
|
||||
|
||||
func _on_OptionButton_item_selected(id):
|
||||
rotate()
|
||||
|
||||
|
||||
func _on_RotateImage_about_to_show():
|
||||
$VBoxContainer/HBoxContainer/HSlider.value = 0
|
81
Prefabs/Dialogs/RotateImage.tscn
Normal file
81
Prefabs/Dialogs/RotateImage.tscn
Normal file
|
@ -0,0 +1,81 @@
|
|||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://Prefabs/Dialogs/RotateImage.gd" type="Script" id=1]
|
||||
|
||||
[node name="RotateImage" type="ConfirmationDialog"]
|
||||
margin_right = 245.0
|
||||
margin_bottom = 241.0
|
||||
window_title = "Confirmá, por favor..."
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = -8.0
|
||||
margin_bottom = -36.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="VBoxContainer"]
|
||||
margin_right = 229.0
|
||||
margin_bottom = 145.0
|
||||
size_flags_vertical = 3
|
||||
expand = true
|
||||
stretch_mode = 6
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 149.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 169.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer2"]
|
||||
margin_top = 3.0
|
||||
margin_right = 34.0
|
||||
margin_bottom = 17.0
|
||||
text = "Type:"
|
||||
|
||||
[node name="OptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer2"]
|
||||
margin_left = 38.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 20.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 173.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 197.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_top = 5.0
|
||||
margin_right = 44.0
|
||||
margin_bottom = 19.0
|
||||
text = "Angle: "
|
||||
|
||||
[node name="HSlider" type="HSlider" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_left = 48.0
|
||||
margin_right = 151.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
max_value = 359.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="SpinBox" type="SpinBox" parent="VBoxContainer/HBoxContainer"]
|
||||
margin_left = 155.0
|
||||
margin_right = 229.0
|
||||
margin_bottom = 24.0
|
||||
max_value = 359.0
|
||||
[connection signal="about_to_show" from="." to="." method="_on_RotateImage_about_to_show"]
|
||||
[connection signal="confirmed" from="." to="." method="_on_RotateImage_confirmed"]
|
||||
[connection signal="item_selected" from="VBoxContainer/HBoxContainer2/OptionButton" to="." method="_on_OptionButton_item_selected"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/HSlider" to="." method="_on_HSlider_value_changed"]
|
||||
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpinBox" to="." method="_on_SpinBox_value_changed"]
|
|
@ -612,6 +612,201 @@ func plot_circle(r : int) -> Array:
|
|||
err += x * 2 + 1
|
||||
return circle_points
|
||||
|
||||
func scale3X(sprite : Image, tol : float = 50) -> Image:
|
||||
var scaled = Image.new()
|
||||
scaled.create(sprite.get_width()*3, sprite.get_height()*3, false, Image.FORMAT_RGBA8)
|
||||
scaled.lock()
|
||||
sprite.lock()
|
||||
var a : Color
|
||||
var b : Color
|
||||
var c : Color
|
||||
var d : Color
|
||||
var e : Color
|
||||
var f : Color
|
||||
var g : Color
|
||||
var h : Color
|
||||
var i : Color
|
||||
|
||||
for x in range(1,sprite.get_width()-1):
|
||||
for y in range(1,sprite.get_height()-1):
|
||||
var xs : float = 3*x
|
||||
var ys : float = 3*y
|
||||
|
||||
a = sprite.get_pixel(x-1,y-1)
|
||||
b = sprite.get_pixel(x,y-1)
|
||||
c = sprite.get_pixel(x+1,y-1)
|
||||
d = sprite.get_pixel(x-1,y)
|
||||
e = sprite.get_pixel(x,y)
|
||||
f = sprite.get_pixel(x+1,y)
|
||||
g = sprite.get_pixel(x-1,y+1)
|
||||
h = sprite.get_pixel(x,y+1)
|
||||
i = sprite.get_pixel(x+1,y+1)
|
||||
|
||||
var db : bool = similarColors(d, b, tol)
|
||||
var dh : bool = similarColors(d, h, tol)
|
||||
var bf : bool = similarColors(f, b, tol)
|
||||
var ec : bool = similarColors(e, c, tol)
|
||||
var ea : bool = similarColors(e, a, tol)
|
||||
var fh : bool = similarColors(f, h, tol)
|
||||
var eg : bool = similarColors(e, g, tol)
|
||||
var ei : bool = similarColors(e, i, tol)
|
||||
|
||||
scaled.set_pixel(xs-1, ys-1, d if (db and !dh and !bf) else e )
|
||||
scaled.set_pixel(xs, ys-1, b if (db and !dh and !bf and !ec) or
|
||||
(bf and !db and !fh and !ea) else e)
|
||||
scaled.set_pixel(xs+1, ys-1, f if (bf and !db and !fh) else e)
|
||||
scaled.set_pixel(xs-1, ys, d if (dh and !fh and !db and !ea) or
|
||||
(db and !dh and !bf and !eg) else e)
|
||||
scaled.set_pixel(xs, ys, e);
|
||||
scaled.set_pixel(xs+1, ys, f if (bf and !db and !fh and !ei) or
|
||||
(fh and !bf and !dh and !ec) else e)
|
||||
scaled.set_pixel(xs-1, ys+1, d if (dh and !fh and !db) else e)
|
||||
scaled.set_pixel(xs, ys+1, h if (fh and !bf and !dh and !eg) or
|
||||
(dh and !fh and !db and !ei) else e)
|
||||
scaled.set_pixel(xs+1, ys+1, f if (fh and !bf and !dh) else e)
|
||||
|
||||
scaled.unlock()
|
||||
sprite.unlock()
|
||||
return scaled
|
||||
|
||||
func rotxel(sprite : Image, angle : float):
|
||||
|
||||
# If angle is simple, then nn rotation is the best
|
||||
|
||||
if angle == 0 || angle == PI/2 || angle == PI || angle == 2*PI:
|
||||
nn_rotate(sprite, angle)
|
||||
return
|
||||
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
var center : Vector2 = Vector2(sprite.get_width()/2, sprite.get_height()/2)
|
||||
var ox : int
|
||||
var oy : int
|
||||
var p : Color
|
||||
aux.lock()
|
||||
sprite.lock()
|
||||
for x in range(sprite.get_width()):
|
||||
for y in range(sprite.get_height()):
|
||||
var dx = 3*(x - center.x)
|
||||
var dy = 3*(y - center.y)
|
||||
var found_pixel : bool = false
|
||||
for k in range(9):
|
||||
var i = -1 + k % 3
|
||||
var j = -1 + int(k / 3)
|
||||
var dir = atan2(dy + j, dx + i)
|
||||
var mag = sqrt(pow(dx + i, 2) + pow(dy + j, 2))
|
||||
dir -= angle
|
||||
ox = round(center.x*3 + 1 + mag*cos(dir))
|
||||
oy = round(center.y*3 + 1 + mag*sin(dir))
|
||||
|
||||
if (sprite.get_width() % 2 != 0):
|
||||
ox += 1
|
||||
oy += 1
|
||||
|
||||
if (ox >= 0 && ox < sprite.get_width()*3
|
||||
&& oy >= 0 && oy < sprite.get_height()*3):
|
||||
found_pixel = true
|
||||
break
|
||||
|
||||
if !found_pixel:
|
||||
sprite.set_pixel(x, y, Color(0,0,0,0))
|
||||
continue
|
||||
|
||||
var fil : int = oy % 3
|
||||
var col : int = ox % 3
|
||||
var index : int = col + 3*fil
|
||||
|
||||
ox = round((ox - 1)/3.0);
|
||||
oy = round((oy - 1)/3.0);
|
||||
var a : Color
|
||||
var b : Color
|
||||
var c : Color
|
||||
var d : Color
|
||||
var e : Color
|
||||
var f : Color
|
||||
var g : Color
|
||||
var h : Color
|
||||
var i : Color
|
||||
if (ox == 0 || ox == sprite.get_width() - 1 ||
|
||||
oy == 0 || oy == sprite.get_height() - 1):
|
||||
p = aux.get_pixel(ox, oy)
|
||||
else:
|
||||
a = aux.get_pixel(ox-1,oy-1);
|
||||
b = aux.get_pixel(ox,oy-1);
|
||||
c = aux.get_pixel(ox+1,oy-1);
|
||||
d = aux.get_pixel(ox-1,oy);
|
||||
e = aux.get_pixel(ox,oy);
|
||||
f = aux.get_pixel(ox+1,oy);
|
||||
g = aux.get_pixel(ox-1,oy+1);
|
||||
h = aux.get_pixel(ox,oy+1);
|
||||
i = aux.get_pixel(ox+1,oy+1);
|
||||
|
||||
match(index):
|
||||
0:
|
||||
p = d if (similarColors(d,b) && !similarColors(d,h)
|
||||
&& !similarColors(b,f)) else e;
|
||||
1:
|
||||
p = b if ((similarColors(d,b) && !similarColors(d,h) &&
|
||||
!similarColors(b,f) && !similarColors(e,c)) ||
|
||||
(similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h) && !similarColors(e,a))) else e;
|
||||
2:
|
||||
p = f if (similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h)) else e;
|
||||
3:
|
||||
p = d if ((similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b) && !similarColors(e,a)) ||
|
||||
(similarColors(d,b) && !similarColors(d,h) &&
|
||||
!similarColors(b,f) && !similarColors(e,g))) else e;
|
||||
4:
|
||||
p = e
|
||||
5:
|
||||
p = f if((similarColors(b,f) && !similarColors(d,b) &&
|
||||
!similarColors(f,h) && !similarColors(e,i))
|
||||
|| (similarColors(f,h) && !similarColors(b,f) &&
|
||||
!similarColors(d,h) && !similarColors(e,c))) else e;
|
||||
6:
|
||||
p = d if (similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b)) else e;
|
||||
7:
|
||||
p = h if ((similarColors(f,h) && !similarColors(f,b) &&
|
||||
!similarColors(d,h) && !similarColors(e,g))
|
||||
|| (similarColors(d,h) && !similarColors(f,h) &&
|
||||
!similarColors(d,b) && !similarColors(e,i))) else e;
|
||||
8:
|
||||
p = f if (similarColors(f,h) && !similarColors(f,b) &&
|
||||
!similarColors(d,h)) else e;
|
||||
sprite.set_pixel(x, y, p)
|
||||
sprite.unlock()
|
||||
aux.unlock()
|
||||
|
||||
func nn_rotate(sprite : Image, angle : float):
|
||||
var aux : Image = Image.new()
|
||||
aux.copy_from(sprite)
|
||||
sprite.lock()
|
||||
aux.lock()
|
||||
var ox: int
|
||||
var oy: int
|
||||
var center : Vector2 = Vector2(sprite.get_width()/2, sprite.get_height()/2)
|
||||
for x in range(sprite.get_width()):
|
||||
for y in range(sprite.get_height()):
|
||||
ox = (x - center.x)*cos(angle) + (y - center.y)*sin(angle) + center.x
|
||||
oy = -(x - center.x)*sin(angle) + (y - center.y)*cos(angle) + center.y
|
||||
if ox >= 0 && ox < sprite.get_width() && oy >= 0 && oy < sprite.get_height():
|
||||
sprite.set_pixel(x, y, aux.get_pixel(ox, oy))
|
||||
else:
|
||||
sprite.set_pixel(x, y, Color(0,0,0,0))
|
||||
sprite.unlock()
|
||||
aux.unlock()
|
||||
|
||||
func similarColors(c1 : Color, c2 : Color, tol : float = 100) -> bool:
|
||||
var dist = colorDistance(c1, c2)
|
||||
return dist <= tol
|
||||
|
||||
func colorDistance(c1 : Color, c2 : Color) -> float:
|
||||
return sqrt(pow((c1.r - c2.r)*255, 2) + pow((c1.g - c2.g)*255, 2)
|
||||
+ pow((c1.b - c2.b)*255, 2) + pow((c1.a - c2.a)*255, 2))
|
||||
|
||||
func _exit_tree() -> void:
|
||||
config_cache.set_value("window", "screen", OS.current_screen)
|
||||
config_cache.set_value("window", "maximized", OS.window_maximized || OS.window_fullscreen)
|
||||
|
|
|
@ -69,7 +69,8 @@ func _ready() -> void:
|
|||
"Flip Vertical" : KEY_MASK_SHIFT + KEY_V,
|
||||
"Invert colors" : 0,
|
||||
"Desaturation" : 0,
|
||||
"Outline" : 0
|
||||
"Outline" : 0,
|
||||
"Rotate Image" : 0
|
||||
}
|
||||
var help_menu_items := {
|
||||
"View Splash Screen" : 0,
|
||||
|
@ -346,6 +347,10 @@ func image_menu_id_pressed(id : int) -> void:
|
|||
Global.canvas.handle_redo("Draw")
|
||||
6: # Outline
|
||||
$OutlineDialog.popup_centered()
|
||||
7: # Rotate
|
||||
var image : Image = Global.canvas.layers[Global.canvas.current_layer_index][0]
|
||||
$RotateImage.set_sprite(image)
|
||||
$RotateImage.popup_centered()
|
||||
|
||||
func help_menu_id_pressed(id : int) -> void:
|
||||
match id:
|
||||
|
|
Loading…
Add table
Reference in a new issue