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 @@
+
\ 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 @@
+
\ 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)