diff --git a/src/activities/submarine/ActivityInfo.qml b/src/activities/submarine/ActivityInfo.qml index b94f18fe3..0f5322e59 100644 --- a/src/activities/submarine/ActivityInfo.qml +++ b/src/activities/submarine/ActivityInfo.qml @@ -1,54 +1,54 @@ /* GCompris - ActivityInfo.qml * * Copyright (C) 2017 Rudra Nil Basu * * 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 GCompris 1.0 ActivityInfo { name: "submarine/Submarine.qml" difficulty: 5 icon: "submarine/submarine.svg" author: "Rudra Nil Basu <rudra.nil.basu.1996@gmail.com>" demo: true title: qsTr("Pilot a Submarine") - description: qsTr("Drive the submarine to the end point") + description: qsTr("Drive the submarine to the end point.") //intro: "Drive the submarine to the right end of the screen without colliding with any objects" goal: qsTr("Learn how to control a submarine") prerequisite: qsTr("Move and click using the mouse, physics basics") manual: qsTr("Control the various parts of the submarine (the engine, rudders and ballast tanks) to reach the end point. Controls: Engine: D / Right arrow: Increase velocity A / Left arrow: Decrease velocity Ballast tanks: Switch filling of Ballast tanks: W / Up arrow: Central ballast tank R: Left ballast tank T: Right ballast tanks Switch flush ballast tanks: S / Down arrow: Central ballast tank F: Left ballast tank G: Right ballast tanks Diving planes: + : Increase diving plane angle - : Decrease diving plane angle") credit: "" section: "experimental" - createdInVersion: 8000 + createdInVersion: 9000 } diff --git a/src/activities/submarine/BallastTank.qml b/src/activities/submarine/BallastTank.qml index 8d94d0343..ef37fa201 100644 --- a/src/activities/submarine/BallastTank.qml +++ b/src/activities/submarine/BallastTank.qml @@ -1,86 +1,86 @@ /* GCompris - BallastTank.qml * * Copyright (C) 2017 RUDRA NIL BASU * * Authors: * Pascal Georges (GTK+ version) * Rudra Nil Basu (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.6 Item { - property int initialWaterLevel + property int initialWaterLevel: 0 property int waterLevel: 0 - property int maxWaterLevel + property int maxWaterLevel: 500 property int waterRate: 100 property bool waterFilling: false property bool waterFlushing: false function fillBallastTanks() { waterFilling = !waterFilling } function flushBallastTanks() { waterFlushing = !waterFlushing } function updateWaterLevel(isInflow) { if (isInflow) { if (waterLevel < maxWaterLevel) { waterLevel += waterRate } } else { if (waterLevel > 0) { waterLevel -= waterRate } } if (waterLevel > maxWaterLevel) { waterLevel = maxWaterLevel } if (waterLevel < 0) { waterLevel = 0 } } function resetBallastTanks() { waterFilling = false waterFlushing = false waterLevel = initialWaterLevel } Timer { id: fillBallastTanks interval: 500 running: waterFilling && !waterFlushing repeat: true onTriggered: updateWaterLevel(true) } Timer { id: flushBallastTanks interval: 500 running: waterFlushing && !waterFilling repeat: true onTriggered: updateWaterLevel(false) } } diff --git a/src/activities/submarine/Submarine.qml b/src/activities/submarine/Submarine.qml index 427ff8e62..40f6b6506 100644 --- a/src/activities/submarine/Submarine.qml +++ b/src/activities/submarine/Submarine.qml @@ -1,989 +1,968 @@ /* GCompris - submarine.qml * * Copyright (C) 2017 RUDRA NIL BASU * * Authors: * Pascal Georges (GTK+ version) * Rudra Nil Basu (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.6 import QtQuick.Particles 2.0 import Box2D 2.0 import QtGraphicalEffects 1.0 import GCompris 1.0 import "../../core" import "submarine.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property string url: "qrc:/gcompris/src/activities/submarine/resource/" pageComponent: Image { id: background source: url + "background.svg" anchors.fill: parent onWidthChanged: updateOnWidthReset.start() onHeightChanged: Activity.resetUpperGate() signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } /* Testing purposes, A / Left Key => Reduces velocity, D / Right Key => Increases velocity */ Keys.onPressed: { if ((event.key == Qt.Key_D || event.key == Qt.Key_Right)) { submarine.increaseHorizontalVelocity(1) } if ((event.key == Qt.Key_A || event.key == Qt.Key_Left)) { submarine.decreaseHorizontalVelocity(1) } if ((event.key == Qt.Key_W || event.key == Qt.Key_Up)) { centralBallastTank.fillBallastTanks() } if ((event.key == Qt.Key_S || event.key == Qt.Key_Down)) { centralBallastTank.flushBallastTanks() } if ((event.key == Qt.Key_Plus)) { submarine.increaseWingsAngle(1) } if ((event.key == Qt.Key_Minus)) { submarine.decreaseWingsAngle(1) } if ((event.key == Qt.Key_R)) { leftBallastTank.fillBallastTanks() } if ((event.key == Qt.Key_F)) { leftBallastTank.flushBallastTanks() } if ((event.key == Qt.Key_T)) { rightBallastTank.fillBallastTanks() } if ((event.key == Qt.Key_G)) { rightBallastTank.flushBallastTanks() } } // 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 alias crown: crown property alias whale: whale property var submarineCategory: Fixture.Category1 property var crownCategory: Fixture.Category2 property var whaleCategory: Fixture.Category3 property var upperGatefixerCategory: Fixture.Category4 property var lowerGatefixerCategory: Fixture.Category5 property var shipCategory: Fixture.Category6 property var rockCategory: Fixture.Category7 property var maxDepthCategory: Fixture.Category8 property alias submarine: submarine property alias tutorial: tutorial property alias upperGate: upperGate property alias ship: ship property alias physicalWorld: physicalWorld + property bool processingAnswer: false } IntroMessage { id: tutorial anchors { top: parent.top topMargin: 10 right: parent.right rightMargin: 5 left: parent.left leftMargin: 5 } z: 100 onIntroDone: { - physicalWorld.turnOn() tutorial.visible = false } } onStart: { Activity.start(items) } onStop: { Activity.stop() } World { id: physicalWorld - running: false + running: !tutorial.visible && !items.processingAnswer gravity: Qt.point(0,0) autoClearForces: false - - function turnOff() { - physicalWorld.running = false - } - - function turnOn() { - physicalWorld.running = true - } - - function status() { - return physicalWorld.running - } } Item { id: waterLevel x: 0 y: background.height / 15 } Rectangle { id: maximumWaterDepth width: background.width height: 10 color: "transparent" y: background.height * 0.65 Body { id: maxDepthBody target: maximumWaterDepth bodyType: Body.Static sleepingAllowed: true linearDamping: 0 fixtures: Box { categories: items.maxDepthCategory collidesWith: items.submarineCategory width: maximumWaterDepth.width height: maximumWaterDepth.height density: 1 friction: 0 restitution: 0 } } } Item { id: submarine z: 1 property point initialPosition: Qt.point(0,0) property bool isHit: false property int terminalVelocityIndex: 75 property int maxAbsoluteRotationAngle: 15 /* Maximum depth the submarine can dive when ballast tank is full */ property real maximumDepthOnFullTanks: maximumWaterDepth.y * 0.45 property real ballastTankDiveSpeed: 10 /* Engine properties */ property point velocity property int maximumXVelocity: 5 property int currentFinalVelocity: 0 /* Wings property */ property int wingsAngle property int initialWingsAngle: 0 property int maxWingsAngle: 2 property int minWingsAngle: -2 function destroySubmarine() { isHit = true submarineImage.broken() } function resetSubmarine() { isHit = false submarineImage.reset() leftBallastTank.resetBallastTanks() rightBallastTank.resetBallastTanks() centralBallastTank.resetBallastTanks() x = initialPosition.x y = initialPosition.y currentFinalVelocity = 0 velocity = Qt.point(0,0) wingsAngle = initialWingsAngle } /* While increasing or decreasing, we can't use submarine.velocity.x since it is interpolating */ function increaseHorizontalVelocity(amount) { if (submarine.currentFinalVelocity + amount <= submarine.maximumXVelocity) { submarine.currentFinalVelocity += amount smoothHorizontalVelocity.stop() smoothHorizontalVelocity.setFinalVelocity(submarine.currentFinalVelocity) smoothHorizontalVelocity.setIncreaseVelocity(true) smoothHorizontalVelocity.start() } } function decreaseHorizontalVelocity(amount) { if (submarine.currentFinalVelocity - amount >= 0) { submarine.currentFinalVelocity -= amount smoothHorizontalVelocity.stop() smoothHorizontalVelocity.setFinalVelocity(submarine.currentFinalVelocity) smoothHorizontalVelocity.setIncreaseVelocity(false) smoothHorizontalVelocity.start() } } function increaseWingsAngle(amount) { if (wingsAngle + amount <= maxWingsAngle) { wingsAngle += amount } else { wingsAngle = maxWingsAngle } } function decreaseWingsAngle(amount) { if (wingsAngle - amount >= minWingsAngle) { wingsAngle -= amount } else { wingsAngle = minWingsAngle } } function changeVerticalVelocity() { /* Check if we are currently using diving planes or ballast tanks */ var isDivingPlanesActive if (submarineImage.y > 0 && submarine.velocity.x > 0 && wingsAngle != 0) { /* * Movement due to planes * Movement is affected only when the submarine is moving forward * When the submarine is on the surface, the planes cannot be used */ isDivingPlanesActive = true } else { isDivingPlanesActive = false } var yPosition if (isDivingPlanesActive) { /* Currently using diving planes */ var multiplier if (wingsAngle == 1) { multiplier = 0.6 } else if (wingsAngle == 2) { multiplier = 0.8 } else if (wingsAngle == -1) { multiplier = 0.2 } else if (wingsAngle == -2) { multiplier = 0.1 } yPosition = multiplier * maximumWaterDepth.y } else { /* Currently under the influence of Ballast Tanks */ yPosition = submarineImage.currentWaterLevel / submarineImage.totalWaterLevel * submarine.maximumDepthOnFullTanks if (bar.level >= 7) { var finalAngle = ((rightBallastTank.waterLevel - leftBallastTank.waterLevel) / leftBallastTank.maxWaterLevel) * submarine.maxAbsoluteRotationAngle submarineRotation.angle = finalAngle } } var depthToMove = yPosition - submarineImage.y submarine.velocity.y = ballastTankDiveSpeed * (depthToMove / background.width) } Timer { id: smoothHorizontalVelocity running: false repeat: true interval: 100 property real finalVelocity property real smoothRate: 0.1 property bool increaseVelocity function increaseVelocitySmoothly() { if (submarine.velocity.x + smoothRate > finalVelocity) { submarine.velocity.x = finalVelocity smoothHorizontalVelocity.stop() } else { submarine.velocity.x += smoothRate } } function decreaseVelocitySmoothly() { if (submarine.velocity.x - smoothRate <= finalVelocity) { submarine.velocity.x = finalVelocity smoothHorizontalVelocity.stop() } else { submarine.velocity.x -= smoothRate } } function setFinalVelocity(_finalVelocity) { finalVelocity = _finalVelocity } function setIncreaseVelocity(value) { increaseVelocity = value } onTriggered: { if (increaseVelocity) { increaseVelocitySmoothly() } else { decreaseVelocitySmoothly() } } } BallastTank { id: leftBallastTank - - initialWaterLevel: 0 - maxWaterLevel: 500 } BallastTank { id: rightBallastTank - - initialWaterLevel: 0 - maxWaterLevel: 500 } BallastTank { id: centralBallastTank - - initialWaterLevel: 0 - maxWaterLevel: 500 } Image { id: submarineImage source: submarine.isHit ? url + "submarine-broken.png" : url + "submarine.png" property int currentWaterLevel: bar.level < 7 ? centralBallastTank.waterLevel : leftBallastTank.waterLevel + rightBallastTank.waterLevel property int totalWaterLevel: bar.level < 7 ? centralBallastTank.maxWaterLevel : leftBallastTank.maxWaterLevel + rightBallastTank.maxWaterLevel width: background.width / 9 height: background.height / 9 function broken() { submarine.isHit = true } function reset() { submarine.isHit = false x = submarine.initialPosition.x y = submarine.initialPosition.y } onXChanged: { if (submarineImage.x >= background.width) { Activity.finishLevel(true) } } transform: Rotation { - id: submarineRotation; + id: submarineRotation origin.x: submarineImage.width / 2; origin.y: 0; angle: 0; Behavior on angle { NumberAnimation { duration: 1000 } } } Loader { anchors.fill: parent active: ApplicationInfo.hasShader && submarine.velocity.x > 0 && submarineImage.y > 0 && !submarine.isHit sourceComponent: ParticleSystem { anchors.fill: parent Emitter { x: parent.x y: parent.y + parent.height / 1.75 width: 1 height: 1 emitRate: 0.8 lifeSpan: 800 lifeSpanVariation: 2500 acceleration: PointDirection { x: -20 xVariation: 5 y: 0 yVariation: 0 } velocity: PointDirection { x: -20 xVariation: 10 y: 0 yVariation: 0 } size: 12 sizeVariation: 8 } ImageParticle { source: "qrc:/gcompris/src/activities/clickgame/resource/bubble.png" } } } } Body { id: submarineBody target: submarineImage bodyType: Body.Dynamic fixedRotation: true linearDamping: 0 linearVelocity: submarine.isHit ? Qt.point(0,0) : submarine.velocity fixtures: [ Box { id: submarineFixer y: submarineImage.height * 0.25 width: submarineImage.width height: submarineImage.height * 0.75 categories: items.submarineCategory collidesWith: Fixture.All density: 1 friction: 0 restitution: 0 onBeginContact: { var collidedObject = other.getBody().target if (collidedObject == whale) { whale.hit() } if (collidedObject == crown) { crown.captureCrown() } else { Activity.finishLevel(false) } } }, Box { id: submarinePeriscopeFixer x: submarineImage.width * 0.55 width: submarineImage.width / 15 height: submarineImage.height * 0.25 categories: items.submarineCategory collidesWith: Fixture.All density: 1 friction: 0 restitution: 0 onBeginContact: { var collidedObject = other.getBody().target if (collidedObject == whale) { whale.hit() } if (collidedObject == crown) { crown.captureCrown() } else { Activity.finishLevel(false) } } } ] } Timer { id: updateVerticalVelocity interval: 50 running: true repeat: true onTriggered: submarine.changeVerticalVelocity() } } Image { id: sparkle source: "qrc:/gcompris/src/activities/mining/resource/sparkle.svg" x: crown.x y: crown.y z: 1 width: crown.width height: width * 0.7 property bool isCaptured: false scale: isCaptured ? 1 : 0 function createSparkle() { isCaptured = true removeSparkleTimer.start() } function removeSparkle() { isCaptured = false } Behavior on scale { NumberAnimation { duration: 100 } } Timer { id: removeSparkleTimer interval: 3000 repeat: false running: false onTriggered: sparkle.removeSparkle() } } Rectangle { id: upperGate visible: (bar.level > 1) ? true : false width: background.width / 18 height: isGateOpen ? background.height * (5 / 36) : background.height * (5 / 12) y: -2 z: 1 color: "#848484" border.color: "black" border.width: 3 anchors.right: background.right property bool isGateOpen: false function openGate() { if (!isGateOpen) { isGateOpen = true } } function closeGate() { if (isGateOpen) { isGateOpen = false } } Body { id: upperGateBody target: upperGate bodyType: Body.Static sleepingAllowed: true fixedRotation: true linearDamping: 0 fixtures: Box { id: upperGatefixer width: upperGate.width height: upperGate.height categories: items.upperGatefixerCategory collidesWith: upperGate.visible ? items.submarineCategory : Fixture.None density: 1 friction: 0 restitution: 0 } } Behavior on height { NumberAnimation { duration: 1000 } } } Rectangle { id: lowerGate z: 1 visible: upperGate.visible width: background.width / 18 height: background.height * (5 / 12) - subSchemaImage.height / 1.4 y: background.height * (5 / 12) color: "#848484" border.color: "black" border.width: 3 anchors.right:background.right Body { id: lowerGateBody target: lowerGate bodyType: Body.Static sleepingAllowed: true fixedRotation: true linearDamping: 0 fixtures: Box { id: lowerGatefixer width: lowerGate.width height: lowerGate.height categories: items.lowerGatefixerCategory collidesWith: lowerGate.visible ? items.submarineCategory : Fixture.None density: 1 friction: 0 restitution: 0 } } } Item { id: subSchemaItems Image { id: subSchemaImage source: url + "sub_schema.svg" width: background.width/1.3 height: background.height/4 x: background.width/9 y: background.height/1.5 } } Image { id: crown width: submarineImage.width * 0.85 height: width * 0.55 visible: (bar.level) > 2 ? true : false source: url + "crown.png" function captureCrown() { upperGate.openGate() sparkle.createSparkle() crown.visible = false } function reset() { crown.visible = (bar.level) > 2 ? true : false upperGate.closeGate() } x: background.width / 2 y: background.height - (subSchemaImage.height * 2) z: 1 Body { id: crownbody target: crown bodyType: Body.Static sleepingAllowed: true fixedRotation: true linearDamping: 0 fixtures: Box { id: crownfixer width: crown.width height: crown.height sensor: true categories: items.crownCategory collidesWith: crown.visible ? items.submarineCategory : Fixture.None density: 0.1 friction: 0 restitution: 0 } } } Whale { id: whale visible: (bar.level > 5) ? true : false y: rock2.y - (rock2.height * 1.15) z: 1 leftLimit: 0 rightLimit: background.width - whale.width - (upperGate.visible ? upperGate.width : 0) } Image { id: ship width: background.width / 9 height: width * 0.3 visible: (bar.level > 3) ? true : false source: url + "asw_frigate.png" x: initialXPosition z: 1 anchors.bottom: waterLevel.top property bool movingLeft: true property bool collided: false property real initialXPosition: background.width - ship.width - (upperGate.visible ? upperGate.width : 0) property real horizontalSpeed: 1 function reset() { ship.collided = false ship.x = initialXPosition } function collide() { /* Add few visual effects */ collided = true } transform: Rotation { - id: rotateShip; + id: rotateShip origin.x: ship.width / 2; origin.y: 0; axis { x: 0; y: 1; z: 0 } angle: 0 } SequentialAnimation { id: rotateShipLeft loops: 1 PropertyAnimation { target: rotateShip properties: "angle" from: 0 to: 180 duration: 500 } } SequentialAnimation { id: rotateShipRight loops: 1 PropertyAnimation { target: rotateShip properties: "angle" from: 180 to: 0 duration: 500 } } onXChanged: { if (x <= 0) { rotateShipLeft.start() movingLeft = false } else if (x >= background.width - ship.width - (upperGate.visible ? upperGate.width : 0)) { rotateShipRight.start() movingLeft = true } } Body { id: shipbody target: ship bodyType: Body.Dynamic sleepingAllowed: true fixedRotation: true linearDamping: 0 linearVelocity: Qt.point( (ship.collided ? 0 : ((ship.movingLeft ? -1 : 1) * ship.horizontalSpeed)), 0) fixtures: Box { id: shipfixer width: ship.width height: ship.height categories: items.shipCategory collidesWith: ship.visible ? items.submarineCategory : Fixture.None density: 1 friction: 0 restitution: 0 onBeginContact: ship.collide() } } } Image { id: rock2 width: background.width / 6 height: width * 0.48 visible: (bar.level > 4) ? true : false anchors.bottom: crown.bottom anchors.left: crown.right source: "qrc:/gcompris/src/activities/mining/resource/stone2.svg" transform: Rotation { origin.x: rock2.width / 2; origin.y: rock2.height / 2 axis { x: 0; y: 0; z: 1 } angle: 180 } Body { id: rock2Body target: rock2 bodyType: Body.Static sleepingAllowed: true linearDamping: 0 fixtures: Box { id: rock2Fixer categories: items.rockCategory collidesWith: rock2.visible ? items.submarineCategory : Fixture.None x: rock2.width / 8 y: rock2.height / 12 width: rock2.width / 1.2 height: rock2.height / 1.5 density: 1 friction: 0 restitution: 0 } } } /* Just a space */ Rectangle { id: space - width: bar.level < 8 ? rock1.width : rock1.width * ( 1 - (Math.random() * 0.5)) + width: bar.level < 8 ? rock1.width : rock1.width * (1 - (Math.random() * 0.5)) height: rock1.height color: "transparent" anchors { right: crown.left bottom: crown.bottom } } Image { id: rock1 width: rock2.width height: width * 0.46 visible: (bar.level > 6) ? true : false anchors.bottom: crown.bottom anchors.right: space.left source: "qrc:/gcompris/src/activities/mining/resource/stone1.svg" Body { id: rock1Body target: rock1 bodyType: Body.Static sleepingAllowed: true linearDamping: 0 fixtures: [ Circle { id: rock1FixerLeft categories: items.rockCategory collidesWith: rock1.visible ? items.submarineCategory : Fixture.None x: rock1.width / 10 radius: rock1.width / 4 density: 1 friction: 0 restitution: 0 },Circle { id: rock1FixerRight categories: items.rockCategory collidesWith: rock1.visible ? items.submarineCategory : Fixture.None x: rock1.width / 1.6 y: rock1.height / 4 radius: rock1.width / 6 density: 1 friction: 0 restitution: 0 } ] } } Timer { /* * A delay is used since on setting fullscreen on/off * first the onWidthChanged is executed, followed by * the width change */ id: updateOnWidthReset repeat: false interval: 100 running: false onTriggered: { whale.reset() ship.reset() } } Controls { id: controls enginePosition.x: background.width * 0.2 enginePosition.y: background.height - bar.height - (engineHeight * 1.25) engineWidth: background.width / 8 engineHeight: 100 submarineHorizontalSpeed: submarine.currentFinalVelocity * 1000 leftTankVisible: bar.level >= 7 ? true : false leftBallastTankPosition.x: background.width * 0.4 leftBallastTankPosition.y: background.height - bar.height - (engineHeight * 1.25) leftBallastTankWidth: background.width / 8 leftBallastTankHeight: 100 centralTankVisible: bar.level < 7 ? true : false centralBallastTankPosition.x: background.width * 0.5 centralBallastTankPosition.y: background.height - bar.height - (engineHeight * 1.25) centralBallastTankWidth: background.width / 8 centralBallastTankHeight: 100 rightTankVisible: bar.level >= 7 ? true : false rightBallastTankPosition.x: background.width * 0.6 rightBallastTankPosition.y: background.height - bar.height - (engineHeight * 1.25) rightBallastTankWidth: background.width / 8 rightBallastTankHeight: 100 divingPlaneVisible: true divingPlanePosition.x: background.width * 0.8 divingPlanePosition.y: background.height - bar.height - engineHeight divingPlaneWidth: background.width / 8 divingPlaneHeight: divingPlaneWidth * 0.2 } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } Bonus { id: bonus onLoose: Activity.initLevel() Component.onCompleted: win.connect(Activity.nextLevel) } /* DebugDraw { id: debugDraw world: physicalWorld anchors.fill: parent opacity: 0.75 visible: false } MouseArea { id: debugMouseArea anchors.fill: parent onPressed: debugDraw.visible = !debugDraw.visible } */ } } diff --git a/src/activities/submarine/Whale.qml b/src/activities/submarine/Whale.qml index b5caeb971..4437b8736 100644 --- a/src/activities/submarine/Whale.qml +++ b/src/activities/submarine/Whale.qml @@ -1,147 +1,145 @@ /* GCompris - Whale.qml * * Copyright (C) 2017 RUDRA NIL BASU * * Authors: * Pascal Georges (GTK+ version) * Rudra Nil Basu (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.6 import QtQuick.Particles 2.0 import Box2D 2.0 import QtGraphicalEffects 1.0 import GCompris 1.0 Image { id: whale source: isHit ? url + "whale_hit.png" : url + "whale.png" width: submarineImage.width height: width * 0.69 property bool isHit: false function hit() { isHit = true } function reset() { isHit = false x = rightLimit } property bool movingLeft: true property real leftLimit property real rightLimit transform: Rotation { id: rotate; origin.x: whale.width / 2; origin.y: 0; axis { x: 0; y: 1; z: 0 } angle: 0 } SequentialAnimation { id: rotateLeftAnimation loops: 1 PropertyAnimation { target: rotate properties: "angle" from: 0 to: 180 duration: 500 } } SequentialAnimation { id: rotateRightAnimation loops: 1 PropertyAnimation { target: rotate properties: "angle" from: 180 to: 0 duration: 500 } } onXChanged: { if (x <= leftLimit) { rotateLeftAnimation.start() whale.movingLeft = false - } else if (x >= rightLimit ) { + } else if (x >= rightLimit) { rotateRightAnimation.start() whale.movingLeft = true } } Loader { id: bubbleEffect anchors.fill: parent active: ApplicationInfo.hasShader sourceComponent: ParticleSystem { anchors.fill: parent Emitter { x: parent.x y: parent.y + parent.height / 2 width: 1 height: 1 emitRate: 0.5 lifeSpan: 1000 lifeSpanVariation: 2500 acceleration: PointDirection { x: -10 xVariation: 10 y: -20 yVariation: 10 } velocity: PointDirection { x: 20 xVariation: 10 y: -20 yVariation: 10 } size: 12 sizeVariation: 8 } ImageParticle { source: "qrc:/gcompris/src/activities/clickgame/resource/bubble.png" } } } Body { - id: whalebody target: whale bodyType: Body.Dynamic sleepingAllowed: true fixedRotation: true linearDamping: 0 linearVelocity: isHit ? Qt.point(0,0) : Qt.point( (whale.movingLeft ? -1 : 1) , 0) fixtures: Box { - id: whalefixer width: whale.width height: whale.height categories: items.whaleCategory collidesWith: whale.visible ? items.submarineCategory : Fixture.None density: 1 friction: 0 restitution: 0 } } } diff --git a/src/activities/submarine/submarine.js b/src/activities/submarine/submarine.js index 60dc455bf..8d5054b3f 100644 --- a/src/activities/submarine/submarine.js +++ b/src/activities/submarine/submarine.js @@ -1,137 +1,136 @@ /* GCompris - submarine.js * * Copyright (C) 2017 Rudra Nil Basu * * Authors: * Pascal Georges (GTK+ version) * Rudra Nil Basu (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 GCompris 1.0 as GCompris .import QtQuick 2.6 as Quick var currentLevel = 0 var numberOfLevel = 10 var items -var processingAnswer var tutorials = [ [ qsTr("Move the submarine to the other side of the screen."), qsTr("The leftmost item in the control panel is the engine of the submarine, indicating the current speed of the submarine."), qsTr("Increase or decrease the velocity of the submarine using the engine."), qsTr("Press the + button to increase the velocity, or the - button to decrease the velocity."), ], [ qsTr("The item next to the engine is the Ballast tanks."), qsTr("The Ballast tanks are used to float or dive under water."), qsTr("If the ballast tanks are empty, the submarine will float. If the ballast tanks are full of water, the submarine will dive underwater."), qsTr("Turning the upper valve on or off will alternatively allow or stop water from filling in the Ballast tanks, thus allowing it to dive underwater."), qsTr("Turning the lower valve on or off will alternatively allow or stop water from flushing out the Ballast tanks, thus allowing it to float on the surface of the water."), ], [ qsTr("The rightmost item in the control panel controls the diving planes of the submarine"), qsTr("The Diving Planes in a submarine is used to control the depth of the submarine accurately once it is underwater."), qsTr("Once the submarine is moving underwater, increasing or decreasing the angle of the planes will increase and decrease the depth of the submarine."), qsTr("The + button will increase the depth of the submarine, while the - button will decrease the depth of the submarine."), qsTr("Grab the crown to open the gate."), qsTr("Check out the help menu for the keyboard controls."), ] ] function start(items_) { items = items_ currentLevel = 0 initLevel() } function stop() { } function initLevel() { items.bar.level = currentLevel + 1 /* Tutorial Levels, display tutorials */ if (currentLevel < tutorials.length) { items.tutorial.visible = true items.tutorial.index = 0 items.tutorial.intro = tutorials[currentLevel] } else { items.tutorial.visible = false - items.physicalWorld.turnOn() } setUpLevelElements() } function setUpLevelElements() { /* Set up initial position and state of the submarine */ items.submarine.resetSubmarine() if(items.ship.visible) { items.ship.reset() } items.crown.reset() items.whale.reset() - processingAnswer = false + items.processingAnswer = false resetUpperGate() } function resetUpperGate() { - if ( items && !items.crown.visible && items.upperGate.visible) { + if (items && items.crown && !items.crown.visible && items.upperGate && items.upperGate.visible) { items.upperGate.openGate() } } function closeGate() { if (items.upperGate.visible) { items.upperGate.closeGate() } } function finishLevel(win) { - if (processingAnswer) + if (items.processingAnswer) return - items.physicalWorld.turnOff() - processingAnswer = true + items.processingAnswer = true if (win) { items.bonus.good("flower") } else { items.submarine.destroySubmarine() items.bonus.bad("flower") } } function nextLevel() { + items.processingAnswer = true closeGate() - if(numberOfLevel <= ++currentLevel ) { + if(numberOfLevel <= ++currentLevel) { currentLevel = 0 } initLevel(); } function previousLevel() { + items.processingAnswer = true closeGate() if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); }