diff --git a/src/activities/submarine/Controls.qml b/src/activities/submarine/Controls.qml index 90db729d4..cd15cce5d 100644 --- a/src/activities/submarine/Controls.qml +++ b/src/activities/submarine/Controls.qml @@ -1,705 +1,696 @@ /* 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 property int buttonSize property int buttonPlusY property int buttonMinusY property string fillColor : "#0DA5CB" - - function resetBallastButtons() { - rotateLeftFill.angle = 0 - rotateLeftFlush.angle = 0 - rotateCentralFill.angle = 0 - rotateCentralFlush.angle = 0 - rotateRightFill.angle = 0 - rotateRightFlush.angle = 0 - } Image { id: controlBackground source: url + "board.svg" width: background.width height: background.height * 0.40 sourceSize.width: controlBackground.width sourceSize.height: controlBackground.height y: background.height - controlBackground.height } Item { Rectangle { id: engine x: enginePosition.x y: enginePosition.y radius: 10 color: "#323232" border.width: 4 border.color: "#AEC6DD" GCText { id: engineValues anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } color: "#D3E1EB" } } Image { id: incSpeed source: url + "up.svg" width: buttonSize height: buttonSize sourceSize.width: incSpeed.width sourceSize.height: incSpeed.height anchors { right: engine.left leftMargin: incSpeed.width / 2 } y: buttonPlusY MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: submarine.increaseHorizontalVelocity(1) } } Image { id: downSpeed source: url + "down.svg" width: buttonSize height: buttonSize sourceSize.width: downSpeed.width sourceSize.height: downSpeed.height anchors { right: engine.left leftMargin: downSpeed.width / 2 } y: buttonMinusY MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: submarine.decreaseHorizontalVelocity(1) } } } // 3 Ballast Tanks Item { id: leftBallastTankController Rectangle { id: leftBallastTankDisplay x: leftBallastTankPosition.x y: leftBallastTankPosition.y radius: 2 color: "#323232" border.width: 4 border.color: "#AEC6DD" Rectangle { width: leftBallastTankWidth * 0.85 height: (leftBallastTank.waterLevel / leftBallastTank.maxWaterLevel) * (leftBallastTankHeight - 8) anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter margins: 4 } color: fillColor Behavior on height { NumberAnimation { duration: 1000 } } } GCText { id: leftBallastTankLabel text: qsTr("Left Ballast Tank") width: parent.width - 8 wrapMode: Text.WordWrap anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter fontSize: 8 color: "#B8D3E1EB" } } Image { id: leftBallastFill source: url + "vanne.svg" x: leftBallastTankDisplay.x - buttonSize * 1.1 y: buttonPlusY width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize rotation: 0 transform: Rotation { id: rotateLeftFill; origin.x: leftBallastFill.width / 2; origin.y: leftBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: 0 } SequentialAnimation { id: leftFillAnim1 loops: 1 PropertyAnimation { target: rotateLeftFill properties: "angle" from: 0 to: 90 duration: 200 } } SequentialAnimation { id: leftFillAnim2 loops: 1 PropertyAnimation { target: rotateLeftFill properties: "angle" from: 90 to: 0 duration: 200 } } MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: { leftBallastTank.fillBallastTanks() if (leftBallastTank.waterFilling) { leftFillAnim1.start() } else { leftFillAnim2.start() } } } } Image { id: leftBallastFlush source: url + "vanne.svg" x: leftBallastTankDisplay.x - buttonSize * 1.1 y: buttonMinusY width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize rotation: 0 transform: Rotation { id: rotateLeftFlush; origin.x: leftBallastFill.width / 2; origin.y: leftBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: 0 } SequentialAnimation { id: leftFlushAnim1 loops: 1 PropertyAnimation { target: rotateLeftFlush properties: "angle" from: 0 to: 90 duration: 200 } } SequentialAnimation { id: leftFlushAnim2 loops: 1 PropertyAnimation { target: rotateLeftFlush properties: "angle" from: 90 to: 0 duration: 200 } } MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: { leftBallastTank.flushBallastTanks() if (leftBallastTank.waterFlushing) { leftFlushAnim1.start() } else { leftFlushAnim2.start() } } } } } Item { id: centralBallastTankController Rectangle { id: centralBallastTankDisplay x: centralBallastTankPosition.x y: centralBallastTankPosition.y radius: 2 color: "#323232" border.width: 4 border.color: "#AEC6DD" Rectangle { width: centralBallastTankWidth * 0.85 height: (centralBallastTank.waterLevel / centralBallastTank.maxWaterLevel) * (centralBallastTankHeight - 8) anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter margins: 4 } color: fillColor Behavior on height { NumberAnimation { duration: 1000 } } } GCText { id: centralBallastTankLabel text: qsTr("Central Ballast Tank") width: parent.width - 10 wrapMode: Text.WordWrap anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter fontSize: 8 color: "#B8D3E1EB" } } Image { id: centralBallastFill source: url + "vanne.svg" x: centralBallastTankDisplay.x - buttonSize * 1.1 y: buttonPlusY width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize rotation: 0 transform: Rotation { id: rotateCentralFill; origin.x: centralBallastFill.width / 2; origin.y: centralBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: 0 } SequentialAnimation { id: centralFillAnim1 loops: 1 PropertyAnimation { target: rotateCentralFill properties: "angle" from: 0 to: 90 duration: 200 } } SequentialAnimation { id: centralFillAnim2 loops: 1 PropertyAnimation { target: rotateCentralFill properties: "angle" from: 90 to: 0 duration: 200 } } MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: { centralBallastTank.fillBallastTanks() if (centralBallastTank.waterFilling) { centralFillAnim1.start() } else { centralFillAnim2.start() } } } } Image { id: centralBallastFlush source: url + "vanne.svg" x: centralBallastTankDisplay.x - buttonSize * 1.1 y: buttonMinusY width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize rotation: 0 transform: Rotation { id: rotateCentralFlush; origin.x: centralBallastFill.width / 2; origin.y: centralBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: 0 } SequentialAnimation { id: centralFlushAnim1 loops: 1 PropertyAnimation { target: rotateCentralFlush properties: "angle" from: 0 to: 90 duration: 200 } } SequentialAnimation { id: centralFlushAnim2 loops: 1 PropertyAnimation { target: rotateCentralFlush properties: "angle" from: 90 to: 0 duration: 200 } } MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: { centralBallastTank.flushBallastTanks() if (centralBallastTank.waterFlushing) { centralFlushAnim1.start() } else { centralFlushAnim2.start() } } } } } Item { id: rightBallastTankController Rectangle { id: rightBallastTankDisplay x: rightBallastTankPosition.x y: rightBallastTankPosition.y radius: 2 color: "#323232" border.width: 4 border.color: "#AEC6DD" Rectangle { width: rightBallastTankWidth * 0.85 height: (rightBallastTank.waterLevel / rightBallastTank.maxWaterLevel) * (rightBallastTankHeight - 8) anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter margins: 4 } color: fillColor Behavior on height { NumberAnimation { duration: 1000 } } } GCText { id: rightBallastTankLabel text: qsTr("Right Ballast Tank") width: parent.width - 8 wrapMode: Text.WordWrap anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter fontSize: 8 color: "#B8D3E1EB" } } Image { id: rightBallastFill source: url + "vanne.svg" x: rightBallastTankDisplay.x - buttonSize * 1.1 y: buttonPlusY width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize rotation: 0 transform: Rotation { id: rotateRightFill; origin.x: rightBallastFill.width / 2; origin.y: rightBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: 0 } SequentialAnimation { id: rightFillAnim1 loops: 1 PropertyAnimation { target: rotateRightFill properties: "angle" from: 0 to: 90 duration: 200 } } SequentialAnimation { id: rightFillAnim2 loops: 1 PropertyAnimation { target: rotateRightFill properties: "angle" from: 90 to: 0 duration: 200 } } MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: { rightBallastTank.fillBallastTanks() if (rightBallastTank.waterFilling) { rightFillAnim1.start() } else { rightFillAnim2.start() } } } } Image { id: rightBallastFlush source: url + "vanne.svg" x: rightBallastTankDisplay.x - buttonSize * 1.1 y: buttonMinusY width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize rotation: 0 transform: Rotation { id: rotateRightFlush; origin.x: rightBallastFill.width / 2; origin.y: rightBallastFill.height / 2 axis { x: 0; y: 0; z: 1 } angle: 0 } SequentialAnimation { id: rightFlushAnim1 loops: 1 PropertyAnimation { target: rotateRightFlush properties: "angle" from: 0 to: 90 duration: 200 } } SequentialAnimation { id: rightFlushAnim2 loops: 1 PropertyAnimation { target: rotateRightFlush properties: "angle" from: 90 to: 0 duration: 200 } } MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: { rightBallastTank.flushBallastTanks() if (rightBallastTank.waterFlushing) { rightFlushAnim1.start() } else { rightFlushAnim2.start() } } } } } Item { id: divingPlaneController visible: divingPlaneVisible property int maxRotationAngle: 30 Image { id: divingPlanesImage source: url + "rudder.svg" width: divingPlaneWidth height: divingPlaneHeight sourceSize.width: divingPlaneWidth sourceSize.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.svg" width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize anchors { left: divingPlanesImage.right // bottom: divingPlanesImage.top } y: buttonPlusY MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: submarine.increaseWingsAngle(1) } } Image { id: divingPlanesRotateDown source: url + "down.svg" width: buttonSize height: buttonSize sourceSize.width: buttonSize sourceSize.height: buttonSize anchors { left: divingPlanesImage.right // top: divingPlanesImage.bottom } y: buttonMinusY MouseArea { anchors.fill: parent enabled: !tutorial.visible onClicked: submarine.decreaseWingsAngle(1) } } } } diff --git a/src/activities/submarine/Submarine.qml b/src/activities/submarine/Submarine.qml index 0218a29c0..9adb0b522 100644 --- a/src/activities/submarine/Submarine.qml +++ b/src/activities/submarine/Submarine.qml @@ -1,972 +1,971 @@ /* 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 sourceSize.height: parent.height sourceSize.width: parent.width 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) && !tutorial.visible) { submarine.increaseHorizontalVelocity(1) } 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) && !tutorial.visible) { centralBallastTank.fillBallastTanks() } if ((event.key == Qt.Key_S || event.key == Qt.Key_Down) && !tutorial.visible) { centralBallastTank.flushBallastTanks() } if ((event.key == Qt.Key_Plus) && !tutorial.visible) { submarine.increaseWingsAngle(1) } if ((event.key == Qt.Key_Minus) && !tutorial.visible) { submarine.decreaseWingsAngle(1) } if ((event.key == Qt.Key_R) && !tutorial.visible) { leftBallastTank.fillBallastTanks() } if ((event.key == Qt.Key_F) && !tutorial.visible) { leftBallastTank.flushBallastTanks() } if ((event.key == Qt.Key_T) && !tutorial.visible) { rightBallastTank.fillBallastTanks() } 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 textContainerHeight: 0.5 * parent.height 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,waterLevel.y - submarineImage.height/2) 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 } function resetSubmarine() { isHit = false submarineImage.reset() leftBallastTank.resetBallastTanks() rightBallastTank.resetBallastTanks() centralBallastTank.resetBallastTanks() - controls.resetBallastButtons() - + 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 if (submarineImage.y <= submarine.initialPosition.y && yPosition == 0){ depthToMove = 0 }else { 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.svg" : url + "submarine.svg" 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 sourceSize.width: submarineImage.width fillMode: Image.PreserveAspectFit function reset() { 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.50 width: submarineImage.width height: submarineImage.height * 0.50 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.5 width: submarineImage.width * 0.25 height: submarineImage.height 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) + 4 y: -2 z: 2 color: "#9E948A" border.color: "#766C62" border.width: 2 anchors.right: background.right anchors.rightMargin: -2 property bool 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: "#9E948A" border.color: "#766C62" border.width: 2 anchors.right:background.right anchors.rightMargin: -2 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 } } } Rectangle { id: subSchemaImage width: background.width/1.3 height: background.height/4 x: background.width/9 y: background.height/1.5 visible: false } Image { id: crown width: submarineImage.width * 0.85 height: crown.width * 0.5 sourceSize.width: crown.width sourceSize.height: crown.height visible: ((bar.level > 2) && !isCaptured) ? true : false source: url + "crown.svg" property bool isCaptured: false function captureCrown() { upperGate.isGateOpen = true isCaptured = true sparkle.createSparkle() } function reset() { 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 sourceSize.width: ship.width fillMode: Image.PreserveAspectFit visible: (bar.level > 3) ? true : false source: collided ? url + "boat-hit.svg" : url + "boat.svg" 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: rock2.width * 0.48 z: 5 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: rock2.width * 0.46 z: 5 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 } ] } } Image { id: rock3 width: background.width height: background.height * 0.25 sourceSize.width: rock3.width sourceSize.height: rock3.height visible: (bar.level > 2) ? true : false anchors.top: crown.top anchors.horizontalCenter: crown.left // anchors.topMargin: height * 0.5 source: url + "rocks.svg" } 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 z: 10 enginePosition.x: background.width * 0.1 enginePosition.y: background.height * 0.64 engineWidth: background.width / 8 engineHeight: 100 submarineHorizontalSpeed: submarine.currentFinalVelocity * 1000 leftTankVisible: bar.level >= 7 ? true : false leftBallastTankPosition.x: background.width * 0.35 leftBallastTankPosition.y: enginePosition.y leftBallastTankWidth: background.width / 8 leftBallastTankHeight: 120 centralTankVisible: bar.level < 7 ? true : false centralBallastTankPosition.x: background.width * 0.45 centralBallastTankPosition.y: enginePosition.y centralBallastTankWidth: background.width / 8 centralBallastTankHeight: 120 rightTankVisible: bar.level >= 7 ? true : false rightBallastTankPosition.x: background.width * 0.55 rightBallastTankPosition.y: enginePosition.y rightBallastTankWidth: background.width / 8 rightBallastTankHeight: 120 divingPlaneVisible: true divingPlanePosition.x: background.width * 0.8 divingPlanePosition.y: enginePosition.y + (engineHeight * 0.5) - (divingPlaneHeight * 0.5) divingPlaneWidth: background.width * 0.1 divingPlaneHeight: divingPlaneWidth * 0.33 buttonSize: subSchemaImage.height * 0.2 buttonPlusY: enginePosition.y - (buttonSize * 0.5) buttonMinusY: enginePosition.y + engineHeight - (buttonSize * 0.5) } 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 } */ } }