File indexing completed on 2024-05-05 15:53:10

0001 /* GCompris - hanoi_real.js
0002  *
0003  * Copyright (C) 2015 <Amit Tomar>
0004  *
0005  * Authors:
0006  *   Bruno Coudoin <bruno.coudoin@gcompris.net> (GTK+ version)
0007  *   Amit Tomar <a.tomar@outlook.com> (Qt Quick hanoi tower port)
0008  *   Johnny Jazeix <jazeix@gmail.com> (Qt Quick hanoi simplified port)
0009  *   Timothée Giet <animtim@gmail.com> (Graphics refactoring)
0010  *
0011  *   SPDX-License-Identifier: GPL-3.0-or-later
0012  */
0013 .pragma library
0014 .import QtQuick 2.12 as Quick
0015 .import "qrc:/gcompris/src/core/core.js" as Core
0016 
0017 var url = "qrc:/gcompris/src/activities/hanoi_real/resource/"
0018 
0019 var numberOfLevel
0020 var items
0021 var activityMode
0022 
0023 // Specific data for simplified mode
0024 var symbols = [
0025     "!", "/", "<",
0026     ">", "&", "~",
0027     "#", "{", "%",
0028     "|", "?", "}",
0029     "=", "+", "*"
0030 ]
0031 
0032 var colors = [
0033     "#e41c1c", // red 0
0034     "#79e41c", // green 65
0035     "#1c8fe4", // blue 146
0036     "#e4bb1c", // yellow 34
0037     "#e41c92", // magenta 230
0038     "#1dc0e3", // cyan 135
0039     "#e3711d", // orange 18
0040     "#a91de3", // purple 200
0041     "#e44b1c", // red2 10
0042     "#1ce463", // green2 100
0043     "#1c4ce4", // blue2 160
0044     "#e4e01c", // yellow2 42
0045     "#f20ee5", // magenta2 215
0046     "#1de3da", // cyan2 126
0047     "#e4921c"  // orange2 25
0048 ]
0049 
0050 var nbTowersLessExpectedAndResultOnes
0051 var nbMaxItemsByTower
0052 
0053 function start(items_, activityMode_) {
0054     items = items_
0055     activityMode = activityMode_
0056     numberOfLevel = (activityMode == "real") ? 3 : 6
0057     items.currentLevel = Core.getInitialLevel(numberOfLevel)
0058 
0059     initLevel()
0060 }
0061 
0062 function stop() {
0063 }
0064 
0065 function initSpecificInfoForSimplified() {
0066     Core.shuffle(symbols);
0067     switch(items.currentLevel) {
0068     case 0:
0069         nbTowersLessExpectedAndResultOnes = 3;
0070         nbMaxItemsByTower = 5;
0071         break;
0072     case 1:
0073         nbTowersLessExpectedAndResultOnes = 4;
0074         nbMaxItemsByTower = 5;
0075         break;
0076     case 2:
0077         nbTowersLessExpectedAndResultOnes = 5;
0078         nbMaxItemsByTower = 6;
0079         break;
0080     case 3:
0081         nbTowersLessExpectedAndResultOnes = 6;
0082         nbMaxItemsByTower = 7;
0083         break;
0084     case 4:
0085         nbTowersLessExpectedAndResultOnes = 6;
0086         nbMaxItemsByTower = 8;
0087         break;
0088     case 5:
0089         nbTowersLessExpectedAndResultOnes = 5;
0090         nbMaxItemsByTower = 9;
0091     }
0092 }
0093 
0094 function initLevel() {
0095 
0096     items.hasWon = false
0097 
0098     if(activityMode == "real") {
0099         items.numberOfDisc = items.currentLevel + 3
0100         items.discRepeater.model = items.numberOfDisc
0101         items.towerModel.model = 3
0102     }
0103     else {
0104         initSpecificInfoForSimplified();
0105         items.towerModel.model = nbTowersLessExpectedAndResultOnes + 2
0106 
0107         items.numberOfDisc = nbTowersLessExpectedAndResultOnes * (nbMaxItemsByTower-1) + nbMaxItemsByTower
0108         items.discRepeater.model = items.numberOfDisc
0109     }
0110 
0111     placeDiscsAtOrigin()
0112 
0113     if(activityMode != "real") {
0114         for(var i = 0 ; i < (items.numberOfDisc-nbMaxItemsByTower); ++i) {
0115             var index = Math.floor(Math.random() * symbols.length);
0116             items.discRepeater.itemAt(i).text = symbols[index];
0117             items.discRepeater.itemAt(i).baseColor = colors[index];
0118         }
0119         // Fill the text discs avoiding duplicates
0120         var currentAnswerId = items.numberOfDisc-nbMaxItemsByTower;
0121         var goodAnswerIndices = [];
0122         do {
0123             var id = Math.floor(Math.random() * (items.numberOfDisc-nbMaxItemsByTower));
0124             if(goodAnswerIndices.indexOf(id) == -1) {
0125                 items.discRepeater.itemAt(currentAnswerId).text = items.discRepeater.itemAt(id).text;
0126                 items.discRepeater.itemAt(currentAnswerId).baseColor = items.discRepeater.itemAt(id).baseColor;
0127                 goodAnswerIndices.push(id);
0128                 currentAnswerId ++;
0129             }
0130         }
0131         while(currentAnswerId < items.numberOfDisc);
0132     }
0133 
0134     disableNonDraggablediscs()
0135 }
0136 
0137 function nextLevel()
0138 {
0139     items.currentLevel = Core.getNextLevel(items.currentLevel, numberOfLevel);
0140     initLevel();
0141 }
0142 
0143 function previousLevel()
0144 {
0145     items.currentLevel = Core.getPreviousLevel(items.currentLevel, numberOfLevel);
0146     initLevel();
0147 }
0148 
0149 function placeDisc(disc, towerImage)
0150 {
0151     disc.towerImage = towerImage
0152     disc.position = getNumberOfDiscOnTower(towerImage)
0153     disc.parent = towerImage
0154     setDiscY(disc, towerImage);
0155 }
0156 
0157 function setDiscY(disc, towerImage)
0158 {
0159     //  -(towerImage.height * 0.12) because we need to remove the base of the tower
0160     // dependent of the image!
0161     disc.y = towerImage.y + towerImage.height - disc.position * disc.height - (towerImage.height * 0.12)
0162 }
0163 
0164 function placeDiscsAtOrigin() {
0165     // Reset the model to get the initial animation
0166     if(items.discRepeater.model === items.numberOfDisc)
0167         items.discRepeater.model = 0
0168     items.discRepeater.model = items.numberOfDisc
0169 
0170     if(activityMode == "real") {
0171         for(var i = 0 ; i < items.numberOfDisc ; ++i) {
0172             placeDisc(items.discRepeater.itemAt(i), items.towerModel.itemAt(0))
0173             items.discRepeater.itemAt(i).baseColor = colors[i];
0174         }
0175     }
0176     else {
0177         // First fill the first towers
0178         for(var i = 0 ; i < (items.numberOfDisc-nbMaxItemsByTower); ++i) {
0179             placeDisc(items.discRepeater.itemAt(i), items.towerModel.itemAt(i%nbTowersLessExpectedAndResultOnes))
0180         }
0181         // Fill last tower
0182         for(var i = items.numberOfDisc-nbMaxItemsByTower ; i < items.numberOfDisc ; ++i) {
0183             placeDisc(items.discRepeater.itemAt(i), items.towerModel.itemAt(nbTowersLessExpectedAndResultOnes+1))
0184         }
0185     }
0186 }
0187 
0188 function discReleased(index)
0189 {
0190     var disc = items.discRepeater.itemAt(index)
0191     var isCorrect = false;
0192 
0193     if(activityMode == "real") {
0194         for(var i = 0 ; i < items.towerModel.model ; ++ i) {
0195             var towerItem = items.towerModel.itemAt(i);
0196             if(checkIfDiscOnTowerImage(disc, towerItem) &&
0197                getNumberOfDiscOnTower(towerItem) < items.numberOfDisc &&
0198                getHigherfDiscOnTower(towerItem) <= index) {
0199                 placeDisc(disc, towerItem)
0200                 isCorrect = true
0201                 break;
0202             }
0203         }
0204     }
0205     else {
0206         for(var i = 0 ; i < items.towerModel.model ; ++ i) {
0207             var towerItem = items.towerModel.itemAt(i);
0208             if(checkIfDiscOnTowerImage(disc, towerItem) &&
0209                getNumberOfDiscOnTower(towerItem) < nbMaxItemsByTower) {
0210                 placeDisc(disc, towerItem)
0211                 isCorrect = true
0212                 break;
0213             }
0214         }
0215     }
0216 
0217     if(!isCorrect) {
0218         // Cancel the drop
0219         setDiscY(disc, disc.towerImage)
0220     }
0221 
0222     disableNonDraggablediscs()
0223     checkSolved()
0224 }
0225 
0226 function sceneSizeChanged()
0227 {
0228     if(!items)
0229         return
0230 
0231     for(var i = 0 ; i < items.numberOfDisc ; ++i) {
0232         var disc = items.discRepeater.itemAt(i)
0233         if(!disc || !disc.towerImage)
0234             continue
0235         setDiscY(disc, disc.towerImage)
0236     }
0237 
0238     disableNonDraggablediscs()
0239 }
0240 
0241 function disableNonDraggablediscs()
0242 {
0243     if(activityMode == "real") {
0244         // Only the highest (index) one is enabled
0245         for(var i = 0 ; i < items.numberOfDisc ; ++i) {
0246             var disc = items.discRepeater.itemAt(i)
0247             if(disc)
0248                 disc.mouseEnabled = (getHigherfDiscOnTower(disc.towerImage) <= i)
0249         }
0250     }
0251     else {
0252         // In simplified, all the last tower discs are disabled
0253         // We disable all except the highest (in position) ones of each tower
0254         var highestOnes = [];
0255         for(var i = 0 ; i < items.numberOfDisc ; ++i) {
0256             var disc = items.discRepeater.itemAt(i)
0257             if(!disc)
0258                 continue
0259 
0260             disc.mouseEnabled = false
0261             if(disc.towerImage == items.towerModel.itemAt(items.towerModel.model-1)) {
0262                 continue;
0263             }
0264             else if(highestOnes[disc.towerImage] == undefined) {
0265                 highestOnes[disc.towerImage] = {"pos": disc.position, "id": i}
0266             }
0267             else if(highestOnes[disc.towerImage].pos < disc.position) {
0268                 highestOnes[disc.towerImage].pos = disc.position
0269                 highestOnes[disc.towerImage].id = i
0270             }
0271         }
0272 
0273         for(var i in highestOnes) {
0274             items.discRepeater.itemAt(highestOnes[i].id).mouseEnabled = true
0275         }
0276     }
0277 }
0278 
0279 function checkIfDiscOnTowerImage(disc, towerImage)
0280 {
0281     var discPosition = items.background.mapFromItem(disc, 0, 0)
0282     var towerPosition = items.background.mapFromItem(towerImage, 0, 0)
0283     return ((discPosition.x + disc.width / 2) > towerPosition.x &&
0284             (discPosition.x + disc.width / 2) < towerPosition.x + towerImage.width)
0285 }
0286 
0287 function getHigherfDiscOnTower(towerImage) {
0288     var higher = 0
0289     for(var i = 0 ; i < items.numberOfDisc ; ++i)
0290     {
0291         if(items.discRepeater.itemAt(i) && items.discRepeater.itemAt(i).towerImage === towerImage)
0292             higher = i
0293     }
0294     return higher
0295 }
0296 
0297 function getNumberOfDiscOnTower(towerImage) {
0298     var count = 0
0299     for(var i = 0 ; i < items.numberOfDisc ; ++i)
0300     {
0301         if(items.discRepeater.itemAt(i).towerImage === towerImage)
0302             count++
0303     }
0304     return count
0305 }
0306 
0307 function checkSolved() {
0308     if(activityMode == "real") {
0309         if(getNumberOfDiscOnTower(items.towerModel.itemAt(items.towerModel.model-1)) === items.numberOfDisc) {
0310             items.hasWon = true
0311             items.bonus.good("flower")
0312         }
0313     }
0314     else {
0315         // Recreate both last towers text
0316         var expectedTower = [];
0317         var actualTower = [];
0318         for(var i = 0 ; i < items.numberOfDisc ; ++i) {
0319             var disc = items.discRepeater.itemAt(i);
0320             if(disc.towerImage == items.towerModel.itemAt(items.towerModel.model-1)) {
0321                 actualTower[disc.position] = disc.text
0322 }
0323             else if(disc.towerImage == items.towerModel.itemAt(items.towerModel.model-2)) {
0324                 expectedTower[disc.position] = disc.text
0325             }
0326         }
0327 
0328         // Don't check if not the same length
0329         var hasWon = (expectedTower.length == actualTower.length)
0330         for (var i = 0; hasWon && i < actualTower.length; ++i) {
0331             if (expectedTower[i] !== actualTower[i]) hasWon = false
0332         }
0333         if(hasWon) {
0334             items.hasWon = true
0335             items.bonus.good("flower")
0336         }
0337     }
0338 }
0339 
0340 function getDiscWidth(index)
0341 {
0342     if(activityMode == "real") {
0343         if( 0 === index ) return items.towerModel.itemAt(0).width * 1.6
0344         else if ( 1 === index ) return items.towerModel.itemAt(0).width * 1.3
0345         else if ( 2 === index ) return items.towerModel.itemAt(0).width * 1
0346         else if ( 3 === index ) return items.towerModel.itemAt(0).width * 0.7
0347         else return items.towerModel.itemAt(0).width * 0.5
0348     }
0349     else {
0350         return items.towerModel.itemAt(0).width
0351     }
0352 }