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 @@ + + + + + + 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 + user-bookmarks + 6 + + + False + True + 0 + + + + + True + False + Featured projects + + + + + + False + True + 1 + + + + + True + False + by us, TurboWarp, etc. + + + + + + False + True + 2 + + + + + + + False + True + 2 + + + + + 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 + + + + + + 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 + + + + + True + True + True + + + 1 + 0 + + + + + True + False + Allow debug messages in terminal + 0 + + + + + + 0 + 2 + + + + + True + True + True + + + 1 + 2 + + + + + True + False + Allow Scratch Addons logs in terminal + 0 + + + + + + 0 + 3 + + + + + True + True + True + + + 1 + 3 + + + + + True + False + Allow terminal output + 0 + + + + + + 0 + 1 + + + + + True + True + True + + + 1 + 1 + + + + + True + True + True + + + 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 + + + + + True + True + True + + + 1 + 0 + + + + + True + False + Allow debug messages in terminal + 0 + + + + + + 0 + 2 + + + + + True + True + True + + + 1 + 2 + + + + + True + False + Allow Scratch Addons logs in terminal + 0 + + + + + + 0 + 3 + + + + + True + True + True + + + 1 + 3 + + + + + True + False + Allow terminal output + 0 + + + + + + 0 + 1 + + + + + True + True + True + + + 1 + 1 + + + + + True + True + True + + + 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