diff --git a/MainMenu.glade b/MainMenu.glade~
similarity index 100%
rename from MainMenu.glade
rename to MainMenu.glade~
diff --git a/MainMenu.ui~ b/MainMenu.ui~
new file mode 100644
index 0000000..4934b8c
--- /dev/null
+++ b/MainMenu.ui~
@@ -0,0 +1,396 @@
+
+
+
+
+
+
+
diff --git a/__pycache__/block.cpython-310.pyc b/__pycache__/block.cpython-310.pyc
index a6a0104..eab4568 100644
Binary files a/__pycache__/block.cpython-310.pyc and b/__pycache__/block.cpython-310.pyc differ
diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc
index 3e6aaa3..d03bbf1 100644
Binary files a/__pycache__/config.cpython-310.pyc and b/__pycache__/config.cpython-310.pyc differ
diff --git a/__pycache__/downloader.cpython-310.pyc b/__pycache__/downloader.cpython-310.pyc
new file mode 100644
index 0000000..115ecd8
Binary files /dev/null and b/__pycache__/downloader.cpython-310.pyc differ
diff --git a/__pycache__/eventContainer.cpython-310.pyc b/__pycache__/eventContainer.cpython-310.pyc
new file mode 100644
index 0000000..55f4405
Binary files /dev/null and b/__pycache__/eventContainer.cpython-310.pyc differ
diff --git a/__pycache__/sb3Unpacker.cpython-310.pyc b/__pycache__/sb3Unpacker.cpython-310.pyc
index 79bd126..c956b51 100644
Binary files a/__pycache__/sb3Unpacker.cpython-310.pyc and b/__pycache__/sb3Unpacker.cpython-310.pyc differ
diff --git a/__pycache__/scratch.cpython-310.pyc b/__pycache__/scratch.cpython-310.pyc
index 2a2213c..8e46dc7 100644
Binary files a/__pycache__/scratch.cpython-310.pyc and b/__pycache__/scratch.cpython-310.pyc differ
diff --git a/__pycache__/target.cpython-310.pyc b/__pycache__/target.cpython-310.pyc
index 97d419b..e59f57d 100644
Binary files a/__pycache__/target.cpython-310.pyc and b/__pycache__/target.cpython-310.pyc differ
diff --git a/__pycache__/targetSprite.cpython-310.pyc b/__pycache__/targetSprite.cpython-310.pyc
index a0415b7..40b61d0 100644
Binary files a/__pycache__/targetSprite.cpython-310.pyc and b/__pycache__/targetSprite.cpython-310.pyc differ
diff --git a/assets/cd21514d0531fdffb22204e0ec5ed84a.svg b/assets/cd21514d0531fdffb22204e0ec5ed84a.svg
deleted file mode 100644
index 15f7311..0000000
--- a/assets/cd21514d0531fdffb22204e0ec5ed84a.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/assets/project.json b/assets/project.json
index 829597d..a89e23f 100644
--- a/assets/project.json
+++ b/assets/project.json
@@ -1 +1 @@
-{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["variabila mea",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"decor1","dataFormat":"svg","assetId":"cd21514d0531fdffb22204e0ec5ed84a","md5ext":"cd21514d0531fdffb22204e0ec5ed84a.svg","rotationCenterX":240,"rotationCenterY":180}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":44100,"sampleCount":1032,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"on","textToSpeechLanguage":"ro"},{"isStage":false,"name":"Personaj1","variables":{},"lists":{},"broadcasts":{},"blocks":{"i0sF-x|[yQ-#%j@rU%f,":{"opcode":"event_whenflagclicked","next":")u+B7H(M9g25%Em.w=5;","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":360,"y":168},")u+B7H(M9g25%Em.w=5;":{"opcode":"control_forever","next":null,"parent":"i0sF-x|[yQ-#%j@rU%f,","inputs":{"SUBSTACK":[2,"/B0dn4qYH?K,.K9rc;A`"]},"fields":{},"shadow":false,"topLevel":false},"/B0dn4qYH?K,.K9rc;A`":{"opcode":"control_if","next":"|LMd9D?RO[2$-2d9tSaH","parent":")u+B7H(M9g25%Em.w=5;","inputs":{"CONDITION":[2,"#Uk8A~y8%JVMqn}NHx[`"],"SUBSTACK":[2,"oh,d/q]c)nT*MAUu,[S@"]},"fields":{},"shadow":false,"topLevel":false},"#Uk8A~y8%JVMqn}NHx[`":{"opcode":"sensing_keypressed","next":null,"parent":"/B0dn4qYH?K,.K9rc;A`","inputs":{"KEY_OPTION":[1,"DNHO5a8nfKM+34^x:43}"]},"fields":{},"shadow":false,"topLevel":false},"DNHO5a8nfKM+34^x:43}":{"opcode":"sensing_keyoptions","next":null,"parent":"#Uk8A~y8%JVMqn}NHx[`","inputs":{},"fields":{"KEY_OPTION":["space",null]},"shadow":true,"topLevel":false},"oh,d/q]c)nT*MAUu,[S@":{"opcode":"looks_switchcostumeto","next":null,"parent":"/B0dn4qYH?K,.K9rc;A`","inputs":{"COSTUME":[1,"R^t{wX)}j(l,vXrOTX(g"]},"fields":{},"shadow":false,"topLevel":false},"R^t{wX)}j(l,vXrOTX(g":{"opcode":"looks_costume","next":null,"parent":"oh,d/q]c)nT*MAUu,[S@","inputs":{},"fields":{"COSTUME":["costum2",null]},"shadow":true,"topLevel":false},"|LMd9D?RO[2$-2d9tSaH":{"opcode":"control_if","next":null,"parent":"/B0dn4qYH?K,.K9rc;A`","inputs":{"CONDITION":[2,"46_hm$E}C?N}KA=/3@`u"],"SUBSTACK":[2,"[+(oxNIM5(qM,kq~)/=K"]},"fields":{},"shadow":false,"topLevel":false},"w!`lxu[+VTbb!a@:2IFM":{"opcode":"sensing_keypressed","next":null,"parent":"46_hm$E}C?N}KA=/3@`u","inputs":{"KEY_OPTION":[1,"@`Y@ky)Etn+{XJ-{4Vsb"]},"fields":{},"shadow":false,"topLevel":false},"@`Y@ky)Etn+{XJ-{4Vsb":{"opcode":"sensing_keyoptions","next":null,"parent":"w!`lxu[+VTbb!a@:2IFM","inputs":{},"fields":{"KEY_OPTION":["space",null]},"shadow":true,"topLevel":false},"[+(oxNIM5(qM,kq~)/=K":{"opcode":"looks_switchcostumeto","next":null,"parent":"|LMd9D?RO[2$-2d9tSaH","inputs":{"COSTUME":[1,"laZz,h-Z/Z?46wV,!;C*"]},"fields":{},"shadow":false,"topLevel":false},"laZz,h-Z/Z?46wV,!;C*":{"opcode":"looks_costume","next":null,"parent":"[+(oxNIM5(qM,kq~)/=K","inputs":{},"fields":{"COSTUME":["costum1",null]},"shadow":true,"topLevel":false},"46_hm$E}C?N}KA=/3@`u":{"opcode":"operator_not","next":null,"parent":"|LMd9D?RO[2$-2d9tSaH","inputs":{"OPERAND":[2,"w!`lxu[+VTbb!a@:2IFM"]},"fields":{},"shadow":false,"topLevel":false}},"comments":{},"currentCostume":0,"costumes":[{"name":"costum1","bitmapResolution":1,"dataFormat":"svg","assetId":"5b8a85774c8c983b993b7d1549b46082","md5ext":"5b8a85774c8c983b993b7d1549b46082.svg","rotationCenterX":47.678985050489445,"rotationCenterY":49.499230353205405},{"name":"costum2","bitmapResolution":1,"dataFormat":"svg","assetId":"1bf03016126b8879eb6228066a9a28a2","md5ext":"1bf03016126b8879eb6228066a9a28a2.svg","rotationCenterX":47.67898252524472,"rotationCenterY":49.49923017660271}],"sounds":[{"name":"Miau","assetId":"83c36d806dc92327b9e7049a565c6bff","dataFormat":"wav","format":"","rate":44100,"sampleCount":37376,"md5ext":"83c36d806dc92327b9e7049a565c6bff.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"}],"monitors":[],"extensions":[],"meta":{"semver":"3.0.0","vm":"1.2.52","agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"}}
\ No newline at end of file
+{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["my variable",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"name":"Hay Field","bitmapResolution":2,"dataFormat":"png","assetId":"da102a69d135973e0fc139131dec785a","rotationCenterX":480,"rotationCenterY":360}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":44100,"sampleCount":1032,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"on","textToSpeechLanguage":null},{"isStage":false,"name":"Sprite1","variables":{},"lists":{},"broadcasts":{},"blocks":{"D:TgXIVyJrJ_k|v^Lisx":{"opcode":"event_whenflagclicked","next":"ptHi3IywC$(o5mihO!ET","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":500,"y":579},"ptHi3IywC$(o5mihO!ET":{"opcode":"control_forever","next":null,"parent":"D:TgXIVyJrJ_k|v^Lisx","inputs":{"SUBSTACK":[2,"vrpIp+Y+4DNbk)DEO@kn"]},"fields":{},"shadow":false,"topLevel":false},"vrpIp+Y+4DNbk)DEO@kn":{"opcode":"motion_pointtowards","next":null,"parent":"ptHi3IywC$(o5mihO!ET","inputs":{"TOWARDS":[1,"9fYz0RDgmONr6LZ3ggiH"]},"fields":{},"shadow":false,"topLevel":false},"9fYz0RDgmONr6LZ3ggiH":{"opcode":"motion_pointtowards_menu","next":null,"parent":"vrpIp+Y+4DNbk)DEO@kn","inputs":{},"fields":{"TOWARDS":["Crystal",null]},"shadow":true,"topLevel":false}},"comments":{},"currentCostume":0,"costumes":[{"name":"Arrow1-a","bitmapResolution":1,"dataFormat":"svg","assetId":"be8fcd10da0b082f8d4775088ef7bd52","rotationCenterX":28,"rotationCenterY":23}],"sounds":[{"name":"Meow","assetId":"83c36d806dc92327b9e7049a565c6bff","dataFormat":"wav","format":"","rate":44100,"sampleCount":37376,"md5ext":"83c36d806dc92327b9e7049a565c6bff.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":0,"y":0,"size":100,"direction":102.09042871030996,"draggable":false,"rotationStyle":"all around"},{"isStage":false,"name":"Crystal","variables":{},"lists":{},"broadcasts":{},"blocks":{"LM+.Uo#HB1kE6,D02hS}":{"opcode":"event_whenflagclicked","next":"k[fbM51((#Fz45U+/c}U","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":499,"y":460},"k[fbM51((#Fz45U+/c}U":{"opcode":"control_forever","next":null,"parent":"LM+.Uo#HB1kE6,D02hS}","inputs":{"SUBSTACK":[2,"u/fFS`XChmDRFm;%GOp:"]},"fields":{},"shadow":false,"topLevel":false},"u/fFS`XChmDRFm;%GOp:":{"opcode":"motion_turnright","next":"Fpi~c0Aar+ud+MdN#CkK","parent":"k[fbM51((#Fz45U+/c}U","inputs":{"DEGREES":[1,[4,"15"]]},"fields":{},"shadow":false,"topLevel":false},"Fpi~c0Aar+ud+MdN#CkK":{"opcode":"motion_movesteps","next":null,"parent":"u/fFS`XChmDRFm;%GOp:","inputs":{"STEPS":[1,[4,"20"]]},"fields":{},"shadow":false,"topLevel":false}},"comments":{},"currentCostume":0,"costumes":[{"name":"crystal-a","bitmapResolution":1,"dataFormat":"svg","assetId":"ecd1e7805b37db4caf207b7eef2b7a42","md5ext":"ecd1e7805b37db4caf207b7eef2b7a42.svg","rotationCenterX":15,"rotationCenterY":15},{"name":"crystal-b","bitmapResolution":1,"dataFormat":"svg","assetId":"0a7b872042cecaf30cc154c0144f002b","md5ext":"0a7b872042cecaf30cc154c0144f002b.svg","rotationCenterX":12,"rotationCenterY":24}],"sounds":[{"name":"Magic Spell","assetId":"1cb60ecdb1075c8769cb346d5c2a22c7","dataFormat":"wav","format":"adpcm","rate":22050,"sampleCount":43689,"md5ext":"1cb60ecdb1075c8769cb346d5c2a22c7.wav"},{"name":"collect","assetId":"32514c51e03db680e9c63857b840ae78","dataFormat":"wav","format":"adpcm","rate":22050,"sampleCount":14225,"md5ext":"32514c51e03db680e9c63857b840ae78.wav"}],"volume":100,"layerOrder":2,"visible":true,"x":50.26527836193807,"y":-10.767159525430722,"size":100,"direction":105,"draggable":false,"rotationStyle":"all around"}],"monitors":[],"extensions":[],"meta":{"semver":"3.0.0","vm":"1.5.76","agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"}}
\ No newline at end of file
diff --git a/block.py b/block.py
index 73ff6a4..d571ea8 100644
--- a/block.py
+++ b/block.py
@@ -37,6 +37,7 @@ def __init__(self):
self.timeDelay = 0 # wait time from the wait block
self.target = None # parent
self.substack = set() # blocks inside
+ self.substack2 = set() # only for if then else
self.script = set() # blocks below
self.screenRefresh = False # do a screen refresh
self.inEventLoop = False
@@ -61,6 +62,7 @@ def evaluateBlockValue(self, eventContainer=eventContainer.EventContainer):
except ZeroDivisionError:
raise ZeroDivisionError(_("zero-division-error"))
return self.value
+
elif self.opcode == "operator_random": # pick random from () to ()
decimals1 = len(str(math.modf(float(self.getInputValue("from"))))) - 2
decimals2 = len(str(math.modf(float(self.getInputValue("to"))))) - 2
@@ -70,26 +72,54 @@ def evaluateBlockValue(self, eventContainer=eventContainer.EventContainer):
decimals = decimals2
self.value = random.randint(int(self.getInputValue("from")) * 10 ** decimals, int(self.getInputValue("to")) * 10 ** decimals) / 10 ** decimals
return self.value
+
+ elif self.opcode == "operator_equals": # () = ()
+ self.value = self.getInputValue("operand1") == self.getInputValue("operand2")
+ return self.value
+ elif self.opcode == "operator_lt": # () < ()
+ self.value = self.getInputValue("operand1") < self.getInputValue("operand2")
+ return self.value
+ elif self.opcode == "operator_gt": # () > ()
+ self.value = self.getInputValue("operand1") > self.getInputValue("operand2")
+ return self.value
+
elif self.opcode == "motion_xposition": # x position
self.value = self.target.x
return self.value
elif self.opcode == "motion_xposition": # y position
self.value = self.target.y
return self.value
+ elif self.opcode == "motion_direction": # y position
+ self.value = self.target.direction
+ return self.value
+
elif self.opcode == "sensing_mousex": # mouse x
newX, newY = pygame.mouse.get_pos()
newX = newX - config.screenWidth // 2
self.value = newX
- return newX
+ return self.value
elif self.opcode == "sensing_mousey": # mouse y
newX, newY = pygame.mouse.get_pos()
- newY = newY - config.screenWidth // 2
+ newY = newY - config.screenHeight // 2
self.value = newY
- return newY
+ return self.value
+
elif self.opcode == "sensing_keypressed": # key pressed?
- return KEY_MAPPING[self.getMenuValue("key_option")] in eventContainer.keys
+ self.value = KEY_MAPPING[self.getMenuValue("key_option")] in eventContainer.keys
+ return self.value
+ elif self.opcode == "sensing_mousedown": # mouse down?
+ self.value = pygame.mouse.get_pressed()[0]
+ return self.value
+
elif self.opcode == "operator_not": # not <>
- return not self.target.blocks[self.getBlockInputValue("operand")].evaluateBlockValue(eventContainer)
+ self.value = not self.target.blocks[self.getBlockInputValue("operand")].evaluateBlockValue(eventContainer)
+ return self.value
+ elif self.opcode == "operator_and": # <> and <>
+ self.value = self.target.blocks[self.getBlockInputValue("operand1")].evaluateBlockValue(eventContainer) and self.target.blocks[self.getBlockInputValue("operand2")].evaluateBlockValue(eventContainer)
+ return self.value
+ elif self.opcode == "operator_or": # <> or <>
+ self.value = self.target.blocks[self.getBlockInputValue("operand1")].evaluateBlockValue(eventContainer) or self.target.blocks[self.getBlockInputValue("operand2")].evaluateBlockValue(eventContainer)
+ return self.value
# Returns block input value
def getBlockInputValue(self, inputId):
@@ -97,7 +127,7 @@ def getBlockInputValue(self, inputId):
# Returns block input value
def getInputValue(self, inputId, lookIn=(1, 1), eventContainer=eventContainer.EventContainer()):
- if self.inputs[inputId.upper()][lookIn[0]][0] in {4, 0, 5, 6}:
+ if self.inputs[inputId.upper()][lookIn[0]][0] in {4, 0, 5, 6, 8}:
return self.inputs[inputId.upper()][lookIn[0]][1] or 0
elif self.inputs[inputId.upper()][0] == 3:
blockLink = self.inputs[inputId.upper()][1]
diff --git a/config.py b/config.py
index 50dc3d0..6dbab2c 100644
--- a/config.py
+++ b/config.py
@@ -24,7 +24,7 @@
# Project file name
# If in test mode, set the Scratch project file to load.
-projectFileName: str = "projects/ChangeColour.sb3"
+projectFileName: str = "projects/PointTowardsSprite.sb3"
# Download cache size
# Number of recent downloaded projects stored. 0 means infinity.
@@ -49,6 +49,9 @@
# message.
pygameWelcomeMessage: bool = True
+# Disable print function
+disablePrint: bool = False
+
# Enable Scratch Addons debugger logs
# This allows projects using Scratch Addons to print messages to the console. Vanilla Scratch doesn't support it.
showSALogs: bool = True
@@ -67,7 +70,7 @@
# Screen width/height
# Stage size. You can change that, but most projects won't work with it.
# A scaling mode will be added later.
-# Vanilla is 480x360.
+# Vanilla is 480x360. Try 640x360 for 16/9 widescreen.
screenWidth: int = 480
screenHeight: int = 360
diff --git a/costume.py b/costume.py
index 608f8b8..9d0ccbd 100644
--- a/costume.py
+++ b/costume.py
@@ -13,6 +13,7 @@ def __init__(self):
self.dataFormat = "svg"
self.rotationCenterX = 0
self.rotationCenterY = 0
+ self.offset = None
self.bitmapResolution = 1
self.file = None
self.name = "" # display name
diff --git a/eventContainer.py b/eventContainer.py
index 9e5a0e0..3e1d957 100644
--- a/eventContainer.py
+++ b/eventContainer.py
@@ -3,4 +3,3 @@ def __init__(self):
self.keys = set()
self.keyEvents = set()
self.otherEvents = set()
-
diff --git a/fonts/Grand9K-Pixel.ttf b/fonts/Grand9K-Pixel.ttf
new file mode 100644
index 0000000..cf6fdf4
Binary files /dev/null and b/fonts/Grand9K-Pixel.ttf differ
diff --git a/fonts/Griffy-Regular.ttf b/fonts/Griffy-Regular.ttf
new file mode 100644
index 0000000..22eea37
Binary files /dev/null and b/fonts/Griffy-Regular.ttf differ
diff --git a/fonts/Knewave.ttf b/fonts/Knewave.ttf
new file mode 100644
index 0000000..7d1545b
Binary files /dev/null and b/fonts/Knewave.ttf differ
diff --git a/fonts/LICENSE_CC_BY-SA_3.0.txt b/fonts/LICENSE_CC_BY-SA_3.0.txt
new file mode 100644
index 0000000..48ae233
--- /dev/null
+++ b/fonts/LICENSE_CC_BY-SA_3.0.txt
@@ -0,0 +1,5 @@
+Applies to: Grand9K-Pixel.ttf
+
+This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To view a copy of this
+license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, PO Box 1866,
+Mountain View, CA 94042, USA.
diff --git a/fonts/LICENSE_SIL_OFL.txt b/fonts/LICENSE_SIL_OFL.txt
new file mode 100644
index 0000000..2fd9b6e
--- /dev/null
+++ b/fonts/LICENSE_SIL_OFL.txt
@@ -0,0 +1,87 @@
+Applies to: Griffy-Regular.ttf, handlee-regular.ttf, Knewave.ttf, NotoSans-Medium.ttf, SourceSansPro-Regular.ttf, SourceSerifPro-Regular.ttf
+
+SIL OPEN FONT LICENSE
+Version 1.1 - 26 February 2007
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting — in part or in whole — any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/fonts/NotoSans-Medium.ttf b/fonts/NotoSans-Medium.ttf
new file mode 100644
index 0000000..25050f7
Binary files /dev/null and b/fonts/NotoSans-Medium.ttf differ
diff --git a/fonts/SourceSansPro-Regular.ttf b/fonts/SourceSansPro-Regular.ttf
new file mode 100644
index 0000000..98e8579
Binary files /dev/null and b/fonts/SourceSansPro-Regular.ttf differ
diff --git a/fonts/SourceSerifPro-Regular.otf b/fonts/SourceSerifPro-Regular.otf
new file mode 100644
index 0000000..bcad74a
Binary files /dev/null and b/fonts/SourceSerifPro-Regular.otf differ
diff --git a/fonts/handlee-regular.ttf b/fonts/handlee-regular.ttf
new file mode 100644
index 0000000..02a94e1
Binary files /dev/null and b/fonts/handlee-regular.ttf differ
diff --git a/gui.py b/gui.py
new file mode 100644
index 0000000..c4286b0
--- /dev/null
+++ b/gui.py
@@ -0,0 +1,51 @@
+import gi
+import os
+import sys
+
+gi.require_version("Gtk", "3.0")
+from gi.repository import Gtk
+
+
+def pause(button):
+ print("PAUSE", file=p.stdin)
+ print("PAUSE")
+
+
+def stop(button):
+ print("STOP", file=p.stdin)
+ print("STOP")
+
+
+handlers = {
+ "onDestroy": Gtk.main_quit,
+ "pause": pause,
+ "stop": stop,
+}
+
+builder = Gtk.Builder()
+builder.add_from_file("main.glade")
+builder.connect_signals(handlers)
+
+window = builder.get_object("test")
+window.show_all()
+
+stdin = sys.stdin.fileno() # usually 0
+stdout = sys.stdout.fileno() # usually 1
+
+parentStdin, childStdout = os.pipe()
+childStdin, parentStdout = os.pipe()
+pid = os.fork()
+if pid:
+ # parent process
+ os.close(childStdout)
+ os.close(childStdin)
+ os.dup2(parentStdin, stdin)
+ os.dup2(parentStdout, stdout)
+ Gtk.main()
+else:
+ # child process
+ os.close(parentStdin)
+ os.close(parentStdout)
+ os.dup2(childStdin, stdin)
+ os.dup2(childStdout, stdout)
+ os.execl("python", "main.py", "main.py")
diff --git a/lang/en.yml b/lang/en.yml
index 5ac630d..a165c8c 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -48,4 +48,5 @@ en:
zero-division-error: "Project was trying to divide by 0"
costumes-count: "%{sprite} has %{costumes} costumes"
stage-not-found: "There is no stage sprite! Invalid project."
- new-sprite-rotation: "Sprite %{name} rotated to %{rot}"
\ No newline at end of file
+ new-sprite-rotation: "Sprite %{name} rotated to %{rot} degrees"
+ new-sprite-size: "Sprite %{name} resized to %{size}%"
\ No newline at end of file
diff --git a/lang/pl.yml b/lang/pl.yml
new file mode 100644
index 0000000..3a244ec
--- /dev/null
+++ b/lang/pl.yml
@@ -0,0 +1,52 @@
+pl:
+ start: "Scratch2Python %{version}, %{os}"
+ unrecognized-os: "Niestety Scratch2Python nie rozpoznaje twojego systemu operacyjnego. Twój platform string to: %{platform}. Jeżeli wystąpi jakiś błąd, proszę go zgłosić tutaj: %{url}"
+ filename-prompt: "Nazwa pliku projektu:"
+ sb3-desc: "Projekty Scratch 3"
+ all-files-desc: "Wszystkie pliki (*.*)"
+ choose-project-title: "Wybierz projekt do załadowania"
+ invalid-setting-error: "Nieznane ustawienie %{setting}"
+ paused-message: "Zapausowane (naciśnij przycisk %{keybind}, aby odpauzować)"
+ window-title: "%{projectName} - %{s2pVersionString}"
+ extracting-project: "Wypakowywanie projektu"
+ screen-width-prompt: "Szerokość:"
+ screen-height-prompt: "Wysokość:"
+ ok: "OK"
+ cancel: "Anuluj"
+ project-started: "Zaczęto projekt"
+ player-closed: "Zamknięto odtwarzacz"
+ nothing-to-see-here: "Nie ma co tu zobaczyć"
+ help-title: "Pomoc"
+ extract-title: "Wypakuj projekt"
+ project-info-title: "Informacje projektu"
+ fps-title: "FPS (klatki na sekundę)"
+ screen-title: "Rozdzielczość ekranu"
+ fps-prompt: "Wprowadź ilość klatek na sekundę (FPS)"
+ fps-message: "Ustawiono FPS na:"
+ extract-prompt: "Wypakować wszystko z projektu?"
+ screen-message: "Ustawiono rozdzielczość ekranu na:"
+ redraw-message: "Ekran przerysowany"
+ config-warning-screen-too-small: "Ta rozdzielczość jest bardzo mała. Zalecana rozdzielczość minimalna to %{res}. Jeżeli chcesz zignorować ten błąd, włącz zmienną INSANE w config.py."
+ config-warning-screen-too-large: "Ta rozdzielczość jest bardzo duża. Zalecana rozdzielczość maksymalna to %{res}. Jeżeli chcesz zignorować ten błąd, włącz zmienną INSANE w config.py."
+ project-file-not-found: "Nie znaleziono pliku projektu."
+ loading-project: "Ładowanie projektu"
+ debug-prefix: "DEBUG:"
+ block-waiting: "Czekanie %{time} ms"
+ keypress-handling: "Naciśnięto %{keyName}"
+ key-any: "każdy"
+ key-left: "strzałka w lewo"
+ key-right: "strzałka w prawo"
+ key-up: "strzałka w górę"
+ key-down: "strzałka w dół"
+ key-space: "spacja"
+ project-log: "PROJEKT:"
+ project-warn: "OSTRZEŻENIE PROJEKTU:"
+ project-error: "BŁĄD PROJEKTU:"
+ unknown-opcode: "Nieznany kod operacji:"
+ new-sprite-position: "Nowa pozycja (%{x}, %{y}) dla duszka %{name}"
+ stage: "Tło"
+ zero-division-error: "Projekt próbował dzielić przez 0"
+ costumes-count: "Duszek %{sprite} ma %{costumes} kostiumów"
+ stage-not-found: "Nie ma tła! Nieprawidłowy projekt."
+ new-sprite-rotation: "Duszek %{name} obrócony %{rot} stopni"
+ new-sprite-size: "Nowy rozmiar duszka %{name}: %{size}%"
diff --git a/lang/ro.yml b/lang/ro.yml
index 8030ccb..3bc73de 100644
--- a/lang/ro.yml
+++ b/lang/ro.yml
@@ -48,4 +48,5 @@ ro:
zero-division-error: "Proiectul a încercat să împartă la 0"
costumes-count: "%{sprite} are %{costumes} costume"
stage-not-found: "Nu există scena! Proiect invalid."
- new-sprite-rotation: "Personajul %{name} a fost rotit la %{rot}"
\ No newline at end of file
+ new-sprite-rotation: "Personajul %{name} a fost rotit la %{rot} grade"
+ new-sprite-size: "Personajul %{name} a fost redimensionat la %{size}%"
\ No newline at end of file
diff --git a/main.glade b/main.glade
new file mode 100644
index 0000000..c2e12c8
--- /dev/null
+++ b/main.glade
@@ -0,0 +1,1648 @@
+
+
+
+
+
+ True
+ False
+ dialog-information
+
+
+ 576
+ 320
+ False
+ 8
+ Scratch2Python
+ False
+ scratch
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Scratch2Python project loader
+ 0
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ version {VERSION}
+ 0
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ center
+ center
+ 8
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ application-x-executable
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Load local project
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ from your computer
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ application-x-partial-download
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Cache project
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ from scratch.mit.edu
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ preferences-system
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Settings
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 3
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ False
+
+
+ Need a GUI for browsing projects on scratch.mit.edu?
+ True
+ True
+ True
+ none
+ https://example.org
+
+
+ False
+ True
+ 0
+
+
+
+
+ About
+ True
+ True
+ True
+ aboutIcon
+ True
+
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ True
+ False
+ dialog-information
+
+
+ 64
+ 2
+ 10
+
+
+ 4096
+ 300
+ 4
+ 10
+
+
+ 3000
+ 250
+ 25
+ 10
+
+
+ 240
+ 15
+ 10
+
+
+
+ False
+ bottom
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Cache and load project
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ projectIDValue
+ Project ID or URL
+
+
+ False
+ True
+ 1
+
+
+
+
+ Load
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ 16
+ 2048
+ 360
+ 8
+ 10
+
+
+ 16
+ 2048
+ 480
+ 8
+ 10
+
+
+ False
+ Scratch2Python Settings
+ 540
+ 360
+
+
+ True
+ True
+ left
+ True
+ True
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Download cache size
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Number of stored recently downloaded projects before they start getting deleted. 0 means infinity.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ cacheSize
+ True
+ 4
+
+
+ 1
+ 0
+
+
+
+
+ Clear download cache
+ True
+ True
+ True
+
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ Clear cache
+ 0
+
+
+
+
+
+ 0
+ 1
+
+
+
+
+
+
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ folder-download
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Download cache
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+
+
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Maximum framerate
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ The maximum framerate for projects. Sometimes changing it can improve things, but most projects will feel too fast or too slow.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ 0
+ maxFPS
+ True
+ 30.000000000223519
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Turbo mode
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Makes screen refreshes as fast as possible.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Screen resolution
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Change the stage size. Most projects won't adapt properly. Try 640x360 for a widescreen version of the default stage.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 2
+
+
+
+
+
+ True
+ False
+
+
+ True
+ True
+ screenWidth
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ W
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ H
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+ 480
+ screenHeight
+ 360
+
+
+ 1
+ 1
+
+
+
+
+ 1
+ 2
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Allow off-screen sprites
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Allow sprites to bypass sprite fencing and completely leave the stage by normal means. Projects may break.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 3
+
+
+
+
+ True
+ True
+
+
+ 1
+ 3
+
+
+
+
+ True
+ False
+ Reset all project defaults
+ 0
+
+
+
+
+
+ 0
+ 5
+
+
+
+
+ Reset
+ True
+ True
+ True
+
+
+
+
+ 1
+ 5
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Clone limit
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Maximum number of clones allowed at one time. 0 means unlimited.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 4
+
+
+
+
+ True
+ True
+ cloneLimit
+
+
+ 1
+ 4
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ computer
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Project defaults
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 1
+ False
+
+
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+ True
+ False
+
+
+
+
+
+
+ 2
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ preferences-desktop-locale
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Languages
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 2
+ False
+
+
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Extract project on run
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ If enabled, Scratch2Python will automatically extract the project that is being run into the "assets" directory inside the Scratch2Python install location.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ Allow debug messages in terminal
+ 0
+
+
+
+
+
+ 0
+ 2
+
+
+
+
+
+ 1
+ 2
+
+
+
+
+ True
+ False
+ Allow Scratch Addons logs in terminal
+ 0
+
+
+
+
+
+ 0
+ 3
+
+
+
+
+
+ 1
+ 3
+
+
+
+
+ True
+ False
+ Allow terminal output
+ 0
+
+
+
+
+
+ 0
+ 1
+
+
+
+
+
+ 1
+ 1
+
+
+
+
+
+ 1
+ 4
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Allow pygame welcome message
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Allow pygame to display
+"pygame X.Y.Z (SDL X.Y.Z, Python 3.Y.Z)
+Hello from the pygame community. https://www.pygame.org/contribute.html"
+on startup.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 4
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ utilities-system-monitor
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Debug
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 3
+ False
+
+
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Key delay
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Milliseconds before keys will start repeating, 0 means keys won't repeat.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ 4
+ keyDelay
+ True
+ 4
+
+
+ 1
+ 0
+
+
+
+
+ 4
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ preferences-desktop-accessibility
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Accesibility
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 4
+ False
+
+
+
+
+
+
+ False
+
+
+ True
+ False
+ 8
+ 8
+ 8
+ 8
+ vertical
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Test controls
+ 0
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ center
+ center
+ 8
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ media-playback-pause
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Pause/resume project
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ media-playback-stop
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Quit player
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ False
+
+
+ Need a GUI for browsing projects on scratch.mit.edu?
+ True
+ True
+ True
+ none
+ https://example.org
+
+
+ False
+ True
+ 0
+
+
+
+
+ About
+ True
+ True
+ True
+ aboutIcon1
+ True
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
diff --git a/main.glade~ b/main.glade~
new file mode 100644
index 0000000..c2e12c8
--- /dev/null
+++ b/main.glade~
@@ -0,0 +1,1648 @@
+
+
+
+
+
+ True
+ False
+ dialog-information
+
+
+ 576
+ 320
+ False
+ 8
+ Scratch2Python
+ False
+ scratch
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Scratch2Python project loader
+ 0
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ version {VERSION}
+ 0
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ center
+ center
+ 8
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ application-x-executable
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Load local project
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ from your computer
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ application-x-partial-download
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Cache project
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ False
+ from scratch.mit.edu
+
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ preferences-system
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Settings
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 3
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ False
+
+
+ Need a GUI for browsing projects on scratch.mit.edu?
+ True
+ True
+ True
+ none
+ https://example.org
+
+
+ False
+ True
+ 0
+
+
+
+
+ About
+ True
+ True
+ True
+ aboutIcon
+ True
+
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ True
+ False
+ dialog-information
+
+
+ 64
+ 2
+ 10
+
+
+ 4096
+ 300
+ 4
+ 10
+
+
+ 3000
+ 250
+ 25
+ 10
+
+
+ 240
+ 15
+ 10
+
+
+
+ False
+ bottom
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Cache and load project
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ projectIDValue
+ Project ID or URL
+
+
+ False
+ True
+ 1
+
+
+
+
+ Load
+ True
+ True
+ True
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
+ 16
+ 2048
+ 360
+ 8
+ 10
+
+
+ 16
+ 2048
+ 480
+ 8
+ 10
+
+
+ False
+ Scratch2Python Settings
+ 540
+ 360
+
+
+ True
+ True
+ left
+ True
+ True
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Download cache size
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Number of stored recently downloaded projects before they start getting deleted. 0 means infinity.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ cacheSize
+ True
+ 4
+
+
+ 1
+ 0
+
+
+
+
+ Clear download cache
+ True
+ True
+ True
+
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ Clear cache
+ 0
+
+
+
+
+
+ 0
+ 1
+
+
+
+
+
+
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ folder-download
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Download cache
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ False
+
+
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Maximum framerate
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ The maximum framerate for projects. Sometimes changing it can improve things, but most projects will feel too fast or too slow.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ 0
+ maxFPS
+ True
+ 30.000000000223519
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Turbo mode
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Makes screen refreshes as fast as possible.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+
+
+ 1
+ 1
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Screen resolution
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Change the stage size. Most projects won't adapt properly. Try 640x360 for a widescreen version of the default stage.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 2
+
+
+
+
+
+ True
+ False
+
+
+ True
+ True
+ screenWidth
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ W
+
+
+ 0
+ 0
+
+
+
+
+ True
+ False
+ H
+
+
+ 0
+ 1
+
+
+
+
+ True
+ True
+ 480
+ screenHeight
+ 360
+
+
+ 1
+ 1
+
+
+
+
+ 1
+ 2
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Allow off-screen sprites
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Allow sprites to bypass sprite fencing and completely leave the stage by normal means. Projects may break.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 3
+
+
+
+
+ True
+ True
+
+
+ 1
+ 3
+
+
+
+
+ True
+ False
+ Reset all project defaults
+ 0
+
+
+
+
+
+ 0
+ 5
+
+
+
+
+ Reset
+ True
+ True
+ True
+
+
+
+
+ 1
+ 5
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Clone limit
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Maximum number of clones allowed at one time. 0 means unlimited.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 4
+
+
+
+
+ True
+ True
+ cloneLimit
+
+
+ 1
+ 4
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ computer
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Project defaults
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 1
+ False
+
+
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+ True
+ False
+
+
+
+
+
+
+ 2
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ preferences-desktop-locale
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Languages
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 2
+ False
+
+
+
+
+ True
+ True
+ 8
+ 8
+ 8
+ 8
+ in
+
+
+ True
+ False
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Extract project on run
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ If enabled, Scratch2Python will automatically extract the project that is being run into the "assets" directory inside the Scratch2Python install location.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+ 1
+ 0
+
+
+
+
+ True
+ False
+ Allow debug messages in terminal
+ 0
+
+
+
+
+
+ 0
+ 2
+
+
+
+
+
+ 1
+ 2
+
+
+
+
+ True
+ False
+ Allow Scratch Addons logs in terminal
+ 0
+
+
+
+
+
+ 0
+ 3
+
+
+
+
+
+ 1
+ 3
+
+
+
+
+ True
+ False
+ Allow terminal output
+ 0
+
+
+
+
+
+ 0
+ 1
+
+
+
+
+
+ 1
+ 1
+
+
+
+
+
+ 1
+ 4
+
+
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Allow pygame welcome message
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Allow pygame to display
+"pygame X.Y.Z (SDL X.Y.Z, Python 3.Y.Z)
+Hello from the pygame community. https://www.pygame.org/contribute.html"
+on startup.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 4
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ utilities-system-monitor
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Debug
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 3
+ False
+
+
+
+
+
+ True
+ False
+ 8
+ 4
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Key delay
+ 0
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Milliseconds before keys will start repeating, 0 means keys won't repeat.
+ True
+ 0
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ 0
+ 0
+
+
+
+
+ True
+ True
+ 4
+ keyDelay
+ True
+ 4
+
+
+ 1
+ 0
+
+
+
+
+ 4
+
+
+
+
+ True
+ False
+
+
+ True
+ False
+ preferences-desktop-accessibility
+ 3
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Accesibility
+ 1
+
+
+ False
+ True
+ 1
+
+
+
+
+ 4
+ False
+
+
+
+
+
+
+ False
+
+
+ True
+ False
+ 8
+ 8
+ 8
+ 8
+ vertical
+
+
+ True
+ False
+ vertical
+
+
+ True
+ False
+ Test controls
+ 0
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ center
+ center
+ 8
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ media-playback-pause
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Pause/resume project
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 0
+
+
+
+
+ 160
+ 128
+ True
+ True
+ True
+ center
+ center
+
+
+
+ True
+ False
+ center
+ center
+ vertical
+
+
+ True
+ False
+ 48
+ media-playback-stop
+ 6
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ False
+ Quit player
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 1
+
+
+
+
+ True
+ True
+ 1
+
+
+
+
+ True
+ False
+
+
+ Need a GUI for browsing projects on scratch.mit.edu?
+ True
+ True
+ True
+ none
+ https://example.org
+
+
+ False
+ True
+ 0
+
+
+
+
+ About
+ True
+ True
+ True
+ aboutIcon1
+ True
+
+
+ False
+ True
+ end
+ 1
+
+
+
+
+ False
+ True
+ 2
+
+
+
+
+
+
diff --git a/main.py b/main.py
index 70673c4..5ebd641 100644
--- a/main.py
+++ b/main.py
@@ -19,7 +19,7 @@
along with this program. If not, see .
"""
-__version__ = "v0.2.0"
+__version__ = "v0.8.0"
__author__ = "Secret-chest"
import tkinter.simpledialog
@@ -74,6 +74,8 @@
from tkinter import filedialog
from targetSprite import TargetSprite
import eventContainer
+import select
+from scratch import display, setProject, mainWindow
sys.stdout = sys.__stdout__
@@ -83,6 +85,13 @@
if not config.enableDebugMessages:
sys.stderr = open(os.devnull, "w")
+# Disable the print function
+def decorator(func):
+ def disabledPrint(*args, **kwargs):
+ if not config.disablePrint:
+ func(*args, **kwargs)
+ return disabledPrint
+print = decorator(print)
# Define a dialog class for screen resolution
class SizeDialog(tkinter.simpledialog.Dialog):
@@ -118,40 +127,17 @@ def buttonbox(self):
self.bind("", lambda event: self.cancelPressed())
-# Start tkinter for showing some popups, and hide main window
-mainWindow = tk.Tk()
-mainWindow.withdraw()
-
# Clean the cache if limit is exceeded
downloads = sorted(Path("./download/").iterdir(), key=os.path.getmtime)
downloadsToDelete = downloads[config.cachedDownloads:]
for f in downloadsToDelete:
os.remove(f)
-# Get project file name based on options and arguments
-if len(sys.argv) > 1:
- setProject = sys.argv[1]
-else:
- if config.testMode:
- if not config.projectFileName.endswith(".sb3"):
- if "http" not in config.projectFileName\
- or "https" not in config.projectFileName:
- setProject = downloader.downloadByID(config.projectFileName, "./download")
- else:
- setProject = downloader.downloadByURL(config.projectFileName, "./download")
- else:
- setProject = config.projectFileName
- else:
- fileTypes = [(_("sb3-desc"), ".sb3"), (_("all-files-desc"), ".*")]
- setProject = filedialog.askopenfilename(parent=mainWindow,
- initialdir=os.getcwd(),
- title=_("choose-project-title"),
- filetypes=fileTypes)
# Get project data and create sprites
targets, project = sb3Unpacker.sb3Unpack(setProject)
allSprites = pygame.sprite.Group()
-for t in targets:
+for t in sorted(targets, key=lambda t: t.layerOrder):
sprite = TargetSprite(t)
t.sprite = sprite
allSprites.add(sprite)
@@ -162,8 +148,8 @@ def buttonbox(self):
pygame.mixer.pre_init(22050, -16, 1, 12193)
pygame.init()
-font = pygame.font.SysFont(pygame.font.get_default_font(), 16)
-fontXl = pygame.font.SysFont(pygame.font.get_default_font(), 36)
+font = pygame.font.Font("./fonts/SourceSansPro-Regular.ttf", 16)
+fontXl = pygame.font.Font("./fonts/SourceSansPro-Regular.ttf", 36)
# Create paused message
paused = fontXl.render(_("paused-message", keybind="F6"), True, (0, 0, 0))
@@ -173,15 +159,6 @@ def buttonbox(self):
HEIGHT = config.projectScreenHeight
WIDTH = config.projectScreenWidth
-# Get project name and set icon
-projectName = Path(setProject).stem
-icon = pygame.image.load("icon.svg")
-
-# Create project player and window
-display = pygame.display.set_mode([WIDTH, HEIGHT])
-pygame.display.set_caption(_("window-title", projectName=projectName, s2pVersionString="Scratch2Python " + __version__))
-pygame.display.set_icon(icon)
-
# Extract if requested
if config.extractOnProjectRun:
print(_("extracting-project"))
@@ -242,6 +219,15 @@ def buttonbox(self):
# Mainloop
lastTime = time.time_ns()
while projectRunning:
+ rlist, _, _ = select.select([sys.stdin], [], [], 0.1)
+ if rlist:
+ line = sys.stdin.readline().strip()
+ if line:
+ if line.startswith("STOP"):
+ pygame.quit()
+ elif line.startswith("PAUSE"):
+ isPaused = not isPaused
+
keyEventContainer.keyEvents = set()
# Process Pygame events
for event in pygame.event.get():
@@ -368,7 +354,7 @@ def buttonbox(self):
allSprites.draw(display)
allSprites.update()
else:
- display.blit(paused, (WIDTH // 2 - pausedWidth // 2, WIDTH // 2 - pausedHeight // 2))
+ display.blit(paused, (WIDTH // 2 - pausedWidth // 2, HEIGHT // 2 - pausedHeight // 2))
pygame.display.flip()
mainWindow.update()
diff --git a/projects/AbsoluteRotation.sb3 b/projects/AbsoluteRotation.sb3
new file mode 100644
index 0000000..733a624
Binary files /dev/null and b/projects/AbsoluteRotation.sb3 differ
diff --git a/projects/Balls.sb3 b/projects/Balls.sb3
new file mode 100644
index 0000000..676e6be
Binary files /dev/null and b/projects/Balls.sb3 differ
diff --git a/projects/ChangeColourWithElse.sb3 b/projects/ChangeColourWithElse.sb3
new file mode 100644
index 0000000..e502930
Binary files /dev/null and b/projects/ChangeColourWithElse.sb3 differ
diff --git a/projects/GtkPauseTest.sb3 b/projects/GtkPauseTest.sb3
new file mode 100644
index 0000000..be76316
Binary files /dev/null and b/projects/GtkPauseTest.sb3 differ
diff --git a/projects/Layering.sb3 b/projects/Layering.sb3
new file mode 100644
index 0000000..15c84e9
Binary files /dev/null and b/projects/Layering.sb3 differ
diff --git a/projects/LeftRight.sb3 b/projects/LeftRight.sb3
new file mode 100644
index 0000000..facb82e
Binary files /dev/null and b/projects/LeftRight.sb3 differ
diff --git a/projects/MouseDown.sb3 b/projects/MouseDown.sb3
new file mode 100644
index 0000000..6ca55f4
Binary files /dev/null and b/projects/MouseDown.sb3 differ
diff --git a/projects/MoveSteps.sb3 b/projects/MoveSteps.sb3
index b520bd6..1f0ee37 100644
Binary files a/projects/MoveSteps.sb3 and b/projects/MoveSteps.sb3 differ
diff --git a/projects/PointTowardsMouse.sb3 b/projects/PointTowardsMouse.sb3
new file mode 100644
index 0000000..299db65
Binary files /dev/null and b/projects/PointTowardsMouse.sb3 differ
diff --git a/projects/PointTowardsSprite.sb3 b/projects/PointTowardsSprite.sb3
new file mode 100644
index 0000000..72d7efb
Binary files /dev/null and b/projects/PointTowardsSprite.sb3 differ
diff --git a/projects/Rotation.sb3 b/projects/Rotation.sb3
new file mode 100644
index 0000000..e1467d3
Binary files /dev/null and b/projects/Rotation.sb3 differ
diff --git a/projects/Rotation2-medium.sb3 b/projects/Rotation2-medium.sb3
new file mode 100644
index 0000000..d4b0fcd
Binary files /dev/null and b/projects/Rotation2-medium.sb3 differ
diff --git a/projects/Rotation2-small.sb3 b/projects/Rotation2-small.sb3
new file mode 100644
index 0000000..486abab
Binary files /dev/null and b/projects/Rotation2-small.sb3 differ
diff --git a/projects/Rotation2.sb3 b/projects/Rotation2.sb3
new file mode 100644
index 0000000..9cb388d
Binary files /dev/null and b/projects/Rotation2.sb3 differ
diff --git a/projects/RotoZoom.sb3 b/projects/RotoZoom.sb3
new file mode 100644
index 0000000..4c570ec
Binary files /dev/null and b/projects/RotoZoom.sb3 differ
diff --git a/projects/Time.sb3 b/projects/Time.sb3
new file mode 100644
index 0000000..d57e686
Binary files /dev/null and b/projects/Time.sb3 differ
diff --git a/projects/VariableStorage.sb3 b/projects/VariableStorage.sb3
new file mode 100644
index 0000000..aad905c
Binary files /dev/null and b/projects/VariableStorage.sb3 differ
diff --git a/projects/WaitUntil.sb3 b/projects/WaitUntil.sb3
new file mode 100644
index 0000000..e152547
Binary files /dev/null and b/projects/WaitUntil.sb3 differ
diff --git a/sb3Unpacker.py b/sb3Unpacker.py
index df8f3a6..6657226 100644
--- a/sb3Unpacker.py
+++ b/sb3Unpacker.py
@@ -9,7 +9,7 @@
import zipfile as zf
import json
import config
-import target, costume, sound, block, variable, monitor # , broadcast
+import target, costume, sound, block, monitor # , broadcast
from pathlib import Path
import io
import pygame
@@ -51,8 +51,10 @@ def sb3Unpack(sb3):
t.y = targetObj["y"]
t.direction = targetObj["direction"]
t.size = targetObj["size"]
+ t.rotationStyle = targetObj["rotationStyle"]
t.currentCostume = targetObj["currentCostume"]
t.isStage = targetObj["isStage"]
+ t.layerOrder = targetObj["layerOrder"]
t.name = targetObj["name"]
# Get costumes
@@ -62,6 +64,7 @@ def sb3Unpack(sb3):
c.md5ext = costumeObj["md5ext"]
c.rotationCenterX, c.rotationCenterY = costumeObj["rotationCenterX"], costumeObj["rotationCenterY"]
c.dataFormat = costumeObj["dataFormat"]
+ c.offset = pygame.math.Vector2(c.rotationCenterX, c.rotationCenterY)
c.file = project.read(costumeObj["assetId"] + "." + costumeObj["dataFormat"])
c.name = costumeObj["name"]
if costumeObj["dataFormat"] != "svg":
@@ -82,6 +85,10 @@ def sb3Unpack(sb3):
s.name = soundObj["name"]
t.sounds.append(s)
+ # Get variables
+ for variable in targetObj["variables"]:
+ t.variables[variable] = targetObj["variables"][variable]
+
# Set blocks to their correct values
for blockId, blockObj in targetObj["blocks"].items():
b = block.Block()
diff --git a/scratch.py b/scratch.py
index 5d47c4d..73b0b7a 100644
--- a/scratch.py
+++ b/scratch.py
@@ -15,6 +15,14 @@
import time
from datetime import datetime
import eventContainer
+from pathlib import Path
+import tkinter as tk
+import downloader
+from tkinter import filedialog
+import tkinter.simpledialog
+
+__version__ = "v0.8.0"
+__author__ = "Secret-chest"
i18n.set("locale", config.language)
i18n.set("filename_format", "{locale}.{format}")
@@ -32,9 +40,42 @@ class SpriteNotFoundError(Exception):
sys.stdout = open(os.devnull, "w")
+# Start tkinter for showing some popups, and hide main window
+mainWindow = tk.Tk()
+mainWindow.withdraw()
+
+
+# Get project file name based on options and arguments
+if len(sys.argv) > 1:
+ setProject = sys.argv[1]
+else:
+ if config.testMode:
+ if not config.projectFileName.endswith(".sb3"):
+ if "http" not in config.projectFileName\
+ or "https" not in config.projectFileName:
+ setProject = downloader.downloadByID(config.projectFileName, "./download")
+ else:
+ setProject = downloader.downloadByURL(config.projectFileName, "./download")
+ else:
+ setProject = config.projectFileName
+ else:
+ fileTypes = [(_("sb3-desc"), ".sb3"), (_("all-files-desc"), ".*")]
+ setProject = filedialog.askopenfilename(parent=mainWindow,
+ initialdir=os.getcwd(),
+ title=_("choose-project-title"),
+ filetypes=fileTypes)
+
+
HEIGHT = config.projectScreenHeight
WIDTH = config.projectScreenWidth
+# Create project player and window
+projectName = Path(setProject).stem
+icon = pygame.image.load("icon.svg")
+display = pygame.display.set_mode([WIDTH, HEIGHT])
+pygame.display.set_caption(_("window-title", projectName=projectName, s2pVersionString="Scratch2Python " + __version__))
+pygame.display.set_icon(icon)
+
# Key maps to convert the key option in blocks to Pygame constants
KEY_MAPPING = {
"up arrow": pygame.K_UP,
@@ -211,6 +252,50 @@ def execute(block, s, events=eventContainer.EventContainer()):
elif opcode == "motion_turnright": # turn cw () degrees
s.setRotDelta(float(block.getInputValue("degrees", eventContainer=events)))
+ elif opcode == "motion_pointindirection": # point in direction ()
+ s.setRot(float(block.getInputValue("direction", eventContainer=events)))
+
+ elif opcode == "looks_changesizeby": # turn cw () degrees
+ s.setSizeDelta(float(block.getInputValue("change", eventContainer=events)))
+
+ elif opcode == "looks_setsizeto": # turn cw () degrees
+ s.setSize(float(block.getInputValue("size", eventContainer=events)))
+
+ elif opcode == "motion_pointindirection": # point in direction ()
+ s.setRot(float(block.getInputValue("direction", eventContainer=events)))
+
+ elif opcode == "motion_pointtowards":
+ nextBlock = block.getBlockInputValue("towards")
+ return s.target.blocks[nextBlock]
+
+ elif opcode == "motion_pointtowards_menu":
+ if block.getFieldValue("towards") == "_mouse_": # go to [mouse pointer v]
+ newX, newY = pygame.mouse.get_pos()
+ newX = newX - WIDTH // 2
+ newY = HEIGHT // 2 - newY
+ s.pointTowards(newX, newY)
+ if s.target.blocks[block.parent].next:
+ return s.target.blocks[s.target.blocks[block.parent].next]
+ return
+
+ elif block.getFieldValue("towards") == "_random_": # go to [random position v]
+ minX = 0 - WIDTH // 2
+ maxX = WIDTH // 2
+ minY = 0 - HEIGHT // 2
+ maxY = HEIGHT // 2
+ newX, newY = (random.randint(minX, maxX), random.randint(minY, maxY))
+ s.setXy(newX, newY)
+ if s.target.blocks[block.parent].next:
+ return s.target.blocks[s.target.blocks[block.parent].next]
+ return
+
+ # TODO: sprite lookup table
+
+ elif opcode == "motion_movesteps": # move () steps
+ offset = pygame.math.Vector2(float(block.getInputValue("steps", eventContainer=events)), 0)
+ offset.rotate_ip(90 + s.direction)
+ s.setXyDelta(-offset.x, -offset.y)
+
elif opcode == "control_wait": # wait () seconds
block.screenRefresh = True
if not block.waiting:
@@ -221,6 +306,16 @@ def execute(block, s, events=eventContainer.EventContainer()):
print(_("debug-prefix"), _("block-waiting", time=block.timeDelay), file=sys.stderr)
return block
+ elif opcode == "control_wait_until": # wait until <>
+ block.screenRefresh = True
+ truth = block.target.blocks[inputs["CONDITION"][1]].evaluateBlockValue(events)
+ if truth:
+ block.blockRan = True
+ nextBlock = s.target.blocks[block.next]
+ return nextBlock
+ else:
+ return block
+
elif opcode == "event_whenflagclicked": # when green flag clicked
pass
@@ -387,11 +482,52 @@ def execute(block, s, events=eventContainer.EventContainer()):
block.blockRan = True
return nextBlock
block.blockRan = True
- # TODO why does it hang???
else:
block.blockRan = True
return s.target.blocks[block.next]
+ elif opcode == "control_if_else": # if <> then {...}
+ if block.target.blocks[inputs["CONDITION"][1]].evaluateBlockValue(events):
+ # If there are blocks, get them
+ if inputs["SUBSTACK"][1]:
+ # No blocks will be flagged as ran inside a forever loop
+ for b in block.substack:
+ s.target.blocks[b].blockRan = False
+ nextBlock = s.target.blocks[inputs["SUBSTACK"][1]]
+ nb = s.target.blocks[inputs["SUBSTACK"][1]]
+ block.substack.add(nb.blockID)
+ while nb.next and nb.next not in block.substack:
+ nb.blockRan = False
+ nb.waiting = False
+ nb.timeDelay = 0
+ nb.executionTime = 0
+ nb = s.target.blocks[nb.next]
+ block.substack.add(nb.blockID)
+ nb.next = block.next
+ block.blockRan = True
+ return nextBlock
+ block.blockRan = True
+ else:
+ # If there are blocks, get them
+ if inputs["SUBSTACK2"][1]:
+ # No blocks will be flagged as ran inside a forever loop
+ for b in block.substack2:
+ s.target.blocks[b].blockRan = False
+ nextBlock = s.target.blocks[inputs["SUBSTACK2"][1]]
+ nb = s.target.blocks[inputs["SUBSTACK2"][1]]
+ block.substack2.add(nb.blockID)
+ while nb.next and nb.next not in block.substack2:
+ nb.blockRan = False
+ nb.waiting = False
+ nb.timeDelay = 0
+ nb.executionTime = 0
+ nb = s.target.blocks[nb.next]
+ block.substack2.add(nb.blockID)
+ nb.next = block.next
+ block.blockRan = True
+ return nextBlock
+ block.blockRan = True
+
elif opcode == "looks_switchcostumeto": # switch costume to [... v]
nextBlock = block.getBlockInputValue("costume")
return s.target.blocks[nextBlock]
diff --git a/sprite.png b/sprite.png
new file mode 100644
index 0000000..498e7bc
Binary files /dev/null and b/sprite.png differ
diff --git a/target.py b/target.py
index e1cefe6..8faf2c3 100644
--- a/target.py
+++ b/target.py
@@ -27,3 +27,14 @@ def __init__(self):
self.rotationStyle = "all around" # all around, left-right or do not rotate
self.sprite = None
self.name = ""
+
+ # Variable functions
+ def getVariableValue(self, varId):
+ return self.variables[varId][1]
+
+ def getVariableName(self, varId):
+ return self.variables[varId][0]
+
+ def setVariableValue(self, varId, newValue):
+ self.variables[varId][1] = newValue
+ return newValue
diff --git a/targetSprite.py b/targetSprite.py
index 21c5388..d17b39a 100644
--- a/targetSprite.py
+++ b/targetSprite.py
@@ -4,7 +4,7 @@
Targets as pygame sprites
"""
import time
-
+import math
import pygame
import cairosvg
import io
@@ -12,6 +12,8 @@
import config
import sys
import i18n
+import copy
+import random
i18n.set("locale", config.language)
i18n.set("filename_format", "{locale}.{format}")
@@ -20,6 +22,13 @@
sprites = set()
+# Disable the print function
+def decorator(func):
+ def disabledPrint(*args, **kwargs):
+ if not config.disablePrint:
+ func(*args, **kwargs)
+ return disabledPrint
+print = decorator(print)
class TargetSprite(pygame.sprite.Sprite):
def __init__(self, target):
@@ -33,7 +42,7 @@ def __init__(self, target):
sprite = pygame.image.load(io.BytesIO(target.costumes[target.currentCostume].file))
initialWidth = sprite.get_width()
initialHeight = sprite.get_height()
- sprite = pygame.transform.smoothscale(sprite, (sprite.get_width() // target.costumes[target.currentCostume].bitmapResolution, sprite.get_height() // target.costumes[target.currentCostume].bitmapResolution))
+ sprite = pygame.transform.smoothscale(sprite, (initialWidth / 2, initialHeight / 2))
else:
self.isBitmap = False
sprite = scratch.loadSvg(target.costumes[target.currentCostume].file)
@@ -41,17 +50,24 @@ def __init__(self, target):
self.y = target.y
self.direction = target.direction
self.size = target.size
- self.image = sprite
self.sprite = sprite
+ self.image = self.sprite.copy()
self.rect = self.image.get_rect()
+ self.spriteRect = self.sprite.get_rect()
self.isStage = target.isStage
+ self.rotationStyle = target.rotationStyle
self.imageSize = sprite.get_size()
+ self.flipped = False
+ self.layerOrder = target.layerOrder
if self.target.name == "Stage":
self.name = _("stage")
else:
self.name = self.target.name
+
self.setXy(self.x, self.y)
self.setRot(self.direction)
+ self.setSize(self.size)
+ print(self.rect.size)
# Set self position
def setXy(self, x, y):
@@ -78,17 +94,43 @@ def setXy(self, x, y):
y = scratch.HEIGHT / 2 + self.rect.height / 2 - 16
elif y < scratch.HEIGHT / -2 - self.rect.height / 2 + 16:
y = scratch.HEIGHT / -2 - self.rect.height / 2 + 16
- # Set X and Y
+
self.x = x
self.y = y
# print(_("debug-prefix"), _("new-sprite-position", x=x, y=y, name=self.name), file=sys.stderr)
- # Scratch really is weird.
- if self.isBitmap:
- self.rect.x = self.x + scratch.WIDTH // 2 - round(self.target.costumes[self.target.currentCostume].rotationCenterX) + round(self.imageSize[0] / 2)
- self.rect.y = scratch.HEIGHT // 2 - self.y - round(self.target.costumes[self.target.currentCostume].rotationCenterY) + round(self.imageSize[1] / 2)
+ #rect = self.sprite.get_rect(topleft=(self.x - self.target.costumes[self.target.currentCostume].rotationCenterX, self.y - self.target.costumes[self.target.currentCostume].rotationCenterY))
+ if not self.isStage:
+ self.image = pygame.transform.smoothscale(self.sprite, (self.size / 100 * self.imageSize[0], self.size / 100 * self.imageSize[1]))
else:
- self.rect.x = self.x + scratch.WIDTH // 2 - round(self.target.costumes[self.target.currentCostume].rotationCenterX)
- self.rect.y = scratch.HEIGHT // 2 - self.y - round(self.target.costumes[self.target.currentCostume].rotationCenterY)
+ self.image = self.sprite
+
+ offset = self.target.costumes[self.target.currentCostume].offset.elementwise() * self.size / 100 - pygame.math.Vector2(self.sprite.get_width() / 2, self.sprite.get_height() / 2).elementwise() * self.size / 100
+
+ if self.rotationStyle == "all around":
+ self.image = pygame.transform.rotate(self.image, 90 - self.direction)
+ offset.rotate_ip(90 + self.direction)
+ elif self.rotationStyle == "left-right":
+ angle = self.direction % 360
+ print(angle, self.flipped)
+ if angle > 180:
+ self.flipped = True
+ else:
+ self.flipped = False
+ if self.flipped:
+ self.image = pygame.transform.flip(self.image, True, False)
+ offset = self.target.costumes[self.target.currentCostume].offset.elementwise() * self.size / 100 - pygame.math.Vector2(self.sprite.get_width() / 2, self.sprite.get_height() / 2).elementwise() * self.size / 100
+ else:
+ offset = pygame.math.Vector2(-self.target.costumes[self.target.currentCostume].offset.x, self.target.costumes[self.target.currentCostume].offset.y).elementwise() * self.size / 100 - pygame.math.Vector2(-self.sprite.get_width() / 2, self.sprite.get_height() / 2).elementwise() * self.size / 100
+ else:
+ self.image = self.image
+
+ relativePosition = pygame.math.Vector2(self.spriteRect.centerx, self.spriteRect.centery)
+ position = pygame.math.Vector2(self.x - self.sprite.get_width() / 2 + scratch.WIDTH / 2, self.y - self.sprite.get_height() / 2 + scratch.HEIGHT / 2)
+
+ if not self.isStage:
+ self.rect = self.image.get_rect(center=position+relativePosition+offset)
+ else:
+ self.rect = self.image.get_rect(center=(scratch.WIDTH / 2, scratch.HEIGHT / 2))
# Relatively set self position
def setXyDelta(self, dx, dy):
@@ -100,14 +142,40 @@ def setXyDelta(self, dx, dy):
def setRot(self, rot):
self.direction = rot
print(_("debug-prefix"), _("new-sprite-rotation", rot=rot, name=self.name), file=sys.stderr)
- self.image = self.sprite
- sprite = pygame.transform.rotate(self.image, 90 - self.direction)
+
+ self.setXy(self.x, self.y)
# Relatively set self rotation (turn)
def setRotDelta(self, drot):
rot = self.direction + drot
self.setRot(rot)
+ def pointTowards(self, x, y):
+ if self.y == y:
+ if self.y < y:
+ direction = -90
+ else:
+ direction = 90
+ else:
+ if self.y < y:
+ direction = math.degrees(math.atan(((self.x - x) / (self.y - y))))
+ else:
+ direction = math.degrees(math.atan(((self.x - x) / (self.y - y)))) + 180
+
+ self.setRot(direction)
+
+ # Set self rotation
+ def setSize(self, size):
+ self.size = size
+ print(_("debug-prefix"), _("new-sprite-size", size=size, name=self.name), file=sys.stderr)
+
+ self.setXy(self.x, self.y)
+
+ # Relatively set self rotation (turn)
+ def setSizeDelta(self, dsize):
+ size = self.size + dsize
+ self.setSize(size)
+
# Change costume
def setCostume(self, costumeId):
self.target.currentCostume = costumeId % len(self.target.costumes)
diff --git a/test-gtk.py b/test-gtk.py
new file mode 100644
index 0000000..d9a1ba5
--- /dev/null
+++ b/test-gtk.py
@@ -0,0 +1,108 @@
+import pygame
+import os
+from gi.repository import GObject
+from gi.repository import Gtk
+from gi.repository import GdkX11
+
+class GameWindow(Gtk.Window):
+ def __init__(self):
+ Gtk.Window.__init__(self)
+ vbox = Gtk.VBox(False, 2)
+ vbox.show()
+ self.add(vbox)
+
+ #create the menu
+ file_menu = Gtk.Menu()
+
+ accel_group = Gtk.AccelGroup()
+ self.add_accel_group(accel_group)
+
+ dialog_item = Gtk.MenuItem()
+ dialog_item.set_label("Dialog")
+ dialog_item.show()
+ dialog_item.connect("activate",self.show_dialog)
+ file_menu.append(dialog_item)
+ dialog_item.show()
+
+ quit_item = Gtk.MenuItem()
+ quit_item.set_label("Quit")
+ quit_item.show()
+ quit_item.connect("activate",self.quit)
+ file_menu.append(quit_item)
+ quit_item.show()
+
+ menu_bar = Gtk.MenuBar()
+ vbox.pack_start(menu_bar, False, False, 0)
+ menu_bar.show()
+
+ file_item = Gtk.MenuItem()
+ file_item.set_label("_File")
+ file_item.set_use_underline(True)
+ file_item.show()
+
+ file_item.set_submenu(file_menu)
+ menu_bar.append(file_item)
+
+ #create the drawing area
+ da = Gtk.DrawingArea()
+ da.set_size_request(300,300)
+ da.show()
+ vbox.pack_end(da, False, False, 0)
+ da.connect("realize",self._realized)
+
+ #set up the pygame objects
+ self.image = pygame.image.load("sprite.png")
+ self.background = pygame.image.load("background.png")
+ self.x = 150
+ self.y = 150
+
+ #collect key press events
+ self.connect("key-press-event", self.key_pressed)
+
+ def key_pressed(self, widget, event, data=None):
+ if event.keyval == 65361:
+ self.x -= 5
+ elif event.keyval == 65362:
+ self.y -= 5
+ elif event.keyval == 65363:
+ self.x += 5
+ elif event.keyval == 65364:
+ self.y += 5
+
+ def show_dialog(self, widget, data=None):
+ #prompts.info("A Pygtk Dialog", "See it works easy")
+ title = "PyGame embedded in Gtk Example"
+ dialog = Gtk.Dialog(title, None, Gtk.DialogFlags.MODAL,(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK))
+ content_area = dialog.get_content_area()
+ label = Gtk.Label("See, it still works")
+ label.show()
+ content_area.add(label)
+ response = dialog.run()
+ dialog.destroy()
+
+ def quit(self, widget, data=None):
+ self.destroy()
+
+ def draw(self):
+ self.screen.blit(self.background,[0,0])
+
+ rect = self.image.get_rect()
+ rect.x = self.x
+ rect.y = self.y
+ self.screen.blit(self.image, rect)
+ pygame.display.flip()
+
+ return True
+
+ def _realized(self, widget, data=None):
+ os.putenv('SDL_WINDOWID', str(widget.get_window().get_xid()))
+ pygame.init()
+ pygame.display.set_mode((300, 300), 0, 0)
+ self.screen = pygame.display.get_surface()
+ GObject.timeout_add(200, self.draw)
+
+if __name__ == "__main__":
+ window = GameWindow()
+ window.connect("destroy",Gtk.main_quit)
+ window.show()
+ Gtk.main()
diff --git a/tests.py b/tests.py
new file mode 100644
index 0000000..d6c00f0
--- /dev/null
+++ b/tests.py
@@ -0,0 +1,63 @@
+import pygame as pg
+from pygame.math import Vector2
+
+
+class Entity(pg.sprite.Sprite):
+
+ def __init__(self, pos):
+ super().__init__()
+ self.image = pg.Surface((122, 70), pg.SRCALPHA)
+ pg.draw.polygon(self.image, pg.Color('dodgerblue1'),
+ ((1, 0), (120, 35), (1, 70)))
+ # A reference to the original image to preserve the quality.
+ self.orig_image = self.image
+ self.rect = self.image.get_rect(center=pos)
+ self.pos = Vector2(pos) # The original center position/pivot point.
+ self.offset = Vector2(50, 0) # We shift the sprite 50 px to the right.
+ self.angle = 0
+
+ def update(self):
+ self.angle += 2
+ self.rotate()
+
+ def rotate(self):
+ """Rotate the image of the sprite around a pivot point."""
+ # Rotate the image.
+ self.image = pg.transform.rotozoom(self.orig_image, -self.angle, 1)
+ # Rotate the offset vector.
+ offset_rotated = self.offset.rotate(self.angle)
+ # Create a new rect with the center of the sprite + the offset.
+ self.rect = self.image.get_rect(center=self.pos+offset_rotated)
+
+
+def main():
+ screen = pg.display.set_mode((640, 480))
+ clock = pg.time.Clock()
+ entity = Entity((320, 240))
+ all_sprites = pg.sprite.Group(entity)
+
+ while True:
+ for event in pg.event.get():
+ if event.type == pg.QUIT:
+ return
+
+ keys = pg.key.get_pressed()
+ if keys[pg.K_d]:
+ entity.pos.x += 5
+ elif keys[pg.K_a]:
+ entity.pos.x -= 5
+
+ all_sprites.update()
+ screen.fill((30, 30, 30))
+ all_sprites.draw(screen)
+ pg.draw.circle(screen, (255, 128, 0), [int(i) for i in entity.pos], 3)
+ pg.draw.rect(screen, (255, 128, 0), entity.rect, 2)
+ pg.draw.line(screen, (100, 200, 255), (0, 240), (640, 240), 1)
+ pg.display.flip()
+ clock.tick(30)
+
+
+if __name__ == '__main__':
+ pg.init()
+ main()
+ pg.quit()
diff --git a/variable.py b/variable.py
deleted file mode 100644
index 3ada38f..0000000
--- a/variable.py
+++ /dev/null
@@ -1,14 +0,0 @@
-"""
-Variable class
-
-======= CLASS INFO =======
-The various files with classes are used by s2p_unpacker and the correct data is
-set. Those are then used to build the project in main.py.
-"""
-
-
-class Variable:
- def __init__(self):
- self.id = None
- self.name = ""
- self.value = 0