diff --git a/src/activities/programmingMaze/ProgrammingMaze.qml b/src/activities/programmingMaze/ProgrammingMaze.qml index 0903ad327..603ded84d 100644 --- a/src/activities/programmingMaze/ProgrammingMaze.qml +++ b/src/activities/programmingMaze/ProgrammingMaze.qml @@ -1,505 +1,504 @@ /* GCompris - programmingMaze.qml * * Copyright (C) 2014 Siddhesh Suthar * * Authors: * Siddhesh Suthar (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.2 import GCompris 1.0 import "../../core" import "qrc:/gcompris/src/core/core.js" as Core import "programmingMaze.js" as Activity import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 ActivityBase { id: activity onStart: focus = true onStop: {} property int oldWidth: width onWidthChanged: { // Reposition planets and asteroids, same for height Activity.repositionObjectsOnWidthChanged(width / oldWidth) oldWidth = width } property int oldHeight: height onHeightChanged: { // Reposition planets and asteroids, same for height Activity.repositionObjectsOnHeightChanged(height / oldHeight) oldHeight = height } pageComponent: Rectangle { id: background anchors.fill: parent color: "#8C8984" property bool keyNavigation: false signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property GCAudio audioEffects: activity.audioEffects property alias mazeModel: mazeModel property alias instructionModel: instructionModel property alias answerModel: answerModel property alias player: player property alias fish: fish } onStart: { Activity.start(items) keyNavigation = false } onStop: { Activity.stop() } Keys.onRightPressed: { keyNavigation = true instruction.incrementCurrentIndex() } Keys.onLeftPressed: { keyNavigation = true instruction.decrementCurrentIndex() } Keys.onDownPressed: { keyNavigation = true instruction.incrementCurrentIndex() } Keys.onUpPressed: { keyNavigation = true instruction.decrementCurrentIndex() } Keys.onSpacePressed: { keyNavigation = true instruction.currentItem.children[3].clicked() } Keys.onEnterPressed: { keyNavigation = true instruction.currentItem.children[3].clicked() } Keys.onReturnPressed: { keyNavigation = true instruction.currentItem.children[3].clicked() } ListModel { id: instructionModel ListElement { - name: "Move Forward" + name: qsTr("Move Forward") } ListElement { - name: "Turn Left" + name: qsTr("Turn Left") } ListElement { - name: "Turn Right" + name: qsTr("Turn Right") } } ListModel { id: answerModel } Repeater { id: mazeModel model: Activity.mazeBlocks[0] anchors.left: parent.left anchors.top: parent.top anchors.bottom: instruction.top Image { x: modelData[0] * background.width / 8 y: modelData[1] * (background.height - background.height/8) / 8 width: background.width / 8 height: background.height / 8 source: Activity.reverseCountUrl + "iceblock.svg" } } Image { id: player source: "qrc:/gcompris/src/activities/maze/resource/" + "tux_top_south.svg" sourceSize.width: background.width / 10 x: 0; y: 0; z: 11 - property int duration: 0 + property int duration: 1000 signal init onInit: { player.rotation = -90 - // Activity.runCode() } Behavior on x { - NumberAnimation { + SmoothedAnimation { duration: player.duration + reversingMode: SmoothedAnimation.Immediate + onRunningChanged: { + Activity.busy = !Activity.busy + Activity.playerRunningChanged() + } } } Behavior on y { - NumberAnimation { + SmoothedAnimation { duration: player.duration + reversingMode: SmoothedAnimation.Immediate + onRunningChanged: { + Activity.busy = !Activity.busy + Activity.playerRunningChanged() + } } } - // Behavior on x { - // SmoothedAnimation { - // reversingMode: SmoothedAnimation.Immediate - // onRunningChanged: Activity.playerRunningChanged() - // duration: player.duration - // } - // } - // Behavior on y { - // SmoothedAnimation { - // reversingMode: SmoothedAnimation.Immediate - // onRunningChanged: Activity.playerRunningChanged() - // duration: player.duration - // } - // } Behavior on rotation { RotationAnimation { duration: player.duration / 2 direction: RotationAnimation.Shortest + onRunningChanged: { + Activity.busy = !Activity.busy + Activity.playerRunningChanged() + } } } } Image { id: fish source: Activity.reverseCountUrl + "blue-fish.svg" sourceSize.width: background.width / 10 // anchors.leftMargin: 20 * ApplicationInfo.ratio x: 0; y: 0; z: 5 } ListView { id: instruction width: parent.width * 0.625 height: parent.height * 0.375 - bar.height / 2 anchors.left: parent.left anchors.bottom: bar.top anchors.margins: 10 * ApplicationInfo.ratio anchors.bottomMargin: bar.height / 2 orientation: Qt.Vertical verticalLayoutDirection: ListView.TopToBottom spacing: 5 * ApplicationInfo.ratio interactive: false model: instructionModel header: instructionHeaderComponent highlight: Rectangle { width: instruction.width height: instruction.buttonHeight color: "lightsteelblue" border.width: 3 border.color: "black" visible: background.keyNavigation y: instruction.currentItem.y Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } } } highlightFollowsCurrentItem: false focus: true keyNavigationWraps: true property int buttonHeight: instruction.height / instructionModel.count - instruction.spacing delegate: Item { width: instruction.width - instruction.spacing height: instruction.buttonHeight Rectangle { id: rect anchors.fill: parent gradient: Gradient { GradientStop { position: 0.0; color: "#0191C8" } GradientStop { position: 1.0; color: "#005B9A" } } opacity: 0.5 } Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } GCText { id: instructionText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter width: instruction.width fontSizeMode: Text.Fit minimumPointSize: 7 fontSize: regularSize wrapMode: Text.WordWrap color: "white" text: name } MouseArea { id: mouseArea anchors.fill: parent signal clicked onClicked: { clickedAnim.start() answerModel.append({"name": instructionText.text, "selected": false}) } onPressed: { clickedAnim.start() answerModel.append({"name": instructionText.text, "selected": false}) } } SequentialAnimation { id: clickedAnim PropertyAnimation { target: rect property: "opacity" to: "1" duration: 300 } PropertyAnimation { target: rect property: "opacity" to: "0.5" duration: 300 } } } } // insert data upon clicking the list items into this answerData // and then process it to run the code ListView { id: answerSheet width: parent.width * 0.350 height: parent.height * 0.80 - bar.height anchors.right: parent.right anchors.top: parent.top anchors.margins: 10 * ApplicationInfo.ratio orientation: Qt.Vertical verticalLayoutDirection: ListView.TopToBottom spacing: 5 * ApplicationInfo.ratio interactive: false model: answerModel header: answerHeaderComponent footer: answerFooterComponent delegate: Item { width: answerSheet.width - answerSheet.spacing height: answerSheet.height / answerModel.count - answerSheet.spacing Rectangle { id: answerRect anchors.fill: parent color: "#005B9A" opacity: 1 } Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } GCText { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter width: parent.width fontSizeMode: Text.Fit minimumPointSize: 7 fontSize: regularSize wrapMode: Text.WordWrap color: "white" text: name } Image { source: "qrc:/gcompris/src/core/resource/bar_exit.svg" sourceSize { height: parent.height ; width: parent.width } width: sourceSize.width / 8 height: sourceSize.height / 3 smooth: false anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.rightMargin: 10 * ApplicationInfo.ratio MouseArea { id: answerRemove anchors.fill: parent onPressed: { answerModel.remove(model.index) } } } } } Component { id: answerFooterComponent Image { id: runCode source:"qrc:/gcompris/src/core/resource/bar_ok.svg" width: background.width / 8 height: background.height / 8 fillMode: Image.PreserveAspectFit MouseArea { id: runCodeMouseArea anchors.fill: parent hoverEnabled: true onEntered: runCode.scale = 1.1 onClicked: { Activity.runCode() } onExited: runCode.scale = 1 } } } Component { id: instructionHeaderComponent Rectangle { id: headerRect width: ListView.view.width height: 40 * ApplicationInfo.ratio color: "#005B9A" opacity: 1 Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } GCText { id: headerText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter width: parent.width fontSizeMode: Font.DemiBold minimumPointSize: 7 fontSize: mediumSize wrapMode: Text.WordWrap color: "white" text: qsTr("Choose the instructions") } } } Component { id: answerHeaderComponent Rectangle { id: answerHeaderRect width: ListView.view.width height: 40 * ApplicationInfo.ratio color: "#005B9A" opacity: 1 Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } GCText { id: answerHeaderText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter width: parent.width fontSizeMode: Font.DemiBold minimumPointSize: 7 fontSize: mediumSize wrapMode: Text.WordWrap color: "white" text: qsTr("Your Code") } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | reload } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.initLevel() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/programmingMaze/programmingMaze.js b/src/activities/programmingMaze/programmingMaze.js index a80a19e62..8bdc80c92 100644 --- a/src/activities/programmingMaze/programmingMaze.js +++ b/src/activities/programmingMaze/programmingMaze.js @@ -1,224 +1,296 @@ /* GCompris - programmingMaze.js * * Copyright (C) 2014 * * Authors: * "Siddhesh Suthar" (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ .pragma library .import QtQuick 2.0 as Quick var currentLevel = 0 var numberOfLevel = 4 var items var reverseCountUrl = "qrc:/gcompris/src/activities/reversecount/resource/" var mazeBlocks = [ [[1,2],[2,2],[3,2]], [[1,3],[2,3],[2,2],[2,1],[3,1]], [[1,1],[2,1],[3,1],[3,2],[3,3],[2,3],[1,3]], [[0,3],[1,3],[1,2],[2,2],[2,1],[3,1]] ] var countOfMazeBlocks var initialX var initialY var stepX var stepY var playerCode = [] var currentInstruction var tuxIceBlockNumber var currentBlock var nextBlock var currentX var currentY var nextX var nextY var changedX var changedY var currentRotation var changedRotation var flag = 0 var tuxIsMoving = false +var j =0 +var busy = false function start(items_) { items = items_ currentLevel = 0 initLevel() } function stop() { } function initLevel() { items.bar.level = currentLevel + 1 items.mazeModel.model = mazeBlocks[currentLevel] items.answerModel.clear() countOfMazeBlocks = mazeBlocks[currentLevel].length stepX = items.background.width / 8 stepY = (items.background.height - items.background.height/8) / 8 initialX = mazeBlocks[currentLevel][0][0] * stepX initialY = mazeBlocks[currentLevel][0][1] * stepY + busy = false + j = 0 + playerCode = [] + items.player.x = initialX items.player.y = initialY items.fish.x = mazeBlocks[currentLevel][countOfMazeBlocks -1][0] * stepX items.fish.y = mazeBlocks[currentLevel][countOfMazeBlocks -1][1] * stepY tuxIceBlockNumber = 0 currentRotation = -90 changedRotation = -90 flag = 0 tuxIsMoving = false items.player.init() } /* 0= SOUTH * 90= WEST * 180 = NORTH * 270 =EAST */ function getPlayerRotation() { - // return ((items.player.rotation % 360) + 360) % 360 return ((changedRotation % 360) + 360) % 360 - } function runCode() { //initiallize back to starting position and code playerCode = [] + busy = false for(var i = 0; i < items.answerModel.count; i++) { playerCode.push(items.answerModel.get([i]).name) } - for( var j = 0; j < playerCode.length; j++) { - currentInstruction = playerCode[j] - tuxIsMoving = false + + if(flag == 1) { + busy = false + } + + if(!busy) { + executeFirstInstruction() + } + checkSuccess() +} + + +function playerRunningChanged() { + + if(busy) { + //do nothing + } + else { + executeNextInstruction() + } +} + +function executeNextInstruction() { + if(!busy && j < playerCode.length ) { changedX = items.player.x changedY = items.player.y currentRotation = getPlayerRotation() - // see how to end it nextX nextY won't be accesible after it - // if(tuxIceBlockNumber > mazeBlocks[currentLevel].length) { - // console.log("can't solve, breaking out of loop") - // break; - // } - + currentInstruction = playerCode[j] + console.log(j + " executing next " +currentInstruction) currentBlock = tuxIceBlockNumber nextBlock = tuxIceBlockNumber + 1 currentX = mazeBlocks[currentLevel][currentBlock][0] currentY = mazeBlocks[currentLevel][currentBlock][1] nextX = mazeBlocks[currentLevel][nextBlock][0] nextY = mazeBlocks[currentLevel][nextBlock][1] - if(flag == 1) { - tuxIsMoving = false - break - } + if ( currentInstruction == "Move Forward") { + ++tuxIceBlockNumber; + if (nextX - currentX > 0 && currentRotation == 270) { //EAST 270 + changedX = currentX * stepX + stepX + console.log("moving forward emitting the signal") + items.player.x = changedX + items.player.y = changedY + } + else if(nextX - currentX < 0 && currentRotation == 90){ //WEST 90 + changedX = currentX * stepX - stepX + items.player.x = changedX + items.player.y = changedY + } + else if (nextY - currentY < 0 && currentRotation == 180) { //NORTH 0 + changedY = currentY * stepY - stepY + console.log("moving forward emitting the signal") + items.player.x = changedX + items.player.y = changedY - // tuxIsMoving = false - executeNextInstruction() + } + else if (nextY - currentY > 0 && currentRotation == 0) { //SOUTH 180 + changedY = currentY * stepY + stepY + items.player.x = changedX + items.player.y = changedY + } + else { + // add an animation to indicate that its not possible + flag = 1 + items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") + console.log("dead end") + } + } + else if ( currentInstruction == "Turn Left") { + changedRotation = (currentRotation - 90) % 360 + // console.log("turning left") + items.player.rotation = changedRotation + } + else if ( currentInstruction == "Turn Right") { + changedRotation = (currentRotation + 90) % 360 + items.player.rotation = changedRotation + } + j = j+ 1 + busy = true checkSuccess() } } -function playerRunningChanged() { - - //TODO: if its moving keep moving don't go to next instruction - // if(tuxIsMoving) { - // executeNextInstruction() - // } -} +function executeFirstInstruction() { + if(!busy && j < playerCode.length) { + changedX = items.player.x + changedY = items.player.y + currentRotation = getPlayerRotation() -function executeNextInstruction() { + currentInstruction = playerCode[j] + console.log(j + " executing first " +currentInstruction) + currentBlock = tuxIceBlockNumber + nextBlock = tuxIceBlockNumber + 1 + currentX = mazeBlocks[currentLevel][currentBlock][0] + currentY = mazeBlocks[currentLevel][currentBlock][1] + nextX = mazeBlocks[currentLevel][nextBlock][0] + nextY = mazeBlocks[currentLevel][nextBlock][1] - if ( currentInstruction == "Move Forward") { - if (nextX - currentX > 0 && currentRotation == 270) { //EAST 270 - // console.log("moving forward") - changedX = currentX * stepX + stepX - items.player.x = changedX - items.player.y = changedY - } - else if(nextX - currentX < 0 && currentRotation == 90){ //WEST 90 - changedX = currentX * stepX - stepX - items.player.x = changedX - items.player.y = changedY + if ( currentInstruction == "Move Forward") { + ++tuxIceBlockNumber; + if (nextX - currentX > 0 && currentRotation == 270) { //EAST 270 + changedX = currentX * stepX + stepX + console.log("moving forward emitting the signal") + items.player.x = changedX + items.player.y = changedY + } + else if(nextX - currentX < 0 && currentRotation == 90){ //WEST 90 + changedX = currentX * stepX - stepX + items.player.x = changedX + items.player.y = changedY + } + else if (nextY - currentY < 0 && currentRotation == 180) { //NORTH 0 + changedY = currentY * stepY - stepY + console.log("moving up emitting the signal") + items.player.x = changedX + items.player.y = changedY + } + else if (nextY - currentY > 0 && currentRotation == 0) { //SOUTH 180 + changedY = currentY * stepY + stepY + items.player.x = changedX + items.player.y = changedY + } + else { + // add an animation to indicate that its not possible + flag = 1 + items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") + console.log("dead end") + } } - else if (nextY - currentY < 0 && currentRotation == 180) { //NORTH 0 - // console.log("moving up") - changedY = currentY * stepY - stepY - items.player.x = changedX - items.player.y = changedY - } - else if (nextY - currentY > 0 && currentRotation == 0) { //SOUTH 180 - changedY = currentY * stepY + stepY - items.player.x = changedX - items.player.y = changedY + + else if ( currentInstruction == "Turn Left") { + changedRotation = (currentRotation - 90) % 360 + console.log("turning left emmitting signal") + items.player.rotation = changedRotation } - else { - // add an animation to indicate that its not possible - flag = 1 - items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") - console.log("dead end") + else if ( currentInstruction == "Turn Right") { + changedRotation = (currentRotation + 90) % 360 + console.log("turning left emmitting signal") + items.player.rotation = changedRotation } - ++tuxIceBlockNumber; - } - else if ( currentInstruction == "Turn Left") { - changedRotation = (currentRotation - 90) % 360 - // console.log("turning left") - items.player.rotation = changedRotation - } - else if ( currentInstruction == "Turn Right") { - changedRotation = (currentRotation + 90) % 360 - items.player.rotation = changedRotation + j = j + 1; + busy = true + checkSuccess() } } function checkSuccess() { if(changedX === items.fish.x && changedY === items.fish.y){ - tuxIsMoving = false + console.log("success") + playerCode = [] + j = 0 + busy = false items.bonus.good("smiley") } } function nextLevel() { if(numberOfLevel <= ++currentLevel ) { currentLevel = 0 } initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); } function repositionObjectsOnWidthChanged(factor) { if(items) initLevel() } function repositionObjectsOnHeightChanged(factor) { if(items) initLevel() }