diff --git a/.gitignore b/.gitignore index 39155b9..1e56d1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ # Project exclude paths -/venv/ \ No newline at end of file +/venv/ +/.idea/ \ No newline at end of file diff --git a/TODOs/bugs.md b/TODOs/bugs.md new file mode 100644 index 0000000..9912ae1 --- /dev/null +++ b/TODOs/bugs.md @@ -0,0 +1,15 @@ +## ✅ Finished + +- [x] `block.script` is not filled correctly +- [x] Keypress event block is marked as not ran too early +- [x] Keypress event block should be marked as not ran only when the last block in the script is done +- [x] Use `block.top` to detect the event which needs to be marked +- [x] Wait blocks below key events don't work +- [x] Key repeat doesn't apply +- [x] Keys which have ASCII codes don't work + +## ❎ In progress + +- [ ] Why do event blocks go in `toExecute`? +- [ ] Cannot use multiple event handlers for one key +- [ ] Sprite fencing is wrong \ No newline at end of file diff --git a/__pycache__/block.cpython-310.pyc b/__pycache__/block.cpython-310.pyc new file mode 100644 index 0000000..a6a0104 Binary files /dev/null and b/__pycache__/block.cpython-310.pyc differ diff --git a/__pycache__/block.cpython-38.pyc b/__pycache__/block.cpython-38.pyc new file mode 100644 index 0000000..6f9f77c Binary files /dev/null and b/__pycache__/block.cpython-38.pyc differ diff --git a/__pycache__/block.cpython-39.pyc b/__pycache__/block.cpython-39.pyc new file mode 100644 index 0000000..bce5fe2 Binary files /dev/null and b/__pycache__/block.cpython-39.pyc differ diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000..3e6aaa3 Binary files /dev/null and b/__pycache__/config.cpython-310.pyc differ diff --git a/__pycache__/config.cpython-38.pyc b/__pycache__/config.cpython-38.pyc new file mode 100644 index 0000000..04aeef4 Binary files /dev/null and b/__pycache__/config.cpython-38.pyc differ diff --git a/__pycache__/configMeta.cpython-310.pyc b/__pycache__/configMeta.cpython-310.pyc new file mode 100644 index 0000000..f71af33 Binary files /dev/null and b/__pycache__/configMeta.cpython-310.pyc differ diff --git a/__pycache__/costume.cpython-310.pyc b/__pycache__/costume.cpython-310.pyc new file mode 100644 index 0000000..4645a01 Binary files /dev/null and b/__pycache__/costume.cpython-310.pyc differ diff --git a/__pycache__/costume.cpython-38.pyc b/__pycache__/costume.cpython-38.pyc new file mode 100644 index 0000000..39d633d Binary files /dev/null and b/__pycache__/costume.cpython-38.pyc differ diff --git a/__pycache__/costume.cpython-39.pyc b/__pycache__/costume.cpython-39.pyc new file mode 100644 index 0000000..1d7102d Binary files /dev/null and b/__pycache__/costume.cpython-39.pyc differ diff --git a/__pycache__/monitor.cpython-310.pyc b/__pycache__/monitor.cpython-310.pyc new file mode 100644 index 0000000..bec026f Binary files /dev/null and b/__pycache__/monitor.cpython-310.pyc differ diff --git a/__pycache__/monitor.cpython-38.pyc b/__pycache__/monitor.cpython-38.pyc new file mode 100644 index 0000000..eccbf76 Binary files /dev/null and b/__pycache__/monitor.cpython-38.pyc differ diff --git a/__pycache__/monitor.cpython-39.pyc b/__pycache__/monitor.cpython-39.pyc new file mode 100644 index 0000000..3ef8a14 Binary files /dev/null and b/__pycache__/monitor.cpython-39.pyc differ diff --git a/__pycache__/sb3Unpacker.cpython-310.pyc b/__pycache__/sb3Unpacker.cpython-310.pyc new file mode 100644 index 0000000..79bd126 Binary files /dev/null and b/__pycache__/sb3Unpacker.cpython-310.pyc differ diff --git a/__pycache__/sb3Unpacker.cpython-38.pyc b/__pycache__/sb3Unpacker.cpython-38.pyc new file mode 100644 index 0000000..cabbdbe Binary files /dev/null and b/__pycache__/sb3Unpacker.cpython-38.pyc differ diff --git a/__pycache__/scratch.cpython-310.pyc b/__pycache__/scratch.cpython-310.pyc new file mode 100644 index 0000000..2a2213c Binary files /dev/null and b/__pycache__/scratch.cpython-310.pyc differ diff --git a/__pycache__/scratch.cpython-38.pyc b/__pycache__/scratch.cpython-38.pyc new file mode 100644 index 0000000..fe8a02c Binary files /dev/null and b/__pycache__/scratch.cpython-38.pyc differ diff --git a/__pycache__/scratch.cpython-39.pyc b/__pycache__/scratch.cpython-39.pyc new file mode 100644 index 0000000..7273ed7 Binary files /dev/null and b/__pycache__/scratch.cpython-39.pyc differ diff --git a/__pycache__/sound.cpython-310.pyc b/__pycache__/sound.cpython-310.pyc new file mode 100644 index 0000000..05c70d6 Binary files /dev/null and b/__pycache__/sound.cpython-310.pyc differ diff --git a/__pycache__/sound.cpython-38.pyc b/__pycache__/sound.cpython-38.pyc new file mode 100644 index 0000000..3fe3413 Binary files /dev/null and b/__pycache__/sound.cpython-38.pyc differ diff --git a/__pycache__/sound.cpython-39.pyc b/__pycache__/sound.cpython-39.pyc new file mode 100644 index 0000000..92c977c Binary files /dev/null and b/__pycache__/sound.cpython-39.pyc differ diff --git a/__pycache__/target.cpython-310.pyc b/__pycache__/target.cpython-310.pyc new file mode 100644 index 0000000..97d419b Binary files /dev/null and b/__pycache__/target.cpython-310.pyc differ diff --git a/__pycache__/target.cpython-38.pyc b/__pycache__/target.cpython-38.pyc new file mode 100644 index 0000000..44e4fe7 Binary files /dev/null and b/__pycache__/target.cpython-38.pyc differ diff --git a/__pycache__/target.cpython-39.pyc b/__pycache__/target.cpython-39.pyc new file mode 100644 index 0000000..0c3d2c5 Binary files /dev/null and b/__pycache__/target.cpython-39.pyc differ diff --git a/__pycache__/targetSprite.cpython-310.pyc b/__pycache__/targetSprite.cpython-310.pyc new file mode 100644 index 0000000..a0415b7 Binary files /dev/null and b/__pycache__/targetSprite.cpython-310.pyc differ diff --git a/__pycache__/targetSprite.cpython-38.pyc b/__pycache__/targetSprite.cpython-38.pyc new file mode 100644 index 0000000..d0360d3 Binary files /dev/null and b/__pycache__/targetSprite.cpython-38.pyc differ diff --git a/__pycache__/targetSprite.cpython-39.pyc b/__pycache__/targetSprite.cpython-39.pyc new file mode 100644 index 0000000..db1666f Binary files /dev/null and b/__pycache__/targetSprite.cpython-39.pyc differ diff --git a/__pycache__/variable.cpython-310.pyc b/__pycache__/variable.cpython-310.pyc new file mode 100644 index 0000000..d2069f0 Binary files /dev/null and b/__pycache__/variable.cpython-310.pyc differ diff --git a/__pycache__/variable.cpython-38.pyc b/__pycache__/variable.cpython-38.pyc new file mode 100644 index 0000000..bbb9491 Binary files /dev/null and b/__pycache__/variable.cpython-38.pyc differ diff --git a/__pycache__/variable.cpython-39.pyc b/__pycache__/variable.cpython-39.pyc new file mode 100644 index 0000000..ea24252 Binary files /dev/null and b/__pycache__/variable.cpython-39.pyc differ diff --git a/assets/3dc0568c0414f091db6da7b90bcc2a64.svg b/assets/3dc0568c0414f091db6da7b90bcc2a64.svg deleted file mode 100644 index a585446..0000000 --- a/assets/3dc0568c0414f091db6da7b90bcc2a64.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/83c36d806dc92327b9e7049a565c6bff.wav b/assets/83c36d806dc92327b9e7049a565c6bff.wav new file mode 100644 index 0000000..45742d5 Binary files /dev/null and b/assets/83c36d806dc92327b9e7049a565c6bff.wav differ diff --git a/assets/b7853f557e4426412e64bb3da6531a99.svg b/assets/b7853f557e4426412e64bb3da6531a99.svg new file mode 100644 index 0000000..a537afb --- /dev/null +++ b/assets/b7853f557e4426412e64bb3da6531a99.svg @@ -0,0 +1,42 @@ + + + + costume1.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg b/assets/e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg new file mode 100644 index 0000000..d49c682 --- /dev/null +++ b/assets/e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg @@ -0,0 +1,42 @@ + + + + costume2.1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/project.json b/assets/project.json index 5d9c299..6b86b94 100644 --- a/assets/project.json +++ b/assets/project.json @@ -1 +1 @@ -{"targets":[{"isStage":true,"name":"Stage","variables":{},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"assetId":"cd21514d0531fdffb22204e0ec5ed84a","name":"backdrop1","md5ext":"cd21514d0531fdffb22204e0ec5ed84a.svg","dataFormat":"svg","rotationCenterX":240,"rotationCenterY":180}],"sounds":[],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"on","textToSpeechLanguage":"ro"},{"isStage":false,"name":"Balloon1","variables":{},"lists":{},"broadcasts":{},"blocks":{"]p0WM]/6Ve;iPTfO~ov:":{"opcode":"event_whenkeypressed","next":"czG|E7P$QI*{rA2iUC-w","parent":null,"inputs":{},"fields":{"KEY_OPTION":["space",null]},"shadow":false,"topLevel":true,"x":48,"y":208},"czG|E7P$QI*{rA2iUC-w":{"opcode":"control_repeat","next":null,"parent":"]p0WM]/6Ve;iPTfO~ov:","inputs":{"TIMES":[1,[6,"10"]],"SUBSTACK":[2,"fMb(Tc_!lhzbgDNihKSq"]},"fields":{},"shadow":false,"topLevel":false},"fMb(Tc_!lhzbgDNihKSq":{"opcode":"motion_changeyby","next":null,"parent":"czG|E7P$QI*{rA2iUC-w","inputs":{"DY":[1,[4,"5"]]},"fields":{},"shadow":false,"topLevel":false},"XDeo_FHmn,lz+e0E9lVL":{"opcode":"event_whenflagclicked","next":"mgHB88^RU,vL)z1QeKRr","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":48,"y":64},"mgHB88^RU,vL)z1QeKRr":{"opcode":"motion_gotoxy","next":null,"parent":"XDeo_FHmn,lz+e0E9lVL","inputs":{"X":[1,[4,"0"]],"Y":[1,[4,"0"]]},"fields":{},"shadow":false,"topLevel":false},"+m(RP9TzxLn/R*+qy)22":{"opcode":"operator_multiply","next":null,"parent":null,"inputs":{"NUM1":[1,[4,"5"]],"NUM2":[1,[4,"2"]]},"fields":{},"shadow":false,"topLevel":true,"x":-312,"y":648}},"comments":{},"currentCostume":0,"costumes":[{"assetId":"3dc0568c0414f091db6da7b90bcc2a64","name":"balloon1-a","bitmapResolution":1,"md5ext":"3dc0568c0414f091db6da7b90bcc2a64.svg","dataFormat":"svg","rotationCenterX":37.8470329351303,"rotationCenterY":43.83676071798408}],"sounds":[{"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","name":"Pop","dataFormat":"wav","format":"","rate":44100,"sampleCount":1032,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":0,"y":150,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"}],"monitors":[],"extensions":[],"meta":{"semver":"3.0.0","vm":"0.2.0-prerelease.20220308092939","agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"}} \ No newline at end of file +{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["variabila mea",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":0,"costumes":[{"assetId":"cd21514d0531fdffb22204e0ec5ed84a","name":"decor1","md5ext":"cd21514d0531fdffb22204e0ec5ed84a.svg","dataFormat":"svg","rotationCenterX":240,"rotationCenterY":180}],"sounds":[{"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","name":"pop","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":"Personaj1","variables":{},"lists":{},"broadcasts":{},"blocks":{"Fu(KfOuh$cmgs0~Auqf9":{"opcode":"event_whenflagclicked","next":"%8(CtQom@,Qrk`;X+c1P","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":233,"y":241},"%YFU}CY;=s+fot2avH}*":{"opcode":"control_repeat","next":"bsblJ:MFwOOpBeuK5bD{","parent":"/%kBdd{_YGZu*[n3aMhW","inputs":{"TIMES":[1,[6,"10"]],"SUBSTACK":[2,"}IRk2OYvn4PF,G6N;b*O"]},"fields":{},"shadow":false,"topLevel":false},"%8(CtQom@,Qrk`;X+c1P":{"opcode":"motion_gotoxy","next":null,"parent":"Fu(KfOuh$cmgs0~Auqf9","inputs":{"X":[1,[4,"0"]],"Y":[1,[4,"0"]]},"fields":{},"shadow":false,"topLevel":false},"8K@.:@oW0!FH}1Q1nVCE":{"opcode":"control_repeat","next":null,"parent":"bsblJ:MFwOOpBeuK5bD{","inputs":{"TIMES":[1,[6,"10"]],"SUBSTACK":[2,"H=wW]C.G5jmE3UatxMgw"]},"fields":{},"shadow":false,"topLevel":false},"bsblJ:MFwOOpBeuK5bD{":{"opcode":"control_wait","next":"8K@.:@oW0!FH}1Q1nVCE","parent":"%YFU}CY;=s+fot2avH}*","inputs":{"DURATION":[1,[5,"0.5"]]},"fields":{},"shadow":false,"topLevel":false},"}IRk2OYvn4PF,G6N;b*O":{"opcode":"motion_changeyby","next":null,"parent":"%YFU}CY;=s+fot2avH}*","inputs":{"DY":[1,[4,"10"]]},"fields":{},"shadow":false,"topLevel":false},"H=wW]C.G5jmE3UatxMgw":{"opcode":"motion_changeyby","next":null,"parent":"8K@.:@oW0!FH}1Q1nVCE","inputs":{"DY":[1,[4,"-10"]]},"fields":{},"shadow":false,"topLevel":false},"/%kBdd{_YGZu*[n3aMhW":{"opcode":"event_whenkeypressed","next":"%YFU}CY;=s+fot2avH}*","parent":null,"inputs":{},"fields":{"KEY_OPTION":["space",null]},"shadow":false,"topLevel":true,"x":235,"y":401}},"comments":{},"currentCostume":0,"costumes":[{"assetId":"b7853f557e4426412e64bb3da6531a99","name":"costum1","bitmapResolution":1,"md5ext":"b7853f557e4426412e64bb3da6531a99.svg","dataFormat":"svg","rotationCenterX":48,"rotationCenterY":50},{"assetId":"e6ddc55a6ddd9cc9d84fe0b4c21e016f","name":"costum2","bitmapResolution":1,"md5ext":"e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg","dataFormat":"svg","rotationCenterX":46,"rotationCenterY":53}],"sounds":[{"assetId":"83c36d806dc92327b9e7049a565c6bff","name":"Miau","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":"0.2.0-prerelease.20201016122132","agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Scratch/3.18.1 Chrome/80.0.3987.165 Electron/8.2.5 Safari/537.36"}} \ No newline at end of file diff --git a/banner.svg b/banner.svg deleted file mode 100644 index e73ed03..0000000 --- a/banner.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/block.py b/block.py index 0a0d3bd..d61d7fa 100644 --- a/block.py +++ b/block.py @@ -10,6 +10,7 @@ import i18n import config import math +import pygame i18n.set("locale", config.language) i18n.set("filename_format", "{locale}.{format}") @@ -23,6 +24,7 @@ def __init__(self): self.opcode = "" # block type self.next = None # next block self.parent = None # previous block + self.top = None # script start self.inputs = {} # string and number inputs self.fields = {} # dropdown menus self.shadow = False # if the block is a reporter or boolean block @@ -71,6 +73,16 @@ def evaluateBlockValue(self): elif self.opcode == "motion_xposition": # y position self.value = self.target.y 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 + elif self.opcode == "sensing_mousey": # mouse y + newX, newY = pygame.mouse.get_pos() + newY = newY - config.screenWidth // 2 + self.value = newY + return newY # Returns block input value def getBlockInputValue(self, inputId): diff --git a/config.py b/config.py index 6b81377..fdb2383 100644 --- a/config.py +++ b/config.py @@ -18,7 +18,7 @@ language: str = "en" # Project load method -# Sets the behavior for loading projects. +# Sets the behaviour for loading projects. # Possible values: # manual: use the project file name defined in the "projectFileName" variable. # interactive: use input(). @@ -29,7 +29,7 @@ # Project file name # If the "manual" mode is chosen, set the Scratch project file to load. -projectFileName: str = "projects/balloon-simple.sb3" +projectFileName: str = "projects/KeyTest1alt.sb3" # Extract on project run # Set whether to extract the project assets on run. @@ -43,6 +43,9 @@ # Set whether debug messages (messages to stderr) should be allowed. enableDebugMessages: bool = True +# pygame welcome message +pygameWelcomeMessage: bool = True + # 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 @@ -54,6 +57,10 @@ # Vanilla is 30. maxFPS: int = 30 +# Key delay +# Set the delay before key events start repeating. (in milliseconds) +keyDelay: int = 250 + # Screen width/height # Stage size. You can change that, but most projects won't work with it. # A scaling mode will be added later. diff --git a/costume.py b/costume.py index 0282656..608f8b8 100644 --- a/costume.py +++ b/costume.py @@ -15,3 +15,4 @@ def __init__(self): self.rotationCenterY = 0 self.bitmapResolution = 1 self.file = None + self.name = "" # display name diff --git a/lang/en.yml b/lang/en.yml index eb94c06..51a1136 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -46,4 +46,7 @@ en: unknown-opcode: "Unknown opcode:" new-sprite-position: "New position (%{x}, %{y}) set for sprite %{name}" stage: "Stage" - zero-division-error: "Project was trying to divide by 0" \ No newline at end of file + 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." + no-any-key: "Sorry, there is no support for the \"any\" key option yet. We're working on other things and it will be added when the code for the main key block is done." \ No newline at end of file diff --git a/lang/ro.yml b/lang/ro.yml index 46cbd22..f535adb 100644 --- a/lang/ro.yml +++ b/lang/ro.yml @@ -46,4 +46,7 @@ ro: unknown-opcode: "Tip de bloc necunoscut:" new-sprite-position: "Poziție nouă (%{x}, %{y}) setată pentru personajul %{name}" stage: "Scena" - zero-division-error: "Proiectul a încercat să împartă la 0" \ No newline at end of file + zero-division-error: "Proiectul a încercat să împartă la 0" + costumes-count: "%{sprite} are %{costumes} costume" + stage-not-found: "Nu există scena! Proiect invalid." + no-any-key: "Scuze, nu există suport pentru opțiunea de taste „oricare” încă. Lucrăm la altceva și va fi adăugat când blocul principal e gata." \ No newline at end of file diff --git a/main.py b/main.py index 91dd98b..3caf5fb 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ along with this program. If not, see . """ -__version__ = "M19 (development version)" +__version__ = "M20 (development version)" __author__ = "Secret-chest" import tkinter.simpledialog @@ -28,6 +28,7 @@ import sys import i18n import config +import time if system() == "Linux": OS = "linux" @@ -58,7 +59,8 @@ print(_("unrecognized-os", platform=platform(), url="https://github.com/Secret-chest/scratch2python/issues"), file=sys.stderr) -sys.stdout = open(os.devnull, "w") +if not config.pygameWelcomeMessage: + sys.stdout = open(os.devnull, "w") import sb3Unpacker from sb3Unpacker import * import shutil @@ -111,8 +113,11 @@ t.sprite = sprite allSprites.add(sprite) sprite.setXy(t.x, t.y) + sprite.setCostume(sprite.target.currentCostume) + +# Start pygame and load fonts +pygame.mixer.pre_init(22050, -16, 1, 12193) -# Start pygame, load fonts and print a debug message pygame.init() font = pygame.font.SysFont(pygame.font.get_default_font(), 16) fontXl = pygame.font.SysFont(pygame.font.get_default_font(), 36) @@ -217,17 +222,23 @@ def buttonbox(self): elif block.opcode.startswith("event_"): # add "when I start as a clone" code later eventHandlers.append(block) +# Prepare keyboard +pygame.key.set_repeat(config.keyDelay, 1000 // config.projectMaxFPS) +keyEvents = set() # Mainloop while projectRunning: # Process Pygame events for event in pygame.event.get(): - # Window quit (ALT-F4 / X button) + # Window quit (ALT-F4 / X button / etc.) if event.type == pygame.QUIT: print(playerClosedText) projectRunning = False # Debug and utility functions + keyEvents = set() + if event.type == pygame.KEYDOWN: + keyEvents.add(event.key) keysRaw = pygame.key.get_pressed() keys = set(k for k in scratch.KEY_MAPPING.values() if keysRaw[k]) @@ -250,6 +261,7 @@ def buttonbox(self): if newFPS is not None: print(fpsMessage, newFPS) config.projectMaxFPS = newFPS + pygame.key.set_repeat(1000, 1000 // config.projectMaxFPS) if pygame.K_F8 in keys: # Set new screen resolution try: # Open special dialog @@ -285,14 +297,20 @@ def buttonbox(self): # print("Running block", block.blockID, "of type", block.opcode) if not isPaused: for e in eventHandlers: - if e.opcode == "event_whenkeypressed" and keys: - nextBlock = scratch.execute(e, e.target.sprite, keys) - if nextBlock: - if isinstance(nextBlock, list): - toExecute.extend(nextBlock) - else: - toExecute.append(nextBlock) + if e.opcode == "event_whenkeypressed" and keys and not e.blockRan: e.blockRan = True + nextBlock = scratch.execute(e, e.target.sprite, keys, keyEvents) + if nextBlock and isinstance(nextBlock, list): + toExecute.extend(nextBlock) + elif nextBlock: + toExecute.append(nextBlock) + + if e.opcode == "event_whenkeypressed": + # print(s.target.blocks, e.script) + if not e.script or all(s.target.blocks[b].blockRan for b in e.script): + e.blockRan = False + for b in e.script: + s.target.blocks[b].blockRan = False while toExecute and not doScreenRefresh: # Run blocks nextBlocks = [] @@ -301,14 +319,23 @@ def buttonbox(self): block.executionTime += clock.get_time() if block.executionTime >= block.timeDelay: block.waiting = False - if block.opcode.startswith("event"): - block.blockRan = False - else: - block.blockRan = True + block.blockRan = True nextBlocks.append(block.target.blocks[block.next]) block.executionTime, block.timeDelay = 0, 0 - if not block.blockRan: - nextBlock = scratch.execute(block, block.target.sprite, keys) + if not block.blockRan and not block.opcode.startswith("event"): + nextBlock = scratch.execute(block, block.target.sprite, keys, keyEvents) + if not block.next \ + and block.top \ + and block.top.opcode.startswith("event") \ + and block.top.opcode != "event_whenflagclicked": + print(block.top.blockRan, block.top.blockID) + waitFinished = False + waitFinishedFor = set() + for b in block.top.script: + if not s.target.blocks[b].waiting and not s.target.blocks[b].blockRan: + waitFinishedFor.add(s.target.blocks[b]) + if len(waitFinishedFor) == len(block.top.script): + block.top.blockRan = False if nextBlock: if isinstance(nextBlock, list): nextBlocks.extend(nextBlock) diff --git a/projects/AKey.sb3 b/projects/AKey.sb3 new file mode 100644 index 0000000..fdfbdc4 Binary files /dev/null and b/projects/AKey.sb3 differ diff --git a/projects/Appel.sb3 b/projects/Appel.sb3 deleted file mode 100644 index 204fa83..0000000 Binary files a/projects/Appel.sb3 and /dev/null differ diff --git a/projects/BackgroundFix.sb3 b/projects/BackgroundFix.sb3 new file mode 100644 index 0000000..d3f295c Binary files /dev/null and b/projects/BackgroundFix.sb3 differ diff --git a/projects/BackgroundImages.sb3 b/projects/BackgroundImages.sb3 new file mode 100644 index 0000000..5c6c5cf Binary files /dev/null and b/projects/BackgroundImages.sb3 differ diff --git a/projects/Garden-rock.sb3 b/projects/Bitmap.sb3 similarity index 100% rename from projects/Garden-rock.sb3 rename to projects/Bitmap.sb3 diff --git a/projects/CoolestProjectsDemo1.sb3 b/projects/CoolestProjectsDemo1.sb3 new file mode 100644 index 0000000..7f51f61 Binary files /dev/null and b/projects/CoolestProjectsDemo1.sb3 differ diff --git a/projects/Costumes1.sb3 b/projects/Costumes1.sb3 new file mode 100644 index 0000000..09d2489 Binary files /dev/null and b/projects/Costumes1.sb3 differ diff --git a/projects/Costumes2.sb3 b/projects/Costumes2.sb3 new file mode 100644 index 0000000..f352969 Binary files /dev/null and b/projects/Costumes2.sb3 differ diff --git a/projects/Costumes3.sb3 b/projects/Costumes3.sb3 new file mode 100644 index 0000000..46d6147 Binary files /dev/null and b/projects/Costumes3.sb3 differ diff --git a/projects/Costumes4.sb3 b/projects/Costumes4.sb3 new file mode 100644 index 0000000..7638bc6 Binary files /dev/null and b/projects/Costumes4.sb3 differ diff --git a/projects/F10Key.sb3 b/projects/F10Key.sb3 new file mode 100644 index 0000000..9fc4877 Binary files /dev/null and b/projects/F10Key.sb3 differ diff --git a/projects/forever.sb3 b/projects/Forever.sb3 similarity index 100% rename from projects/forever.sb3 rename to projects/Forever.sb3 diff --git a/projects/Full 16 Frame Scratch Cat Walk Cycle basic.sb3 b/projects/Full 16 Frame Scratch Cat Walk Cycle basic.sb3 new file mode 100644 index 0000000..2bcd508 Binary files /dev/null and b/projects/Full 16 Frame Scratch Cat Walk Cycle basic.sb3 differ diff --git a/projects/mouse-follow.sb3 b/projects/GoToMouse.sb3 similarity index 100% rename from projects/mouse-follow.sb3 rename to projects/GoToMouse.sb3 diff --git a/projects/gotomouse-up.sb3 b/projects/GoToMouseAlt.sb3 similarity index 100% rename from projects/gotomouse-up.sb3 rename to projects/GoToMouseAlt.sb3 diff --git a/projects/gotoxy.sb3 b/projects/GoToXY.sb3 similarity index 100% rename from projects/gotoxy.sb3 rename to projects/GoToXY.sb3 diff --git a/projects/gotoxy2.sb3 b/projects/GoToXYAlt.sb3 similarity index 100% rename from projects/gotoxy2.sb3 rename to projects/GoToXYAlt.sb3 diff --git a/projects/ifonedgebounce.sb3 b/projects/IfOnEdgeBounce.sb3 similarity index 100% rename from projects/ifonedgebounce.sb3 rename to projects/IfOnEdgeBounce.sb3 diff --git a/projects/KeyJump.sb3 b/projects/KeyJump.sb3 new file mode 100644 index 0000000..8422240 Binary files /dev/null and b/projects/KeyJump.sb3 differ diff --git a/projects/KeyLogger.sb3 b/projects/KeyLogger.sb3 new file mode 100644 index 0000000..0ff065e Binary files /dev/null and b/projects/KeyLogger.sb3 differ diff --git a/projects/KeyTest1.sb3 b/projects/KeyTest1.sb3 new file mode 100644 index 0000000..55ac104 Binary files /dev/null and b/projects/KeyTest1.sb3 differ diff --git a/projects/KeyTest1alt.sb3 b/projects/KeyTest1alt.sb3 new file mode 100644 index 0000000..1140180 Binary files /dev/null and b/projects/KeyTest1alt.sb3 differ diff --git a/projects/KeyTest1alt2.sb3 b/projects/KeyTest1alt2.sb3 new file mode 100644 index 0000000..4184a29 Binary files /dev/null and b/projects/KeyTest1alt2.sb3 differ diff --git a/projects/KeyTest1alt3.sb3 b/projects/KeyTest1alt3.sb3 new file mode 100644 index 0000000..13c9ee6 Binary files /dev/null and b/projects/KeyTest1alt3.sb3 differ diff --git a/projects/KeyTest1alt3basic.sb3 b/projects/KeyTest1alt3basic.sb3 new file mode 100644 index 0000000..7fa57d2 Binary files /dev/null and b/projects/KeyTest1alt3basic.sb3 differ diff --git a/projects/KeyTest1alt3basic2.sb3 b/projects/KeyTest1alt3basic2.sb3 new file mode 100644 index 0000000..e4002a5 Binary files /dev/null and b/projects/KeyTest1alt3basic2.sb3 differ diff --git a/projects/KeyTest1alt3basic3.sb3 b/projects/KeyTest1alt3basic3.sb3 new file mode 100644 index 0000000..2bfdfec Binary files /dev/null and b/projects/KeyTest1alt3basic3.sb3 differ diff --git a/projects/KeyTest1alt3forever.sb3 b/projects/KeyTest1alt3forever.sb3 new file mode 100644 index 0000000..60a3f56 Binary files /dev/null and b/projects/KeyTest1alt3forever.sb3 differ diff --git a/projects/KeyTest1alt3long.sb3 b/projects/KeyTest1alt3long.sb3 new file mode 100644 index 0000000..c1a8699 Binary files /dev/null and b/projects/KeyTest1alt3long.sb3 differ diff --git a/projects/KeyTest1alt3short.sb3 b/projects/KeyTest1alt3short.sb3 new file mode 100644 index 0000000..b883f3a Binary files /dev/null and b/projects/KeyTest1alt3short.sb3 differ diff --git a/projects/KeyTest1alt4.sb3 b/projects/KeyTest1alt4.sb3 new file mode 100644 index 0000000..24a80e6 Binary files /dev/null and b/projects/KeyTest1alt4.sb3 differ diff --git a/projects/KeyTest1alt5.sb3 b/projects/KeyTest1alt5.sb3 new file mode 100644 index 0000000..1669899 Binary files /dev/null and b/projects/KeyTest1alt5.sb3 differ diff --git a/projects/KeyTest2.sb3 b/projects/KeyTest2.sb3 new file mode 100644 index 0000000..9e466b6 Binary files /dev/null and b/projects/KeyTest2.sb3 differ diff --git a/projects/Math.sb3 b/projects/Math1.sb3 similarity index 100% rename from projects/Math.sb3 rename to projects/Math1.sb3 diff --git a/projects/Media1.sb3 b/projects/Media1.sb3 new file mode 100644 index 0000000..5f67235 Binary files /dev/null and b/projects/Media1.sb3 differ diff --git a/projects/Media2.sb3 b/projects/Media2.sb3 new file mode 100644 index 0000000..466363e Binary files /dev/null and b/projects/Media2.sb3 differ diff --git a/projects/motion-blocks.sb3 b/projects/Motion.sb3 similarity index 100% rename from projects/motion-blocks.sb3 rename to projects/Motion.sb3 diff --git a/projects/MouseX.sb3 b/projects/MouseX.sb3 new file mode 100644 index 0000000..0175f2f Binary files /dev/null and b/projects/MouseX.sb3 differ diff --git a/projects/move.sb3 b/projects/MoveSteps.sb3 similarity index 100% rename from projects/move.sb3 rename to projects/MoveSteps.sb3 diff --git a/projects/pen_pattern.sb3 b/projects/Pen.sb3 similarity index 100% rename from projects/pen_pattern.sb3 rename to projects/Pen.sb3 diff --git a/projects/random-position.sb3 b/projects/RandomPosition.sb3 similarity index 100% rename from projects/random-position.sb3 rename to projects/RandomPosition.sb3 diff --git a/projects/random-fast.sb3 b/projects/RandomPositionFast.sb3 similarity index 100% rename from projects/random-fast.sb3 rename to projects/RandomPositionFast.sb3 diff --git a/projects/balloon.sb3 b/projects/Repeat1.sb3 similarity index 100% rename from projects/balloon.sb3 rename to projects/Repeat1.sb3 diff --git a/projects/balloon-simple.sb3 b/projects/Repeat2.sb3 similarity index 100% rename from projects/balloon-simple.sb3 rename to projects/Repeat2.sb3 diff --git a/projects/balloon-start.sb3 b/projects/Repeat3.sb3 similarity index 100% rename from projects/balloon-start.sb3 rename to projects/Repeat3.sb3 diff --git a/projects/RightArrowKey.sb3 b/projects/RightArrowKey.sb3 new file mode 100644 index 0000000..6d424c1 Binary files /dev/null and b/projects/RightArrowKey.sb3 differ diff --git a/projects/rotation-center.sb3 b/projects/RotationCenter.sb3 similarity index 100% rename from projects/rotation-center.sb3 rename to projects/RotationCenter.sb3 diff --git a/projects/sa-logs.sb3 b/projects/SALogs.sb3 similarity index 100% rename from projects/sa-logs.sb3 rename to projects/SALogs.sb3 diff --git a/projects/Sound1.sb3 b/projects/Sound1.sb3 new file mode 100644 index 0000000..e874279 Binary files /dev/null and b/projects/Sound1.sb3 differ diff --git a/projects/Sound2.sb3 b/projects/Sound2.sb3 new file mode 100644 index 0000000..1c87292 Binary files /dev/null and b/projects/Sound2.sb3 differ diff --git a/projects/SpaceKey.sb3 b/projects/SpaceKey.sb3 new file mode 100644 index 0000000..f96517e Binary files /dev/null and b/projects/SpaceKey.sb3 differ diff --git a/projects/sprite-fencing.sb3 b/projects/SpriteFencing1.sb3 similarity index 100% rename from projects/sprite-fencing.sb3 rename to projects/SpriteFencing1.sb3 diff --git a/projects/sprite-fencing2.sb3 b/projects/SpriteFencing2.sb3 similarity index 100% rename from projects/sprite-fencing2.sb3 rename to projects/SpriteFencing2.sb3 diff --git a/projects/sprite-fencing2-slow.sb3 b/projects/SpriteFencing3.sb3 similarity index 100% rename from projects/sprite-fencing2-slow.sb3 rename to projects/SpriteFencing3.sb3 diff --git a/projects/StringBlocks1.sb3 b/projects/StringBlocks1.sb3 new file mode 100644 index 0000000..139337c Binary files /dev/null and b/projects/StringBlocks1.sb3 differ diff --git a/projects/Substack.sb3 b/projects/Substack.sb3 new file mode 100644 index 0000000..cd583fa Binary files /dev/null and b/projects/Substack.sb3 differ diff --git a/projects/project.sb3 b/projects/Test1.sb3 similarity index 100% rename from projects/project.sb3 rename to projects/Test1.sb3 diff --git a/projects/cosmicat.sb3 b/projects/Test10.sb3 similarity index 100% rename from projects/cosmicat.sb3 rename to projects/Test10.sb3 diff --git a/projects/Test11.sb3 b/projects/Test11.sb3 new file mode 100644 index 0000000..51f58e1 Binary files /dev/null and b/projects/Test11.sb3 differ diff --git a/projects/project_b.sb3 b/projects/Test2.sb3 similarity index 100% rename from projects/project_b.sb3 rename to projects/Test2.sb3 diff --git a/projects/two_sprites.sb3 b/projects/Test3.sb3 similarity index 100% rename from projects/two_sprites.sb3 rename to projects/Test3.sb3 diff --git a/projects/mascots.sb3 b/projects/Test4.sb3 similarity index 100% rename from projects/mascots.sb3 rename to projects/Test4.sb3 diff --git a/projects/Untitled-78.sb3 b/projects/Test5.sb3 similarity index 100% rename from projects/Untitled-78.sb3 rename to projects/Test5.sb3 diff --git a/projects/Untitled-97.sb3 b/projects/Test6.sb3 similarity index 100% rename from projects/Untitled-97.sb3 rename to projects/Test6.sb3 diff --git a/projects/wait_gotoxy.sb3 b/projects/Test7.sb3 similarity index 100% rename from projects/wait_gotoxy.sb3 rename to projects/Test7.sb3 diff --git a/projects/Untitled-122.sb3 b/projects/Test8.sb3 similarity index 100% rename from projects/Untitled-122.sb3 rename to projects/Test8.sb3 diff --git a/projects/gobo_cat.sb3 b/projects/Test9.sb3 similarity index 100% rename from projects/gobo_cat.sb3 rename to projects/Test9.sb3 diff --git a/projects/arrows.sb3 b/projects/WhenKeyPressed.sb3 similarity index 100% rename from projects/arrows.sb3 rename to projects/WhenKeyPressed.sb3 diff --git a/projects/xy-reporters.sb3 b/projects/XYReporters.sb3 similarity index 100% rename from projects/xy-reporters.sb3 rename to projects/XYReporters.sb3 diff --git a/projects/rwsr.sb3 b/projects/rwsr.sb3 deleted file mode 100644 index ce9ccc3..0000000 Binary files a/projects/rwsr.sb3 and /dev/null differ diff --git a/requirements.txt b/requirements.txt index 8aa6d34..ef8efc7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ numpy~=1.21.2 pygame~=2.0.1 CairoSVG~=2.5.2 -python-i18n[YAML] \ No newline at end of file +python-i18n[YAML] +beautifulsoup4~=4.11.1 +lxml \ No newline at end of file diff --git a/sandbox/AKey.json b/sandbox/AKey.json new file mode 100644 index 0000000..66e597c --- /dev/null +++ b/sandbox/AKey.json @@ -0,0 +1,168 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "variables": { + "`jEk@4|i[#Fk?(8x)AV.-my variable": [ + "variabila mea", + 0 + ] + }, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "assetId": "cd21514d0531fdffb22204e0ec5ed84a", + "name": "decor1", + "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", + "dataFormat": "svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "name": "pop", + "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": "Personaj1", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": { + "[_TPYe1f0U#J6fC3Z-H9": { + "opcode": "event_whenflagclicked", + "next": "TrO*p@+rW#b|n.cRboA$", + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 227, + "y": 227 + }, + "TrO*p@+rW#b|n.cRboA$": { + "opcode": "looks_switchcostumeto", + "next": null, + "parent": "[_TPYe1f0U#J6fC3Z-H9", + "inputs": { + "COSTUME": [ + 1, + "qC-grBho7d-C}[D}D5+c" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false + }, + "qC-grBho7d-C}[D}D5+c": { + "opcode": "looks_costume", + "next": null, + "parent": "TrO*p@+rW#b|n.cRboA$", + "inputs": {}, + "fields": { + "COSTUME": [ + "costum1", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "SXNG`gV~`q%fX8J{m_fO": { + "opcode": "event_whenkeypressed", + "next": "$xg78~48L;;5Q).$!n)T", + "parent": null, + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "a", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 267, + "y": 543 + }, + "$xg78~48L;;5Q).$!n)T": { + "opcode": "looks_nextcostume", + "next": null, + "parent": "SXNG`gV~`q%fX8J{m_fO", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": false + } + }, + "comments": {}, + "currentCostume": 1, + "costumes": [ + { + "assetId": "b7853f557e4426412e64bb3da6531a99", + "name": "costum1", + "bitmapResolution": 1, + "md5ext": "b7853f557e4426412e64bb3da6531a99.svg", + "dataFormat": "svg", + "rotationCenterX": 48, + "rotationCenterY": 50 + }, + { + "assetId": "e6ddc55a6ddd9cc9d84fe0b4c21e016f", + "name": "costum2", + "bitmapResolution": 1, + "md5ext": "e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg", + "dataFormat": "svg", + "rotationCenterX": 46, + "rotationCenterY": 53 + } + ], + "sounds": [ + { + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "name": "Miau", + "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": "0.2.0-prerelease.20201016122132", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Scratch/3.18.1 Chrome/80.0.3987.165 Electron/8.2.5 Safari/537.36" + } +} diff --git a/sandbox/RightArrowKey.json b/sandbox/RightArrowKey.json new file mode 100644 index 0000000..372552f --- /dev/null +++ b/sandbox/RightArrowKey.json @@ -0,0 +1,168 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "variables": { + "`jEk@4|i[#Fk?(8x)AV.-my variable": [ + "variabila mea", + 0 + ] + }, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "assetId": "cd21514d0531fdffb22204e0ec5ed84a", + "name": "decor1", + "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", + "dataFormat": "svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "name": "pop", + "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": "Personaj1", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": { + "[_TPYe1f0U#J6fC3Z-H9": { + "opcode": "event_whenflagclicked", + "next": "TrO*p@+rW#b|n.cRboA$", + "parent": null, + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": true, + "x": 227, + "y": 227 + }, + "TrO*p@+rW#b|n.cRboA$": { + "opcode": "looks_switchcostumeto", + "next": null, + "parent": "[_TPYe1f0U#J6fC3Z-H9", + "inputs": { + "COSTUME": [ + 1, + "qC-grBho7d-C}[D}D5+c" + ] + }, + "fields": {}, + "shadow": false, + "topLevel": false + }, + "qC-grBho7d-C}[D}D5+c": { + "opcode": "looks_costume", + "next": null, + "parent": "TrO*p@+rW#b|n.cRboA$", + "inputs": {}, + "fields": { + "COSTUME": [ + "costum1", + null + ] + }, + "shadow": true, + "topLevel": false + }, + "SXNG`gV~`q%fX8J{m_fO": { + "opcode": "event_whenkeypressed", + "next": "$xg78~48L;;5Q).$!n)T", + "parent": null, + "inputs": {}, + "fields": { + "KEY_OPTION": [ + "right arrow", + null + ] + }, + "shadow": false, + "topLevel": true, + "x": 267, + "y": 543 + }, + "$xg78~48L;;5Q).$!n)T": { + "opcode": "looks_nextcostume", + "next": null, + "parent": "SXNG`gV~`q%fX8J{m_fO", + "inputs": {}, + "fields": {}, + "shadow": false, + "topLevel": false + } + }, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "assetId": "b7853f557e4426412e64bb3da6531a99", + "name": "costum1", + "bitmapResolution": 1, + "md5ext": "b7853f557e4426412e64bb3da6531a99.svg", + "dataFormat": "svg", + "rotationCenterX": 48, + "rotationCenterY": 50 + }, + { + "assetId": "e6ddc55a6ddd9cc9d84fe0b4c21e016f", + "name": "costum2", + "bitmapResolution": 1, + "md5ext": "e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg", + "dataFormat": "svg", + "rotationCenterX": 46, + "rotationCenterY": 53 + } + ], + "sounds": [ + { + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "name": "Miau", + "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": "0.2.0-prerelease.20201016122132", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Scratch/3.18.1 Chrome/80.0.3987.165 Electron/8.2.5 Safari/537.36" + } +} diff --git a/sb3Unpacker.py b/sb3Unpacker.py index ca37d99..df8f3a6 100644 --- a/sb3Unpacker.py +++ b/sb3Unpacker.py @@ -52,6 +52,7 @@ def sb3Unpack(sb3): t.direction = targetObj["direction"] t.size = targetObj["size"] t.currentCostume = targetObj["currentCostume"] + t.isStage = targetObj["isStage"] t.name = targetObj["name"] # Get costumes @@ -59,15 +60,28 @@ def sb3Unpack(sb3): c = costume.Costume() if "md5ext" in costumeObj: c.md5ext = costumeObj["md5ext"] - c.rotationCenterX, c.rotationCenterY = costumeObj["rotationCenterX"], costumeObj["rotationCenterY"] + c.rotationCenterX, c.rotationCenterY = costumeObj["rotationCenterX"], costumeObj["rotationCenterY"] c.dataFormat = costumeObj["dataFormat"] c.file = project.read(costumeObj["assetId"] + "." + costumeObj["dataFormat"]) + c.name = costumeObj["name"] if costumeObj["dataFormat"] != "svg": c.bitmapResolution = int(costumeObj["bitmapResolution"]) else: c.bitmapResolution = 1 t.costumes.append(c) + # Get sounds + for soundObj in targetObj["sounds"]: + s = sound.Sound() + if "md5ext" in soundObj: + s.md5ext = soundObj["md5ext"] + s.dataFormat = soundObj["dataFormat"] + s.rate = soundObj["rate"] + s.sampleCount = soundObj["sampleCount"] + s.file = project.read(soundObj["assetId"] + "." + soundObj["dataFormat"]) + s.name = soundObj["name"] + t.sounds.append(s) + # Set blocks to their correct values for blockId, blockObj in targetObj["blocks"].items(): b = block.Block() @@ -85,6 +99,14 @@ def sb3Unpack(sb3): b.blockRan = False b.target = t t.blocks[blockId] = b + for blockId, blockObj in targetObj["blocks"].items(): + b = t.blocks[blockId] + if b.topLevel: + b.top = b + elif b.parent: + b.top = t.blocks[b.parent].top + else: + b.top = None targets.append(t) return targets, project diff --git a/scratch.py b/scratch.py index bdecfc9..21ca7f7 100644 --- a/scratch.py +++ b/scratch.py @@ -10,6 +10,10 @@ import io import config import i18n +import targetSprite +import bs4 +import time +from datetime import datetime i18n.set("locale", config.language) i18n.set("filename_format", "{locale}.{format}") @@ -17,6 +21,10 @@ _ = i18n.t +class SpriteNotFoundError(Exception): + pass + + if not config.enableDebugMessages: sys.stderr = open(os.devnull, "w") if not config.enableTerminalOutput: @@ -100,6 +108,7 @@ # Scratch2Python only "backspace": pygame.K_BACKSPACE, + "escape": pygame.K_ESCAPE, "f1": pygame.K_F1, "f2": pygame.K_F2, "f3": pygame.K_F3, @@ -111,13 +120,16 @@ "f9": pygame.K_F9, "f10": pygame.K_F10, "f11": pygame.K_F11, - "f12": pygame.K_F12 + "f12": pygame.K_F12, } # Load SVG def loadSvg(svgBytes): - newBytes = cairosvg.svg2png(bytestring=svgBytes) + svg = bs4.BeautifulSoup(svgBytes, "lxml-xml") + if svg.find("svg")["width"] == "0" or svg.find("svg")["height"] == "0": + svg.find("svg")["width"], svg.find("svg")["height"] = 1, 1 + newBytes = cairosvg.svg2png(bytestring=str(svg)) byteIo = io.BytesIO(newBytes) return pygame.image.load(byteIo) @@ -130,8 +142,16 @@ def refreshScreenResolution(): WIDTH = config.projectScreenWidth +# Get the stage sprite in the current project +def getStage(): + for s in targetSprite.sprites: + if s.isStage: + return s + raise SpriteNotFoundError(_("stage-not-found")) + + # Run the given block object -def execute(block, s, keys=[]): +def execute(block, s, keys=set(), keyEvents=set()): # Get block values opcode = block.opcode id = block.blockID @@ -154,7 +174,9 @@ def execute(block, s, keys=[]): newX = newX - WIDTH // 2 newY = HEIGHT // 2 - newY s.setXy(newX, newY) - return s.target.blocks[s.target.blocks[block.parent].next] + if s.target.blocks[block.parent].next: + return s.target.blocks[s.target.blocks[block.parent].next] + return elif block.getFieldValue("to") == "_random_": # go to [random position v] minX = 0 - WIDTH // 2 @@ -163,7 +185,9 @@ def execute(block, s, keys=[]): maxY = HEIGHT // 2 newX, newY = (random.randint(minX, maxX), random.randint(minY, maxY)) s.setXy(newX, newY) - return s.target.blocks[s.target.blocks[block.parent].next] + if s.target.blocks[block.parent].next: + return s.target.blocks[s.target.blocks[block.parent].next] + return elif opcode == "motion_setx": # set x to () s.setXy(int(block.getInputValue("x")), s.y) @@ -191,6 +215,7 @@ def execute(block, s, keys=[]): pass elif opcode == "event_whenkeypressed": + # print("Handling key event") # if not block.waiting: # # Get time delay and convert it to milliseconds # block.timeDelay = 500 @@ -198,57 +223,58 @@ def execute(block, s, keys=[]): # block.executionTime = 0 # print("DEBUG: Waiting for", block.timeDelay, "ms") key = block.getFieldValue("key_option", lookIn=0) + # print(key) if key == "any": # when key [any v] pressed - if keys: - print(_("debug-prefix"), _("keypress-handling", keyName=_("key-any")), file=sys.stderr) + # TODO any key + + pass + + elif KEY_MAPPING[key] in keys and block.next: # when key [. . . v] pressed + if KEY_MAPPING[key] in keys: + if key == "left arrow": + keyName = _("key-left") + elif key == "right arrow": + keyName = _("key-right") + elif key == "up arrow": + keyName = _("key-up") + elif key == "down arrow": + keyName = _("key-down") + elif key == "space": + keyName = _("key-space") + else: + keyName = key + print(_("debug-prefix"), _("keypress-handling", keyName=keyName), file=sys.stderr) + # print(time.time_ns() // 1000000, keyName) for b in block.script: s.target.blocks[b].blockRan = False nb = block # s.target.blocks[block.next] - nb.blockRan = False + # nb.blockRan = False block.script.add(nb.blockID) + nb = s.target.blocks[nb.next] while nb.next and nb.next != block.blockID: + # Reset block nb.blockRan = False nb.timeDelay = 0 nb.executionTime = 0 - nb = s.target.blocks[nb.next] + block.script.add(nb.blockID) + nb = s.target.blocks[nb.next] if not nb.next: nb.next = block.blockID + if nb: + block.script.add(nb.blockID) + block.script.remove(block.blockID) + print("script:", block.script) nb.blockRan = False nextBlock = s.target.blocks[block.next] return nextBlock + else: + # print(f"Unknown event: { key } in { keyEvents }, all keys: { keys }") + pass - elif KEY_MAPPING[key] in keys and block.next: # when key [. . . v] pressed - if key == "left arrow": - keyName = _("key-left") - elif key == "right arrow": - keyName = _("key-right") - elif key == "up arrow": - keyName = _("key-up") - elif key == "down arrow": - keyName = _("key-down") - elif key == "space": - keyName = _("key-space") - else: - keyName = key - print(_("debug-prefix"), _("keypress-handling", keyName=keyName), file=sys.stderr) - for b in block.script: - s.target.blocks[b].blockRan = False - nb = block # s.target.blocks[block.next] - nb.blockRan = False - block.script.add(nb.blockID) - while nb.next and nb.next != block.blockID: - nb.blockRan = False - nb.timeDelay = 0 - nb.executionTime = 0 - nb = s.target.blocks[nb.next] - block.script.add(nb.blockID) - if not nb.next: - nb.next = block.blockID - nb.blockRan = False - nextBlock = s.target.blocks[block.next] - return nextBlock + block.blockRan = False + return None elif opcode == "control_forever": # forever {..} # Don't mark the loop as ran, and do a screen refresh @@ -273,15 +299,19 @@ def execute(block, s, keys=[]): nb.next = block.blockID return nextBlock - elif opcode == "control_repeat": # repeat (10) {..} + elif opcode == "control_repeat": # repeat (10) {...} if block.repeatCounter is None: block.repeatCounter = int(block.getInputValue("times")) # Don't mark the loop as ran until done, and do a screen refresh - if block.repeatCounter > 1: + if block.repeatCounter > 0: block.blockRan = False else: block.blockRan = True block.repeatCounter = None + if block.next: + return s.target.blocks[block.next] + else: + return block.screenRefresh = True if block.repeatCounter is not None: @@ -305,14 +335,73 @@ def execute(block, s, keys=[]): nb.next = block.blockID return nextBlock + elif opcode == "looks_switchcostumeto": # switch costume to [... v] + nextBlock = block.getBlockInputValue("costume") + return s.target.blocks[nextBlock] + + elif opcode == "looks_nextcostume": # next costume + s.setCostume(s.target.currentCostume + 1) + + elif opcode == "looks_costume": + if s.target.blocks[block.parent].opcode == "looks_switchcostumeto": + costumeName = block.getFieldValue("costume") + newCostume = 0 + for c in s.target.costumes: + if c.name == costumeName: + break + newCostume += 1 + s.setCostume(newCostume) + if s.target.blocks[block.parent].next: + return s.target.blocks[s.target.blocks[block.parent].next] + return + + elif opcode == "looks_switchbackdropto": # switch backdrop to [... v] + nextBlock = block.getBlockInputValue("backdrop") + return s.target.blocks[nextBlock] + + elif opcode == "looks_nextbackdrop": # next backdrop + getStage().setCostume(getStage().target.currentCostume + 1) + + elif opcode == "looks_backdrops": + if s.target.blocks[block.parent].opcode == "looks_switchbackdropto": + backdropName = block.getFieldValue("backdrop") + newBackdrop = 0 + for c in getStage().target.costumes: + if c.name == backdropName: + break + newBackdrop += 1 + getStage().setCostume(newBackdrop) + if s.target.blocks[block.parent].next: + return s.target.blocks[s.target.blocks[block.parent].next] + return + + elif opcode == "sound_play": # start sound [... v] + nextBlock = block.getBlockInputValue("sound_menu") + return s.target.blocks[nextBlock] + + elif opcode == "sound_sounds_menu": + if s.target.blocks[block.parent].opcode == "sound_play": + soundName = block.getFieldValue("sound_menu") + newSound = None + for so in s.target.sounds: + if so.name == soundName: + newSound = so + break + newSound.play() + if s.target.blocks[block.parent].next: + return s.target.blocks[s.target.blocks[block.parent].next] + return + elif opcode == "procedures_call": if config.showSALogs: + # These are Scratch Addons debugger blocks. if block.proccode == "​​log​​ %s": # Scratch Addons log () - print(_("project-log"), block.getCustomInputValue(0), file=sys.stderr) + print("[", datetime.now().strftime("%H:%M:%S:%f"), "]", _("project-log"), block.getCustomInputValue(0), file=sys.stderr) elif block.proccode == "​​warn​​ %s": # Scratch Addons warn () print(_("project-warn"), block.getCustomInputValue(0), file=sys.stderr) elif block.proccode == "​​error​​ %s": # Scratch Addons error () print(_("project-error"), block.getCustomInputValue(0), file=sys.stderr) + else: print(_("unknown-opcode"), opcode) diff --git a/sound.py b/sound.py index 238483d..3ac4fbc 100644 --- a/sound.py +++ b/sound.py @@ -6,10 +6,19 @@ set. Those are then used to build the project in main.py. """ +import pygame.mixer class Sound: def __init__(self): self.dataFormat = "wav" self.rate = 44100 - self.sampleCount = 1032 - self.md5ext = "" \ No newline at end of file + self.sampleCount = 1024 + self.md5ext = "" + self.file = None + self.name = "" # display name + + def play(self): + # TODO + mixer = pygame.mixer.Sound(buffer=self.file) + mixer.play() + pygame.time.wait(int(mixer.get_length()) * 1000) diff --git a/targetSprite.py b/targetSprite.py index 89b99e2..6d97df9 100644 --- a/targetSprite.py +++ b/targetSprite.py @@ -3,6 +3,7 @@ Targets as pygame sprites """ +import time import pygame import cairosvg @@ -17,21 +18,25 @@ i18n.load_path.append("lang/") _ = i18n.t +sprites = set() + class TargetSprite(pygame.sprite.Sprite): def __init__(self, target): + sprites.add(self) pygame.sprite.Sprite.__init__(self) self.padX = 0 self.padY = 0 self.target = target + self.target.currentCostume = target.currentCostume # Load costume - if target.costumes[target.currentCostume].dataFormat != "svg": + if target.costumes[self.target.currentCostume].dataFormat != "svg": 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)) - self.padX = initialWidth - sprite.get_width() - self.padY = initialHeight - sprite.get_height() + # self.padX = initialWidth - sprite.get_width() + # self.padY = initialHeight - sprite.get_height() else: sprite = scratch.loadSvg(target.costumes[target.currentCostume].file) sprite = pygame.transform.rotate(sprite, 90 - target.direction) @@ -45,11 +50,14 @@ def __init__(self, target): self.name = _("stage") else: self.name = self.target.name + self.setXy(self.x, self.y) - # Convert Scratch coordinates into Pygame coordinates - self.rect.x = (self.x + scratch.WIDTH // 2 - self.target.costumes[self.target.currentCostume].rotationCenterX) - self.rect.y = (scratch.HEIGHT // 2 - self.y - self.target.costumes[self.target.currentCostume].rotationCenterY) - pygame.transform.scale(self.image, (int(round(self.rect.width * self.size / 100)), int(round(self.rect.height * self.size / 100)))) + # # Convert Scratch coordinates into Pygame coordinates + # self.rect.x = (self.x + scratch.WIDTH // 2 - self.target.costumes[self.target.currentCostume].rotationCenterX) + # self.rect.y = (scratch.HEIGHT // 2 - self.y - self.target.costumes[self.target.currentCostume].rotationCenterY) + # pygame.transform.scale(self.image, (int(round(self.rect.width * self.size / 100)), int(round(self.rect.height * self.size / 100)))) + # + # print(_("costumes-count", sprite=self.name, costumes=len(self.target.costumes))) # Set self position def setXy(self, x, y): @@ -79,38 +87,29 @@ def setXy(self, x, y): self.x = x + self.padX // 2 self.y = y - self.padY // 2 print(_("debug-prefix"), _("new-sprite-position", x=x, y=y, name=self.name), file=sys.stderr) - self.rect.x = self.x + scratch.WIDTH // 2 - self.target.costumes[self.target.currentCostume].rotationCenterX - self.rect.y = scratch.HEIGHT // 2 - self.y - self.target.costumes[self.target.currentCostume].rotationCenterY + 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) # Move def setXyDelta(self, dx, dy): x = self.x + dx y = self.y + dy - # Do sprite fencing - if not config.allowOffScreenSprites: - if self.rect.width > 32: - if x > scratch.WIDTH - scratch.WIDTH / 2 + (self.rect.width / 2 - 16): - x = scratch.WIDTH - scratch.WIDTH / 2 + (self.rect.width / 2 - 16) - if x < -scratch.WIDTH / 2 - (self.rect.width / 2 - 16): - x = -scratch.WIDTH / 2 - (self.rect.width / 2 - 16) - else: - if x > scratch.WIDTH - scratch.WIDTH / 2: - x = scratch.WIDTH - scratch.WIDTH / 2 - if x < scratch.WIDTH / 2 - scratch.WIDTH: - x = scratch.WIDTH / 2 - scratch.WIDTH - if self.rect.height > 32: - if y > scratch.HEIGHT - scratch.HEIGHT / 2 + (self.rect.height / 2 - 16): - y = scratch.HEIGHT - scratch.HEIGHT / 2 + (self.rect.height / 2 - 16) - if y < -scratch.HEIGHT / 2 - (self.rect.height / 2 - 16): - y = -scratch.HEIGHT / 2 - (self.rect.height / 2 - 16) - else: - if y > scratch.HEIGHT - scratch.HEIGHT / 2: - y = scratch.HEIGHT - scratch.HEIGHT / 2 - if y < scratch.HEIGHT / 2 - scratch.HEIGHT: - y = scratch.HEIGHT / 2 - scratch.HEIGHT - # Set X and Y - self.x = x + self.padX // 2 - self.y = y - self.padY // 2 - print(_("debug-prefix"), _("new-sprite-position", x=x, y=y, name=self.name), file=sys.stderr) - self.rect.x = self.x + scratch.WIDTH // 2 - self.target.costumes[self.target.currentCostume].rotationCenterX - self.rect.y = scratch.HEIGHT // 2 - self.y - self.target.costumes[self.target.currentCostume].rotationCenterY + self.setXy(x, y) + + # Change costume + def setCostume(self, costumeId): + self.target.currentCostume = costumeId % len(self.target.costumes) + + # Load costume + if self.target.costumes[self.target.currentCostume].dataFormat != "svg": + sprite = pygame.image.load(io.BytesIO(self.target.costumes[self.target.currentCostume].file)) + initialWidth = sprite.get_width() + initialHeight = sprite.get_height() + sprite = pygame.transform.smoothscale(sprite, (sprite.get_width() // self.target.costumes[self.target.currentCostume].bitmapResolution, sprite.get_height() // self.target.costumes[self.target.currentCostume].bitmapResolution)) + self.padX = initialWidth - sprite.get_width() + self.padY = initialHeight - sprite.get_height() + else: + sprite = scratch.loadSvg(self.target.costumes[self.target.currentCostume].file) + self.image = sprite + self.rect = self.image.get_rect() + self.setXy(self.x, self.y)