diff --git a/src/Sprite.js b/src/Sprite.js index 62aff76..26ee11e 100644 --- a/src/Sprite.js +++ b/src/Sprite.js @@ -606,6 +606,53 @@ export class Sprite extends SpriteBase { } while (t < 1); } + ifOnEdgeBounce() { + const nearestEdge = this.nearestEdge(); + if (!nearestEdge) return; + const rad = this.scratchToRad(this.direction); + let dx = Math.cos(rad); + let dy = Math.sin(rad); + switch (nearestEdge) { + case Sprite.Edge.LEFT: + dx = Math.max(0.2, Math.abs(dx)); + break; + case Sprite.Edge.RIGHT: + dx = -Math.max(0.2, Math.abs(dx)); + break; + case Sprite.Edge.TOP: + dy = -Math.max(0.2, Math.abs(dy)); + break; + case Sprite.Edge.BOTTOM: + dy = Math.max(0.2, Math.abs(dy)); + break; + } + this.direction = this.radToScratch(Math.atan2(dy, dx)); + this.positionInFence(); + } + + positionInFence() { + // https://github.com/LLK/scratch-vm/blob/develop/src/sprites/rendered-target.js#L949 + const fence = this.stage.fence; + const bounds = this._project.renderer.getTightBoundingBox(this); + + let dx = 0, + dy = 0; + if (bounds.left < fence.left) { + dx += fence.left - bounds.left; + } + if (bounds.right > fence.right) { + dx += fence.right - bounds.right; + } + if (bounds.top > fence.top) { + dy += fence.top - bounds.top; + } + if (bounds.bottom < fence.bottom) { + dy += fence.bottom - bounds.bottom; + } + + this.goto(this.x + dx, this.y + dy); + } + get penDown() { return this._penDown; } @@ -705,6 +752,38 @@ export class Sprite extends SpriteBase { } } + nearestEdge() { + const bounds = this._project.renderer.getTightBoundingBox(this); + const { width: stageWidth, height: stageHeight } = this.stage; + const distLeft = Math.max(0, stageWidth / 2 + bounds.left); + const distTop = Math.max(0, stageHeight / 2 - bounds.top); + const distRight = Math.max(0, stageWidth / 2 - bounds.right); + const distBottom = Math.max(0, stageHeight / 2 + bounds.bottom); + // Find the nearest edge. + let nearestEdge = ""; + let minDist = Infinity; + if (distLeft < minDist) { + minDist = distLeft; + nearestEdge = Sprite.Edge.LEFT; + } + if (distTop < minDist) { + minDist = distTop; + nearestEdge = Sprite.Edge.TOP; + } + if (distRight < minDist) { + minDist = distRight; + nearestEdge = Sprite.Edge.RIGHT; + } + if (distBottom < minDist) { + minDist = distBottom; + nearestEdge = Sprite.Edge.BOTTOM; + } + if (minDist > 0) { + nearestEdge = null; + } + return nearestEdge; + } + say(text) { clearTimeout(this._speechBubble.timeout); this._speechBubble = { text: String(text), style: "say", timeout: null }; @@ -750,6 +829,13 @@ Sprite.RotationStyle = Object.freeze({ DONT_ROTATE: Symbol("DONT_ROTATE") }); +Sprite.Edge = Object.freeze({ + BOTTOM: Symbol("BOTTOM"), + LEFT: Symbol("LEFT"), + RIGHT: Symbol("RIGHT"), + TOP: Symbol("TOP") +}); + export class Stage extends SpriteBase { constructor(initialConditions, ...args) { super(initialConditions, ...args); @@ -767,6 +853,13 @@ export class Stage extends SpriteBase { } }); + this.fence = { + left: -this.width / 2, + right: this.width / 2, + top: this.height / 2, + bottom: -this.height / 2 + }; + this.name = "Stage"; // For obsolete counter blocks.