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

0001 /* GCompris - path.js
0002  *
0003  * SPDX-FileCopyrightText: 2021 Harsh Kumar <hadron43@yahoo.com>
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  *
0006  */
0007 .pragma library
0008 .import QtQuick 2.12 as Quick
0009 .import "qrc:/gcompris/src/core/core.js" as Core
0010 
0011 var numberOfLevel
0012 var items
0013 
0014 var currPos = [-1, -1]
0015 var prevPos = [-1, -1]
0016 var map
0017 var decodeIndex
0018 
0019 var successSoundPath = 'qrc:/gcompris/src/core/resource/sounds/completetask.wav'
0020 var errorSoundPath = 'qrc:/gcompris/src/core/resource/sounds/crash.wav'
0021 
0022 var Directions = {
0023     UP: 'UP',
0024     RIGHT: 'RIGHT',
0025     DOWN: 'DOWN',
0026     LEFT: 'LEFT'
0027 }
0028 
0029 var mapModel = {
0030     "path": false,
0031     "flag": false,
0032     "invisible": false,
0033     "rock": false,
0034     "tree": false,
0035     "bush": false,
0036     "grass": false,
0037     "water": false
0038 }
0039 
0040 function start(items_) {
0041     items = items_
0042     numberOfLevel = items.levels.length
0043     items.currentLevel = Core.getInitialLevel(numberOfLevel)
0044     initLevel()
0045 }
0046 
0047 function stop() {
0048 }
0049 
0050 function initLevel() {
0051 
0052     items.rows = items.levels[items.currentLevel].path.length
0053     items.cols = items.levels[items.currentLevel].path[0].length
0054 
0055     items.mapListModel.clear()
0056     items.movesListModel.clear()
0057 
0058     for(var i=0; i < items.rows * items.cols; ++i)
0059         items.mapListModel.append(mapModel)
0060 
0061     map = items.levels[items.currentLevel].path
0062 
0063     // reset initial position
0064     prevPos = [-1, -1];
0065     // intialize position of tux
0066     currPos = findStartAndLoadObstacles()
0067 
0068     // find the initial direciton of tux
0069     items.tux.init(findCorrectDirectionAbsolute(currPos[0], currPos[1], -1, -1))
0070 
0071     // reset mapView
0072     items.mapView.init()
0073 
0074     // reset the error counter
0075     items.errorsCount = 0
0076 
0077     // load the moves if in decode mode
0078     if(items.mode === 'decode') {
0079         loadMoves()
0080         decodeIndex = 0
0081         items.movesListModel.set(0, {"active": true})
0082     }
0083 
0084     items.movesGridView.currentIndex = 0
0085 }
0086 
0087 function loadMoves() {
0088     var pos = currPos.slice()
0089     prevPos = [-1, -1]
0090     var direction = items.tux.direction
0091 
0092     while(map[pos[1]][pos[0]].toUpperCase() !== 'E') {
0093         var nextDirectionAbsolute = findCorrectDirectionAbsolute(pos[0], pos[1], prevPos[0], prevPos[1])
0094         var nextDirectionRelative = absoluteDirectionToRelative(nextDirectionAbsolute, direction)
0095 
0096         items.movesListModel.append({
0097             "direction" : (items.movement === 'absolute' ? nextDirectionAbsolute : nextDirectionRelative),
0098             "faded" : false,
0099             "active" : false
0100         })
0101 
0102         prevPos = pos
0103         pos = findNextPositionAbsolute(pos[0], pos[1], nextDirectionAbsolute)
0104         direction = nextDirectionAbsolute
0105     }
0106 
0107     prevPos = [-1, -1]
0108 }
0109 
0110 function absoluteDirectionToRelative(absoluteDirection, currentDirection) {
0111     var directions = [Directions.DOWN, Directions.LEFT, Directions.UP, Directions.RIGHT]
0112 
0113     var diff = directions.indexOf(absoluteDirection) - directions.indexOf(currentDirection)
0114 
0115     if(diff === -1 || diff === 3)
0116         return Directions.LEFT
0117     else if(diff === 1 || diff === -3)
0118         return Directions.RIGHT
0119     else if(diff === 0)
0120         return Directions.UP
0121     else
0122         return Directions.DOWN
0123 }
0124 
0125 function findCorrectDirectionAbsolute(fromX, fromY, prevX, prevY) {
0126     if(isValidPos([fromX, fromY + 1]) && !(fromX === prevX && fromY + 1 === prevY))
0127         return Directions.DOWN
0128     else if(isValidPos([fromX + 1, fromY]) && !(fromX + 1 === prevX && fromY === prevY))
0129         return Directions.RIGHT
0130     else if(isValidPos([fromX - 1, fromY]) && !(fromX - 1 === prevX && fromY === prevY))
0131         return Directions.LEFT
0132     else if(isValidPos([fromX, fromY - 1]) && !(fromX === prevX && fromY - 1 === prevY))
0133         return Directions.UP
0134     return null
0135 }
0136 
0137 function findStartAndLoadObstacles() {
0138     var start = [-1, -1]
0139     for(var i=0; i < map.length; ++i) {
0140         for(var j=0; j < map[i].length; ++j) {
0141             var c = map[i][j].toUpperCase()
0142             var index = positionToIndex([j, i])
0143 
0144             if(items.mode === 'encode' && ['*', 'E'].indexOf(c) != -1 )
0145                 items.mapListModel.set(index, {"path": true})
0146 
0147             if(c === 'S') {
0148                 items.mapListModel.set(index, {"path": true})
0149                 start = [j, i];
0150             }
0151             else if(c === 'E')
0152                 items.mapListModel.set(index, {"flag": true})
0153             else if(c === 'I')
0154                 items.mapListModel.set(index, {"invisible": true})
0155             else if(c === 'R')
0156                 items.mapListModel.set(index, {"rock": true})
0157             else if(c === 'T')
0158                 items.mapListModel.set(index, {"tree": true})
0159             else if(c === 'B')
0160                 items.mapListModel.set(index, {"bush": true})
0161             else if(c === 'G')
0162                 items.mapListModel.set(index, {"grass": true})
0163             else if(c === 'W')
0164                 items.mapListModel.set(index, {"water": true})
0165         }
0166     }
0167     return start
0168 }
0169 
0170 function isValidPos(pos) {
0171     if(prevPos[0] === pos[0] && prevPos[1] === pos[1])
0172         return false;
0173 
0174     if(pos[1] >= map.length || pos[1] < 0 || pos[0] >= map[0].length || pos[0] < 0)
0175         return false;
0176 
0177     return map[pos[1]][pos[0]] === '*' || map[pos[1]][pos[0]].toUpperCase() === 'S' || map[pos[1]][pos[0]].toUpperCase() === 'E';
0178 }
0179 
0180 function positionToIndex(posArray) {
0181     return posArray[1] * items.cols + posArray[0];
0182 }
0183 
0184 function indexToPosition(index) {
0185     return [index % items.cols, Math.floor(index / items.rows)]
0186 }
0187 
0188 function moveTuxToBlock() {
0189     items.tux.x = items.mapView.x + currPos[0] * items.mapView.cellSize
0190     items.tux.y = items.mapView.y + currPos[1] * items.mapView.cellSize
0191 }
0192 
0193 function updateTux() {
0194     moveTuxToBlock()
0195 
0196     items.audioEffects.play(successSoundPath)
0197 
0198     if(map[currPos[1]][currPos[0]].toUpperCase() === 'E')
0199         items.bonus.good ("tux")
0200 }
0201 
0202 function findNextPositionAbsolute(fromX, fromY, direction) {
0203     // find direction change in case of absolute movement
0204     if(direction === Directions.DOWN)
0205         return  [fromX, fromY + 1]
0206     else if(direction === Directions.UP)
0207         return [fromX, fromY - 1]
0208     else if(direction === Directions.RIGHT)
0209         return [fromX + 1, fromY]
0210     else if(direction === Directions.LEFT)
0211         return [fromX - 1, fromY]
0212     return [-1, -1]
0213 }
0214 
0215 function findNextDirectionRelative(fromX, fromY, direction) {
0216     // find direction change in case of relative movement
0217     var directions = [Directions.DOWN, Directions.LEFT, Directions.UP, Directions.RIGHT]
0218     var keyboardDirections = [Directions.UP, Directions.RIGHT, Directions.DOWN, Directions.LEFT]
0219     var newRelativeCardinalDirection = items.tux.rotation + keyboardDirections.indexOf(direction) * 90
0220 
0221     if(newRelativeCardinalDirection < 0)
0222         newRelativeCardinalDirection += 360
0223 
0224     newRelativeCardinalDirection = newRelativeCardinalDirection % 360
0225 
0226     return directions[newRelativeCardinalDirection / 90]
0227 }
0228 
0229 function findNextPositionRelative(fromX, fromY, direction) {
0230     var absoluteDirection = findNextDirectionRelative(fromX, fromY, direction)
0231     return findNextPositionAbsolute(fromX, fromY, absoluteDirection)
0232 }
0233 
0234 function moveTowards(direction) {
0235     if(items.tux.isAnimationRunning || items.bonus.isPlaying)
0236         return
0237 
0238     var absolutePosition = findNextPositionAbsolute(currPos[0], currPos[1], direction)
0239 
0240     var relativeDirection = findNextDirectionRelative(currPos[0], currPos[1], direction)
0241     var relativePosition = findNextPositionRelative(currPos[0], currPos[1],  direction)
0242 
0243     if((items.movement === 'absolute' && isValidPos(absolutePosition)) ||
0244      (items.movement === 'relative' && isValidPos(relativePosition))) {
0245 
0246         if(items.mode === 'encode') {
0247             items.movesListModel.append({
0248                 "direction" : direction,
0249                 "active" : false,
0250                 "faded" : false
0251             })
0252 
0253             items.movesGridView.currentIndex = items.movesListModel.count - 1
0254         }
0255 
0256         prevPos = currPos
0257 
0258         if(items.movement === 'absolute') {
0259             currPos = absolutePosition
0260             items.tux.direction = direction
0261         }
0262         else {
0263             currPos = relativePosition
0264             items.tux.direction = relativeDirection
0265         }
0266 
0267         updateTux()
0268     }
0269     else {
0270         items.audioEffects.play(errorSoundPath)
0271         items.errorsCount ++
0272     }
0273 }
0274 
0275 function processBlockClick(pos) {
0276     if(decodeIndex >= items.movesListModel.count || items.bonus.isPlaying)
0277         return
0278 
0279     var correctPos
0280     if(items.movement === 'absolute')
0281         correctPos = findNextPositionAbsolute(currPos[0], currPos[1], items.movesListModel.get(decodeIndex).direction)
0282     else
0283         correctPos = findNextPositionRelative(currPos[0], currPos[1], items.movesListModel.get(decodeIndex).direction)
0284 
0285     if(correctPos[0] === pos[0] && correctPos[1] === pos[1]) {
0286         moveTowards(items.movesListModel.get(decodeIndex).direction)
0287 
0288         items.movesListModel.set(decodeIndex, {"active" : false, "faded" : true})
0289         decodeIndex++
0290         if(decodeIndex < items.movesListModel.count) {
0291             items.movesListModel.set(decodeIndex, {"active" : true})
0292             items.movesGridView.currentIndex = decodeIndex
0293         }
0294 
0295         items.mapListModel.set(positionToIndex(currPos), {"path": true})
0296     }
0297     else {
0298         items.audioEffects.play(errorSoundPath)
0299         items.errorsCount ++
0300     }
0301 }
0302 
0303 function nextLevel() {
0304     items.currentLevel = Core.getNextLevel(items.currentLevel, numberOfLevel);
0305     initLevel();
0306 }
0307 
0308 function previousLevel() {
0309     items.currentLevel = Core.getPreviousLevel(items.currentLevel, numberOfLevel);
0310     initLevel();
0311 }