Skip to content

Commit ba53162

Browse files
authored
Merge pull request code-dot-org#10283 from code-dot-org/sprite-groups-impl
Add blocks for managing sprite groups
2 parents 0bc6ccf + efac94e commit ba53162

File tree

8 files changed

+387
-21
lines changed

8 files changed

+387
-21
lines changed

apps/i18n/studio/en_us.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"addPoints300": "add 300 points",
1313
"addPoints1000": "add 1000 points",
1414
"addPointsTooltip" : "Add points to the score.",
15+
"alien": "alien",
1516
"alienInvasion": "Alien Invasion!",
1617
"backgroundBlack": "black",
1718
"backgroundCave": "cave",
@@ -26,6 +27,8 @@
2627
"backgroundSpace": "space",
2728
"backgroundTennis": "tennis",
2829
"backgroundWinter": "winter",
30+
"bat": "bat",
31+
"bird": "bird",
2932
"calloutPutCommandsHereRunStart": "Put commands here to have them run when the program starts",
3033
"calloutBlocklyPlaceGoUpHere": "Place a \"go up\" block here to go up",
3134
"calloutClickEvents": "Click on the events header to see event function blocks.",
@@ -43,6 +46,7 @@
4346
"calloutSetDroidAndSpeed": "Set your droid and its speed",
4447
"calloutFinishButton": "Click here when you are ready to share your game",
4548
"calloutBlocklyCategories": "You can click on Commands to see all your commands and Events to see all the events",
49+
"cat": "cat",
4650
"catActions": "Actions",
4751
"catCommands": "Commands",
4852
"catControl": "Loops",
@@ -52,6 +56,8 @@
5256
"catProcedures": "Functions",
5357
"catText": "Text",
5458
"catVariables": "Variables",
59+
"caveboy": "cave boy",
60+
"cavegirl": "cave girl",
5561
"changeScoreTooltip" : "Add or remove a point to the score.",
5662
"changeScoreTooltipK1" : "Add a point to the score.",
5763
"tapOrClickToPlay" : "Tap or click to play",
@@ -62,6 +68,9 @@
6268
"continue": "Continue",
6369
"decrementPlayerScore" : "remove point",
6470
"defaultSayText": "type here",
71+
"dinosaur": "dinosaur",
72+
"dog": "dog",
73+
"dragon": "dragon",
6574
"dropletBlock_addCharacter_description": "Add a character to the scene.",
6675
"dropletBlock_addCharacter_param0": "type",
6776
"dropletBlock_addCharacter_param0_description": "The type of the character to be added ('random', 'Stormtrooper', 'RebelPilot', 'PufferPig', 'Mynock', 'MouseDroid', 'Tauntaun', or 'Probot').",
@@ -161,6 +170,7 @@
161170
"foodFight": "Food fight!",
162171
"for": "for",
163172
"getScoreTooltip": "A variable representing the player's score",
173+
"ghost": "ghost",
164174
"goSprite": "go",
165175
"hello": "hello",
166176
"helloWorld": "Hello World!",
@@ -237,6 +247,7 @@
237247
"itemMouseDroid": "Mouse Droid",
238248
"itemTauntaun": "Tauntaun",
239249
"itemProbot": "Probot",
250+
"knight": "knight",
240251
"localFunction": "You have placed {funcName} inside another function. You should move {funcName} to a different location in your program.",
241252
"loseMessage": "You lose!",
242253
"makeProjectileDisappear": "disappear",
@@ -270,6 +281,7 @@
270281
"makeProjectileIAProjectile5": "make ice crystal",
271282
"makeProjectileTooltip": "Make the projectile that just collided disappear or bounce.",
272283
"makeYourOwn": "Make Your Own Play Lab App",
284+
"monster": "monster",
273285
"moveDirectionDown": "down",
274286
"moveDirectionLeft": "left",
275287
"moveDirectionRight": "right",
@@ -304,10 +316,14 @@
304316
"moveUpRightTooltip": "Move an actor diagonally up and to the right.",
305317
"moveTooltip": "Move an actor.",
306318
"nextLevel": "Congratulations! You have completed this puzzle.",
319+
"ninja": "masked ninja",
307320
"no": "No",
308321
"numBlocksNeeded": "This puzzle can be solved with %1 blocks.",
322+
"octopus": "octopus",
309323
"onEventTooltip": "Execute code in response to the specified event.",
310324
"ouchExclamation": "Ouch!",
325+
"penguin": "penguin",
326+
"pirate": "pirate",
311327
"playSoundCrunch": "play crunch sound",
312328
"playSoundGoal1": "play goal 1 sound",
313329
"playSoundGoal2": "play goal 2 sound",
@@ -365,6 +381,7 @@
365381
"playSoundApplause": "play applause sound",
366382
"playSoundRandom": "play random sound",
367383
"points": "points",
384+
"princess": "princess",
368385
"projectileIAProjectile1": "hearts",
369386
"projectileIAProjectile2": "boulder",
370387
"projectileIAProjectile3": "ice cube",
@@ -412,6 +429,7 @@
412429
"repeatForever": "repeat forever",
413430
"repeatDo": "do",
414431
"repeatForeverTooltip": "Execute the actions in this block repeatedly while the story is running.",
432+
"robot": "robot",
415433
"spaceInvasion": "Space invasion!",
416434
"saySprite": "say",
417435
"saySpriteN": "actor {spriteIndex} say",
@@ -531,6 +549,10 @@
531549
"setDroidSpeedFast": "set droid to a fast speed",
532550
"setDroidSpeedTooltip": "Sets the speed of the droid",
533551
"setEnemySpeed": "set enemy speed",
552+
"setEverySpriteNameChaseActor": "set every {spriteName} to chase actor",
553+
"setEverySpriteNameFleeActor": "set every {spriteName} to flee actor",
554+
"setEverySpriteNameSpeed": "set every {spriteName} speed",
555+
"setEverySpriteNameWander": "set every {spriteName} to wander",
534556
"setItemSpeedSet": "set type",
535557
"setItemSpeedTooltip": "Sets the speed for a set of characters",
536558
"setPlayerSpeed": "set player speed",
@@ -611,6 +633,10 @@
611633
"setSpriteSpeedVeryFast": "to a very fast speed",
612634
"setSpriteSpeedTooltip": "Sets the speed of an actor",
613635
"setSpriteZombie": "to a zombie image",
636+
"setSpritesChaseTooltip": "Makes all the selected actors chase another actor",
637+
"setSpritesFleeTooltip": "Makes all the selected actors flee from another actor",
638+
"setSpritesStopTooltip": "Makes all the selected actors stop wandering, chasing, or fleeing",
639+
"setSpritesWanderTooltip": "Makes all the selected actors wander around the screen at random",
614640
"setMap": "set map",
615641
"setMapRandom": "set random map",
616642
"setMapBlank": "set blank map",
@@ -639,6 +665,8 @@
639665
"setDroidC3PO": "to C-3PO",
640666
"setSprite": "set",
641667
"setSpriteN": "set actor {spriteIndex}",
668+
"soccerboy": "soccer boy",
669+
"soccergirl": "soccer girl",
642670
"soundCrunch": "crunch",
643671
"soundGoal1": "goal 1",
644672
"soundGoal2": "goal 2",
@@ -652,17 +680,24 @@
652680
"soundWinPoint2": "win point 2",
653681
"soundWood": "wood",
654682
"soundRandom": "random",
683+
"spacebot": "spacebot",
655684
"speed": "speed",
685+
"squirrel": "squirrel",
656686
"startSetValue": "start (function)",
657687
"startSetVars": "game_vars (title, subtitle, background, target, danger, player)",
658688
"startSetFuncs": "game_funcs (update-target, update-danger, update-player, collide?, on-screen?)",
689+
"stopEverySpriteName": "stop every {spriteName}",
659690
"stopSprite": "stop",
660691
"stopSpriteN": "stop actor {spriteIndex}",
661692
"stopTooltip": "Stops an actor's movement.",
693+
"tennisboy": "tennis boy",
694+
"tennisgirl": "tennis girl",
662695
"throwSprite": "throw",
663696
"throwSpriteN": "actor {spriteIndex} throw",
664697
"throwTooltip": "Throws a projectile from the specified actor.",
665698
"toSet": "to set",
699+
"toWander": "to wander",
700+
"unicorn": "unicorn",
666701
"vanish": "vanish",
667702
"vanishActorN": "vanish actor {spriteIndex}",
668703
"vanishTooltip": "Vanishes the actor.",
@@ -761,7 +796,10 @@
761796
"whenUp": "when up arrow",
762797
"whenUpTooltip": "Execute the actions below when the up arrow key is pressed.",
763798
"winMessage": "You win!",
799+
"witch": "witch",
800+
"wizard": "wizard",
764801
"yes": "Yes",
802+
"zombie": "zombie",
765803
"failedHasSetSprite": "Next time, set a droid.",
766804
"failedHassetDroidSpeed": "Next time, set a droid speed.",
767805
"failedHasSetBackground": "Next time, set the background.",

apps/src/studio/Item.js

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export default class Item extends Collidable {
4444
this.displayDir = Direction.SOUTH;
4545
this.startFadeTime = null;
4646
this.fadeTime = constants.ITEM_FADE_TIME;
47+
this.targetSpriteIndex = 0;
4748

4849
/** @private {StudioAnimation} */
4950
this.animation_ = new StudioAnimation(Object.assign({}, options, {
@@ -150,21 +151,30 @@ export default class Item extends Collidable {
150151
// Has the item reached its destination grid position?
151152
// (There is a small margin of error to allow for per-update movements greater
152153
// than a single pixel.)
153-
var speed = valueOr(this.speed, 0);
154-
if (this.destGridX !== undefined &&
155-
(Math.abs(this.x - (this.destGridX * Studio.SQUARE_SIZE + Studio.HALF_SQUARE)) <= speed &&
156-
Math.abs(this.y - (this.destGridY * Studio.SQUARE_SIZE + Studio.HALF_SQUARE)) <= speed)) {
157-
this.gridX = this.destGridX;
158-
this.gridY = this.destGridY;
159-
reachedDestinationGridPosition = true;
154+
if (this.destGridX !== undefined) {
155+
var speed = valueOr(this.speed, 0);
156+
var dirUnit = Direction.getUnitVector(this.dir);
157+
var center = this.getCenterPos();
158+
var destVector = {
159+
x: (this.destGridX * Studio.SQUARE_SIZE + Studio.HALF_SQUARE) - center.x,
160+
y: (this.destGridY * Studio.SQUARE_SIZE + Studio.HALF_SQUARE) - center.y
161+
};
162+
// Take the dot product of dirUnit and destVector to see if continuing to
163+
// move in that direction will bring the item any closer to its
164+
// destination.
165+
if (dirUnit.x * destVector.x + dirUnit.y * destVector.y <= speed) {
166+
this.gridX = this.destGridX;
167+
this.gridY = this.destGridY;
168+
reachedDestinationGridPosition = true;
169+
}
160170
}
161171

162172
// Are we missing a destination location in grid coords?
163173
// Or have we already reached our prior destination location in grid coords?
164174
// If not, determine it.
165175
if (this.destGridX === undefined || reachedDestinationGridPosition) {
166176

167-
var sprite = Studio.sprite[0];
177+
var sprite = Studio.sprite[this.targetSpriteIndex];
168178

169179
var spriteX = sprite.x + sprite.width/2;
170180
var spriteY = sprite.y + sprite.height/2;
@@ -233,10 +243,7 @@ export default class Item extends Collidable {
233243
// cull candidates that won't be possible
234244
for (var i = candidates.length-1; i >= 0; i--) {
235245
var candidate = candidates[i];
236-
var atEdge = candidate.gridX < 0 || candidate.gridX >= Studio.COLS ||
237-
candidate.gridY < 0 || candidate.gridY >= Studio.ROWS;
238-
var hasWall = !atEdge && Studio.getWallValue(candidate.gridY, candidate.gridX);
239-
if (atEdge || hasWall || candidate.score === 0) {
246+
if (candidate.score === 0 || this.atEdge(candidate) || this.hasWall(candidate)) {
240247
candidates.splice(i, 1);
241248
}
242249
}
@@ -277,6 +284,15 @@ export default class Item extends Collidable {
277284
}
278285
}
279286

287+
atEdge(candidate) {
288+
return candidate.gridX < 0 || candidate.gridX >= Studio.COLS ||
289+
candidate.gridY < 0 || candidate.gridY >= Studio.ROWS;
290+
}
291+
292+
hasWall(candidate) {
293+
return Studio.getWallValue(candidate.gridY, candidate.gridX);
294+
}
295+
280296
/**
281297
* Isolated update logic for "watchActor" activity where the "item" keeps
282298
* turning to look at the actor with the given sprite index.
@@ -309,9 +325,13 @@ export default class Item extends Collidable {
309325
/**
310326
* Sets the activity property for this item.
311327
* @param {string} type Valid options are: none, watchActor, roam, chase, or flee
328+
* @param {number} targetSpriteIndex optional target sprite used with chase and flee
312329
*/
313-
setActivity(type) {
330+
setActivity(type, targetSpriteIndex) {
314331
this.activity = type;
332+
if (targetSpriteIndex !== undefined) {
333+
this.targetSpriteIndex = targetSpriteIndex;
334+
}
315335
}
316336

317337
/**
@@ -389,6 +409,13 @@ export default class Item extends Collidable {
389409
Studio.tickCount);
390410
}
391411

412+
getCenterPos() {
413+
return {
414+
x: this.x,
415+
y: this.y,
416+
};
417+
}
418+
392419
getNextPosition() {
393420
var unit = Direction.getUnitVector(this.dir);
394421
var speed = this.speed;

apps/src/studio/Sprite.js

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as constants from './constants';
2-
import Collidable from './collidable';
2+
import Item from './Item';
33
import StudioAnimation from './StudioAnimation';
44
import StudioSpriteSheet from './StudioSpriteSheet';
55
import { valueOr } from '../utils';
@@ -10,11 +10,11 @@ const Emotions = constants.Emotions;
1010
const NextTurn = constants.NextTurn;
1111

1212
/**
13-
* A Sprite is a type of Collidable.
13+
* A Sprite is a type of Item.
1414
* Note: x/y represent x/y of center in gridspace
15-
* @extends {Collidable}
15+
* @extends {Item}
1616
*/
17-
export default class Sprite extends Collidable {
17+
export default class Sprite extends Item {
1818
constructor(options) {
1919
// call collidable constructor
2020
super(options);
@@ -310,10 +310,8 @@ export default class Sprite extends Collidable {
310310
/**
311311
* This function should be called every frame, and moves the sprite around.
312312
*/
313-
314-
// TODO (cpirich): ensure that update is called for Sprite object
315-
316313
update() {
314+
super.update();
317315

318316
// Draw the sprite's current location.
319317
Studio.drawDebugRect("spriteCenter", this.x, this.y, 3, 3);
@@ -452,6 +450,15 @@ export default class Sprite extends Collidable {
452450
this.lastDrawPosition = drawPosition;
453451
}
454452

453+
// TODO(ram): make x and y props consistent with Item. In sprites they
454+
// represent the top left corner, in items they're the center.
455+
getCenterPos() {
456+
return {
457+
x: this.x + this.width / 2,
458+
y: this.y + this.height / 2,
459+
};
460+
}
461+
455462
getNextPosition() {
456463
var unit = Direction.getUnitVector(this.dir);
457464
var speed = this.speed;
@@ -527,4 +534,18 @@ export default class Sprite extends Collidable {
527534
this.legacyAnimation_.setOpacity(newOpacity);
528535
}
529536
}
537+
538+
atEdge(candidate) {
539+
return candidate.gridX < 0 ||
540+
(candidate.gridX * Studio.SQUARE_SIZE + this.width) > Studio.MAZE_WIDTH ||
541+
candidate.gridY < 0 ||
542+
(candidate.gridY * Studio.SQUARE_SIZE + this.height) > Studio.MAZE_HEIGHT;
543+
}
544+
545+
hasWall(candidate) {
546+
return Studio.willSpriteTouchWall(
547+
this,
548+
candidate.gridX * Studio.SQUARE_SIZE,
549+
candidate.gridY * Studio.SQUARE_SIZE);
550+
}
530551
}

apps/src/studio/api.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,39 @@ exports.setSpriteXY = function (id, spriteIndex, xpos, ypos) {
9999
});
100100
};
101101

102+
exports.setSpritesWander = function (id, spriteName) {
103+
Studio.queueCmd(id, 'setSpritesWander', {
104+
'spriteName': spriteName
105+
});
106+
};
107+
108+
exports.setSpritesStop = function (id, spriteName) {
109+
Studio.queueCmd(id, 'setSpritesStop', {
110+
'spriteName': spriteName
111+
});
112+
};
113+
114+
exports.setSpritesChase = function (id, targetSpriteIndex, spriteName) {
115+
Studio.queueCmd(id, 'setSpritesChase', {
116+
'spriteName': spriteName,
117+
'targetSpriteIndex': targetSpriteIndex,
118+
});
119+
};
120+
121+
exports.setSpritesFlee = function (id, targetSpriteIndex, spriteName) {
122+
Studio.queueCmd(id, 'setSpritesFlee', {
123+
'spriteName': spriteName,
124+
'targetSpriteIndex': targetSpriteIndex,
125+
});
126+
};
127+
128+
exports.setSpritesSpeed = function (id, speed, spriteName) {
129+
Studio.queueCmd(id, 'setSpritesSpeed', {
130+
'spriteName': spriteName,
131+
'speed': speed,
132+
});
133+
};
134+
102135
exports.addGoal = function (id, value) {
103136
Studio.queueCmd(id, 'addGoal', {
104137
'value': Number(value)

0 commit comments

Comments
 (0)