|
@@ -0,0 +1,776 @@
|
|
|
|
+/******************************
|
|
|
|
+ * This file holds game states.
|
|
|
|
+ ******************************/
|
|
|
|
+
|
|
|
|
+/** [GAME STATE]
|
|
|
|
+ *
|
|
|
|
+ * ....scale..... = gameType
|
|
|
|
+ * ..../...\.....
|
|
|
|
+ * ...A.....B.... = gameMode
|
|
|
|
+ * .....\./......
|
|
|
|
+ * ......|.......
|
|
|
|
+ * ...../.\......
|
|
|
|
+ * .Plus...Minus. = gameOperation
|
|
|
|
+ * .....\./......
|
|
|
|
+ * ......|.......
|
|
|
|
+ * ....1,2,3..... = gameDifficulty
|
|
|
|
+ *
|
|
|
|
+ * Character : tractor
|
|
|
|
+ * Theme : farm
|
|
|
|
+ * Concept : Player associates 'blocks carried by the tractor' and 'floor spaces to be filled by them'
|
|
|
|
+ * Represent fractions as : blocks/rectangles
|
|
|
|
+ *
|
|
|
|
+ * Game modes can be :
|
|
|
|
+ *
|
|
|
|
+ * A : Player can select # of 'floor blocks' (hole in the ground)
|
|
|
|
+ * Selects size of hole to be made in the ground (to fill with the blocks in front of the truck)
|
|
|
|
+ * B : Player can select # of 'stacked blocks' (in front of the truck)
|
|
|
|
+ * Selects number of blocks in front of the truck (to fill the hole on the ground)
|
|
|
|
+ *
|
|
|
|
+ * Operations can be :
|
|
|
|
+ *
|
|
|
|
+ * Plus : addition of fractions
|
|
|
|
+ * Represented by : tractor going to the right (floor positions 0..8)
|
|
|
|
+ * Minus : subtraction of fractions
|
|
|
|
+ * Represented by: tractor going to the left (floor positions 8..0)
|
|
|
|
+ *
|
|
|
|
+ * @namespace
|
|
|
|
+ */
|
|
|
|
+const scaleOne = {
|
|
|
|
+ /**
|
|
|
|
+ * Main code
|
|
|
|
+ */
|
|
|
|
+ create: function () {
|
|
|
|
+ // // CONTROL VARIABLES
|
|
|
|
+ // this.checkAnswer = false; // When true allows game to run 'check answer' code in update
|
|
|
|
+ // this.animate = false; // When true allows game to run 'tractor animation' code in update (turns animation of the moving tractor ON/OFF)
|
|
|
|
+ // this.animateEnding = false; // When true allows game to run 'tractor ending animation' code in update (turns 'ending' animation of the moving tractor ON/OFF)
|
|
|
|
+ // this.hasClicked = false; // Checks if player 'clicked' on a block
|
|
|
|
+ // this.result = false; // Checks player 'answer'
|
|
|
|
+ // this.count = 0; // An 'x' position counter used in the tractor animation
|
|
|
|
+
|
|
|
|
+ // this.divisorsList = ''; // Hold the divisors for each fraction on stacked blocks (created for postScore())
|
|
|
|
+
|
|
|
|
+ // this.direc_level = gameOperation == 'Minus' ? -1 : 1; // Will be multiplied to values to easily change tractor direction when needed
|
|
|
|
+ // this.animationSpeed = 2 * this.direc_level; // X distance in which the tractor moves in each iteration of the animation
|
|
|
|
+
|
|
|
|
+ // // GAME VARIABLES
|
|
|
|
+ // this.defaultBlockWidth = context.canvas.width / 11; // Base block width
|
|
|
|
+ // this.defaultBlockHeight = 40 * 1.5; // Base block height
|
|
|
|
+
|
|
|
|
+ // this.startX =
|
|
|
|
+ // gameOperation == 'Minus' ? context.canvas.width - 170 * 1.5 : 170 * 1.5; // Initial 'x' coordinate for the tractor and stacked blocks
|
|
|
|
+ // this.startY = context.canvas.height - 157 * 1.5 + 10;
|
|
|
|
+
|
|
|
|
+ renderBackground();
|
|
|
|
+
|
|
|
|
+ // // Calls function that loads navigation icons
|
|
|
|
+
|
|
|
|
+ // // FOR MOODLE
|
|
|
|
+ // if (moodle) {
|
|
|
|
+ // navigationIcons.add(
|
|
|
|
+ // false,
|
|
|
|
+ // false,
|
|
|
|
+ // false, // Left icons
|
|
|
|
+ // true,
|
|
|
|
+ // false, // Right icons
|
|
|
|
+ // false,
|
|
|
|
+ // false
|
|
|
|
+ // );
|
|
|
|
+ // } else {
|
|
|
|
+ // navigationIcons.add(
|
|
|
|
+ // true,
|
|
|
|
+ // true,
|
|
|
|
+ // true, // Left icons
|
|
|
|
+ // true,
|
|
|
|
+ // false, // Right icons
|
|
|
|
+ // 'customMenu',
|
|
|
|
+ // this.viewHelp
|
|
|
|
+ // );
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ // // TRACTOR
|
|
|
|
+ // this.tractor = game.add.sprite(this.startX, this.startY, 'tractor', 0, 1.2);
|
|
|
|
+
|
|
|
|
+ // if (gameOperation == 'Plus') {
|
|
|
|
+ // this.tractor.anchor(1, 0.5);
|
|
|
|
+ // this.tractor.animation = ['move', [0, 1, 2, 3, 4], 4];
|
|
|
|
+ // } else {
|
|
|
|
+ // this.tractor.anchor(0, 0.5);
|
|
|
|
+ // this.tractor.animation = ['move', [5, 6, 7, 8, 9], 4];
|
|
|
|
+ // this.tractor.curFrame = 5;
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ // // STACKED BLOCKS variables
|
|
|
|
+ // this.stck = {
|
|
|
|
+ // blocks: [], // Group of 'stacked' block objects
|
|
|
|
+ // labels: [], // Group of fraction labels on the side of 'stacked' blocks
|
|
|
|
+
|
|
|
|
+ // index: undefined, // (gameMode 'B') index of 'stacked' block selected by player
|
|
|
|
+
|
|
|
|
+ // // Control variables for animation
|
|
|
|
+ // curIndex: 0, // (needs to be 0)
|
|
|
|
+ // curBlockEnd: undefined,
|
|
|
|
+
|
|
|
|
+ // // Correct values
|
|
|
|
+ // correctIndex: undefined, // (gameMode 'B') index of the CORRECT 'stacked' block
|
|
|
|
+ // };
|
|
|
|
+
|
|
|
|
+ // // FLOOR BLOCKS variables
|
|
|
|
+ // this.floor = {
|
|
|
|
+ // blocks: [], // Group of 'floor' block objects
|
|
|
|
+ // index: undefined, // (gameMode 'A') index of 'floor' block selected by player
|
|
|
|
+
|
|
|
|
+ // // Control variables for animation
|
|
|
|
+ // curIndex: -1, // (needs to be -1)
|
|
|
|
+
|
|
|
|
+ // // Correct values
|
|
|
|
+ // correctIndex: undefined, // (gameMode 'A') index of the CORRECT 'floor' block
|
|
|
|
+ // correctX: undefined, // 'x' coordinate of CORRECT 'floor' block
|
|
|
|
+ // correctXA: undefined, // Temporary variable
|
|
|
|
+ // correctXB: undefined, // Temporary variable
|
|
|
|
+ // };
|
|
|
|
+
|
|
|
|
+ // // CREATING STACKED BLOCKS
|
|
|
|
+ // this.restart = this.createStckBlocks();
|
|
|
|
+
|
|
|
|
+ // // CREATING FLOOR BLOCKS
|
|
|
|
+ // this.createFloorBlocks();
|
|
|
|
+
|
|
|
|
+ // // SELECTION ARROW
|
|
|
|
+
|
|
|
|
+ // if (gameMode == 'A') {
|
|
|
|
+ // this.arrow = game.add.image(
|
|
|
|
+ // this.startX + this.defaultBlockWidth * this.direc_level,
|
|
|
|
+ // this.startY + 35,
|
|
|
|
+ // 'arrow_down',
|
|
|
|
+ // 1.5
|
|
|
|
+ // );
|
|
|
|
+ // this.arrow.anchor(0.5, 0.5);
|
|
|
|
+ // this.arrow.alpha = 0.5;
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ // Help pointer
|
|
|
|
+ this.help = game.add.image(0, 0, 'help_pointer', 1.7);
|
|
|
|
+ this.help.anchor(0.5, 0);
|
|
|
|
+ this.help.alpha = 0;
|
|
|
|
+
|
|
|
|
+ if (!this.restart) {
|
|
|
|
+ game.timer.start(); // Set a timer for the current level (used in postScore())
|
|
|
|
+ game.event.add('click', this.onInputDown);
|
|
|
|
+ game.event.add('mousemove', this.onInputOver);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Game loop
|
|
|
|
+ */
|
|
|
|
+ update: function () {
|
|
|
|
+ // AFTER PLAYER SELECTION
|
|
|
|
+
|
|
|
|
+ // Starts tractor moving animation
|
|
|
|
+ if (self.animate) {
|
|
|
|
+ const stck = self.stck;
|
|
|
|
+ const floor = self.floor;
|
|
|
|
+
|
|
|
|
+ // MANAGE HORIZONTAL MOVEMENT
|
|
|
|
+
|
|
|
|
+ // Move 'tractor'
|
|
|
|
+ self.tractor.x += self.animationSpeed;
|
|
|
|
+
|
|
|
|
+ // Move 'stacked blocks'
|
|
|
|
+ for (let i in stck.blocks) {
|
|
|
|
+ stck.blocks[i].x += self.animationSpeed;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // MANAGE BLOCKS AND FLOOR GAPS
|
|
|
|
+ // If block is 1/n (not 1/1) there's an extra block space to go through before the start of next block
|
|
|
|
+ const restOfCurBlock =
|
|
|
|
+ (self.defaultBlockWidth - stck.blocks[stck.curIndex].width) *
|
|
|
|
+ self.direc_level;
|
|
|
|
+
|
|
|
|
+ // Check if block falls
|
|
|
|
+ if (
|
|
|
|
+ (gameOperation == 'Plus' &&
|
|
|
|
+ stck.blocks[0].x >= stck.curBlockEnd + restOfCurBlock) ||
|
|
|
|
+ (gameOperation == 'Minus' &&
|
|
|
|
+ stck.blocks[0].x <= stck.curBlockEnd + restOfCurBlock)
|
|
|
|
+ ) {
|
|
|
|
+ let lowerBlock = true;
|
|
|
|
+
|
|
|
|
+ const curEnd =
|
|
|
|
+ stck.blocks[0].x +
|
|
|
|
+ stck.blocks[stck.curIndex].width * self.direc_level;
|
|
|
|
+
|
|
|
|
+ // If current index is (A) last stacked index (correct index - fixed)
|
|
|
|
+ // If current index is (B) selected stacked index
|
|
|
|
+ if (stck.curIndex == stck.index) {
|
|
|
|
+ // floor.index : (A) selected floor index
|
|
|
|
+ // floor.index : (B) last floor index (correct index - fixed)
|
|
|
|
+ const selectedEnd =
|
|
|
|
+ floor.blocks[floor.index].x +
|
|
|
|
+ floor.blocks[0].width * self.direc_level;
|
|
|
|
+
|
|
|
|
+ // (A) last stacked block (fixed) doesnt fit selected gap AKA NOT ENOUGH FLOOR BLOCKS (DOESNT CHECK TOO MANY)
|
|
|
|
+ // (B) selected stacked index doesnt fit last floor gap (fixed) AKA TOO MANY STACKED BLOCKS (DOESNT CHECK NOT ENOUGH)
|
|
|
|
+ if (
|
|
|
|
+ (gameOperation == 'Plus' && curEnd > selectedEnd) ||
|
|
|
|
+ (gameOperation == 'Minus' && curEnd < selectedEnd)
|
|
|
|
+ ) {
|
|
|
|
+ lowerBlock = false;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // Update to next block end
|
|
|
|
+ stck.curBlockEnd +=
|
|
|
|
+ stck.blocks[stck.curIndex + 1].width * self.direc_level;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Fill floor gap
|
|
|
|
+ if (lowerBlock) {
|
|
|
|
+ // Until (A) selected floor index
|
|
|
|
+ // Until (B) last floor index (correct index - fixed)
|
|
|
|
+ // Updates floor index to be equivalent to stacked index (and change alpha so floor appears to be filled)
|
|
|
|
+ for (let i = 0; i <= floor.index; i++) {
|
|
|
|
+ if (
|
|
|
|
+ (gameOperation == 'Plus' && floor.blocks[i].x < curEnd) ||
|
|
|
|
+ (gameOperation == 'Minus' && floor.blocks[i].x > curEnd)
|
|
|
|
+ ) {
|
|
|
|
+ floor.blocks[i].alpha = 0.2;
|
|
|
|
+ floor.curIndex = i;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Lower
|
|
|
|
+ stck.blocks[stck.curIndex].alpha = 0;
|
|
|
|
+ stck.blocks.forEach((cur) => {
|
|
|
|
+ cur.y += self.defaultBlockHeight - 2;
|
|
|
|
+ }); // Lower stacked blocks
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ stck.curIndex++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // WHEN REACHED END POSITION
|
|
|
|
+ if (stck.curIndex > stck.index || floor.curIndex == floor.index) {
|
|
|
|
+ self.animate = false;
|
|
|
|
+ self.checkAnswer = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // When animation ends check answer
|
|
|
|
+ if (self.checkAnswer) {
|
|
|
|
+ game.timer.stop();
|
|
|
|
+
|
|
|
|
+ game.animation.stop(self.tractor.animation[0]);
|
|
|
|
+
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ self.result = self.floor.index == self.floor.correctIndex;
|
|
|
|
+ } else {
|
|
|
|
+ self.result = self.stck.index == self.stck.correctIndex;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Give feedback to player and turns on sprite animation
|
|
|
|
+ if (self.result) {
|
|
|
|
+ // Correct answer
|
|
|
|
+ game.animation.play(self.tractor.animation[0]);
|
|
|
|
+
|
|
|
|
+ // Displays feedback image and sound
|
|
|
|
+ game.add
|
|
|
|
+ .image(context.canvas.width / 2, context.canvas.height / 2, 'ok')
|
|
|
|
+ .anchor(0.5, 0.5);
|
|
|
|
+ if (audioStatus) game.audio.okSound.play();
|
|
|
|
+
|
|
|
|
+ completedLevels++; // Increases number os finished levels
|
|
|
|
+ if (debugMode) console.log('Completed Levels: ' + completedLevels);
|
|
|
|
+ } else {
|
|
|
|
+ // Incorrect answer
|
|
|
|
+ // Displays feedback image and sound
|
|
|
|
+ game.add
|
|
|
|
+ .image(context.canvas.width / 2, context.canvas.height / 2, 'error')
|
|
|
|
+ .anchor(0.5, 0.5);
|
|
|
|
+ if (audioStatus) game.audio.errorSound.play();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.postScore();
|
|
|
|
+
|
|
|
|
+ // AFTER CHECK ANSWER
|
|
|
|
+ self.checkAnswer = false;
|
|
|
|
+ self.animateEnding = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Starts 'ending' tractor moving animation
|
|
|
|
+ if (self.animateEnding) {
|
|
|
|
+ // ANIMATE ENDING
|
|
|
|
+
|
|
|
|
+ self.count++;
|
|
|
|
+
|
|
|
|
+ // If CORRECT ANSWER runs final tractor animation (else tractor desn't move, just wait)
|
|
|
|
+ if (self.result) self.tractor.x += self.animationSpeed;
|
|
|
|
+
|
|
|
|
+ // WHEN REACHED END POSITION calls map state
|
|
|
|
+ if (self.count >= 140) {
|
|
|
|
+ // If CORRECT ANSWER, player goes to next level in map
|
|
|
|
+ if (self.result) mapMove = true;
|
|
|
|
+ else mapMove = false;
|
|
|
|
+
|
|
|
|
+ game.state.start('map');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ game.render.all();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Function called by self.onInputOver() when cursor is over a valid rectangle
|
|
|
|
+ *
|
|
|
|
+ * @param {object} cur rectangle the cursor is over
|
|
|
|
+ */
|
|
|
|
+ overSquare: function (cur) {
|
|
|
|
+ if (!self.hasClicked) {
|
|
|
|
+ document.body.style.cursor = 'pointer';
|
|
|
|
+
|
|
|
|
+ // On gameMode A
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ for (let i in self.floor.blocks) {
|
|
|
|
+ self.floor.blocks[i].alpha = i <= cur.index ? 1 : 0.5;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Saves the index of the selected 'floor' block
|
|
|
|
+ self.floor.index = cur.index;
|
|
|
|
+
|
|
|
|
+ // On gameMode B
|
|
|
|
+ } else {
|
|
|
|
+ for (let i in self.stck.blocks) {
|
|
|
|
+ self.stck.blocks[i].alpha = i <= cur.index ? 0.5 : 0.2;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Saves the index of the selected 'stack' block
|
|
|
|
+ self.stck.index = cur.index;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Function called by self.onInputOver() when cursos is out of a valid rectangle
|
|
|
|
+ */
|
|
|
|
+ outSquare: function () {
|
|
|
|
+ if (!self.hasClicked) {
|
|
|
|
+ document.body.style.cursor = 'auto';
|
|
|
|
+
|
|
|
|
+ // On game mode A
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ for (let i in self.floor.blocks) {
|
|
|
|
+ self.floor.blocks[i].alpha = 0.5; // Back to normal
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.floor.index = -1;
|
|
|
|
+ // On game mode B
|
|
|
|
+ } else {
|
|
|
|
+ for (let i in self.stck.blocks) {
|
|
|
|
+ self.stck.blocks[i].alpha = 0.5; // Back to normal
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.stck.index = -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Function called by self.onInputDown() when player clicks on a valid rectangle.
|
|
|
|
+ */
|
|
|
|
+ clickSquare: function () {
|
|
|
|
+ if (!self.hasClicked && !self.animateEnding) {
|
|
|
|
+ document.body.style.cursor = 'auto';
|
|
|
|
+
|
|
|
|
+ // On gameMode A
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ // Turns selection arrow completely visible
|
|
|
|
+ self.arrow.alpha = 1;
|
|
|
|
+
|
|
|
|
+ // Make the unselected blocks invisible (look like there's only the ground)
|
|
|
|
+ for (let i in self.floor.blocks) {
|
|
|
|
+ // (SELECTION : self.FLOOR.index)
|
|
|
|
+ if (i > self.floor.index) self.floor.blocks[i].alpha = 0; // Make unselected 'floor' blocks invisible
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // (FIXED : self.STCK.index) save the 'stacked' blocks index
|
|
|
|
+ self.stck.index = self.stck.blocks.length - 1;
|
|
|
|
+ // On gameMode B
|
|
|
|
+ } else {
|
|
|
|
+ for (let i in self.stck.blocks) {
|
|
|
|
+ // (FIXED : self.STCK.index)
|
|
|
|
+ if (i > self.stck.index) self.stck.blocks[i].alpha = 0; // Make unselected 'stacked' blocks invisible
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // (SELECTION : self.FLOOR.index) save the 'floor' blocks index to compare to the stacked index in update
|
|
|
|
+ self.floor.index = self.floor.blocks.length - 1;
|
|
|
|
+
|
|
|
|
+ // Save the updated total stacked blocks to compare in update
|
|
|
|
+ self.stck.blocks.length = self.stck.index + 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Play beep sound
|
|
|
|
+ if (audioStatus) game.audio.popSound.play();
|
|
|
|
+
|
|
|
|
+ // Hide labels
|
|
|
|
+ if (fractionLabel) {
|
|
|
|
+ self.stck.labels.forEach((cur) => {
|
|
|
|
+ cur.forEach((cur) => {
|
|
|
|
+ cur.alpha = 0;
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ // Hide solution pointer
|
|
|
|
+ if (self.help != undefined) self.help.alpha = 0;
|
|
|
|
+
|
|
|
|
+ // Turn tractir animation on
|
|
|
|
+ game.animation.play(self.tractor.animation[0]);
|
|
|
|
+
|
|
|
|
+ self.hasClicked = true;
|
|
|
|
+ self.animate = true;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create stacked blocks for the level in create()
|
|
|
|
+ *
|
|
|
|
+ * @returns {boolean}
|
|
|
|
+ */
|
|
|
|
+ createStckBlocks: function () {
|
|
|
|
+ let hasBaseDifficulty = false; // Will be true after next for loop if level has at least one '1/difficulty' fraction (if false, restart)
|
|
|
|
+ const max = gameMode == 'B' ? 10 : mapPosition + 4; // Maximum number of stacked blocks for the level
|
|
|
|
+
|
|
|
|
+ const total = game.math.randomInRange(mapPosition + 2, max); // Current number of stacked blocks for the level
|
|
|
|
+
|
|
|
|
+ self.floor.correctXA =
|
|
|
|
+ self.startX + self.defaultBlockWidth * self.direc_level;
|
|
|
|
+
|
|
|
|
+ for (let i = 0; i < total; i++) {
|
|
|
|
+ // For each stacked block
|
|
|
|
+ let divisor = game.math.randomInRange(1, gameDifficulty); // Set divisor for fraction
|
|
|
|
+ if (divisor == gameDifficulty) hasBaseDifficulty = true;
|
|
|
|
+ if (divisor == 3) divisor = 4; // Make sure valid divisors are 1, 2 and 4 (not 3)
|
|
|
|
+ self.divisorsList += divisor + ','; // List of divisors (for postScore())
|
|
|
|
+
|
|
|
|
+ const curBlockWidth = self.defaultBlockWidth / divisor; // Current width is a fraction of the default
|
|
|
|
+
|
|
|
|
+ self.floor.correctXA += curBlockWidth * self.direc_level;
|
|
|
|
+
|
|
|
|
+ // Create stacked block (close to tractor)
|
|
|
|
+ const lineColor = gameOperation == 'Minus' ? colors.red : colors.blueDark;
|
|
|
|
+ const lineSize = 2;
|
|
|
|
+ const block = game.add.geom.rect(
|
|
|
|
+ self.startX,
|
|
|
|
+ self.startY + 17 - i * (self.defaultBlockHeight - lineSize),
|
|
|
|
+ curBlockWidth - lineSize,
|
|
|
|
+ self.defaultBlockHeight - lineSize,
|
|
|
|
+ lineColor,
|
|
|
|
+ lineSize,
|
|
|
|
+ colors.white,
|
|
|
|
+ 1
|
|
|
|
+ );
|
|
|
|
+ const anchor = gameOperation == 'Minus' ? 1 : 0;
|
|
|
|
+ block.anchor(anchor, 0);
|
|
|
|
+
|
|
|
|
+ // If game is type B, adding events to stacked blocks
|
|
|
|
+ if (gameMode == 'B') {
|
|
|
|
+ block.alpha = 0.5;
|
|
|
|
+ block.index = i;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.stck.blocks.push(block);
|
|
|
|
+
|
|
|
|
+ // If 'show fractions' is turned on, create labels that display the fractions on the side of each block
|
|
|
|
+ if (fractionLabel) {
|
|
|
|
+ const x = self.startX + (curBlockWidth + 15) * self.direc_level;
|
|
|
|
+ const y = self.defaultBlockHeight - lineSize;
|
|
|
|
+
|
|
|
|
+ const label = [];
|
|
|
|
+
|
|
|
|
+ if (divisor == 1) {
|
|
|
|
+ label[0] = game.add.text(
|
|
|
|
+ x,
|
|
|
|
+ self.startY - (i - 1) * y,
|
|
|
|
+ divisor,
|
|
|
|
+ textStyles.h2_blueDark
|
|
|
|
+ );
|
|
|
|
+ } else {
|
|
|
|
+ label[0] = game.add.text(
|
|
|
|
+ x,
|
|
|
|
+ self.startY + 45 - i * y + 23,
|
|
|
|
+ divisor,
|
|
|
|
+ textStyles.p_blueDark
|
|
|
|
+ );
|
|
|
|
+ label[1] = game.add.text(
|
|
|
|
+ x,
|
|
|
|
+ self.startY + 40 - i * y,
|
|
|
|
+ '1',
|
|
|
|
+ textStyles.p_blueDark
|
|
|
|
+ );
|
|
|
|
+ label[2] = game.add.text(
|
|
|
|
+ x,
|
|
|
|
+ self.startY + 40 - i * y,
|
|
|
|
+ '_',
|
|
|
|
+ textStyles.p_blueDark
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ // Add current label to group of labels
|
|
|
|
+ self.stck.labels.push(label);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Will be used as a counter in update, adding in the width of each stacked block to check if the end matches the floor selected position
|
|
|
|
+ self.stck.curBlockEnd =
|
|
|
|
+ self.startX + self.stck.blocks[0].width * self.direc_level;
|
|
|
|
+
|
|
|
|
+ let restart = false;
|
|
|
|
+
|
|
|
|
+ // Check for errors (level too easy for its difficulty or end position out of bounds)
|
|
|
|
+ if (
|
|
|
|
+ !hasBaseDifficulty ||
|
|
|
|
+ (gameOperation == 'Plus' &&
|
|
|
|
+ (self.floor.correctXA < self.startX + self.defaultBlockWidth ||
|
|
|
|
+ self.floor.correctXA > self.startX + 8 * self.defaultBlockWidth)) ||
|
|
|
|
+ (gameOperation == 'Minus' &&
|
|
|
|
+ (self.floor.correctXA < self.startX - 8 * self.defaultBlockWidth ||
|
|
|
|
+ self.floor.correctXA > self.startX - self.defaultBlockWidth))
|
|
|
|
+ ) {
|
|
|
|
+ restart = true; // If any error is found restart the level
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (debugMode)
|
|
|
|
+ console.log(
|
|
|
|
+ 'Stacked blocks: ' +
|
|
|
|
+ total +
|
|
|
|
+ ' (min: ' +
|
|
|
|
+ (mapPosition + 2) +
|
|
|
|
+ ', max: ' +
|
|
|
|
+ max +
|
|
|
|
+ ')'
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ return restart;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create floor blocks for the level in create()
|
|
|
|
+ */
|
|
|
|
+ createFloorBlocks: function () {
|
|
|
|
+ // For each floor block
|
|
|
|
+ const divisor = gameDifficulty == 3 ? 4 : gameDifficulty; // Make sure valid divisors are 1, 2 and 4 (not 3)
|
|
|
|
+
|
|
|
|
+ let total = 8 * divisor; // Number of floor blocks
|
|
|
|
+
|
|
|
|
+ const blockWidth = self.defaultBlockWidth / divisor; // Width of each floor block
|
|
|
|
+
|
|
|
|
+ // If game is type B, selectiong a random floor x position
|
|
|
|
+ if (gameMode == 'B') {
|
|
|
|
+ self.stck.correctIndex = game.math.randomInRange(
|
|
|
|
+ 0,
|
|
|
|
+ self.stck.blocks.length - 1
|
|
|
|
+ ); // Correct stacked index
|
|
|
|
+
|
|
|
|
+ self.floor.correctXB =
|
|
|
|
+ self.startX + self.defaultBlockWidth * self.direc_level;
|
|
|
|
+
|
|
|
|
+ for (let i = 0; i <= self.stck.correctIndex; i++) {
|
|
|
|
+ self.floor.correctXB += self.stck.blocks[i].width * self.direc_level; // Equivalent x position on the floor
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let flag = true;
|
|
|
|
+
|
|
|
|
+ for (let i = 0; i < total; i++) {
|
|
|
|
+ // For each floor block
|
|
|
|
+ // 'x' coordinate for floor block
|
|
|
|
+ const x =
|
|
|
|
+ self.startX +
|
|
|
|
+ (self.defaultBlockWidth + i * blockWidth) * self.direc_level;
|
|
|
|
+
|
|
|
|
+ if (flag && gameMode == 'A') {
|
|
|
|
+ if (
|
|
|
|
+ (gameOperation == 'Plus' && x >= self.floor.correctXA) ||
|
|
|
|
+ (gameOperation == 'Minus' && x <= self.floor.correctXA)
|
|
|
|
+ ) {
|
|
|
|
+ self.floor.correctIndex = i - 1; // Set index of correct floor block
|
|
|
|
+ flag = false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (gameMode == 'B') {
|
|
|
|
+ if (
|
|
|
|
+ (gameOperation == 'Plus' && x >= self.floor.correctXB) ||
|
|
|
|
+ (gameOperation == 'Minus' && x <= self.floor.correctXB)
|
|
|
|
+ ) {
|
|
|
|
+ total = i;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Create floor block
|
|
|
|
+ const lineSize = 0.9;
|
|
|
|
+ const block = game.add.geom.rect(
|
|
|
|
+ x,
|
|
|
|
+ self.startY + 17 + self.defaultBlockHeight - lineSize,
|
|
|
|
+ blockWidth - lineSize,
|
|
|
|
+ self.defaultBlockHeight - lineSize,
|
|
|
|
+ colors.blueBg,
|
|
|
|
+ lineSize,
|
|
|
|
+ colors.blueBgInsideLevel,
|
|
|
|
+ 1
|
|
|
|
+ );
|
|
|
|
+ const anchor = gameOperation == 'Minus' ? 1 : 0;
|
|
|
|
+ block.anchor(anchor, 0);
|
|
|
|
+
|
|
|
|
+ // If game is type A, adding events to floor blocks
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ block.alpha = 0.5;
|
|
|
|
+ block.index = i;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Add current label to group of labels
|
|
|
|
+ self.floor.blocks.push(block);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (gameMode == 'A') self.floor.correctX = self.floor.correctXA;
|
|
|
|
+ else if (gameMode == 'B') self.floor.correctX = self.floor.correctXB;
|
|
|
|
+
|
|
|
|
+ // Creates labels on the floor to display the numbers
|
|
|
|
+ for (let i = 1; i < 10; i++) {
|
|
|
|
+ const x = self.startX + i * self.defaultBlockWidth * self.direc_level;
|
|
|
|
+ game.add.text(
|
|
|
|
+ x,
|
|
|
|
+ self.startY + self.defaultBlockHeight + 78 * 1.5,
|
|
|
|
+ i - 1,
|
|
|
|
+ textStyles.h2_blueDark
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Display correct answer
|
|
|
|
+ */
|
|
|
|
+ viewHelp: function () {
|
|
|
|
+ if (!self.hasClicked) {
|
|
|
|
+ // On gameMode A
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ const aux = self.floor.blocks[0];
|
|
|
|
+ self.help.x = self.floor.correctX - (aux.width / 2) * self.direc_level;
|
|
|
|
+ self.help.y = 501;
|
|
|
|
+ // On gameMode B
|
|
|
|
+ } else {
|
|
|
|
+ const aux = self.stck.blocks[self.stck.correctIndex];
|
|
|
|
+ self.help.x = aux.x + (aux.width / 2) * self.direc_level;
|
|
|
|
+ self.help.y = aux.y;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.help.alpha = 0.7;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Called by mouse click event
|
|
|
|
+ *
|
|
|
|
+ * @param {object} mouseEvent contains the mouse click coordinates
|
|
|
|
+ */
|
|
|
|
+ onInputDown: function (mouseEvent) {
|
|
|
|
+ const x = game.math.getMouse(mouseEvent).x;
|
|
|
|
+ const y = game.math.getMouse(mouseEvent).y;
|
|
|
|
+
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ self.floor.blocks.forEach((cur) => {
|
|
|
|
+ if (game.math.isOverIcon(x, y, cur)) self.clickSquare(cur);
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ self.stck.blocks.forEach((cur) => {
|
|
|
|
+ if (game.math.isOverIcon(x, y, cur)) self.clickSquare(cur);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ navigationIcons.onInputDown(x, y);
|
|
|
|
+
|
|
|
|
+ game.render.all();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Called by mouse move event
|
|
|
|
+ *
|
|
|
|
+ * @param {object} mouseEvent contains the mouse move coordinates
|
|
|
|
+ */
|
|
|
|
+ onInputOver: function (mouseEvent) {
|
|
|
|
+ const x = game.math.getMouse(mouseEvent).x;
|
|
|
|
+ const y = game.math.getMouse(mouseEvent).y;
|
|
|
|
+ let flagA = false;
|
|
|
|
+ let flagB = false;
|
|
|
|
+
|
|
|
|
+ if (gameMode == 'A') {
|
|
|
|
+ // Make arrow follow mouse
|
|
|
|
+ if (!self.hasClicked && !self.animateEnding) {
|
|
|
|
+ if (game.math.distanceToPointer(self.arrow.x, x, self.arrow.y, y) > 8) {
|
|
|
|
+ self.arrow.x = x < 250 ? 250 : x; // Limits the arrow left position to 250
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ self.floor.blocks.forEach((cur) => {
|
|
|
|
+ if (game.math.isOverIcon(x, y, cur)) {
|
|
|
|
+ flagA = true;
|
|
|
|
+ self.overSquare(cur);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (!flagA) self.outSquare('A');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (gameMode == 'B') {
|
|
|
|
+ self.stck.blocks.forEach((cur) => {
|
|
|
|
+ if (game.math.isOverIcon(x, y, cur)) {
|
|
|
|
+ flagB = true;
|
|
|
|
+ self.overSquare(cur);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if (!flagB) self.outSquare('B');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ navigationIcons.onInputOver(x, y);
|
|
|
|
+
|
|
|
|
+ game.render.all();
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Saves players data after level ends - to be sent to database <br>
|
|
|
|
+ *
|
|
|
|
+ * Attention: the 'line_' prefix data table must be compatible to data table fields (MySQL server)
|
|
|
|
+ *
|
|
|
|
+ * @see /php/save.php
|
|
|
|
+ */
|
|
|
|
+ postScore: function () {
|
|
|
|
+ // Creates string that is going to be sent to db
|
|
|
|
+ const data =
|
|
|
|
+ '&line_game=' +
|
|
|
|
+ gameShape +
|
|
|
|
+ '&line_mode=' +
|
|
|
|
+ gameMode +
|
|
|
|
+ '&line_oper=' +
|
|
|
|
+ gameOperation +
|
|
|
|
+ '&line_leve=' +
|
|
|
|
+ gameDifficulty +
|
|
|
|
+ '&line_posi=' +
|
|
|
|
+ mapPosition +
|
|
|
|
+ '&line_resu=' +
|
|
|
|
+ self.result +
|
|
|
|
+ '&line_time=' +
|
|
|
|
+ game.timer.elapsed +
|
|
|
|
+ '&line_deta=' +
|
|
|
|
+ 'numBlocks:' +
|
|
|
|
+ self.stck.blocks.length +
|
|
|
|
+ ', valBlocks: ' +
|
|
|
|
+ self.divisorsList + // Ends in ','
|
|
|
|
+ ' blockIndex: ' +
|
|
|
|
+ self.stck.index +
|
|
|
|
+ ', floorIndex: ' +
|
|
|
|
+ self.floor.index;
|
|
|
|
+
|
|
|
|
+ // FOR MOODLE
|
|
|
|
+ sendToDatabase(data);
|
|
|
|
+ },
|
|
|
|
+};
|