diff --git a/src/activities/submarine/Controls.qml b/src/activities/submarine/Controls.qml index c4a8ed2ba..eec4fb3ae 100644 --- a/src/activities/submarine/Controls.qml +++ b/src/activities/submarine/Controls.qml @@ -1,460 +1,443 @@ /* GCompris - Controls.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 "../../core" Item { id: controls /* Engine Controller Properties */ property point enginePosition property alias engineWidth : engine.width property alias engineHeight : engine.height property alias submarineHorizontalSpeed : engineValues.text /* Ballast tanks Controller Properties */ property alias leftTankVisible : leftBallastTankController.visible property point leftBallastTankPosition property alias leftBallastTankWidth : leftBallastTankDisplay.width property alias leftBallastTankHeight : leftBallastTankDisplay.height property alias centralTankVisible : centralBallastTankController.visible property point centralBallastTankPosition property alias centralBallastTankWidth : centralBallastTankDisplay.width property alias centralBallastTankHeight : centralBallastTankDisplay.height property alias rightTankVisible : rightBallastTankController.visible property point rightBallastTankPosition property alias rightBallastTankWidth : rightBallastTankDisplay.width property alias rightBallastTankHeight : rightBallastTankDisplay.height /* Diving Plane Controller properties */ property bool divingPlaneVisible property point divingPlanePosition property int divingPlaneWidth property int divingPlaneHeight Rectangle { id: controlBackground width: background.width height: background.height * 0.35 color: "grey" y: background.height - height } Item { Rectangle { id: engine x: enginePosition.x y: enginePosition.y radius: 10 color: "black" GCText { id: engineValues anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } color: "green" } } Image { id: incSpeed source: url + "up.png" x: engine.x - width * 1.5 y: engine.y - engine.height * 0.1 MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: submarine.increaseHorizontalVelocity(1) } } Image { id: downSpeed source: url + "down.png" x: engine.x - width * 1.5 y: engine.y + engine.height * 0.9 MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: submarine.decreaseHorizontalVelocity(1) } } } // 3 Ballast Tanks Item { id: leftBallastTankController Rectangle { id: leftBallastTankDisplay x: leftBallastTankPosition.x y: leftBallastTankPosition.y radius: 10 color: "black" Rectangle { width: leftBallastTankWidth * 0.9 height: (leftBallastTank.waterLevel / leftBallastTank.maxWaterLevel) * leftBallastTankHeight anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter } color: "green" Behavior on height { NumberAnimation { duration: 1000 } } } - } - - Rectangle { - x: leftBallastTankPosition.x - y: leftBallastTankPosition.y + (leftBallastTankDisplay.height * 1.1) - width: leftBallastTankWidth - height: leftBallastTankLabel.height - color: "black" - radius: 10 GCText { id: leftBallastTankLabel text: qsTr("Left Ballast Tank") width: parent.width wrapMode: Text.WordWrap anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter - fontSize: 10 - color: "green" + fontSize: 8 + color: "white" } } Image { id: leftBallastFill source: url + "vanne.svg" x: leftBallastTankDisplay.x - width * 1.5 y: leftBallastTankDisplay.y - leftBallastTankDisplay.height * 0.1 transform: Rotation { id: rotateLeftTank; origin.x: leftBallastFill.width / 2; origin.y: leftBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: leftBallastTank.waterFilling ? 90 : 0 } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: leftBallastTank.fillBallastTanks() } } Image { id: leftBallastFlush source: url + "vanne.svg" x: leftBallastTankDisplay.x - width * 1.5 y: leftBallastTankDisplay.y + leftBallastTankDisplay.height * 0.9 transform: Rotation { id: rotateLeftTankFlush; origin.x: leftBallastFill.width / 2; origin.y: leftBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: leftBallastTank.waterFlushing ? 90 : 0 } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: leftBallastTank.flushBallastTanks() } } } Item { id: centralBallastTankController Rectangle { id: centralBallastTankDisplay x: centralBallastTankPosition.x y: centralBallastTankPosition.y radius: 10 color: "black" Rectangle { width: centralBallastTankWidth * 0.9 height: (centralBallastTank.waterLevel / centralBallastTank.maxWaterLevel) * centralBallastTankHeight anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter } color: "green" Behavior on height { NumberAnimation { duration: 1000 } } } - } - - Rectangle { - x: centralBallastTankPosition.x - y: centralBallastTankPosition.y + (centralBallastTankDisplay.height * 1.1) - width: centralBallastTankWidth - height: centralBallastTankLabel.height - color: "black" - radius: 10 GCText { id: centralBallastTankLabel text: qsTr("Central Ballast Tank") width: parent.width wrapMode: Text.WordWrap anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter - fontSize: 10 - color: "green" + fontSize: 8 + color: "white" } } Image { id: centralBallastFill source: url + "vanne.svg" x: centralBallastTankDisplay.x - width * 1.5 y: centralBallastTankDisplay.y - centralBallastTankDisplay.height * 0.1 transform: Rotation { id: rotateCentralTank; origin.x: centralBallastFill.width / 2; origin.y: centralBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: centralBallastTank.waterFilling ? 90 : 0 } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: centralBallastTank.fillBallastTanks() } } Image { id: centralBallastFlush source: url + "vanne.svg" x: centralBallastTankDisplay.x - width * 1.5 y: centralBallastTankDisplay.y + centralBallastTankDisplay.height * 0.9 transform: Rotation { id: rotateCentralTankFlush; origin.x: centralBallastFill.width / 2; origin.y: centralBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle:centralBallastTank.waterFlushing ? 90 : 0 } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: centralBallastTank.flushBallastTanks() } } } Item { id: rightBallastTankController Rectangle { id: rightBallastTankDisplay x: rightBallastTankPosition.x y: rightBallastTankPosition.y radius: 10 color: "black" Rectangle { width: rightBallastTankWidth * 0.9 height: (rightBallastTank.waterLevel / rightBallastTank.maxWaterLevel) * rightBallastTankHeight anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter } color: "green" Behavior on height { NumberAnimation { duration: 1000 } } } - } - - Rectangle { - x: rightBallastTankPosition.x - y: rightBallastTankPosition.y + (rightBallastTankDisplay.height * 1.1) - width: rightBallastTankWidth - height: rightBallastTankLabel.height - color: "black" - radius: 10 GCText { id: rightBallastTankLabel text: qsTr("Right Ballast Tank") width: parent.width wrapMode: Text.WordWrap anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter - fontSize: 10 - color: "green" + fontSize: 8 + color: "white" } } Image { id: rightBallastFill source: url + "vanne.svg" x: rightBallastTankDisplay.x - width * 1.5 y: rightBallastTankDisplay.y - rightBallastTankDisplay.height * 0.1 transform: Rotation { id: rotateRightTank; origin.x: rightBallastFill.width / 2; origin.y: rightBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: rightBallastTank.waterFilling ? 90 : 0 } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: rightBallastTank.fillBallastTanks() } } Image { id: rightBallastFlush source: url + "vanne.svg" x: rightBallastTankDisplay.x - width * 1.5 y: rightBallastTankDisplay.y + rightBallastTankDisplay.height * 0.9 transform: Rotation { id: rotateRightTankFlush; origin.x: rightBallastFill.width / 2; origin.y: rightBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: rightBallastTank.waterFlushing ? 90 : 0 } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: rightBallastTank.flushBallastTanks() } } } Item { id: divingPlaneController visible: divingPlaneVisible property int maxRotationAngle: 30 Image { id: divingPlanesImage source: url + "rudder.png" width: divingPlaneWidth height: divingPlaneHeight x: divingPlanePosition.x y: divingPlanePosition.y transform: Rotation { id: rotateDivingPlanes; origin.x: divingPlanesImage.width; origin.y: divingPlanesImage.height / 2 axis { x: 0; y: 0; z: 1 } angle: (submarine.wingsAngle / submarine.maxWingsAngle) * divingPlaneController.maxRotationAngle } } Image { id: divingPlanesRotateUp source: url + "up.png" anchors { left: divingPlanesImage.right bottom: divingPlanesImage.top } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: submarine.increaseWingsAngle(1) } } Image { id: divingPlanesRotateDown source: url + "down.png" anchors { left: divingPlanesImage.right top: divingPlanesImage.bottom } MouseArea { anchors.fill: parent + visible: !tutorial.visible onClicked: submarine.decreaseWingsAngle(1) } } } } diff --git a/src/activities/submarine/Submarine.qml b/src/activities/submarine/Submarine.qml index 40f6b6506..c9ee48c79 100644 --- a/src/activities/submarine/Submarine.qml +++ b/src/activities/submarine/Submarine.qml @@ -1,968 +1,953 @@ /* 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)) { + if ((event.key == Qt.Key_D || event.key == Qt.Key_Right) && !tutorial.visible) { submarine.increaseHorizontalVelocity(1) } - if ((event.key == Qt.Key_A || event.key == Qt.Key_Left)) { + if ((event.key == Qt.Key_A || event.key == Qt.Key_Left) && !tutorial.visible) { submarine.decreaseHorizontalVelocity(1) } - if ((event.key == Qt.Key_W || event.key == Qt.Key_Up)) { + if ((event.key == Qt.Key_W || event.key == Qt.Key_Up) && !tutorial.visible) { centralBallastTank.fillBallastTanks() } - if ((event.key == Qt.Key_S || event.key == Qt.Key_Down)) { + if ((event.key == Qt.Key_S || event.key == Qt.Key_Down) && !tutorial.visible) { centralBallastTank.flushBallastTanks() } - if ((event.key == Qt.Key_Plus)) { + if ((event.key == Qt.Key_Plus) && !tutorial.visible) { submarine.increaseWingsAngle(1) } - if ((event.key == Qt.Key_Minus)) { + if ((event.key == Qt.Key_Minus) && !tutorial.visible) { submarine.decreaseWingsAngle(1) } - if ((event.key == Qt.Key_R)) { + if ((event.key == Qt.Key_R) && !tutorial.visible) { leftBallastTank.fillBallastTanks() } - if ((event.key == Qt.Key_F)) { + if ((event.key == Qt.Key_F) && !tutorial.visible) { leftBallastTank.flushBallastTanks() } - if ((event.key == Qt.Key_T)) { + if ((event.key == Qt.Key_T) && !tutorial.visible) { rightBallastTank.fillBallastTanks() } - if ((event.key == Qt.Key_G)) { + if ((event.key == Qt.Key_G) && !tutorial.visible) { 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: { tutorial.visible = false } } onStart: { Activity.start(items) } onStop: { Activity.stop() } World { id: physicalWorld running: !tutorial.visible && !items.processingAnswer gravity: Qt.point(0,0) autoClearForces: false } 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) + smoothHorizontalVelocity.stop() 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 } BallastTank { id: rightBallastTank } BallastTank { id: centralBallastTank } 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 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 + visible: ((bar.level > 2) && !isCaptured) ? true : false source: url + "crown.png" + property bool isCaptured: false + function captureCrown() { - upperGate.openGate() + upperGate.isGateOpen = true + isCaptured = true sparkle.createSparkle() - crown.visible = false } function reset() { - crown.visible = (bar.level) > 2 ? true : false - upperGate.closeGate() + isCaptured = false + upperGate.isGateOpen = false } 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 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)) 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/submarine.js b/src/activities/submarine/submarine.js index 8d5054b3f..b199099f6 100644 --- a/src/activities/submarine/submarine.js +++ b/src/activities/submarine/submarine.js @@ -1,136 +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 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 } 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() items.processingAnswer = false resetUpperGate() } function resetUpperGate() { if (items && items.crown && !items.crown.visible && items.upperGate && items.upperGate.visible) { - items.upperGate.openGate() + items.upperGate.isGateOpen = true } } function closeGate() { if (items.upperGate.visible) { - items.upperGate.closeGate() + items.upperGate.isGateOpen = false } } function finishLevel(win) { if (items.processingAnswer) return 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) { currentLevel = 0 } initLevel(); } function previousLevel() { items.processingAnswer = true closeGate() if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); }