Scratch 3 runtime for J2ME devices.

feat: block execution actually works now

+27 -7
src/main/java/leap/s3me/App.java
··· 1 1 package leap.s3me; 2 2 3 - import gnu.classpath.java.util.zip.ZipInputStream; 4 - import java.io.ByteArrayInputStream; 5 - import gnu.classpath.java.util.zip.ZipEntry; 3 + import java.io.ByteArrayOutputStream; 6 4 import java.io.InputStream; 7 - import java.io.ByteArrayOutputStream; 5 + 8 6 import javax.microedition.io.Connector; 9 7 import javax.microedition.io.file.FileConnection; 8 + import javax.microedition.lcdui.Command; 9 + import javax.microedition.lcdui.CommandListener; 10 10 import javax.microedition.lcdui.Display; 11 - import javax.microedition.lcdui.Form; 12 - import javax.microedition.lcdui.StringItem; 11 + import javax.microedition.lcdui.Displayable; 12 + import javax.microedition.lcdui.TextBox; 13 + import javax.microedition.lcdui.TextField; 13 14 import javax.microedition.midlet.MIDlet; 14 15 15 16 import cc.nnproject.json.JSON; ··· 86 87 byte[] buffer = null; 87 88 if ((buffer = Unzip.extractFileToBytes("project.json")) != null) { 88 89 JSONObject obj = JSON.getObject(new String(buffer)); 89 - Interpret.executor.registerHandlers(); 90 90 Interpret.loadSprites(obj); 91 91 System.out.println("Project width: " + String.valueOf(Scratch.projectWidth)); 92 92 System.out.println("Project height: " + String.valueOf(Scratch.projectHeight)); 93 93 System.out.println("Project FPS: " + String.valueOf(Scratch.FPS)); 94 94 } 95 95 mainLoop.start(); 96 + App.display.setCurrent(App.render); 96 97 } catch (Exception e) { 97 98 e.printStackTrace(); 98 99 } finally { ··· 112 113 synchronized (this) { 113 114 Scratch.paused = true; 114 115 } 116 + } 117 + 118 + public void showAskAndWaitInput() { 119 + final TextBox inputField = new TextBox(Scratch.inputQuestion, "", -1, TextField.ANY); 120 + Command okCommand = new Command("OK", Command.OK, 1); 121 + 122 + inputField.addCommand(okCommand); 123 + inputField.setCommandListener(new CommandListener() { 124 + public void commandAction(Command c, Displayable d) { 125 + synchronized(App.context) { 126 + Interpret.answer = inputField.getString(); 127 + Scratch.awaitingUserInput = false; 128 + display.setCurrent(render); 129 + App.context.notifyAll(); 130 + } 131 + } 132 + }); 133 + 134 + display.setCurrent(inputField); 115 135 } 116 136 }
-3
src/main/java/leap/s3me/Image.java
··· 1 1 package leap.s3me; 2 2 3 - import java.io.ByteArrayOutputStream; 4 - import java.io.IOException; 5 3 import java.util.Enumeration; 6 4 import java.util.Hashtable; 7 5 8 - import gnu.classpath.java.util.zip.ZipInputStream; 9 6 import leap.s3me.scratch.Costume; 10 7 11 8 /**
+12 -16
src/main/java/leap/s3me/Interpret.java
··· 1 1 package leap.s3me; 2 2 3 - import java.util.Vector; 4 3 import java.util.Enumeration; 5 4 import java.util.Hashtable; 5 + import java.util.Vector; 6 6 7 - import cc.nnproject.json.AbstractJSON; 8 7 import cc.nnproject.json.JSON; 9 8 import cc.nnproject.json.JSONArray; 10 9 import cc.nnproject.json.JSONObject; ··· 19 18 import leap.s3me.scratch.Monitor; 20 19 import leap.s3me.scratch.ParsedField; 21 20 import leap.s3me.scratch.ParsedInput; 22 - import leap.s3me.scratch.Sprite; 23 21 import leap.s3me.scratch.Sound; 22 + import leap.s3me.scratch.Sprite; 24 23 import leap.s3me.scratch.Value; 25 24 import leap.s3me.scratch.Variable; 26 25 ··· 276 275 Block block = new Block(); 277 276 block.id = id; 278 277 if (data.has("opcode")) { 279 - block.opcode = data.getString("opcode", ""); 278 + block.opcode = data.getString("opcode"); 280 279 if (block.opcode.equals("event_whenthisspriteclicked")) 281 280 sprite.shouldDoSpriteClick = true; 282 281 } 283 - block.next = data.getString("next", null); 282 + if (data.has("next") && !data.isNull("next")) { 283 + block.next = data.getString("next"); 284 + } 284 285 block.parent = data.getString("parent", "null"); 285 286 if (data.has("fields")) { 286 287 JSONObject fields = data.getObject("fields"); ··· 331 332 block.topLevel = data.getBoolean("topLevel", false); 332 333 block.shadow = data.getBoolean("shadow", false); 333 334 if (data.has("mutation")) { 334 - if (data.getObject("mutation").has("proccode")) 335 - block.customBlockId = data.getObject("mutation").getString("proccode"); 336 - else 337 - block.customBlockId = ""; 335 + block.customBlockId = data.getObject("mutation").getString("proccode", ""); 338 336 } 339 337 340 338 sprite.blocks.put(id, block); ··· 595 593 } 596 594 597 595 public static Block findBlock(String blockID) { 598 - if (blockLookup.contains(blockID)) 596 + if (blockLookup.containsKey(blockID)) 599 597 return (Block)blockLookup.get(blockID); 600 598 return null; 601 599 } ··· 629 627 } 630 628 } 631 629 632 - currentBlock = findBlock(currentBlock.next); 630 + if (currentBlock.next != null) 631 + currentBlock = findBlock(currentBlock.next); 632 + else 633 + currentBlock = null; 633 634 } 634 - System.out.println(outID == null); 635 - System.out.println(outID.toString() == null); 636 - System.out.println(outID.toString().equals("")); 637 - System.out.println(outID.toString().length() > 0); 638 - System.out.println(outID.toString()); 639 635 return blockChain; 640 636 } 641 637
+2 -6
src/main/java/leap/s3me/Render.java
··· 1 1 package leap.s3me; 2 2 3 - import java.io.IOException; 4 3 import java.util.Hashtable; 5 4 import java.util.Vector; 6 5 7 6 import javax.microedition.lcdui.Canvas; 8 - import javax.microedition.lcdui.Font; 9 7 import javax.microedition.lcdui.Graphics; 10 8 import javax.microedition.lcdui.Image; 11 9 import javax.microedition.lcdui.game.Sprite; 12 10 13 11 import leap.s3me.scratch.BlockExecutor; 14 12 import leap.s3me.scratch.Costume; 15 - import leap.s3me.scratch.Monitor; 16 13 import leap.s3me.scratch.Input; 14 + import leap.s3me.scratch.Monitor; 17 15 18 16 public class Render extends Canvas { 19 17 public int windowWidth; 20 18 public int windowHeight; 21 19 22 20 public Vector visibleVariables; 23 - private Hashtable images = new Hashtable(); 21 + public Hashtable images = new Hashtable(); 24 22 private Image penLayer; 25 23 26 24 public Render() { ··· 97 95 currentSprite.rotationCenterX = (int)costume.rotationCenterX; 98 96 currentSprite.rotationCenterY = (int)costume.rotationCenterY; 99 97 100 - // First, get the base image to determine its original dimensions 101 98 leap.s3me.Image baseImageWrapper = leap.s3me.Image.getImage(costume); 102 99 if (baseImageWrapper != null && baseImageWrapper.image != null) { 103 100 int imgWidth = baseImageWrapper.image.getWidth(); ··· 135 132 sprite.setTransform(transform); 136 133 sprite.setPosition(renderX, renderY); 137 134 sprite.paint(g); 138 - 139 135 } else { 140 136 g.setColor(0, 0, 0); 141 137 int x = (int)((currentSprite.xPosition * scale) + (windowWidth / 2));
+7 -2
src/main/java/leap/s3me/Scratch.java
··· 17 17 public static boolean miscellaneousLimits = true; 18 18 public static boolean paused = false; 19 19 public static boolean shouldStop = false; 20 + public static boolean awaitingUserInput = false; 21 + public static String inputQuestion = ""; 20 22 21 23 public static double counter = 0; 22 24 ··· 35 37 while (paused) { 36 38 App.context.wait(); 37 39 } 40 + if (awaitingUserInput) { 41 + App.context.showAskAndWaitInput(); 42 + App.context.wait(); 43 + } 38 44 } 39 45 40 46 Input.update(); 41 47 BlockExecutor.runRepeatBlocks(); 42 48 BlockExecutor.runBroadcasts(); 43 49 App.render.repaint(); 44 - App.display.setCurrent(App.render); 45 50 46 - //Thread.sleep(1000 / FPS); 51 + Thread.sleep(1000 / FPS); 47 52 } catch (Exception e) { 48 53 e.printStackTrace(); 49 54 }
+30 -5
src/main/java/leap/s3me/scratch/BlockExecutor.java
··· 16 16 import leap.s3me.scratch.blocks.ControlBlocks; 17 17 import leap.s3me.scratch.blocks.DataBlocks; 18 18 import leap.s3me.scratch.blocks.OperatorBlocks; 19 + import leap.s3me.scratch.blocks.SensingBlocks; 19 20 import leap.s3me.scratch.blocks.ProcedureBlocks; 20 21 21 22 public class BlockExecutor { ··· 40 41 registerHandlers(); 41 42 } 42 43 43 - public void registerHandlers() { 44 + private void registerHandlers() { 44 45 // motion 45 46 handlers.put("motion_movesteps", new MotionBlocks.MoveSteps()); 46 47 handlers.put("motion_gotoxy", new MotionBlocks.GoToXY()); ··· 143 144 valueHandlers.put("data_lengthoflist", new DataBlocks.LengthOfList()); 144 145 valueHandlers.put("data_listcontainsitem", new DataBlocks.ListContainsItem()); 145 146 147 + // sensing 148 + handlers.put("sensing_resettimer", new SensingBlocks.ResetTimer()); 149 + handlers.put("sensing_askandwait", new SensingBlocks.AskAndWait()); 150 + handlers.put("sensing_setdragmode", new SensingBlocks.SetDragMode()); 151 + valueHandlers.put("sensing_timer", new SensingBlocks.SensingTimer()); 152 + valueHandlers.put("sensing_of", new SensingBlocks.Of()); 153 + valueHandlers.put("sensing_mousex", new SensingBlocks.MouseX()); 154 + valueHandlers.put("sensing_mousey", new SensingBlocks.MouseY()); 155 + valueHandlers.put("sensing_distanceto", new SensingBlocks.DistanceTo()); 156 + valueHandlers.put("sensing_dayssince2000", new SensingBlocks.DaysSince2000()); 157 + valueHandlers.put("sensing_current", new SensingBlocks.Current()); 158 + valueHandlers.put("sensing_answer", new SensingBlocks.SensingAnswer()); 159 + valueHandlers.put("sensing_keypressed", new SensingBlocks.KeyPressed()); 160 + valueHandlers.put("sensing_touchingobject", new SensingBlocks.TouchingObject()); 161 + valueHandlers.put("sensing_mousedown", new SensingBlocks.MouseDown()); 162 + valueHandlers.put("sensing_username", new SensingBlocks.Username()); 163 + 146 164 // procedures / arguments 147 165 handlers.put("procedures_call", new ProcedureBlocks.Call()); 148 166 handlers.put("procedures_definition", new ProcedureBlocks.Definition()); 149 167 valueHandlers.put("argument_reporter_string_number", new ProcedureBlocks.StringNumber()); 150 168 valueHandlers.put("argument_reporter_boolean", new ProcedureBlocks.BooleanArgument()); 169 + 170 + // Other (Don't know where else to put these) 171 + valueHandlers.put("matrix", new ValueBlockHandler() { 172 + public Value run(Block block, Sprite sprite) { 173 + return new Value(Scratch.getFieldValue(block, "MATRIX")); 174 + } 175 + }); 151 176 } 152 177 153 178 public Vector runBlock(Block block, Sprite sprite, boolean[] withoutScreenRefresh, boolean fromRepeat) { ··· 205 230 206 231 private int executeBlock(Block block, Sprite sprite, boolean[] withoutScreenRefresh, boolean fromRepeat) { 207 232 BlockHandler handler = (BlockHandler)handlers.get(block.opcode); 208 - System.out.println("Handler: " + block.opcode); 233 + //System.out.println("Handler: " + block.opcode); 209 234 if (handler != null) { 210 235 return handler.run(block, sprite, withoutScreenRefresh, fromRepeat); 211 236 } ··· 215 240 public Value getBlockValue(Block block, Sprite sprite) { 216 241 if (block == null) return new Value(); 217 242 ValueBlockHandler handler = (ValueBlockHandler)valueHandlers.get(block.opcode); 218 - System.out.println("Value handler: " + block.opcode); 243 + //System.out.println("Value handler: " + block.opcode); 219 244 if (handler != null) { 220 245 return handler.run(block, sprite); 221 246 } ··· 223 248 } 224 249 225 250 public static Vector runAllBlocksByOpcode(String opcodeToFind) { 226 - System.out.println("Running all " + opcodeToFind + " blocks"); 251 + //System.out.println("Running all " + opcodeToFind + " blocks"); 227 252 Vector blocksRun = new Vector(); 228 253 for (int i = 0; i < Interpret.sprites.size(); i++) { 229 254 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); ··· 562 587 } 563 588 564 589 public static boolean hasActiveRepeats(Sprite sprite, String blockChainID) { 565 - if (sprite.blockChains.contains(blockChainID)) { 590 + if (sprite.blockChains.containsKey(blockChainID)) { 566 591 if (!((BlockChain)sprite.blockChains.get(blockChainID)).blocksToRepeat.isEmpty()) return true; 567 592 } 568 593 return false;
+27 -6
src/main/java/leap/s3me/scratch/Sprite.java
··· 4 4 import java.util.Hashtable; 5 5 import java.util.Map; 6 6 7 + import leap.s3me.Image; 8 + 7 9 public class Sprite { 8 10 public String name; 9 11 public String id; ··· 48 50 public Hashtable customBlocks = new Hashtable(); 49 51 public Hashtable blockChains = new Hashtable(); 50 52 53 + public Sprite() {} 54 + 55 + public Sprite(Sprite other) {} 56 + 51 57 public void clearAll() { 52 58 variables.clear(); 53 59 blocks.clear(); ··· 63 69 64 70 public void copyFrom(Sprite other) { 65 71 this.name = other.name; 66 - // Don't copy ID, clones get a new one 67 72 this.isStage = other.isStage; 68 73 this.draggable = other.draggable; 69 74 this.visible = other.visible; ··· 79 84 this.brightnessEffect = other.brightnessEffect; 80 85 this.colorEffect = other.colorEffect; 81 86 82 - // Deep copy collections to avoid shared state issues 83 87 this.variables = new Hashtable(); 84 88 for (java.util.Enumeration e = other.variables.keys(); e.hasMoreElements();) { 85 - String key = (String) e.nextElement(); 86 - // This assumes Variable has a copy constructor or similar method 87 - // For now, we'll just share them, but this can be risky for non-global vars 88 - this.variables.put(key, other.variables.get(key)); 89 + String key = (String)e.nextElement(); 90 + Variable originalVar = (Variable)other.variables.get(key); 91 + this.variables.put(key, new Variable(originalVar)); 89 92 } 90 93 91 94 this.blocks = other.blocks; ··· 96 99 this.broadcasts = other.broadcasts; 97 100 this.customBlocks = other.customBlocks; 98 101 this.blockChains = other.blockChains; 102 + } 103 + 104 + public void loadCurrentCostume() { 105 + if (costumes.isEmpty() || currentCostume >= costumes.size()) { 106 + return; 107 + } 108 + 109 + Costume costume = (Costume)costumes.elementAt(currentCostume); 110 + if (costume.id.equals(lastCostumeId)) { 111 + return; 112 + } 113 + 114 + Image baseImageWrapper = Image.getImage(costume); 115 + if (baseImageWrapper != null && baseImageWrapper.image != null) { 116 + this.spriteWidth = baseImageWrapper.image.getWidth(); 117 + this.spriteHeight = baseImageWrapper.image.getHeight(); 118 + this.lastCostumeId = costume.id; 119 + } 99 120 } 100 121 }
+15
src/main/java/leap/s3me/scratch/Value.java
··· 34 34 this.value = val; 35 35 } 36 36 37 + /** 38 + * Copy constructor for creating a deep copy of a Value object. 39 + * This is crucial for cloning sprites to ensure variables are not shared. 40 + * @param other The Value object to copy. 41 + */ 42 + public Value(Value other) { 43 + if (other.value == null) { 44 + this.value = null; 45 + } else if (other.isColor()) { 46 + this.value = new Color(((Color)other.value).hue, ((Color)other.value).saturation, ((Color)other.value).brightness); 47 + } else { 48 + this.value = other.value; // Immutable types (String, Integer, etc.) can be assigned directly. 49 + } 50 + } 51 + 37 52 public boolean isInteger() { 38 53 return value instanceof Integer; 39 54 }
+11
src/main/java/leap/s3me/scratch/Variable.java
··· 4 4 public String id; 5 5 public String name; 6 6 public Value value; 7 + 8 + public Variable() {} 9 + 10 + /** 11 + * Copy constructor for creating a deep copy of a Variable. 12 + */ 13 + public Variable(Variable other) { 14 + this.id = other.id; 15 + this.name = other.name; 16 + this.value = new Value(other.value); // Use Value's copy constructor 17 + } 7 18 }
+18 -18
src/main/java/leap/s3me/scratch/blocks/DataBlocks.java
··· 109 109 110 110 Sprite targetSprite = null; 111 111 112 - if (sprite.lists.contains(listID)) { 112 + if (sprite.lists.containsKey(listID)) { 113 113 targetSprite = sprite; 114 114 } else { 115 115 for (int i = 0; i < Interpret.sprites.size(); i++) { 116 116 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 117 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 117 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 118 118 targetSprite = currentSprite; 119 119 break; 120 120 } ··· 134 134 135 135 Sprite targetSprite = null; 136 136 137 - if (sprite.lists.contains(listID)) { 137 + if (sprite.lists.containsKey(listID)) { 138 138 targetSprite = sprite; 139 139 } else { 140 140 for (int i = 0; i < Interpret.sprites.size(); i++) { 141 141 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 142 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 142 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 143 143 targetSprite = currentSprite; 144 144 break; 145 145 } ··· 181 181 182 182 Sprite targetSprite = null; 183 183 184 - if (sprite.lists.contains(listID)) { 184 + if (sprite.lists.containsKey(listID)) { 185 185 targetSprite = sprite; 186 186 } else { 187 187 for (int i = 0; i < Interpret.sprites.size(); i++) { 188 188 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 189 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 189 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 190 190 targetSprite = currentSprite; 191 191 break; 192 192 } ··· 209 209 210 210 Sprite targetSprite = null; 211 211 212 - if (sprite.lists.contains(listID)) { 212 + if (sprite.lists.containsKey(listID)) { 213 213 targetSprite = sprite; 214 214 } else { 215 215 for (int i = 0; i < Interpret.sprites.size(); i++) { 216 216 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 217 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 217 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 218 218 targetSprite = currentSprite; 219 219 break; 220 220 } ··· 257 257 258 258 Sprite targetSprite = null; 259 259 260 - if (sprite.lists.contains(listID)) { 260 + if (sprite.lists.containsKey(listID)) { 261 261 targetSprite = sprite; 262 262 } else { 263 263 for (int i = 0; i < Interpret.sprites.size(); i++) { 264 264 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 265 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 265 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 266 266 targetSprite = currentSprite; 267 267 break; 268 268 } ··· 305 305 306 306 Sprite targetSprite = null; 307 307 308 - if (sprite.lists.contains(listID)) { 308 + if (sprite.lists.containsKey(listID)) { 309 309 targetSprite = sprite; 310 310 } else { 311 311 for (int i = 0; i < Interpret.sprites.size(); i++) { 312 312 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 313 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 313 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 314 314 targetSprite = currentSprite; 315 315 break; 316 316 } ··· 344 344 345 345 Sprite targetSprite = null; 346 346 347 - if (sprite.lists.contains(listID)) { 347 + if (sprite.lists.containsKey(listID)) { 348 348 targetSprite = sprite; 349 349 } else { 350 350 for (int i = 0; i < Interpret.sprites.size(); i++) { 351 351 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 352 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 352 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 353 353 targetSprite = currentSprite; 354 354 break; 355 355 } ··· 376 376 377 377 Sprite targetSprite = null; 378 378 379 - if (sprite.lists.contains(listID)) { 379 + if (sprite.lists.containsKey(listID)) { 380 380 targetSprite = sprite; 381 381 } else { 382 382 for (int i = 0; i < Interpret.sprites.size(); i++) { 383 383 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 384 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 384 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 385 385 targetSprite = currentSprite; 386 386 break; 387 387 } ··· 403 403 404 404 Sprite targetSprite = null; 405 405 406 - if (sprite.lists.contains(listID)) { 406 + if (sprite.lists.containsKey(listID)) { 407 407 targetSprite = sprite; 408 408 } else { 409 409 for (int i = 0; i < Interpret.sprites.size(); i++) { 410 410 Sprite currentSprite = (Sprite)Interpret.sprites.elementAt(i); 411 - if (currentSprite.isStage && sprite.lists.contains(listID)) { 411 + if (currentSprite.isStage && sprite.lists.containsKey(listID)) { 412 412 targetSprite = currentSprite; 413 413 break; 414 414 }
+5 -5
src/main/java/leap/s3me/scratch/blocks/LooksBlocks.java
··· 16 16 public static class Show implements BlockExecutor.BlockHandler { 17 17 public int run(Block block, Sprite sprite, boolean[] withoutScreenRefresh, boolean fromRepeat) { 18 18 sprite.visible = true; 19 - //Image.getImage((Costume)sprite.costumes.get(sprite.currentCostume)); 19 + sprite.loadCurrentCostume(); 20 20 return BlockExecutor.RESULT_CONTINUE; 21 21 } 22 22 } ··· 59 59 } 60 60 } 61 61 62 - //Image.getImage((Costume)sprite.costumes.get(sprite.currentCostume)); 62 + sprite.loadCurrentCostume(); 63 63 64 64 return BlockExecutor.RESULT_CONTINUE; 65 65 } ··· 71 71 if (sprite.currentCostume >= sprite.costumes.size()) { 72 72 sprite.currentCostume = 0; 73 73 } 74 - //Image.getImage((Costume)sprite.costumes.get(sprite.currentCostume)); 74 + sprite.loadCurrentCostume(); 75 75 return BlockExecutor.RESULT_CONTINUE; 76 76 } 77 77 } ··· 111 111 } 112 112 } 113 113 114 - //Image.getImage((Costume)currentSprite.costumes.get(currentSprite.currentCostume)); 114 + currentSprite.loadCurrentCostume(); 115 115 116 116 Enumeration e = currentSprite.blocks.keys(); 117 117 while (e.hasMoreElements()) { ··· 142 142 if (currentSprite.currentCostume >= currentSprite.costumes.size()) { 143 143 currentSprite.currentCostume = 0; 144 144 } 145 - //Image.getImage((Costume)currentSprite.costumes.get(currentSprite.currentCostume)); 145 + currentSprite.loadCurrentCostume(); 146 146 147 147 Enumeration e = currentSprite.blocks.keys(); 148 148 while (e.hasMoreElements()) {
+293
src/main/java/leap/s3me/scratch/blocks/SensingBlocks.java
··· 1 + package leap.s3me.scratch.blocks; 2 + 3 + import java.util.Calendar; 4 + import java.util.Enumeration; 5 + import java.util.Vector; 6 + 7 + import leap.s3me.App; 8 + import leap.s3me.Interpret; 9 + import leap.s3me.MathUtils; 10 + import leap.s3me.Scratch; 11 + import leap.s3me.scratch.Block; 12 + import leap.s3me.scratch.BlockExecutor; 13 + import leap.s3me.scratch.Costume; 14 + import leap.s3me.scratch.Input; 15 + import leap.s3me.scratch.Sprite; 16 + import leap.s3me.scratch.Value; 17 + import leap.s3me.scratch.Variable; 18 + 19 + public class SensingBlocks { 20 + public static class ProjectTimer { 21 + private long startTime; 22 + 23 + public ProjectTimer() { 24 + startTime = System.currentTimeMillis(); 25 + } 26 + 27 + public void start() { 28 + startTime = System.currentTimeMillis(); 29 + } 30 + 31 + public double getTimeMs() { 32 + return System.currentTimeMillis() - startTime; 33 + } 34 + } 35 + public static ProjectTimer projectTimer = new ProjectTimer(); 36 + 37 + public static class ResetTimer implements BlockExecutor.BlockHandler { 38 + public int run(Block block, Sprite sprite, boolean[] withoutScreenRefresh, boolean fromRepeat) { 39 + projectTimer.start(); 40 + return BlockExecutor.RESULT_CONTINUE; 41 + } 42 + } 43 + 44 + public static class AskAndWait implements BlockExecutor.BlockHandler { 45 + public int run(Block block, Sprite sprite, boolean[] withoutScreenRefresh, boolean fromRepeat) { 46 + // This block is blocking, meaning script execution pauses until user input is received. 47 + // We use Scratch.awaitingUserInput and Scratch.inputQuestion flags to signal the UI. 48 + // The main loop in Scratch.startScratchProject() will wait until input is provided. 49 + 50 + if (block.repeatTimes != -1 && !fromRepeat) { 51 + block.repeatTimes = -1; 52 + } 53 + 54 + if (block.repeatTimes == -1) { 55 + block.repeatTimes = -9; // Special code for ask and wait to indicate it's waiting for input 56 + BlockExecutor.addToRepeatQueue(sprite, block); 57 + 58 + Scratch.awaitingUserInput = true; 59 + Scratch.inputQuestion = Scratch.getInputValue(block, "QUESTION", sprite).asString(); 60 + 61 + // Return RESULT_RETURN to pause this script's execution. 62 + // The main loop will then handle displaying the input prompt and waiting. 63 + return BlockExecutor.RESULT_RETURN; 64 + } 65 + 66 + // If we reach here, it means the user has provided input, and the block is resuming. 67 + // Interpret.answer should have been set by the UI. 68 + block.repeatTimes = -1; // Reset for next execution 69 + BlockExecutor.removeFromRepeatQueue(sprite, block); 70 + return BlockExecutor.RESULT_CONTINUE; 71 + } 72 + } 73 + 74 + public static class SetDragMode implements BlockExecutor.BlockHandler { 75 + public int run(Block block, Sprite sprite, boolean[] withoutScreenRefresh, boolean fromRepeat) { 76 + String mode = Scratch.getFieldValue(block, "DRAG_MODE"); 77 + if (mode.equals("draggable")) { 78 + sprite.draggable = true; 79 + } else if (mode.equals("not draggable")) { 80 + sprite.draggable = false; 81 + } 82 + return BlockExecutor.RESULT_CONTINUE; 83 + } 84 + } 85 + 86 + public static class SensingTimer implements BlockExecutor.ValueBlockHandler { 87 + public Value run(Block block, Sprite sprite) { 88 + return new Value(projectTimer.getTimeMs() / 1000.0); 89 + } 90 + } 91 + 92 + public static class Of implements BlockExecutor.ValueBlockHandler { 93 + public Value run(Block block, Sprite sprite) { 94 + String property = Scratch.getFieldValue(block, "PROPERTY"); 95 + String objectName; 96 + 97 + // Get the target object name from the input. This can be "_stage_", "_myself_", or a sprite name. 98 + Value objectValue = Scratch.getInputValue(block, "OBJECT", sprite); 99 + objectName = objectValue.asString(); 100 + 101 + Sprite targetSprite = null; 102 + 103 + if (objectName.equals("_stage_")) { 104 + for (int i = 0; i < Interpret.sprites.size(); i++) { 105 + Sprite s = (Sprite) Interpret.sprites.elementAt(i); 106 + if (s.isStage) { 107 + targetSprite = s; 108 + break; 109 + } 110 + } 111 + } else if (objectName.equals("_myself_")) { 112 + targetSprite = sprite; 113 + } else { 114 + // Find sprite by name (non-clone) 115 + for (int i = 0; i < Interpret.sprites.size(); i++) { 116 + Sprite s = (Sprite) Interpret.sprites.elementAt(i); 117 + if (s.name.equals(objectName) && !s.isClone) { 118 + targetSprite = s; 119 + break; 120 + } 121 + } 122 + } 123 + 124 + if (targetSprite == null) { 125 + // If target sprite not found, return a default value (0 for numbers, empty string for names) 126 + if (property.equals("costume name") || property.equals("backdrop name")) { 127 + return new Value(""); 128 + } 129 + return new Value(0); 130 + } 131 + 132 + if (property.equals("x position")) { 133 + return new Value(targetSprite.xPosition); 134 + } else if (property.equals("y position")) { 135 + return new Value(targetSprite.yPosition); 136 + } else if (property.equals("direction")) { 137 + return new Value(targetSprite.rotation); 138 + } else if (property.equals("costume #") || property.equals("backdrop #")) { 139 + return new Value(targetSprite.currentCostume + 1); 140 + } else if (property.equals("costume name") || property.equals("backdrop name")) { 141 + if (targetSprite.costumes.isEmpty()) return new Value(""); 142 + return new Value(((Costume)targetSprite.costumes.elementAt(targetSprite.currentCostume)).name); 143 + } else if (property.equals("size")) { 144 + return new Value(targetSprite.size); 145 + } else if (property.equals("volume")) { 146 + return new Value(targetSprite.volume); 147 + } else if (property.equals("timer")) { 148 + return new Value(projectTimer.getTimeMs() / 1000.0); 149 + } 150 + 151 + // Check for variables of the target sprite by name 152 + Enumeration varEnum = targetSprite.variables.elements(); 153 + while(varEnum.hasMoreElements()) { 154 + Variable var = (Variable)varEnum.nextElement(); 155 + if (var.name.equals(property)) { 156 + return var.value; 157 + } 158 + } 159 + 160 + return new Value(0); 161 + } 162 + } 163 + 164 + public static class MouseX implements BlockExecutor.ValueBlockHandler { 165 + public Value run(Block block, Sprite sprite) { 166 + return new Value(Input.mousePointer.x); 167 + } 168 + } 169 + 170 + public static class MouseY implements BlockExecutor.ValueBlockHandler { 171 + public Value run(Block block, Sprite sprite) { 172 + return new Value(Input.mousePointer.y); 173 + } 174 + } 175 + 176 + public static class DistanceTo implements BlockExecutor.ValueBlockHandler { 177 + public Value run(Block block, Sprite sprite) { 178 + Value targetValue = Scratch.getInputValue(block, "DISTANCETOMENU", sprite); 179 + String objectName = targetValue.asString(); 180 + 181 + if (objectName.equals("_mouse_")) { 182 + double dx = Input.mousePointer.x - sprite.xPosition; 183 + double dy = Input.mousePointer.y - sprite.yPosition; 184 + return new Value(Math.sqrt(dx * dx + dy * dy)); 185 + } 186 + 187 + Sprite targetSprite = null; 188 + // Find sprite by name (non-clone) 189 + for (int i = 0; i < Interpret.sprites.size(); i++) { 190 + Sprite s = (Sprite) Interpret.sprites.elementAt(i); 191 + if (s.name.equals(objectName) && !s.isClone) { 192 + targetSprite = s; 193 + break; 194 + } 195 + } 196 + 197 + if (targetSprite != null) { 198 + double dx = targetSprite.xPosition - sprite.xPosition; 199 + double dy = targetSprite.yPosition - sprite.yPosition; 200 + return new Value(Math.sqrt(dx * dx + dy * dy)); 201 + } 202 + 203 + return new Value(10000); 204 + } 205 + } 206 + 207 + public static class DaysSince2000 implements BlockExecutor.ValueBlockHandler { 208 + public Value run(Block block, Sprite sprite) { 209 + Calendar cal = Calendar.getInstance(); 210 + //cal.set(2000, Calendar.JANUARY, 1, 0, 0, 0); 211 + cal.set(Calendar.YEAR, 2000); 212 + cal.set(Calendar.MONTH, Calendar.JANUARY); 213 + cal.set(Calendar.DATE, 1); 214 + cal.set(Calendar.HOUR_OF_DAY, 0); 215 + cal.set(Calendar.MINUTE, 0); 216 + cal.set(Calendar.SECOND, 0); 217 + long millis2000 = cal.getTime().getTime(); 218 + long currentMillis = System.currentTimeMillis(); 219 + long diffMillis = currentMillis - millis2000; 220 + double days = diffMillis / (1000.0 * 60 * 60 * 24); 221 + return new Value(days); 222 + } 223 + } 224 + 225 + public static class Current implements BlockExecutor.ValueBlockHandler { 226 + public Value run(Block block, Sprite sprite) { 227 + String unit = Scratch.getFieldValue(block, "CURRENTMENU"); 228 + Calendar cal = Calendar.getInstance(); // Current time 229 + 230 + if (unit.equals("YEAR")) return new Value(cal.get(Calendar.YEAR)); 231 + if (unit.equals("MONTH")) return new Value(cal.get(Calendar.MONTH) + 1); // Calendar.MONTH is 0-indexed 232 + if (unit.equals("DATE")) return new Value(cal.get(Calendar.DAY_OF_MONTH)); 233 + if (unit.equals("DAYOFWEEK")) { 234 + // Scratch's day of week: Sunday=7, Monday=1, ..., Saturday=6 235 + // Java's Calendar.DAY_OF_WEEK: Sunday=1, Monday=2, ..., Saturday=7 236 + return new Value((cal.get(Calendar.DAY_OF_WEEK) + 5) % 7 + 1); 237 + } 238 + if (unit.equals("HOUR")) return new Value(cal.get(Calendar.HOUR_OF_DAY)); // 24-hour format 239 + if (unit.equals("MINUTE")) return new Value(cal.get(Calendar.MINUTE)); 240 + if (unit.equals("SECOND")) return new Value(cal.get(Calendar.SECOND)); 241 + 242 + return new Value(); 243 + } 244 + } 245 + 246 + public static class SensingAnswer implements BlockExecutor.ValueBlockHandler { 247 + public Value run(Block block, Sprite sprite) { 248 + return new Value(Interpret.answer); 249 + } 250 + } 251 + 252 + public static class KeyPressed implements BlockExecutor.ValueBlockHandler { 253 + public Value run(Block block, Sprite sprite) { 254 + Value keyOptionValue = Scratch.getInputValue(block, "KEY_OPTION", sprite); 255 + String keyToCheck = keyOptionValue.asString(); 256 + return new Value(Input.isKeyPressed(keyToCheck)); 257 + } 258 + } 259 + 260 + public static class TouchingObject implements BlockExecutor.ValueBlockHandler { 261 + public Value run(Block block, Sprite sprite) { 262 + Value targetValue = Scratch.getInputValue(block, "TOUCHINGOBJECTMENU", sprite); 263 + String objectName = targetValue.asString(); 264 + 265 + if (objectName.equals("_mouse_")) { 266 + return new Value(Interpret.isColliding("mouse", sprite, null, null)); 267 + } else if (objectName.equals("_edge_")) { 268 + return new Value(Interpret.isColliding("edge", sprite, null, null)); 269 + } else { 270 + // Find sprite by name (non-clone) 271 + for (int i = 0; i < Interpret.sprites.size(); i++) { 272 + Sprite currentSprite = (Sprite) Interpret.sprites.elementAt(i); 273 + if (currentSprite.name.equals(objectName) && !currentSprite.isClone) { 274 + return new Value(Interpret.isColliding("sprite", sprite, currentSprite, objectName)); 275 + } 276 + } 277 + } 278 + return new Value(false); 279 + } 280 + } 281 + 282 + public static class MouseDown implements BlockExecutor.ValueBlockHandler { 283 + public Value run(Block block, Sprite sprite) { 284 + return new Value(Input.mousePointer.isPressed); 285 + } 286 + } 287 + 288 + public static class Username implements BlockExecutor.ValueBlockHandler { 289 + public Value run(Block block, Sprite sprite) { 290 + return new Value(Input.getUsername()); 291 + } 292 + } 293 + }