Browse Source

Merge branch 'fix/game-feedback' of LInE/Ifractions-web into develop

laira 2 months ago
parent
commit
0fe5a81a83
1 changed files with 333 additions and 169 deletions
  1. 333 169
      js/games/circleOne.js

+ 333 - 169
js/games/circleOne.js

@@ -63,32 +63,57 @@ const circleOne = {
         text: undefined,
         text: undefined,
       },
       },
     };
     };
-    this.road = {
-      x: 150,
-      y: context.canvas.height - game.image['floor_grass'].width * 1.5,
-      width: 1620,
+
+    const roadMapper = () => {
+      const _pointWidth = (game.sprite['map_place'].width / 2) * 0.45;
+
+      const defaultX = 150;
+      const defaultY =
+        context.canvas.height - game.image['floor_grass'].width * 1.5;
+      const defaultWidth = 1620;
+
+      // Initial 'x' coordinate for the kid and the baloon
+      const x =
+        gameOperation === 'minus'
+          ? context.canvas.width - defaultX - _pointWidth / 2
+          : defaultX + _pointWidth / 2;
+      const y = defaultY;
+      const width = defaultWidth - _pointWidth;
+      const _divisions = 5;
+      const _subdivisions = gameDifficulty === 3 ? 4 : gameDifficulty;
+      const numberOfBlocks = _divisions * _subdivisions;
+
+      return { x, y, width, numberOfBlocks, defaultX, defaultY, defaultWidth };
+    };
+    this.road = roadMapper();
+
+    const blocksMapper = () => {
+      let width = this.road.width / this.road.numberOfBlocks;
+      if (gameOperation === 'minus') width = -width;
+      const height = 50;
+      return { width, height, list: [], cur: undefined };
     };
     };
+    this.blocks = blocksMapper();
+
     this.walkedPath = [];
     this.walkedPath = [];
 
 
     const pointWidth = (game.sprite['map_place'].width / 2) * 0.45;
     const pointWidth = (game.sprite['map_place'].width / 2) * 0.45;
+
     const distanceBetweenPoints =
     const distanceBetweenPoints =
-      (context.canvas.width - this.road.x * 2 - pointWidth) / 5; // Distance between road points
-    const y0 = this.road.y + 20;
-    const x0 =
-      gameOperation === 'minus'
-        ? context.canvas.width - this.road.x - pointWidth / 2
-        : this.road.x + pointWidth / 2; // Initial 'x' coordinate for the kid and the baloon
+      (context.canvas.width - this.road.defaultX * 2 - pointWidth) / 5; // Distance between road points
 
 
-    const diameter =
-      game.math.getRadiusFromCircunference(distanceBetweenPoints) * 2;
+    const y0 = this.road.defaultY + 20;
+    const x0 = this.road.x;
 
 
     this.circles = {
     this.circles = {
-      diameter: diameter, // (Fixed) Circles Diameter
+      diameter: game.math.getRadiusFromCircunference(distanceBetweenPoints) * 2, // (Fixed) Circles Diameter
       cur: 0, // Current circle index
       cur: 0, // Current circle index
       list: [], // Circle objects
       list: [], // Circle objects
     };
     };
 
 
     this.control = {
     this.control = {
+      directionModifier: gameOperation === 'minus' ? -1 : 1,
+
       correctX: x0, // Correct position (accumulative)
       correctX: x0, // Correct position (accumulative)
       nextX: undefined, // Next point position
       nextX: undefined, // Next point position
       divisorsList: '', // used in postScore (Accumulative)
       divisorsList: '', // used in postScore (Accumulative)
@@ -133,6 +158,7 @@ const circleOne = {
 
 
     const validPath = { x0, y0, distanceBetweenPoints };
     const validPath = { x0, y0, distanceBetweenPoints };
 
 
+    this.utils.renderRoadBlocks();
     this.utils.renderRoad(validPath);
     this.utils.renderRoad(validPath);
 
 
     const [restart, balloonX] = this.utils.renderCircles(validPath);
     const [restart, balloonX] = this.utils.renderCircles(validPath);
@@ -171,27 +197,47 @@ const circleOne = {
   },
   },
 
 
   utils: {
   utils: {
+    renderRoadBlocks: function () {
+      for (let i = 0; i < self.road.numberOfBlocks; i++) {
+        const block = game.add.geom.rect(
+          self.road.x + i * self.blocks.width,
+          self.road.y,
+          self.blocks.width, // If gameOperation is minus, block width is negative
+          self.blocks.height,
+          'transparent',
+          0.5,
+          colors.red,
+          2
+        );
+        block.info = { index: i };
+        self.blocks.list.push(block);
+      }
+    },
     // RENDERS
     // RENDERS
     renderRoad: function (validPath) {
     renderRoad: function (validPath) {
-      const directionModifier = gameOperation === 'minus' ? -1 : 1;
+      const offset = 40;
       for (let i = 0; i <= 5; i++) {
       for (let i = 0; i <= 5; i++) {
-        // place
+        // Gray place
         game.add
         game.add
           .sprite(
           .sprite(
             validPath.x0 +
             validPath.x0 +
-              i * validPath.distanceBetweenPoints * directionModifier,
+              i *
+                validPath.distanceBetweenPoints *
+                self.control.directionModifier,
             validPath.y0,
             validPath.y0,
             'map_place',
             'map_place',
             0,
             0,
             0.45
             0.45
           )
           )
           .anchor(0.5, 0.5);
           .anchor(0.5, 0.5);
-        // circle behind number
+        // White circle behind number
+        const curX =
+          validPath.x0 +
+          i * validPath.distanceBetweenPoints * self.control.directionModifier;
         game.add.geom
         game.add.geom
           .circle(
           .circle(
-            validPath.x0 +
-              i * validPath.distanceBetweenPoints * directionModifier,
-            validPath.y0 + 60,
+            curX,
+            validPath.y0 + 60 + offset,
             60,
             60,
             undefined,
             undefined,
             0,
             0,
@@ -199,16 +245,25 @@ const circleOne = {
             0.8
             0.8
           )
           )
           .anchor(0, 0.25);
           .anchor(0, 0.25);
-        // number
+        game.add.geom.rect(
+          curX,
+          validPath.y0 + 60 - 28,
+          4,
+          25,
+          colors.white,
+          0.8,
+          undefined,
+          0
+        );
+        // Number
         game.add.text(
         game.add.text(
-          validPath.x0 +
-            i * validPath.distanceBetweenPoints * directionModifier,
-          validPath.y0 + 60,
-          i * directionModifier,
+          curX,
+          validPath.y0 + 60 + offset,
+          i * self.control.directionModifier,
           {
           {
             ...textStyles.h2_,
             ...textStyles.h2_,
             font: 'bold ' + textStyles.h2_.font,
             font: 'bold ' + textStyles.h2_.font,
-            fill: directionModifier === 1 ? colors.green : colors.red,
+            fill: gameOperation === 'minus' ? colors.red : colors.green,
           }
           }
         );
         );
       }
       }
@@ -229,9 +284,9 @@ const circleOne = {
       let hasBaseDifficulty = false;
       let hasBaseDifficulty = false;
       let balloonX = context.canvas.width / 2;
       let balloonX = context.canvas.width / 2;
 
 
-      const directionModifier = gameOperation === 'minus' ? -1 : 1;
       const fractionX =
       const fractionX =
-        validPath.x0 - (self.circles.diameter - 10) * directionModifier;
+        validPath.x0 -
+        (self.circles.diameter - 10) * self.control.directionModifier;
       const font = {
       const font = {
         ...textStyles.h2_,
         ...textStyles.h2_,
         font: 'bold ' + textStyles.h2_.font,
         font: 'bold ' + textStyles.h2_.font,
@@ -246,7 +301,7 @@ const circleOne = {
 
 
       const total = game.math.randomInRange(min, max); // Total number of circles
       const total = game.math.randomInRange(min, max); // Total number of circles
 
 
-      // for mode 'b'
+      // For mode 'b'
       self.control.numberOfPlusFractions = game.math.randomInRange(
       self.control.numberOfPlusFractions = game.math.randomInRange(
         1,
         1,
         total - 1
         total - 1
@@ -271,10 +326,11 @@ const circleOne = {
             denominator: undefined,
             denominator: undefined,
           },
           },
         };
         };
-        const curDivisor = game.math.randomInRange(1, gameDifficulty); // Set fraction 'divisor' (depends on difficulty)
-
+        let curDivisor = game.math.randomInRange(1, gameDifficulty); // Set fraction 'divisor' (depends on difficulty)
         if (curDivisor === gameDifficulty) hasBaseDifficulty = true; // True if after for ends has at least 1 '1/difficulty' fraction
         if (curDivisor === gameDifficulty) hasBaseDifficulty = true; // True if after for ends has at least 1 '1/difficulty' fraction
 
 
+        curDivisor = curDivisor === 3 ? 4 : curDivisor; // Turns 1/3 into 1/4 fractions
+
         self.control.divisorsList += curDivisor + ','; // Add this divisor to the list of divisors (for postScore())
         self.control.divisorsList += curDivisor + ','; // Add this divisor to the list of divisors (for postScore())
 
 
         // Set each circle direction
         // Set each circle direction
@@ -474,6 +530,13 @@ const circleOne = {
         }
         }
 
 
         finalPosition = balloonX;
         finalPosition = balloonX;
+
+        self.blocks.list.forEach((cur) => {
+          self.utils.fillCurrentBlock(balloonX, cur.x, cur);
+          if (self.utils.isOverBlock(balloonX, cur.x, cur.width, cur))
+            self.blocks.cur = cur;
+        });
+
         // Restart if
         // Restart if
         // In Game mode 'b' : Top circle position is out of bounds (when on the ground)
         // In Game mode 'b' : Top circle position is out of bounds (when on the ground)
         if (gameOperation === 'minus') {
         if (gameOperation === 'minus') {
@@ -563,27 +626,103 @@ const circleOne = {
       /**
       /**
        * if game mode A:
        * if game mode A:
        * - left: selected balloon position (user selection)
        * - left: selected balloon position (user selection)
-       * - right: line created from the stack of arcs (pre-set)
+       * - right: correct sum of stack of arcs (pre-set)
        *
        *
        * if game mode B:
        * if game mode B:
        * - left: line created from the stack of arcs (user selection)
        * - left: line created from the stack of arcs (user selection)
        * - right: baloon position (pre-set)
        * - right: baloon position (pre-set)
        */
        */
 
 
-      let validCircles = self.circles.list;
-      if (gameMode === 'b') {
-        validCircles = [];
-        for (let i = 0; i <= self.control.selectedIndex; i++) {
-          validCircles.push(self.circles.list[i]);
+      const renderFloorFractions = (lastIndex) => {
+        const divisor = gameDifficulty == 3 ? 4 : gameDifficulty;
+
+        const operator = gameOperation === 'minus' ? '-' : '+';
+        const index = lastIndex;
+        const blocks = index + 1;
+
+        const valueReal = blocks / divisor;
+        const valueFloor = Math.floor(valueReal);
+        const valueRest = valueReal - valueFloor;
+
+        let fracNomin = (fracDenomin = fracLine = '');
+        // Adds sign on the left of the equation
+        if (gameOperation === 'minus') {
+          fracNomin += ' ';
+          fracDenomin += ' ';
+          fracLine += operator;
+        }
+        // 1 _ _
+        if (valueFloor) {
+          fracNomin += ' ';
+          fracDenomin += ' ';
+          fracLine += valueFloor;
+        }
+        // _ + _
+        if (valueFloor && valueRest) {
+          fracNomin += ' ';
+          fracDenomin += ' ';
+          fracLine += operator;
+        }
+        // _ _ 1/5
+        if (valueRest) {
+          fracNomin += `${valueRest * divisor}`;
+          fracDenomin += `${divisor}`;
+          fracLine += '-';
         }
         }
-      }
 
 
-      const font = textStyles.fraction;
-      font.fill = colors.green;
+        return [fracNomin, fracDenomin, fracLine, valueReal];
+      };
 
 
-      const nominators = [];
-      const denominators = [];
-      const renderList = [];
+      const renderStackFractions = (lastIndex) => {
+        const operator = gameOperation === 'minus' ? '-' : '+';
+        const index = lastIndex;
+        const blocks = index + 1;
+
+        const nominators = [];
+        const denominators = [];
+        const values = [];
+        let valueReal = 0;
+        let fracNomin = (fracDenomin = fracLine = '');
+
+        for (let i = 0; i < blocks; i++) {
+          const m = self.circles.list[i].info.fraction.denominator || 1;
+          const temp = self.circles.list[i].info.fraction.nominator || 0;
+          const n = gameOperation === 'minus' ? -temp : +temp;
+          const nm = n / m;
+          nominators[i] = n + 0;
+          denominators[i] = m + 0;
+          values[i] = nm;
+          valueReal += nm;
+        }
+
+        for (let i = 0; i < blocks; i++) {
+          const valueReal = values[i];
+          const valueFloor = Math.floor(valueReal);
+          const valueRest = valueReal - valueFloor;
+
+          if (i > 0 || gameOperation === 'minus') {
+            fracNomin += ' ';
+            fracDenomin += ' ';
+            fracLine += operator;
+          }
+          if (valueFloor && !valueRest) {
+            fracNomin += ' ';
+            fracDenomin += ' ';
+            fracLine += valueFloor;
+          }
+          if (valueRest) {
+            fracNomin += `${nominators[i]}`;
+            fracDenomin += `${denominators[i]}`;
+            fracLine += '-';
+          }
+        }
+
+        return [fracNomin, fracDenomin, fracLine, valueReal];
+      };
+
+      // Initial setup
+      const font = textStyles.fraction;
+      font.fill = colors.black;
 
 
       const padding = 100;
       const padding = 100;
       const offsetX = 100;
       const offsetX = 100;
@@ -597,7 +736,9 @@ const circleOne = {
       const cardX = x0 - padding;
       const cardX = x0 - padding;
       const cardY = y0;
       const cardY = y0;
 
 
-      // Card
+      const renderList = [];
+
+      // Render Card
       const card = game.add.geom.rect(
       const card = game.add.geom.rect(
         cardX,
         cardX,
         cardY,
         cardY,
@@ -612,128 +753,95 @@ const circleOne = {
       card.anchor(0, 0.5);
       card.anchor(0, 0.5);
       renderList.push(card);
       renderList.push(card);
 
 
-      // Fraction list
-      for (let i in validCircles) {
-        const curFraction = validCircles[i].info.fraction;
-        const curFractionString = curFraction.labels[0].name;
-        let curFractionSign = i !== '0' ? '+' : '';
-        if (curFraction.labels[1].name === '-') {
-          curFractionSign = '-';
-          font.fill = colors.red;
-        }
-
-        const fractionText = game.add.text(
-          x0 + i * offsetX + offsetX / 2,
-          curFractionString === '1' ? y0 + 40 : y0,
-          curFractionString,
-          font
+      // Fraction setup
+      const [floorNominators, floorDenominators, floorLines, floorValue] =
+        renderFloorFractions(self.blocks.cur.info.index);
+      const [stackNominators, stackDenominators, stackLines, stackValue] =
+        renderStackFractions(self.circles.cur - 1);
+
+      const renderFloorOperationLine = (x) => {
+        font.fill = colors.black;
+        const floorNom = game.add.text(
+          x + offsetX / 2,
+          y0,
+          floorNominators,
+          font,
+          60
         );
         );
-        fractionText.lineHeight = 70;
-
-        renderList.push(
-          game.add.text(x0 + i * offsetX, y0 + 35, curFractionSign, font)
+        const floorDenom = game.add.text(
+          x + offsetX / 2,
+          y0 + 70,
+          floorDenominators,
+          font,
+          60
         );
         );
-        renderList.push(fractionText);
-        renderList.push(
-          game.add.text(
-            x0 + offsetX / 2 + i * offsetX,
-            y0,
-            curFractionString === '1' ? '' : '_',
-            font
-          )
+        const floorLin = game.add.text(
+          x + offsetX / 2,
+          y0 + 35,
+          floorLines,
+          font,
+          60
         );
         );
+        renderList.push(floorNom);
+        renderList.push(floorDenom);
+        renderList.push(floorLin);
+      };
 
 
-        nominators.push(curFraction.nominator);
-        denominators.push(curFraction.denominator);
-      }
-
-      // Setup for fraction operation with least common multiple
-      font.fill = colors.black;
-      const updatedNominators = [];
-      const mmc = game.math.mmcArray(denominators);
-      let resultNominator = 0;
-      for (let i in nominators) {
-        const temp = nominators[i] * (mmc / denominators[i]);
-        updatedNominators.push(temp);
-        resultNominator += temp;
-      }
-      const resultNominatorUnsigned =
-        resultNominator < 0 ? -resultNominator : resultNominator;
-      const resultAux = resultNominator / mmc;
-      const result =
-        ('' + resultAux).length > 4 ? resultAux.toFixed(2) : resultAux;
-
-      // Fraction operation with least common multiple
-      nextX = x0 + validCircles.length * offsetX + 20;
-
-      // Fraction result
-      renderList.push(game.add.text(nextX, y0 + 35, '=', font));
-
-      font.align = 'center';
-      nextX += offsetX;
+      const renderStackOperationLine = (x) => {
+        font.fill = colors.black;
+        const stackNom = game.add.text(
+          x + offsetX / 2,
+          y0,
+          stackNominators,
+          font,
+          60
+        );
+        const stackDenom = game.add.text(
+          x + offsetX / 2,
+          y0 + 70,
+          stackDenominators,
+          font,
+          60
+        );
+        const stackLin = game.add.text(
+          x + offsetX / 2,
+          y0 + 35,
+          stackLines,
+          font,
+          60
+        );
+        renderList.push(stackNom);
+        renderList.push(stackDenom);
+        renderList.push(stackLin);
+      };
 
 
-      if (result < 0) {
-        nextX -= 30;
+      // Render LEFT part of the operation
+      if (gameMode === 'a') renderFloorOperationLine(x0);
+      else renderStackOperationLine(x0);
 
 
-        renderList.push(game.add.text(nextX, y0 + 35, '-', font));
+      let curNominators = gameMode === 'a' ? floorNominators : stackNominators;
+      nextX = x0 + (curNominators.length + 2) * widthOfChar;
 
 
-        nextX += 60;
+      // Render middle sign - equal by default
+      font.fill = colors.green;
+      let comparisonSign = '=';
+      // Render middle sign - if not equal
+      if (floorValue != stackValue) {
+        font.fill = colors.red;
+        let leftSideIsLarger = floorValue > stackValue;
+        if (gameMode === 'b') leftSideIsLarger = !leftSideIsLarger;
+        if (gameOperation === 'minus') leftSideIsLarger = !leftSideIsLarger;
+        comparisonSign = leftSideIsLarger ? '>' : '<';
       }
       }
+      renderList.push(game.add.text(nextX, y0 + 35, comparisonSign, font));
 
 
-      const fractionResult = game.add.text(
-        nextX,
-        mmc === 1 || resultNominatorUnsigned === 0 ? y0 + 40 : y0,
-        mmc === 1 || resultNominatorUnsigned === 0
-          ? resultNominatorUnsigned
-          : resultNominatorUnsigned + '\n' + mmc,
-        font
-      );
-      fractionResult.lineHeight = 70;
-      renderList.push(fractionResult);
-
-      const fractionLine = game.add.geom.line(
-        nextX,
-        y0 + 15,
-        nextX + 60,
-        y0 + 15,
-        4,
-        colors.black,
-        mmc === 1 || resultNominatorUnsigned === 0 ? 0 : 1
-      );
-      fractionLine.anchor(0.5, 0);
-      renderList.push(fractionLine);
-
-      // Fraction result simplified setup
-      const mdcAux = game.math.mdc(resultNominator, mmc);
-      const mdc = mdcAux < 0 ? -mdcAux : mdcAux;
-      if (mdc !== 1 && resultNominatorUnsigned !== 0) {
-        // alert(mdc + ' ' + resultNominatorUnsigned);
-        nextX += offsetX;
-        renderList.push(game.add.text(nextX, y0 + 35, '=', font));
-
-        nextX += offsetX;
-        renderList.push(
-          game.add.text(nextX - 50, y0 + 35, result > 0 ? '' : '-', font)
-        );
-        renderList.push(
-          game.add.text(nextX, y0, resultNominatorUnsigned / mdc, font)
-        );
-        renderList.push(game.add.text(nextX, y0 + 70, mmc / mdc, font));
+      // Render RIGHT part of the operationf
+      if (gameMode === 'a') renderStackOperationLine(nextX);
+      else renderFloorOperationLine(nextX);
 
 
-        const fractionLine = game.add.geom.line(
-          nextX,
-          y0 + 15,
-          nextX + 60,
-          y0 + 15,
-          4,
-          colors.black
-        );
-        fractionLine.anchor(0.5, 0);
-        renderList.push(fractionLine);
-      }
+      curNominators = gameMode === 'a' ? stackNominators : floorNominators;
+      const resultWidth = (curNominators.length + 2) * widthOfChar;
 
 
-      // Decimal result
-      let resultWidth = '_'.length * widthOfChar;
       const cardWidth = nextX - x0 + resultWidth + padding * 2;
       const cardWidth = nextX - x0 + resultWidth + padding * 2;
       card.width = cardWidth;
       card.width = cardWidth;
 
 
@@ -746,7 +854,7 @@ const circleOne = {
 
 
       return endSignX;
       return endSignX;
     },
     },
-    renderEndUI: () => {
+    renderEndUI: function () {
       let btnColor = colors.green;
       let btnColor = colors.green;
       let btnText = game.lang.continue;
       let btnText = game.lang.continue;
 
 
@@ -858,7 +966,6 @@ const circleOne = {
       self.control.isCorrect = game.math.isOverlap(self.basket, self.kid);
       self.control.isCorrect = game.math.isOverlap(self.basket, self.kid);
 
 
       const x = self.utils.renderOperationUI();
       const x = self.utils.renderOperationUI();
-
       if (self.control.isCorrect) {
       if (self.control.isCorrect) {
         completedLevels++;
         completedLevels++;
         self.kid.curFrame = self.kid.curFrame < 12 ? 24 : 25;
         self.kid.curFrame = self.kid.curFrame < 12 ? 24 : 25;
@@ -909,7 +1016,7 @@ const circleOne = {
         // On gameMode (a)
         // On gameMode (a)
         if (gameMode === 'a') {
         if (gameMode === 'a') {
           self.ui.help.x = self.control.correctX - 20;
           self.ui.help.x = self.control.correctX - 20;
-          self.ui.help.y = self.road.y;
+          self.ui.help.y = self.road.defaultY;
           // On gameMode (b)
           // On gameMode (b)
         } else {
         } else {
           self.ui.help.x = self.circles.list[self.control.correctIndex - 1].x;
           self.ui.help.x = self.circles.list[self.control.correctIndex - 1].x;
@@ -1012,6 +1119,41 @@ const circleOne = {
         });
         });
       }
       }
     },
     },
+    /** TODO */
+    isOverBlock: function (x, blockX, blockWidth) {
+      if (
+        ((gameOperation === 'plus' || gameOperation === 'mixed') &&
+          x >= blockX &&
+          x < blockX + blockWidth) ||
+        (gameOperation === 'minus' && x <= blockX && x > blockX + blockWidth)
+      )
+        return true;
+      return false;
+    },
+    /** TODO */
+    isOverRoad: function (x, y, roadX, roadWidth) {
+      if (y > 150) {
+        if (
+          ((gameOperation === 'plus' || gameOperation === 'mixed') &&
+            x >= roadX &&
+            x < roadX + roadWidth) ||
+          (gameOperation === 'minus' &&
+            x <= roadX &&
+            x > roadX + roadWidth * self.control.directionModifier)
+        )
+          return true;
+      }
+      return false;
+    },
+    /** TODO */
+    fillCurrentBlock: function (x, blockX, block) {
+      block.fillColor =
+        ((gameOperation === 'plus' || gameOperation === 'mixed') &&
+          x > blockX) ||
+        (gameOperation === 'minus' && x < blockX)
+          ? colors.red
+          : 'transparent';
+    },
   },
   },
 
 
   events: {
   events: {
@@ -1026,9 +1168,18 @@ const circleOne = {
 
 
       // GAME MODE A : click road
       // GAME MODE A : click road
       if (gameMode === 'a') {
       if (gameMode === 'a') {
-        const isValid =
-          y > 150 && x >= self.road.x && x <= self.road.x + self.road.width;
-        if (isValid) self.utils.clickCircleHandler(x);
+        const isValidX = self.utils.isOverRoad(
+          x,
+          y,
+          self.road.x,
+          self.road.width
+        );
+        if (isValidX) {
+          self.utils.clickCircleHandler(
+            self.blocks.cur.x + self.blocks.cur.width
+          );
+          document.body.style.cursor = 'auto';
+        }
       }
       }
 
 
       // GAME MODE B : click circle
       // GAME MODE B : click circle
@@ -1058,7 +1209,6 @@ const circleOne = {
 
 
       game.render.all();
       game.render.all();
     },
     },
-
     /**
     /**
      * Called by mouse move event
      * Called by mouse move event
      *
      *
@@ -1069,15 +1219,29 @@ const circleOne = {
       const y = game.math.getMouse(mouseEvent).y;
       const y = game.math.getMouse(mouseEvent).y;
       let isOverCircle = false;
       let isOverCircle = false;
 
 
-      // GAME MODE A : balloon follow mouse
       if (gameMode === 'a' && !self.control.hasClicked) {
       if (gameMode === 'a' && !self.control.hasClicked) {
-        if (
-          game.math.distanceToPointer(x, self.balloon.x, y, self.balloon.y) > 8
-        ) {
-          self.balloon.x = x;
-          self.basket.x = x;
+        const isValidX = self.utils.isOverRoad(
+          x,
+          y,
+          self.road.x,
+          self.road.width
+        );
+        if (isValidX) {
+          // GAME MODE A : balloon follow mouse
+          self.blocks.cur = self.blocks.list[0];
+          self.blocks.list.forEach((cur) => {
+            self.utils.fillCurrentBlock(x, cur.x, cur);
+            if (self.utils.isOverBlock(x, cur.x, cur.width, cur))
+              self.blocks.cur = cur;
+          });
+          const newX = self.blocks.cur.x + self.blocks.cur.width;
+          self.balloon.x = newX;
+          self.basket.x = newX;
+
+          document.body.style.cursor = 'pointer';
+        } else {
+          document.body.style.cursor = 'auto';
         }
         }
-        document.body.style.cursor = 'auto';
       }
       }
 
 
       // GAME MODE B : hover circle
       // GAME MODE B : hover circle