File indexing completed on 2024-05-12 03:42:48
0001 /* GCompris - graduated_line_read.js 0002 * 0003 * SPDX-FileCopyrightText: 2023 Bruno ANSELME <be.root@free.fr> 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 * 0006 */ 0007 .pragma library 0008 .import QtQuick 2.12 as Quick 0009 .import GCompris 1.0 as GCompris // for ApplicationInfo 0010 .import "qrc:/gcompris/src/core/core.js" as Core 0011 0012 var numberOfLevel 0013 var items 0014 var activityMode 0015 var maxSolutionSize = 0 0016 var mapToPad = {} // Maps keyboard charcodes to numPad's indexes to animate graphics from computer's numpad 0017 var segmentThickness = 2 0018 var exercices = [] 0019 0020 function randInt(max) { return Math.floor(Math.random() * max) } 0021 0022 function longInt(val) { // Format number to string (avoid exponential notation) 0023 val = Number(val) 0024 var str = "" 0025 while (val !== 0) { 0026 var remainder = val % 10 0027 str = String(remainder) + str 0028 val = Math.floor(val / 10) 0029 } 0030 if (str === "") str = "0" 0031 return str 0032 } 0033 0034 function createExercice(idx, rules, s) { 0035 var nbSeg = 2 0036 var step = rules.steps[s] 0037 var maxOffset = 0 0038 if (rules.fitLimits) { 0039 nbSeg = Math.floor((rules.range[1] - rules.range[0]) / step) + 1 0040 } else { 0041 nbSeg = 1 + rules.segments[0] + randInt(1 + rules.segments[1] - rules.segments[0]) 0042 maxOffset = rules.range[1] - (nbSeg * step) 0043 while (maxOffset < 0) { // if data is not valid, reduce the number of segment 0044 nbSeg-- 0045 maxOffset = rules.range[1] - (nbSeg * step) 0046 } 0047 maxOffset = rules.range[1] - ((nbSeg - 1) * step) 0048 } 0049 if (rules.fitLimits) { 0050 idx = 1 + (idx % (nbSeg - 2)) 0051 } else { 0052 idx = randInt(nbSeg - 2) + 1 0053 } 0054 var start = rules.range[0] + randInt(maxOffset) 0055 return { 0056 "solution": idx, 0057 "step": step, 0058 "nbSeg": nbSeg, 0059 "start": start, 0060 "rangeMin": rules.range[0], 0061 "rangeMax": rules.range[1] 0062 } 0063 } 0064 0065 function createLevel() { // Create an array of exercice 0066 exercices = [] 0067 var levelRules = items.levels[items.currentLevel].rules 0068 for(var s = 0; s < levelRules.steps.length; s++) { 0069 var nbTicks = Math.floor((levelRules.range[1] - levelRules.range[0]) / levelRules.steps[s]) -1 0070 for(var i = 0; i < nbTicks; i++) { 0071 var exo = createExercice(i, levelRules, s) 0072 exercices.push(exo) 0073 } 0074 } 0075 } 0076 0077 function buildRuler() { // Read from exercices with currentSubLevel index 0078 items.solutionGrad = 0 0079 items.rulerModel.clear() 0080 if (items.currentSubLevel % exercices.length === 0) // if first time or all exercices already done 0081 Core.shuffle(exercices) // shuffle exercices before restarting 0082 var exo = exercices[items.currentSubLevel % exercices.length] 0083 var start = exo.start 0084 var thickStep = Math.floor(exo.rangeMax / 10) 0085 if (thickStep < 10) 0086 thickStep = 10 0087 var i = 0 0088 for (i = 0; i < exo.nbSeg; i++) { // Create rulerModel 0089 var thick = segmentThickness 0090 if (start % thickStep === 0) 0091 thick = 2 * segmentThickness 0092 if ((i === 0) || (i === exo.nbSeg - 1)) 0093 thick = 3 * segmentThickness 0094 items.rulerModel.append({ "value_": start 0095 , "thickness_": thick }) 0096 start += exo.step 0097 } 0098 maxSolutionSize = start.toString().length 0099 var min = (items.orientation === Qt.LeftToRight) ? 0 : items.rulerModel.count -1 0100 var max = (items.orientation === Qt.LeftToRight) ? items.rulerModel.count -1 : 0 0101 items.leftLimit.text = longInt(items.rulerModel.get(min).value_) 0102 items.rightLimit.text = longInt(items.rulerModel.get(max).value_) 0103 0104 items.solutionGrad = exo.solution 0105 items.answer = items.rulerModel.get(items.solutionGrad).value_.toString() 0106 if (activityMode === "number2tick") // Choose an other starting tick 0107 items.solutionGrad = randInt(exo.nbSeg - 2) + 1 0108 } 0109 0110 function createRuler() { 0111 var levelRules = items.levels[items.currentLevel].rules 0112 items.numberOfSubLevel = levelRules.nbOfQuestions 0113 buildRuler() 0114 var title = items.levels[items.currentLevel].title 0115 if (!levelRules.fitLimits) 0116 title = title.substr(0, title.length - 1) + " " + qsTr("(variable boundaries)") + title.substr(title.length - 1) 0117 } 0118 0119 function moveLeft() { 0120 if (items.score.isWinAnimationPlaying || items.buttonsBlocked) 0121 return 0122 if (items.solutionGrad > 1) { 0123 items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/audioclick.wav') 0124 items.solutionGrad-- 0125 } 0126 } 0127 0128 function moveRight() { 0129 if (items.score.isWinAnimationPlaying || items.buttonsBlocked) 0130 return 0131 if (items.solutionGrad < items.rulerModel.count - 2) { 0132 items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/audioclick.wav') 0133 items.solutionGrad++ 0134 } 0135 } 0136 0137 function checkResult() { 0138 if (items.score.isWinAnimationPlaying || items.buttonsBlocked) 0139 return 0140 items.buttonsBlocked = true; 0141 var success = false; 0142 switch (activityMode) { 0143 case "tick2number": 0144 success = (items.cursor.children[items.solutionGrad].textValue === items.answer); 0145 break 0146 case "number2tick": 0147 success = (items.rulerModel.get(items.solutionGrad).value_.toString() === items.answer); 0148 if (success) 0149 items.cursor.children[items.solutionGrad].textValue = items.answer; 0150 break 0151 } 0152 if (success) { 0153 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/completetask.wav"); 0154 items.currentSubLevel ++; 0155 items.score.playWinAnimation(); 0156 } else { 0157 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav") 0158 items.buttonsBlocked = true; 0159 items.errorRectangle.startAnimation(); 0160 } 0161 } 0162 0163 function start(items_, activityMode_) { 0164 items = items_; 0165 activityMode = activityMode_ 0166 items.orientation = (Core.isLeftToRightLocale(GCompris.ApplicationSettings.locale)) ? Qt.LeftToRight : Qt.RightToLeft 0167 // items.orientation = Qt.RightToLeft // Force RightToLeft here 0168 // Make sure numberOfLevel is initialized before calling Core.getInitialLevel 0169 numberOfLevel = items.levels.length 0170 items.currentLevel = Core.getInitialLevel(numberOfLevel) 0171 initLevel(); 0172 } 0173 0174 function stop() { 0175 } 0176 0177 function initLevel() { 0178 items.errorRectangle.resetState(); 0179 items.buttonsBlocked = false; 0180 items.bar.level = items.currentLevel + 1; 0181 items.currentSubLevel = 0; 0182 createLevel(); 0183 createRuler(); 0184 } 0185 0186 function nextLevel() { 0187 items.score.stopWinAnimation() 0188 items.currentLevel = Core.getNextLevel(items.currentLevel, numberOfLevel); 0189 initLevel(); 0190 } 0191 0192 function previousLevel() { 0193 items.score.stopWinAnimation() 0194 items.currentLevel = Core.getPreviousLevel(items.currentLevel, numberOfLevel); 0195 initLevel(); 0196 } 0197 0198 function nextSubLevel() { 0199 if(items.currentSubLevel >= items.numberOfSubLevel) 0200 items.bonus.good("sun"); 0201 else { 0202 items.buttonsBlocked = false; 0203 createRuler(); // initLevel(); 0204 } 0205 } 0206 0207 function previousSubLevel() { 0208 if( --items.currentSubLevel < 0) 0209 previousLevel(); 0210 else { 0211 items.buttonsBlocked = false; 0212 createRuler(); // initLevel(); 0213 } 0214 } 0215 0216 function handleKeys(key) { 0217 if (items.score.isWinAnimationPlaying || items.buttonsBlocked) 0218 return 0219 if (items.orientation === Qt.RightToLeft) { 0220 switch (key) { 0221 case Qt.Key_Left: 0222 key = Qt.Key_Right 0223 break 0224 case Qt.Key_Right: 0225 key = Qt.Key_Left 0226 break 0227 } 0228 } 0229 switch (key) { 0230 case Qt.Key_Space: 0231 case Qt.Key_Return: 0232 case Qt.Key_Enter: 0233 if (activityMode === "tick2number") { 0234 if (items.cursor.children[items.solutionGrad].textValue !== "") 0235 checkResult() 0236 } else { 0237 checkResult() 0238 } 0239 0240 break 0241 case Qt.Key_Left: 0242 if (activityMode === "number2tick") 0243 moveLeft() 0244 break 0245 case Qt.Key_Right: 0246 if (activityMode === "number2tick") 0247 moveRight() 0248 break 0249 case Qt.Key_0: 0250 case Qt.Key_1: 0251 case Qt.Key_2: 0252 case Qt.Key_3: 0253 case Qt.Key_4: 0254 case Qt.Key_5: 0255 case Qt.Key_6: 0256 case Qt.Key_7: 0257 case Qt.Key_8: 0258 case Qt.Key_9: 0259 if (activityMode === "number2tick") 0260 return 0261 if (items.cursor.children[items.solutionGrad].textValue.length < maxSolutionSize) { 0262 items.cursor.children[items.solutionGrad].textValue += key - '0'.charCodeAt(0) 0263 items.numPad.currentIndex = mapToPad[key] 0264 items.numPad.currentItem.state = "pressed" 0265 } else { 0266 items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/smudge.wav') 0267 } 0268 0269 break 0270 case Qt.Key_Backspace: 0271 if (activityMode === "number2tick") 0272 return 0273 items.cursor.children[items.solutionGrad].textValue = items.cursor.children[items.solutionGrad].textValue.slice(0, -1) 0274 items.numPad.currentIndex = mapToPad[key] 0275 items.numPad.currentItem.state = "pressed" 0276 break 0277 case Qt.Key_Delete: 0278 if (activityMode === "number2tick") 0279 return 0280 items.cursor.children[items.solutionGrad].textValue = "" 0281 items.numPad.currentIndex = mapToPad[key] 0282 items.numPad.currentItem.state = "pressed" 0283 break 0284 } 0285 } 0286 0287 function handleEvents(event) { 0288 handleKeys(event.key) 0289 }