/************************************************************** * Copyright (c) 2015-2018. 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. * * Can you find it game * Base game: Picture card game * * Version 1.5 * Moved the replay button to right out of the box * Center and resize the text. Miminum bax size required * Version 1.4 * - Use background asset as background * - Fixed bug when click replay btn at last picture * **************************************************************/ /************************************************************** * 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(FindItGame, 800, 426, GameControl, PStatePlay); // DO NOT change this // 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: FindItGame // Genari.StatePlay: preloadState() pre-load game theme assets // Genari.StatePlay: loadThemeAssets() // Genari.StatePlay: createLayersGroups() Create rendering layers to organize rendering order and game object groups to manage them. // 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 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 FindItGame = function(game_themes){ if (!(this instanceof FindItGame)) return new FindItGame();// this ensures Javascript classes functions as intended Genari.Game.call(this, game_themes); // call the parent constructor this.gc_score_text_x = this.game_width - 40; /** @type{Genari.Hud} */ this.hud = null; this.checkEnd = false; this.isFinish = false; }; FindItGame.prototype = Object.create(Genari.Game.prototype); // extend DBGame FindItGame.prototype.constructor = FindItGame; // set constructor property /** * Check the end of the game level and load the next level if the current level is completed */ FindItGame.prototype.checkLevelEnd = function(){ // check if answered all questions correctly if (this.scoreBox.getValue() < this.groupPictures.pictureList[0].length) return; this.isFinish = true; 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 {FindItGame} FindItGame The only game object * @param {Genari.RunPlayer} player The player character that user controls * @param {string} bulletSpriteKey sprite key. This must be already loaded during player-state.create() * @param {string} bulletSoundKey sprite key. This must be already loaded during player-state.create() * * @constructor * @extends {Genari.Control} **************************************************************/ var GameControl = function(FindItGame, player, bulletSpriteKey, bulletSoundKey){ if (!(this instanceof GameControl)){return new GameControl(FindItGame, player, bulletSpriteKey, bulletSoundKey);} Genari.Control.call(this, FindItGame, player); // must call the parent class constructor // Never redefine inherited properties and methods. // 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.FindItGame = FindItGame; }; 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; // Add some buttons here if needed var replayBtn = this.addControlButton( this.dcGame.assetCoordinate[1][0], //this.dcGame.groupPictures.textXpos-85, this.dcGame.assetCoordinate[1][1], //this.dcGame.groupPictures.textYpos - 2, Genari.k.replayBtnKey, function(){ this.dcGame.groupPictures.playThisAudio(this.dcGame.groupPictures.currentIdx); }.bind(this), function(){}, this ); replayBtn.width *= this.dcGame.replayScale/100; replayBtn.height *= this.dcGame.replayScale/100; // if added some buttons here, uncomment to add button hit area for the buttons that are added. this.createButtonHitArea(); // Hide the on-screen buttons until the play-state starts. Buttons will automatically shown when it starts. this.hideOnScreenButtons(); }; /** * 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() */ GameControl.prototype.update = function(){ 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 if (this.pointerUp){ // pointer is just down from up state this.dcGame.groupPictures.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 } } 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.pointerUp = true; // set to pointer up } } } }; /************************************************************** * Phaser state class: Play state. * This is started when the player click/enter on the application banner to start the game. * * Customise this class for your own game * @param {FindItGame} 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 DBGame 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.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; var defaultAssetCoor = [[this.dcGame.game_width/2,110], [this.dcGame.game_width-150,50]]; thisGame.defaultAnswerCoor = [[150,235], [150,120+235], [(1*120)+150,235], [(1*120)+150,120+235], [(2*120)+150,235], [(2*120)+150,120+235], [(3*120)+150,235], [(3*120)+150,120+235], [(4*120)+150,235], [(4*120)+150,120+235]]; if(Genari.getThemeAttribute(Genari.k.coordinateAttKey) == "default"){ thisGame.assetCoordinate = defaultAssetCoor; } else { thisGame.assetCoordinate = Genari.getThemeArrayStringAttribute(Genari.k.coordinateAttKey, defaultAssetCoor); } if(Genari.getThemeAttribute(Genari.k.answerCoordinateAttKey) == "default"){ thisGame.answerCoordinate = "default"; } else { thisGame.answerCoordinate = Genari.getThemeArrayStringAttribute(Genari.k.answerCoordinateAttKey, thisGame.defaultAnswerCoor); } thisGame.hintScale = Genari.getThemeAttribute(Genari.k.hintFrameScaleAttKey, 60); thisGame.replayScale = Genari.getThemeAttribute(Genari.k.replayBtnScaleAttKey, 100); thisGame.answerScale = Genari.getThemeAttribute(Genari.k.answerScaleAttKey, 50); }; /** * 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.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.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.groupPictures) this.dcGame.groupPictures.destroy(); // create one object for both hint image objects and answer image objects... this.dcGame.groupPictures = new Genari.PictureGroup(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; // before placing objects in the stage, we need to set to scaled mode so we can use unscaled coordiate values when placing objects in the stage. thisGame.setToScaledMode(); this.checkQuestionText(); // 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.groupPictures.spawnPictureObjs(); // now we go back to based scale mode for proper pointer coordiates thisGame.setToBaseScaleMode(); }; /** * Check question text if contains "collect" word */ PStatePlay.prototype.checkQuestionText = function(){ // get the question's text this.checkQuestion = Genari.getQuestion().includes("Collect") || Genari.getQuestion().includes("collect"); // check if question Text contains "Collect" or "collect" if(this.checkQuestion){ // change the question Genari.getAllQuestions()[0].name = "What is this ?"; Genari.getAllQuestions()[0].question = "What is this ?"; Genari.getAllQuestions()[0].question2 = "What is this ?"; Genari.getAllQuestions()[0].snd1 = 0; } }; /** * 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; // Add the background image and set the world bound Genari.addBackground( thisGame.layerBottom, Genari.k.backgroundImgKey, thisGame.game_width, // world width thisGame.game_height, // world height null, null, 800, 432 ); }; /** * 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); this.dcGame.isFinish = false; }; /** * 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 // handle control events thisGame.gameControl.update(); thisGame.groupPictures.update(); // ** Must call this this.stateCheck(); thisGame.checkLevelEnd(); }; /** * Called just before the play state is shutdown * @override */ PStatePlay.prototype.shutdownState = function(){ Genari.phaserGame.cache.removeSound(Genari.k.backgroundMusicKey); }.bind(this); //============================================================================== // 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 /********************************************************************************************* * Group for managing Answer objects. * * This game generates hint image objects from correct answer data and answer image objects from incorrect answer data. * Rows of hint images will be shown on the left and rows of answer images will be shown on the right. * * @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.PictureGroup = function( layer, fixedToCamera, enableBody, physicsBodyType ){ if (!(this instanceof Genari.PictureGroup)) return new Genari.PictureGroup(layer, fixedToCamera, enableBody, physicsBodyType); Genari.Group.call(this, layer, fixedToCamera, enableBody, physicsBodyType); // answer data from the server. See Genari.getAnswers for the data structure this.pictureList = Genari.getAnswers(); //array of comment sound effect this.correctCommentSndKey = [Genari.k.correctComment1SndKey, Genari.k.correctComment2SndKey, Genari.k.correctComment3SndKey]; this.wrongCommentSndKey = [Genari.k.wrongComment1SndKey, Genari.k.wrongComment2SndKey, Genari.k.wrongComment3SndKey]; this.clickedImage = null; this.indexList = []; this.currentIdx = null; this.currentHintText = null; this.showNextHint = true; this.clickEN = true; // attributes this.rowNum = 2; this.imgInterval = 120; this.imgLeftPos = 150; this.imgTopPos = 235; }; Genari.PictureGroup.prototype = Object.create( Genari.Group.prototype ); Genari.PictureGroup.prototype.constructor = Genari.PictureGroup; /** * Spawn hint image objects in a column. Max 5 rows. * this.correctAnswer_#.sndKey will be played when a correct answer is dropped on it. */ Genari.PictureGroup.prototype.spawnPictureObjs = function(){ var pictureSprite, i, j, x, y, idx; idx = 0; if(this.dcGame.answerCoordinate === "default"){ for(i = 0; i < this.pictureList[0].length/this.rowNum; i++){ for(j = 0; j < this.rowNum; j++){ // calculate location for this answer image x = this.imgLeftPos + (i*this.imgInterval); y = this.imgTopPos + (j*this.imgInterval); // add a sprite //pictureSprite = this.create(x, y, this.pictureList[0][idx].imageKey); pictureSprite = this.addImageAsSpriteAnimation( x, y, // location of the sprite this.pictureList[0][idx].imageKey, 0, // starting frame number this.pictureList[0][idx].frameNo, // number of frames "move", // animation name (optional) Math.floor(this.pictureList[0][i].frameNo/2), // frame rate per second true // loop enabled? ); //add sound and position properties pictureSprite.answer = this.pictureList[0][idx].answer; pictureSprite.soundKey = this.pictureList[0][idx].soundKey; // sound key sk_## pictureSprite.posOrder = idx; this.indexList[idx] = [idx, pictureSprite]; idx++; // move the anchor to the center of the image pictureSprite.anchor.x = pictureSprite.anchor.y = 0.5; // resize width and height pictureSprite.width *= this.dcGame.answerScale/100; pictureSprite.height *= this.dcGame.answerScale/100; } } } else { for(i=0; i