/** * Copyright (c) 2015-2019. Docentron PTY LTD. All rights reserved. * This material may not be reproduced, displayed, modified or distributed without * the express prior written permission of the copyright holder. * * This is a Docentron application created using Docentron Web Application API * Click on DC API tab to browse DC API classes and functions. * Click on Asset List tab to browse game assets defined for this applicaiton template. * This applicaiton also uses Phaser/PixiJS game API. * * Many application templates are provided to get started quickly. The templates demonstrate how the API can be used * to quickly create web applications and games. * * Applicaiton Name: Ordering game. * Application type: Arcade Puzzle block game. * Base game: Grumpy Bird 2. * Author: Docentron * * v1.7 Added multi point support up to 2 pointers * v1.6: added missing comments * V1.5: Now support sprite for answer images. Score is the number of frames * v1.4 * - Add correct effect sprite * - Add correct effect sound * - Add wrong effect sprite * - Add wrong effect sprite **************************************************************/ /************************************************************** * Entry point of the game app * This is automatically called when the application is loaded. **************************************************************/ window.ibsgl = ibStartGameLevel; // Do not change this line window.startDCApp = function(){ibStartGameLevel("game_stage_one");}; // DEVELOPER_CODE Do not change this line function ibStartGameLevel(gameContainer){ //----------------------------------------------- // In order to create a new game based on this based game, follow these steps: // Step 1: update/replace PuzzleGame class with your own Game Class // Step 2: update/replace PStatePlay class with your own StatePlay class Genari.prepareGame(OrderingGame, 800, 426, GameControl, PStatePlay); // game size is 800x426 // All game assets are accessible using Genari.dcGame.k.assetKeyName // These are defined in the asset list (please see Asset List tab). // Step 3: override and customise Genari.StatePlay methods // Genari.Control: GameControl // Genari.Game: OrderingGame // Genari.StatePlay: preloadState() Load theme assets // Genari.StatePlay: loadThemeAssets() // Genari.StatePlay: createLayersGroups() // Genari.StatePlay: createWorld() // Genari.StatePlay: createHud() Setup the HUD of the game: score bars, pause button, player icon,... // Genari.StatePlay: createPlayer() Create player character // Genari.StatePlay: createStateObjects() Create background, tilemaps, monsters, pickup items // Genari.StatePlay: shutdownState() Clean up game assets after completing the game level // Genari.StatePlay: updateState() Update state for each frame // Genari.Group: Add Group classes to manage your game objects (monsters, bullets, pickup items, boss,...) // the tilemap asset has two layers: BackgroundLayer and EnvironmentLayer. // it also has object layer: AnswerObj, which defines the locations of the drop locations of the answers. Genari.k.backgroundLayerKey = 'BackgroundLayer'; Genari.k.dropBoxObjName = 'DropBoxObj'; } /************************************************************** * The game class that holds game specific information. * * An instance of this is automatically created by Genari.prepareGame() and stored in Genari.dcGame. * This is not a Phaser.Game object. * Genari.dcGame.phaserGame contains Phaser.Game. * * Use Javascript annotations to document your code: * https://developers.google.com/closure/compiler/docs/js-for-compiler#tags * * @param {Object} game_themes An array of themes. A theme is a dictionary. See app.js for theme data * @param {number|undefined} [game_width] Default 800 Width of the canvas * @param {number|undefined} [game_height] Default 426 Height of the canvas * * @constructor * @extends {Genari.Game} ***************************************************************/ var OrderingGame = function(game_themes, game_width, game_height){ if (!(this instanceof OrderingGame)) return new OrderingGame(game_themes, game_width, game_height); // this ensures Javascript classes functions as intended Genari.Game.call(this, game_themes, game_width, game_height); // must call the parent class constructor // adding new class attributes (properties) specific to this game this.gc_score_text_x = this.game_width - 40; /** @type{Genari.Hud} */ this.hud = null; this.checkEnd = false; }; OrderingGame.prototype = Object.create(Genari.Game.prototype); // extend Genari.Game OrderingGame.prototype.constructor = OrderingGame; // set constructor property /** * Check the end of the game level and load the next level if the current level is completed */ OrderingGame.prototype.checkLevelEnd = function(){ // check if answered all questions correctly if (this.scoreBox.getValue() < this.groupAnswers.numberOfAnswers){ return; } if (this.checkEnd === false){ // delay 1 second before level end setTimeout(function(){ this.checkEnd = false; this.saveAndStartNextLevel(); // Record score and start next level }.bind(this), 1000); // make levelEnd just execute once this.checkEnd = true; } }; /************************************************************** * Control class for checking and updating buttons, keyboard, mouse, and finger touchs. * * Manages creation and control of all keyboard controls and on-screen button or joystick controls * * @param {PhysicsGame} dcGame * @param {Genari.RunPlayer|undefined} [player] The player character that user controls * @param {string|undefined} [bulletSpriteKey] sprite key. This must be already loaded during player-state.create() * @param {string|undefined} [bulletSoundKey] sprite key. This must be already loaded during player-state.create() * * @constructor * @extends {Genari.Control} **************************************************************/ var GameControl = function(dcGame, player, bulletSpriteKey, bulletSoundKey){ if (!(this instanceof GameControl)){return new GameControl(dcGame, player, bulletSpriteKey, bulletSoundKey);} Genari.Control.call(this, dcGame, player, bulletSpriteKey, bulletSoundKey); // Never redefine inherited properties and methods here. // To override methods, must use prototype. // Constants related control buttons this.BUTTON_EDGE_MARGIN = 10; // Other attributes that we often need to refer to this.player = player; this.dcGame = dcGame; this.pointerUp = [true, true, true]; }; GameControl.prototype = Object.create(Genari.Control.prototype); // extends Genari.Control GameControl.prototype.constructor = GameControl; // set constructor property /** * This is called when the play state is about to start for us to prepare * the buttons, keyboard shortcut keys, pointers, etc. * * Put here code for creating control buttons etc. */ GameControl.prototype.createControls = function(){ // Destroy any existing buttons this.destroyControlButtons(); this.pointerUp = [true, true, true]; // Add some buttons here if needed // at the moment we are not using any buttons in this game. Comment this out if you are adding some. //this.createButtonHitArea(); // if added some buttons here, uncomment to add button hit area for the buttons that are added. // Hide the on-screen buttons until the play-state starts. Buttons will automatically shown when it starts. this.hideOnScreenButtons(); }; /** * Chcek a pointer and update pickup/drag/drop of hint/answer images * @param {Array} pointers Array of pointer states [mousePointerState, touchPointer1State, touchPointer2State] * @param {number} pointerId */ GameControl.prototype.updatePointer = function (pointers, pointerId) { var pointer; pointer = pointers[pointerId]; // get the selected pointer state [x, y, isDown] if(pointer[2]){ // pointer down // pickup a hint image under the pointer. // We make effect sound if pointer moves over hint images // We also us this later to check if the answer image is dropped at the correct location this.dcGame.groupDropBoxes.pickupDropBox(pointer, pointerId); // check if there is a hint image under cur loc and play sound if visiting a new hint image // if pointer was up, if (this.pointerUp[pointerId]){ // pointer is just down from up state // pickup an answer image if there is an answer image under the pointer this.dcGame.groupAnswers.pickupAnswerImage(pointer, pointerId); // pick up an answer image object and start to drag it this.pointerUp[pointerId] = false; // set to pointer down state. We use this to track state change event } // Perform drag action of a selected answer image if an asnwer image is selected. this.dcGame.groupAnswers.dragAnswerImage(pointer, pointerId); } else { // pointer up // if pointer was down and picked up an ansewr, drop it and check if dropped in the right location. if (!this.pointerUp[pointerId]){ // drop the selected answer image and check if it is dropped at the correct loacation this.dcGame.groupAnswers.dropAnswerImage(pointerId); this.dcGame.groupDropBoxes.selectedDropBox[pointerId] = null; this.pointerUp[pointerId] = true; // set to pointer up } } }; /** * Called by play-state updateState() for each frame, before the frame is rendered to the display. * This allow us to check keyboards, joystick, finger touches, and mouse clicks. * * Make sure we call this from play-state.updateState() * * In this game, we check if a pointer (finger touch or mouse button) is down or up and perform some actions: * pickup an answer image, drag answer image, drop answer image. */ GameControl.prototype.update = function(){ if (this.dcGame.isPaused) return; // TODO: learning point. We get pointer states for all three pointers: mouse pointer, touch pointer 1, touch // game is not paused, so we pickUpHintImage or drag or drop var pointers = Genari.getPointerStates(); // get states of mouse pointer, touch pointer 1, touch pointer 2 // we track pointer actions seperately. Now pickingup/dragging/dropping hint/answer images are indexed by pointerId this.updatePointer(pointers, 0); // pointerId = 0: mouse pointer this.updatePointer(pointers, 1); // pointerId = 1: touch pointer 1 this.updatePointer(pointers, 2); // pointerId = 2: touch pointer 2 // if (!this.dcGame.isPaused){ // // game is not paused, so we pickupDropBox // if (Genari.phaserGame.input.activePointer.isDown){ // // a pointer (mouse or finger touch) is in down state // this.dcGame.groupDropBoxes.pickupDropBox(); // check if there is a drop box image under cur loc and play sound if visiting a new drop box image // if (this.pointerUp){ // // pointer is just down from up state // this.dcGame.groupAnswers.pickupAnswerImage(); // pick up an answer image object and start to drag it // this.pointerUp = false; // set to pointer down state. We use this to track state change event // } // // Perform drag action of a selected answer image if an asnwer image is selected. // this.dcGame.groupAnswers.dragAnswerImage(); // } else { // // pointer up // // if pointer was down and picked up an ansewr, drop it and check if dropped in the right location. // if (!this.pointerUp){ // this.dcGame.groupAnswers.dropAnswerImage(); // this.dcGame.groupDropBoxes.selectedDropBox = null; // this.pointerUp = true; // set to pointer up // } // } // } }; /************************************************************** * Phaser state class, Play state. * You can extend this class to customise your Play state * * Customise this class for your own game. * * @param {Genari.Game} theGameObj the only game object * @param {string} key name of the state E.g., "play" * * @constructor * @extends {Genari.StatePlay} **************************************************************/ var PStatePlay = function( theGameObj, key ) { if (!(this instanceof PStatePlay)) return new PStatePlay(theGameObj, key); Genari.StatePlay.call(this, theGameObj, key); // call parent constructor }; PStatePlay.prototype = Object.create(Genari.StatePlay.prototype); // extend Genari.StatePlay PStatePlay.prototype.constructor = PStatePlay; // set constructor property /** * Called before play-state starts to for us to prepare. * * This downloads theme assets from server. The loading screen will show the progress of the downloading. * * @override overriding the base class method to cusomise for our purpose */ PStatePlay.prototype.loadThemeAssets = function(){ // Load assets before use Genari.loadThemeTileMaps(); Genari.loadThemeImageSpriteAssets(); // load all images and sprites including tileset images here Genari.loadThemeAudioAssets(); this.loadAttributes(); }; /** * Read the attribute asset values */ PStatePlay.prototype.loadAttributes = function(){ var thisGame = this.dcGame; // answer image size thisGame.answerImgSize = Genari.getThemeAttribute(Genari.k.answerImageSizeAttKey, 120); // Answer Box Interval (px) thisGame.answerInterval = Genari.getThemeAttribute(Genari.k.answerBoxIntervalAttKey, 90); // First Answer Image Left Position (px) thisGame.firstAnswerLeftPos = Genari.getThemeAttribute(Genari.k.firstAnswerLeftPosAttKey, 80); // First Answer Image Top Position (px) thisGame.firstAnswerTopPos = Genari.getThemeAttribute(Genari.k.firstAnswerTopPosAttKey, 330); }; /** * Called before play-state starts for us to prepare. * * This allows us to create Layers and Groups for our game. * Layers are used to control the rendering order. Some game objects will be on top of something else. * Groups are used to control groups of game objects such as monsters, pickup items. * @override */ PStatePlay.prototype.createLayersGroups = function(){ Genari.StatePlay.prototype.createLayersGroups.call(this); // destroy previous assets/objects loaded if any. if (this.dcGame.layerBottom0) this.dcGame.layerBottom.destroy(true); if (this.dcGame.layerBottom) this.dcGame.layerBottom.destroy(true); if (this.dcGame.layerPlayer) this.dcGame.layerPlayer.destroy(true); if (this.dcGame.layerEffects) this.dcGame.layerEffects.destroy(true); if (this.dcGame.layerButtons) this.dcGame.layerButtons.destroy(true); if (this.dcGame.layerDialog) this.dcGame.layerDialog.destroy(true); // Layers to control the rendering order. You can add your own layers to control z-order. // layers are rendered based on the order they are created this.dcGame.layerBottom0 = new Genari.Layer(); // Use this as the bottom layer, Always show at the bottom. Holds word boxes this.dcGame.layerBottom = new Genari.Layer(); // Use this as the bottom layer, Always show at the bottom. Holds word boxes this.dcGame.layerPlayer = new Genari.Layer(); // holds players, monsters, player interactive items. this.dcGame.layerEffects = new Genari.Layer(); // holds explosions, bullets, effects this.dcGame.layerButtons = new Genari.Layer(); // holds control buttons, frames, windows, HUD items this.dcGame.layerDialog = new Genari.Layer(); // Always show at the top. Holds dialog boxes. Top layer // Groups to manage multiples of similar objects. // Use predefined groups which provides method of creating common types of monsters, word boxes, bullets, pick-up items // Groups also provide methods for handling collisions Genari.Group.checkOverlap() if (this.dcGame.groupDropBoxes) this.dcGame.groupDropBoxes.destroy(); if (this.dcGame.groupAnswers) this.dcGame.groupAnswers.destroy(); // create one object for both hint image objects and answer image objects... this.dcGame.groupDropBoxes = new Genari.DropBoxGroup(this.dcGame.layerPlayer, true, true); this.dcGame.groupAnswers = new Genari.AnswersGroup(this.dcGame.layerPlayer, true, true); }; /** * Called before play-state starts for us to prepare. * * This allows us to place the assets to the stage to show. * The assets must be loaded before we can place them. * loadThemeAssets would have already loaded all theme assets for us. * @override */ PStatePlay.prototype.createStateObjects = function() { var thisGame = this.dcGame; // Prepare the background and set world bounds this.createWorld(); // Create the HUD this.createHud(); // Create effects this.dcGame.correctEffectAnim = Genari.Effect.addExplosion(Genari.k.correctEffectSpriteKey, 'correct', this.dcGame.layerEffects); this.dcGame.wrongEffectAnim = Genari.Effect.addExplosion(Genari.k.wrongEffectSpriteKey, 'wrong', this.dcGame.layerEffects); // Add control keys and onscreen control buttons. We have no player: pass null thisGame.gameControl = new GameControl(thisGame, null, Genari.k.bulletSpriteKey, Genari.k.bulletSoundKey); // game, player, bullet obj, bullet sound key thisGame.gameControl.createControls(); // create game assets thisGame.groupDropBoxes.spawnDropBoxes(); thisGame.groupAnswers.spawnAnswerImageObjs(); // fill the first order thisGame.groupDropBoxes.fillThefirstDropBoxSprite(); //-------------------------------------------------------- // Lesson 6.5.b.ii Add a monster to the game: // add a pet monster to the game stage // this.flyingPet = Genari.phaserAddSprite(10, 100, // Genari.k.petSpriteKey, [0,1], this.dcGame.layerButtons); // // create animation sequences // this.flyingPet.animations.add(“flyLeft”, [0,1]); // this.flyingPet.animations.add(“flyRight”, [2,3]); // play flying right animation at 30 frames/s in loop // this.flyingPet.animations.play(“flyRight”, 30, true); // // let’s add a parameter to remember directions. We will use this later // this.flyXDirection = 1; // +1 = right, -1 = left // this.flyYDirection = 1; // +1 = down, -1 = up //-------------------------------------------------------- // Lesson 7.3.b Let's display information about game level data when the game starts. //Game level data is already loaded. We can see how it looks like //console.log("All Questions: ", Genari.getLevelData()); //console.log("Answers: ", Genari.getAnswers()); //console.log("Added Audio assets", this.dcGame.audioAssets); // Lesson 7.3.c Show answer data to the game stage: // We can add text object to the game stage //Genari.phaserAddText(0, 80, "Hi", { "font":"24px Arial", "fontWeight":"bold", "fill": "#FFFFFF", "stroke":'#000000', "strokeThickness":6 }); // "fontSize": '18px' //-------------------------------------------------------- // Lesson 8.3.a Let’s print out the file IDs: // console.log( // Genari.getAllQuestions()[0]["img1"], // file id of Activity Image // Genari.getAllQuestions()[0]["img2"], // file id of End image // Genari.getLevelActivityImageKey(), // Genari.getLevelEndImageKey() // ); // Lesson 8.3.b Add the images if file ID > 0: // if(Genari.getLevelActivityImageKey() !== null){ // var img1 = Genari.phaserAddImage(0, 80, Genari.getLevelActivityImageKey()); // // Lesson 8.3.e Images too big? Can we resize them to make them smaller? // //img1.width *= 0.5; // //img1.height *= 0.5; // } // if(Genari.getLevelEndImageKey() !== null ){ // var img2 = Genari.phaserAddImage(0, 200, Genari.getLevelEndImageKey()); // // Lesson 8.3.e Images too big? Can we resize them to make them smaller? // //img2.width *= 0.5; // //img2.height *= 0.5; // } // lesson 8.4.b Play the game opening audio //var snd1Key = Genari.getLevelOpeningSoundKey(); //this.dcGame.audioPlay(snd1Key); }; /** * Called before play-state starts for us to prepare. * * This allow us to place (add) display objects in the stage to show. * @override */ PStatePlay.prototype.createWorld = function() { var thisGame = this.dcGame; // Load all tilemaps for the current theme this.addThemeTileMaps(16, 16); // load the background layer and show. For this game, we are using image for the background. //this.loadTileMapBackgroundLayer( thisGame.layerBottom2, Genari.k.environmentLayerKey ); this.loadTileMapBackgroundLayer( thisGame.layerBottom0, Genari.k.backgroundLayerKey ); }; /** * Called before play-state starts for us to prepare. * * We use this to add HUD items that shows player icon, life remaining, scores, etc. * @override */ PStatePlay.prototype.createHud = function() { var thisGame = this.dcGame; //Create the hud for the game. thisGame.hud = Genari.add.hud(thisGame.layerButtons); //Create the pause button so that player can pause the game. thisGame.hud.addPauseButton( thisGame.BUTTON_EDGE_MARGIN, thisGame.BUTTON_EDGE_MARGIN, Genari.k.pauseButtonSpriteKey, 45, thisGame.pause.bind(thisGame), //GApp.pages.pause_game.init thisGame ); //Use user's photo to represent score. thisGame.hud.addUserPhoto(thisGame.game_width - 160, thisGame.BUTTON_EDGE_MARGIN, Genari.k.userPhotoKey, Genari.k.userPhotoFrameKey, 40); //Create the elements of the status bar on the top. thisGame.scoreBox = Genari.add.scoreBox(Genari.k.scoreReelSpriteKey, 30, thisGame.gc_score_text_x, 15, thisGame.layerButtons); // Create Level text thisGame.hud.addLevelText( thisGame.game_width, 3, 'Level ' + (thisGame.currentGameLevel + 1), {"fontSize": '18px', "fill": "#000000"} ); // Create Game objective text thisGame.hud.addGameObjectiveText( thisGame.game_width, 25, Genari.getQuestion(), {"fontSize": '14px', "fill": "#000000"} ); }; /** * When play-state finally starts, it shows the level opening dialog box for the player to prepare. * When the user click/enters in the level opening dialog box, this function is called to close the dialog box. * * To customise the level opening dialog box, override this.showLevelOpeningDialogBox. See DC API for its description. * * @param [playBackgroundMusic] Default true. Set to true to play the game level background music * @override */ PStatePlay.prototype.openingDialogAction = function(playBackgroundMusic){ // Call the parent class method. Genari.GameState.prototype.openingDialogAction.call(this, playBackgroundMusic); this.dcGame.audioPlay(Genari.k.openingSndKey); }; /** * When play-state is running, this is called at every frame just before the frame is rendered to the display. * ** The frame rate varies. Mobile devices will have very low frame rate. * Use Javascript timer if need to measure time. * @override */ PStatePlay.prototype.updateState = function(){ var thisGame = this.dcGame; if (!this.isPlaying){ return; // stop processing if the game level is over } // ** Must call this this.stateCheck(); // handle control events thisGame.gameControl.update(); // Add here code for updating game states, monsters, players, etc //------------------------------------------------ // Lesson 6.6.b let’s make the pet fly around on the screen // fly to right // this.flyingPet.x += this.flyXDirection; // this.flyingPet.animations.play("flyRight"); // Lesson 6.7.a If it the pet hits the right wall, what shall it do? // if( this.flyingPet.x >800) { // this.flyingPet.x = 800; // this.flyXDirection = -1; // change to flying left // this.flyingPet.animations.play("flyLeft", 30, true); // } // Lesson 6.7.a If it the pet hits the right wall, what shall it do? // if( this.flyingPet.x ___ ____ ) { // this.flyingPet.x = ____; // this.flyXDirection = ___; // change to flying right // this.flyingPet.animations.play("flyRight", 30, true); // } // Lesson 6.8.a make it fly up and down as well: // this.flyingPet.y += this.flyYDirection; // Lesson 6.8.b Make it bounce down when it hits the top: // if( this.flyingPet.y ___ _____) { // this.flyingPet.y = ______; // this.flyYDirection = ______; // change to flying down // this.flyingPet.animations.play("flyRight", 30, true); // } // Lesson 6.8.c Make it bounce up when it hits the bottom: // if( this.flyingPet.y ___ _____) { // this.flyingPet.y = ______; // this.flyYDirection = ______; // change to flying up // this.flyingPet.animations.play("flyRight", 30, true); // } // Finally check if the game level is completed by checking if all questions are answered thisGame.checkLevelEnd(); }; /** * Called just before the play state is shutdown * @override */ PStatePlay.prototype.shutdownState = function(){ Genari.phaserGame.cache.removeSound(Genari.k.backgroundMusicKey); }; //============================================================================== // We must place classes that extend classes defined in Genari.Group or their subclasses here. // This is because Genari could be loaded after this code is loaded. var myClassLoader = function () { // DEVELOPER_CODE REMOVE_FOR_THEME // define classes here that extends classes defined in Genari.loadClasses /********************************************************************************************* * Group for managing answer place objects, the images indicating where the answer images should be dropped. * * This game generates hint image objects from correct answer data. * Rows of hint images will be shown on the left. * * @param {Object} layer Phaser.Group. Rendering layer of this group. * @param {boolean} [fixedToCamera] If set to true, it move with camera. The location is then camera offsets. * @param {boolean} [enableBody] If set to true, any objects added will have physics enables * @param {number} [physicsBodyType] Default 0. Phaser.Physics.ARCADE, Phaser.Physics.P2, Phaser.Physics.NINJA * @constructor * @extends {Genari.Group} ********************************************************************************************/ Genari.DropBoxGroup = function(layer, fixedToCamera, enableBody, physicsBodyType){ if (!(this instanceof Genari.DropBoxGroup)) return new Genari.DropBoxGroup(layer, fixedToCamera, enableBody, physicsBodyType); Genari.Group.call(this, layer, fixedToCamera, enableBody, physicsBodyType); // We can define attributes here like this. All objects created will have this attribute values with this initilie value. // picked-up drop box this.selectedDropBox = [null, null, null]; // array of comment sound effects this.correctCommentSndKey = [Genari.k.correctComment1SndKey, Genari.k.correctComment2SndKey, Genari.k.correctComment3SndKey]; this.wrongCommentSndKey = [Genari.k.wrongComment1SndKey, Genari.k.wrongComment2SndKey, Genari.k.wrongComment3SndKey]; }; Genari.DropBoxGroup.prototype = Object.create(Genari.Group.prototype); Genari.DropBoxGroup.prototype.constructor = Genari.DropBoxGroup; /** * Spawn drio box image objects based on objects position on tilemaps * this.dropBox_#.answer is the answer key code that will be used to check if a correct answer image is dropped on it */ Genari.DropBoxGroup.prototype.spawnDropBoxes = function() { var imageBox; // get drop box objects in the tileMap and place them in correct positions var dropBoxSprite; this.dcGame.statePlay.tileMaps.worldTileMap.objects[Genari.k.dropBoxObjName].forEach( function(dropBoxObj){ // spawn a dropbox using the location information of the drop box objects dropBoxSprite = this.create( dropBoxObj.x, dropBoxObj.y, Genari.k.dropBoxImgKey ); // change the anchor point to the center of the dropBoxSprite dropBoxSprite.anchor.x = dropBoxSprite.anchor.y = 0.5; // resize the dropbox display object so the game will work regardless of the size of the drop box image size dropBoxSprite.width = dropBoxSprite.height = this.dcGame.answerImgSize; dropBoxSprite.alpha = 0.3; // make the drop box transparent to blend with the background nicely. This is optional // add additional properties dropBoxSprite.answer = Number(dropBoxObj.name); // answer code to match with answer code of answer image // store the first order dropBoxect's location if( dropBoxObj.name == "1" ){ // ** the first drop box object in the tilemap must have name == "1" this.firstDropBoxSprite = dropBoxSprite; } // Lesson 6.3 // dropBoxSprite.y -= 20; }, this); }; /** * Put the first order answer to the first drop box */ Genari.DropBoxGroup.prototype.fillThefirstDropBoxSprite = function(){ var thisGame = this.dcGame; // Search for the first answer in the order thisGame.groupAnswers.getGroup().forEach( function(answerImageSprite){ if (answerImageSprite.answer == "1"){ answerImageSprite.x = this.firstDropBoxSprite.x; answerImageSprite.y = this.firstDropBoxSprite.y; // move the completed dropbox and answer image to the bottom layer thisGame.layerBottom.addChild(this.firstDropBoxSprite); thisGame.layerBottom.addChild(answerImageSprite); thisGame.updateScore(1); } }, this ); }; /** * Pick up a hint image object under the given location * @param {number} x * @param {number} y * @return {Object|null} Phaser.Sprite. Answer image object under the location */ Genari.DropBoxGroup.prototype.getDropBoxImage = function(x, y){ //var thisGame = this.dcGame; var hover_box = null; // iterate through each drop box sprite added to this group this.getGroup().forEach(function(dropBoxSprite){ // check if this drop box object is under the pointer location if((x > dropBoxSprite.x - (dropBoxSprite.width/4)) && (x < dropBoxSprite.x + (dropBoxSprite.width/4)) && (y < dropBoxSprite.y + (dropBoxSprite.height/2)) && (y > dropBoxSprite.y - (dropBoxSprite.height/2))){ // found a hint image object under the location hover_box = dropBoxSprite; } }); return hover_box; }; /** * This is called when the pointer is in down state. * We check if we are dragging an answer image and * if the pointer is on a drop box, store the drop box object and play sound effect * @param {Array} pointer * @param {number} pointerIdx */ Genari.DropBoxGroup.prototype.pickupDropBox = function(pointer, pointerIdx){ //var pointer = Genari.getActivePointerLocation(); // find a draop box image object under the current active point var curDropBoxSprite = this.getDropBoxImage(pointer[0], pointer[1]); if (!(curDropBoxSprite && this.dcGame.groupAnswers.selectedAnswerSprite[pointerIdx])){ // not found a drop box image under the pointer location nor dragging an asnwer image this.selectedDropBox[pointerIdx] = null; return; } else { // we are dragging an answer image and the pointer is on an hint image object if (!this.selectedDropBox[pointerIdx] || this.selectedDropBox[pointerIdx].answer != curDropBoxSprite.answer // if not the same drop box ){ // we have never visited a hint img object before OR // the current hint image and the previously selected hint image are not the same. // we update selectedDropBox and play the select sound this.selectedDropBox[pointerIdx] = curDropBoxSprite; this.dcGame.audioPlay(Genari.k.selectSndKey); } } }; /********************************************************************************************* * Group for managing Answer objects. * * This game generates answer image objects from correct answer data. * rows of answer images will be shown at the bottom * * @param {Object} layer Phaser.Group. Rendering layer of this group. * @param {boolean} [fixedToCamera] If set to true, it move with camera. The location is then camera offsets. * @param {boolean} [enableBody] If set to true, any objects added will have physics enables * @param {number} [physicsBodyType] Default 0. Phaser.Physics.ARCADE, Phaser.Physics.P2, Phaser.Physics.NINJA * @constructor * @extends {Genari.Group} ********************************************************************************************/ Genari.AnswersGroup = function( layer, fixedToCamera, enableBody, physicsBodyType ){ if (!( this instanceof Genari.AnswersGroup )) return new Genari.AnswersGroup( layer, fixedToCamera, enableBody, physicsBodyType ); Genari.Group.call( this, layer, fixedToCamera, enableBody, physicsBodyType ); this.answerList = Genari.getAnswers(); // get the array of all answers including their properties this.selectedAnswerSprite = [null, null, null]; // this holds the currently pickup answer this.draggingAnswerImage = [false, false, false]; this.numberOfAnswers = this.answerList[0].length; }; Genari.AnswersGroup.prototype = Object.create( Genari.Group.prototype ); Genari.AnswersGroup.prototype.constructor = Genari.AnswersGroup; /** * Spawn answer image objects in a row. * this.answerImage_#.answer is the answer key code that will be used to check if a correct answer image is dropped on it * this.answerImage_#.soundKey will be played when a correct answer is dropped on it. * this.answerImage_#.atype = 0 says that this is a answer image obj, not hint image obj */ Genari.AnswersGroup.prototype.spawnAnswerImageObjs = function(){ var x, y, answerImageSprite; // for each answer image this.firstAnswerNo = 10000; for( var i = 0; i < this.numberOfAnswers; i++ ){ // the maximum number of answers is 5 if( i >= 5 ){ return; } // calculate location for this answer image x = this.dcGame.firstAnswerLeftPos + ( i * this.dcGame.answerInterval ); // lay out in fixed intervals y = this.dcGame.firstAnswerTopPos; // in a row // Get the first answer if(this.firstAnswerNo > this.answerList[0][i].answer) this.firstAnswerNo = this.answerList[0][i].answer; //answerImageSprite = this.create( x, y, this.answerList[0][i].imageKey ); answerImageSprite = this.addImageAsSpriteAnimation( x, y, // location of the sprite this.answerList[0][i].imageKey, 0, // starting frame number this.answerList[0][i].frameNo, // number of frames "move", // animation name (optional) 10, // frame rate per second true // loop enabled? ); //add text, sound, atype and position properties answerImageSprite.answer = this.answerList[0][i].answer; // this is the answer code that match with drop box answer code answerImageSprite.soundKey = this.answerList[0][i].soundKey; // sound of associated with the answer image in the game level content // move the anchor to the center of the image answerImageSprite.anchor.x = answerImageSprite.anchor.y = 0.5; // resize the answer image to fixed size so it will work with any answer image size answerImageSprite.width = answerImageSprite.height = this.dcGame.answerImgSize; // Lesson 6.4: // answerImageSprite.y -= 20; } }; /** * Pick an answer image object under the given location. * Return an answer image object if found. * Return null if there are no answer image object under the location. * @param {number} x coordinate of the location to check. * @param {number} y coordinate of the location. * @return {Object|null} Phaser.Sprite {DisplayObject} */ Genari.AnswersGroup.prototype.getAnswerImage = function(x, y){ var pickedAnswer = null; // Check each answer object this.getGroup().forEach(function(answerSPrite){ // we gets Phaser.Sprite {DisplayObject} if (answerSPrite.getBounds().contains(x,y)){ // Phaser.Rectangle.contains(answerSPrite, x, y) // It is answer image object pickedAnswer = answerSPrite; this.dcGame.audioPlay(Genari.k.selectSndKey); } }.bind(this)); return pickedAnswer; }; /** * Pointer is just down. This is called only once when the pointer is changed from up to down * Pickup an answer image under the current pointer down location. * It sets selectedAnswerSprite (Phaser.Sprite) * @param {Array} pointer * @param {number} pointerIdx */ Genari.AnswersGroup.prototype.pickupAnswerImage = function(pointer, pointerIdx){ var answerSprite; //pointer = Genari.getActivePointerLocation(); // get the most recent active pointer location answerSprite = this.getAnswerImage(pointer[0], pointer[1]); if (!answerSprite){ // not found an answer image object under the clicked location return; } else { // we have an answer image under the clicked location this.selectedAnswerSprite[pointerIdx] = answerSprite; // store the Phaser.Sprite object as the selected answer object. this.selectedAnswerSprite[pointerIdx].bringToTop(); // show the image on top in that layer // store the original pickup location so we can put this object back if dropped in a wrong location this.selectedAnswerSprite[pointerIdx].originX = this.selectedAnswerSprite[pointerIdx].x; this.selectedAnswerSprite[pointerIdx].originY = this.selectedAnswerSprite[pointerIdx].y; // now we drag the answer image this.draggingAnswerImage[pointerIdx] = true; } }; /** * Move the selected answer image object to the current active pointer location * This drags an anwer image picked up by the player. * @param {Array} pointer * @param {number} pointerIdx */ Genari.AnswersGroup.prototype.dragAnswerImage = function(pointer, pointerIdx){ if (this.draggingAnswerImage[pointerIdx] === false || !this.selectedAnswerSprite[pointerIdx]){ return; } // get pointer current location //var pointer = Genari.getActivePointerLocation(); // move the answer image to the pointer's position this.selectedAnswerSprite[pointerIdx].x = pointer[0]; this.selectedAnswerSprite[pointerIdx].y = pointer[1]; }; /** * Player drops the answer image * @param {number} pointerIdx */ Genari.AnswersGroup.prototype.dropAnswerImage = function(pointerIdx){ this.checkAnswer(pointerIdx); this.selectedAnswerSprite[pointerIdx] = null; this.draggingAnswerImage[pointerIdx] = false; }; /** * Check if player dropped the image on the right location or not * @param {number} pointerIdx */ Genari.AnswersGroup.prototype.checkAnswer = function(pointerIdx){ var thisGame, dropBoxGroup, idx; thisGame = this.dcGame; dropBoxGroup = this.dcGame.groupDropBoxes; // if not selected an answer, ignore if (!this.selectedAnswerSprite[pointerIdx]){ return; } // if not on a drop box, return the answer image back to the original location if (!dropBoxGroup.selectedDropBox[pointerIdx]){ this.selectedAnswerSprite[pointerIdx].x = this.selectedAnswerSprite[pointerIdx].originX; this.selectedAnswerSprite[pointerIdx].y = this.selectedAnswerSprite[pointerIdx].originY; thisGame.audioPlay(Genari.k.deniedEffectSndKey); // play the return sound return; } // get random index for commend sounds idx = Math.floor(Math.random()*3); // now, let's check if the answr image and the drop box matches if (dropBoxGroup.selectedDropBox[pointerIdx].answer == this.selectedAnswerSprite[pointerIdx].answer){ thisGame.updateScore(1); this.selectedAnswerSprite[pointerIdx].x = dropBoxGroup.selectedDropBox[pointerIdx].x; this.selectedAnswerSprite[pointerIdx].y = dropBoxGroup.selectedDropBox[pointerIdx].y; // show effect Genari.Effect.explosion(this.selectedAnswerSprite[pointerIdx], false, this.dcGame.correctEffectAnim, 'correct'); this.dcGame.audioPlay(Genari.k.correctEffectSndKey); // play answer image sound asset thisGame.audioPlay( this.selectedAnswerSprite[pointerIdx].soundKey, undefined, undefined, undefined, undefined, undefined, undefined, // callback function after completing playing the sound function(){ // play a second sound followed by the first sound thisGame.audioPlay(dropBoxGroup.correctCommentSndKey[idx]); }.bind(this) ); // Move the completed answer and drop box to bottom layer thisGame.layerBottom.addChild(dropBoxGroup.selectedDropBox[pointerIdx]); thisGame.layerBottom.addChild(this.selectedAnswerSprite[pointerIdx]); } else { //Player dropped in a long drop box this.returnObj(this.selectedAnswerSprite[pointerIdx], 500, this.dcGame.wrongEffectAnim); thisGame.audioPlay(dropBoxGroup.wrongCommentSndKey[idx]); } }; /** * Show incorrect effect and sound, and then return the answer to the original location * @param {Object} targetSprite * @param {number} delay * @param {Object} explosionSprite */ Genari.AnswersGroup.prototype.returnObj = function(targetSprite, delay, explosionSprite){ Genari.Effect.explosion(targetSprite, false, explosionSprite, 'wrong'); this.dcGame.audioPlay(Genari.k.wrongEffectSndKey); // hide and return to the original place targetSprite.alpha = 0; targetSprite.x = targetSprite.originX; targetSprite.y = targetSprite.originY; // show it after some dealy this.dcGame.phaserGame.time.events.add(delay, function(){ targetSprite.alpha = 1; } ); // var sprite = obj; // Genari.Effect.explosion(sprite, false, anim, 'wrong'); // this.dcGame.audioPlay(Genari.k.wrongEffectSndKey); // sprite.alpha = 0; // sprite.x = sprite.originX; // sprite.y = sprite.originY; // this.dcGame.phaserGame.time.events.add(delay, // function(){ // sprite.alpha = 1; // } // ); }; }; // DEVELOPER_CODE REMOVE_FOR_THEME //