diff --git a/src/activities/railroad/railroad.js b/src/activities/railroad/railroad.js index 203f663ca..88e787640 100644 --- a/src/activities/railroad/railroad.js +++ b/src/activities/railroad/railroad.js @@ -1,204 +1,204 @@ /* GCompris - railroad.js * * Copyright (C) 2016 Utkarsh Tiwari * Copyright (C) 2018 Amit Sagtani * * Authors: * (GTK+ version) * Utkarsh Tiwari (Qt Quick port) * Amit Sagtani (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ .pragma library .import QtQuick 2.6 as Quick .import GCompris 1.0 as GCompris var currentLevel = 0 var numberOfLevel = 10 var solutionArray = [] var backupListModel = [] var isNewLevel = true var resourceURL = "qrc:/gcompris/src/activities/railroad/resource/" var items var uniqueId = [] /** * Stores configuration for each level. * 'WagonsInCorrectAnswers' contains no. of wagons in correct answer. * 'memoryTime' contains time(in seconds) for memorizing the wagons. * 'numberOfSubLevels' contains no. of sublevels in each level. * 'columnsInHorizontalMode' contains no. of columns in a row of sampleZone in horizontal mode. * 'columnsInVerticalMode' contains no. of columns in a row of sampleZone in vertical mode. * 'noOfLocos' stores no. of locos to be displayed in sampleZone. * 'noOfWagons' stores no. of wagons to be displayed in sampleZone. */ var dataset = { "WagonsInCorrectAnswers": [1, 1, 2, 2, 3, 3, 4, 4, 5, 5], "memoryTime": [4, 4, 6, 6, 7, 7, 8, 8, 10, 10], "numberOfSubLevels": 3, "columnsInHorizontalMode": [3, 5, 3, 5, 3, 5, 3, 5, 3, 5], "columsInVerticalMode": [3, 4, 3, 4, 3, 4, 3, 4, 3, 4], "noOfLocos": [8, 9, 4, 9, 4, 9, 4, 9, 4, 9], "noOfWagons": [4, 11, 8, 11, 8, 11, 8, 11, 8, 11] } function start(items_) { items = items_ currentLevel = 0 items.score.numberOfSubLevels = dataset["numberOfSubLevels"]; items.score.currentSubLevel = 1; initLevel() } function stop() { } function initLevel() { generateUniqueId(); items.mouseEnabled = true; items.memoryMode = false; items.trainAnimationTimer.stop(); items.animateFlow.stop(); // Stops any previous animations items.listModel.clear(); items.answerZone.currentIndex = 0; items.sampleList.currentIndex = 0; items.answerZone.selectedSwapIndex = -1; if(isNewLevel) { // Initiates a new level backupListModel = []; solutionArray = []; //Adds wagons to display in answerZone var identifier; var idLoco; // Adds a loco at the beginning idLoco = "loco" + Math.floor(Math.random() * dataset["noOfLocos"][currentLevel]) addWagon(idLoco, items.listModel.length); for(var i = 0; i < dataset["WagonsInCorrectAnswers"][currentLevel] - 1; i++) { do { identifier = "wagon" + Math.floor(Math.random() * dataset["noOfWagons"][currentLevel]) } while (solutionArray.indexOf(identifier) != -1) solutionArray.push(identifier); addWagon(identifier, i); } solutionArray.push(idLoco); } else { // Re-setup the same level for(var i = 0; i < solutionArray.length; i++) { addWagon(solutionArray[i], i); } } if(items.introMessage.visible === false && isNewLevel) { items.trainAnimationTimer.start() } items.bar.level = currentLevel + 1; items.trainAnimationTimer.interval = dataset["memoryTime"][currentLevel] * 1000 } function nextLevel() { if(numberOfLevel <= ++currentLevel) { currentLevel = 0 } items.score.currentSubLevel = 1; isNewLevel = true; initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } items.score.currentSubLevel = 1; isNewLevel = true; initLevel(); } function restoreLevel() { backupListModel = []; for (var index = 0; index < items.listModel.count; index++) { backupListModel.push(items.listModel.get(index).id); } isNewLevel = false; initLevel(); } function nextSubLevel() { /* Sets up the next sublevel */ items.score.currentSubLevel ++; if(items.score.currentSubLevel > dataset["numberOfSubLevels"]) { nextLevel(); } else { isNewLevel = true; initLevel(); } } function checkAnswer() { /* Checks if the top level setup equals the solutions */ if(items.listModel.count === solutionArray.length) { var isSolution = true; for (var index = 0; index < items.listModel.count; index++) { if(items.listModel.get(index).id !== solutionArray[index]) { isSolution = false; break; } } if(isSolution == true) { items.mouseEnabled = false; // Disables the touch items.bonus.good("flower"); } else { - items.bonus.bad("flower"); + items.bonus.bad("flower", items.bonus.checkAnswer); } } else { - items.bonus.bad("flower"); + items.bonus.bad("flower", items.bonus.checkAnswer); } } function addWagon(uniqueID, dropIndex) { /* Appends wagons to the display area */ items.listModel.insert(dropIndex, {"id": uniqueID}); } function getDropIndex(x) { var count = items.listModel.count; for (var index = 0; index < count; index++) { var xVal = items.answerZone.cellWidth * index var itemWidth = items.answerZone.cellWidth if(x < xVal && index == 0) { return 0; } else if((xVal + itemWidth + items.background.width * 0.0025) <= x && index == (count - 1)) { return count; } else if(xVal <= x && x < (xVal + itemWidth + items.background.width * 0.0025)) { return index + 1; } } return 0; } function generateUniqueId() { uniqueId = []; var index; for(index = 0; index < dataset["noOfLocos"][currentLevel]; index++) { uniqueId.push("loco" + index) } for(index = 0; index < dataset["noOfWagons"][currentLevel]; index++) { uniqueId.push("wagon" + index) } } diff --git a/src/activities/roman_numerals/RomanNumerals.qml b/src/activities/roman_numerals/RomanNumerals.qml index 0470096b7..49c42112f 100644 --- a/src/activities/roman_numerals/RomanNumerals.qml +++ b/src/activities/roman_numerals/RomanNumerals.qml @@ -1,530 +1,530 @@ /* GCompris - roman_numerals.qml * * Copyright (C) 2016 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 GCompris 1.0 import "../../core" ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background source: items.toArabic ? "qrc:/gcompris/src/activities/roman_numerals/resource/arcs.svg" : "qrc:/gcompris/src/activities/roman_numerals/resource/torrazzo-crema.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: Math.max(parent.width, parent.height) signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property alias score: score property alias textInput: textInput property bool toArabic: dataset[currentLevel].toArabic property string questionText: dataset[currentLevel].question property string instruction: dataset[currentLevel].instruction property string questionValue property int currentLevel: 0 property int numberOfLevel: dataset.length property var dataset: [ { values: ['I', 'V', 'X', 'L', 'C', 'D', 'M'], instruction: qsTr("The roman numbers are all built out of these 7 numbers:\nI and V (units, 1 and 5)\nX and L (tens, 10 and 50)\nC and D (hundreds, 100 and 500)\n and M (1000).\n An interesting observation here is that the roman numeric system lacks the number 0."), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { values: [1, 5, 10, 50, 100, 500, 1000], instruction: qsTr("The roman numbers are all built out of these 7 numbers:\nI and V (units, 1 and 5)\nX and L (tens, 10 and 50)\nC and D (hundreds, 100 and 500)\n and M (1000).\n An interesting observation here is that the roman numeric system lacks the number 0."), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'], instruction: qsTr("All the units except 4 and 9 are built using sums of I and V:\nI, II, III, V, VI, VII, VIII.\n The 4 and the 9 units are built using differences:\nIV (5 – 1) and IX (10 – 1)"), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { values: [2, 3, 4, 5, 6, 7, 8, 9], instruction: qsTr("All the units except 4 and 9 are built using sums of I and V:\nI, II, III, V, VI, VII, VIII.\n The 4 and the 9 units are built using differences:\nIV (5 – 1) and IX (10 – 1)"), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['XX', 'XXX', 'XL', 'LX', 'LXX', 'LXXX', 'XC'], instruction: qsTr("All the tens except 40 and 90 are built using sums of X and L:\nX, XX, XXX, L, LX, LXX, LXXX.\nThe 40 and the 90 tens are built using differences:\nXL (10 taken from 50) and XC (10 taken from 100)\n "), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { values: [20, 30, 40, 60, 70, 80, 90], instruction: qsTr("All the tens except 40 and 90 are built using sums of X and L:\nX, XX, XXX, L, LX, LXX, LXXX.\nThe 40 and the 90 tens are built using differences:\nXL (10 taken from 50) and XC (10 taken from 100)\n "), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['CC', 'CCC', 'CD', 'DC', 'DCC', 'DCCC', 'CM', ], instruction: qsTr("All the hundreds except 400 and 900 are built using sums of C and D:\nC, CC, CCC, D, DC, DCC, DCCC.\nThe 400 and the 900 hundreds are built using differences:\nCD (100 taken from 500) and CM (100 taken from 1000)"), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { values: [200, 300, 400, 600, 700, 800, 900], instruction: qsTr("All the hundreds except 400 and 900 are built using sums of C and D:\nC, CC, CCC, D, DC, DCC, DCCC.\nThe 400 and the 900 hundreds are built using differences:\nCD (100 taken from 500) and CM (100 taken from 1000)"), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['MM', 'MMM'], instruction: qsTr("Sums of M are used for building thousands: M, MM, MMM.\nNotice that you cannot join more than three identical symbols. The first implication of this rule is that you cannot use just sums for building all possible units, tens or hundreds, you must use differences too. On the other hand, it limits the maximum roman number to 3999 (MMMCMXCIX).\n"), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { values: [2000, 3000], instruction: qsTr("Sums of M are used for building thousands: M, MM, MMM.\nNotice that you cannot join more than three identical symbols. The first implication of this rule is that you cannot use just sums for building all possible units, tens or hundreds, you must use differences too. On the other hand, it limits the maximum roman number to 3999 (MMMCMXCIX).\n"), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 50 /* up to this number */ , 10 /* sublevels */], instruction: qsTr("Now you know the rules, you can read and write numbers in roman numerals."), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 100, 10], instruction: '', question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 500, 10], instruction: '', question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 1000, 10], instruction: '', question: qsTr("Convert the arabic number %1 in roman."), toArabic: false } ] onQuestionValueChanged: { textInput.text = '' romanConverter.arabic = 0 if(toArabic) keyboard.populateArabic() else keyboard.populateRoman() } function start() { if (!ApplicationInfo.isMobile) textInput.forceActiveFocus(); items.currentLevel = 0 initLevel() } function initLevel() { score.currentSubLevel = 1 initSubLevel() } function initSubLevel() { if(dataset[currentLevel].values[0] === '_random_') { questionValue = Math.round(Math.random() * dataset[currentLevel].values[1] + 1) score.numberOfSubLevels = dataset[currentLevel].values[2] } else { questionValue = dataset[currentLevel].values[score.currentSubLevel - 1] score.numberOfSubLevels = dataset[currentLevel].values.length } } function nextLevel() { if(numberOfLevel - 1 == currentLevel ) { currentLevel = 0 } else { currentLevel++ } initLevel(); } function previousLevel() { if(currentLevel == 0) { currentLevel = numberOfLevel - 1 } else { currentLevel-- } initLevel(); } function nextSubLevel() { if(++score.currentSubLevel > score.numberOfSubLevels) { nextLevel() } else { initSubLevel(); } } property bool isChecking: false function check() { if(isChecking) { return } isChecking = true if(feedback.value == items.questionValue) { bonus.good('tux') } else { - bonus.bad('tux') + bonus.bad('tux', bonus.checkAnswer) } } } onStart: { items.start() } onStop: { } Keys.onPressed: { if ((event.key === Qt.Key_Enter) || (event.key === Qt.Key_Return)) { items.check() } } QtObject { id: romanConverter property int arabic property string roman // conversion code copied from: // http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter function arabic2Roman(num) { if (!+num) return ''; var digits = String(+num).split(""), key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM", "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC", "","I","II","III","IV","V","VI","VII","VIII","IX"], roman = "", i = 3; while (i--) roman = (key[+digits.pop() + (i * 10)] || "") + roman; return new Array(+digits.join("") + 1).join("M") + roman; } function roman2Arabic(str) { var str = str.toUpperCase(), validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/, token = /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, key = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1}, num = 0, m; if (!(str && validator.test(str))) return false; while (m = token.exec(str)) num += key[m[0]]; return num; } onArabicChanged: roman = arabic2Roman(arabic) onRomanChanged: arabic = roman2Arabic(roman) } Column { id: column anchors.fill: parent anchors.topMargin: 10 GCText { id: questionLabel anchors.horizontalCenter: parent.horizontalCenter wrapMode: TextEdit.WordWrap text: items.questionValue ? items.questionText.arg(items.questionValue) : '' color: 'white' width: parent.width * 0.9 horizontalAlignment: Text.AlignHCenter Rectangle { z: -1 border.color: 'black' border.width: 1 anchors.centerIn: parent width: parent.width * 1.1 height: parent.height opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 } } Item { // Just a margin width: 1 height: 5 * ApplicationInfo.ratio } TextInput { id: textInput x: parent.width / 2 width: 60 * ApplicationInfo.ratio color: 'white' text: '' maximumLength: items.toArabic ? ('' + romanConverter.roman2Arabic(items.questionValue)).length + 1 : romanConverter.arabic2Roman(items.questionValue).length + 1 horizontalAlignment: Text.AlignHCenter verticalAlignment: TextInput.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter font.pointSize: questionLabel.pointSize font.weight: Font.DemiBold font.family: GCSingletonFontLoader.fontLoader.name font.capitalization: ApplicationSettings.fontCapitalization font.letterSpacing: ApplicationSettings.fontLetterSpacing cursorVisible: true validator: RegExpValidator{regExp: items.toArabic ? /[0-9]+/ : /[ivxlcdmIVXLCDM]*/} onTextChanged: if(text) { text = text.toUpperCase(); if(items.toArabic) romanConverter.arabic = parseInt(text) else romanConverter.roman = text } Rectangle { z: -1 opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 border.color: 'black' border.width: 1 anchors.horizontalCenter: parent.horizontalCenter width: column.width * 0.7 height: parent.height } function appendText(car) { if(car === keyboard.backspace) { if(text && cursorPosition > 0) { var oldPos = cursorPosition text = text.substring(0, cursorPosition - 1) + text.substring(cursorPosition) cursorPosition = oldPos - 1 } return } var oldPos = cursorPosition text = text.substring(0, cursorPosition) + car + text.substring(cursorPosition) cursorPosition = oldPos + 1 } } Item { // Just a margin width: 1 height: 5 * ApplicationInfo.ratio } GCText { id: feedback anchors.horizontalCenter: parent.horizontalCenter text: items.toArabic ? qsTr("Roman value: %1").arg(value) : qsTr('Arabic value: %1').arg(value) color: 'white' Rectangle { z: -1 opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 border.color: 'black' border.width: 1 anchors.centerIn: parent width: parent.width * 1.1 height: parent.height } property string value: items.toArabic ? romanConverter.roman : romanConverter.arabic ? romanConverter.arabic : '' } Item { // Just a margin width: 1 height: 5 * ApplicationInfo.ratio } Rectangle { color: "transparent" width: parent.width height: (background.height - (y + bar.height + okButton.height + keyboard.height) * 1.1 ) Flickable { width: parent.width height: parent.height contentWidth: parent.width contentHeight: instructionContainer.height anchors.fill: parent flickableDirection: Flickable.VerticalFlick clip: true GCText { id: instruction visible: items.instruction != '' wrapMode: TextEdit.WordWrap fontSize: tinySize anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.95 text: items.instruction horizontalAlignment: Text.AlignHCenter color: 'white' Rectangle { id: instructionContainer z: -1 opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 border.color: 'black' border.width: 1 anchors.centerIn: parent width: parent.width height: parent.contentHeight } } } } } Score { id: score anchors.bottom: bar.top currentSubLevel: 0 numberOfSubLevels: 1 } DialogHelp { id: dialogHelp onClose: home() } VirtualKeyboard { id: keyboard anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter function populateArabic() { layout = [ [ { label: "0" }, { label: "1" }, { label: "2" }, { label: "3" }, { label: "4" }, { label: "5" }, { label: "6" }, { label: "7" }, { label: "8" }, { label: "9" }, { label: keyboard.backspace } ] ] } function populateRoman() { layout = [ [ { label: "I" }, { label: "V" }, { label: "X" }, { label: "L" }, { label: "C" }, { label: "D" }, { label: "M" }, { label: keyboard.backspace } ] ] } onKeypress: textInput.appendText(text) onError: console.log("VirtualKeyboard error: " + msg); } Bar { id: bar anchors.bottom: keyboard.top content: BarEnumContent { value: help | home | level | hint } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: items.previousLevel() onNextLevelClicked: items.nextLevel() onHomeClicked: activity.home() level: items.currentLevel + 1 onHintClicked: feedback.visible = !feedback.visible } BarButton { id: okButton source: "qrc:/gcompris/src/core/resource/bar_ok.svg"; sourceSize.width: 60 * ApplicationInfo.ratio visible: true anchors { verticalCenter: score.verticalCenter right: score.left rightMargin: 10 * ApplicationInfo.ratio bottomMargin: 10 * ApplicationInfo.ratio } onClicked: items.check() } Bonus { id: bonus Component.onCompleted: win.connect(items.nextSubLevel) onWin: items.isChecking = false onLoose: items.isChecking = false } } } diff --git a/src/core/Bonus.qml b/src/core/Bonus.qml index 51add58ae..8a9117279 100644 --- a/src/core/Bonus.qml +++ b/src/core/Bonus.qml @@ -1,204 +1,222 @@ /* GCompris - Bonus.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 GCompris 1.0 // Requires the global property in the scope: // property GCAudio audioEffects, audioVoices /** * A QML component providing user feedback upon winning/loosing. * @ingroup components * * Usually triggered by an activity when a user has won/lost a level via the * @ref good / @ref bad methods. Bonus then provides visual and auditive * feedback to the user and emits the @ref win / @ref loose signals when * finished. * - * Maintains a list of possible audio voice resources to be playebd back + * Maintains a list of possible audio voice resources to be played back * upon winning/loosing events, and selects randomly from them when triggered. * * @inherit QtQuick.Image */ Image { id: bonus /** * type:string * Url of the audio resource to be used as winning sound. */ property string winSound: url + "sounds/bonus.wav" /** * type:string * Url of the audio resource to be used as loosing sound. */ property string looseSound /** * type:int * Interval in milliseconds after which the bonus will be played (default is 500ms) */ property alias interval: timer.interval /** * Emitted when the bonus starts */ signal start /** * Emitted when the bonus stops */ signal stop /** * Emitted when the win event is over. * * After the animation has finished. */ signal win /** * Emitted when the loose event is over. * * After the animation has finished. */ signal loose /// @cond INTERNAL_DOCS property string url: "qrc:/gcompris/src/core/resource/" property bool isWin: false property var winVoices: [ "voices-$CA/$LOCALE/misc/congratulation.$CA", "voices-$CA/$LOCALE/misc/great.$CA", "voices-$CA/$LOCALE/misc/good.$CA", "voices-$CA/$LOCALE/misc/awesome.$CA", "voices-$CA/$LOCALE/misc/fantastic.$CA", "voices-$CA/$LOCALE/misc/waytogo.$CA", "voices-$CA/$LOCALE/misc/super.$CA", "voices-$CA/$LOCALE/misc/perfect.$CA" ] property var looseVoices: [ + "voices-$CA/$LOCALE/misc/try_again.$CA", "voices-$CA/$LOCALE/misc/check_answer.$CA" ] + readonly property int tryAgain: 0 + readonly property int checkAnswer: 1 /// @endcond + property int nextAudioIndex: 0 /** * type:bool * True between the moment we have the win/lose signal emitted and the * bonus image is no more displayed */ property bool isPlaying: animation.running || timer.running visible: true opacity: 0 anchors.fill: parent anchors.margins: 50 * ApplicationInfo.ratio fillMode: Image.PreserveAspectFit z: 1000 sourceSize.width: parent.width * 0.5 /** * Triggers win feedback. * * Tries to play back a voice resource for winning, if not found fall back * to the winSound. * * @param name Type of win image to show. * Possible values are "flower", "gnu", "lion", "note", "smiley", "tux" */ function good(name) { source = url + "bonus/" + name + "_good.svg" isWin = true timer.start() } /** * Triggers loose feedback. * * Tries to play back a voice resource for loosing, if not found fall back * to the looseSound. * * @param name Type of win image to show. * Possible values are "flower", "gnu", "lion", "note", "smiley", "tux" */ - function bad(name) { + function bad(name, audioIndex) { source = url + "bonus/" + name + "_bad.svg" isWin = false; timer.start() + if(audioIndex) { + nextAudioIndex = audioIndex + } + else { + nextAudioIndex = bonus.tryAgain + } } /** * Private: Triggers win feedback after the timer completion */ function _good() { if(!audioVoices.play( ApplicationInfo.getAudioFilePath( winVoices[Math.floor(Math.random()*winVoices.length)]))) if(winSound) audioEffects.play(winSound) start() animation.start() } /** * Private: Triggers loose feedback after the timer completion. */ function _bad(name) { - if(!audioVoices.play( - ApplicationInfo.getAudioFilePath( - looseVoices[Math.floor(Math.random()*looseVoices.length)]))) + var audio = ApplicationInfo.getAudioFilePath( + looseVoices[nextAudioIndex]) + // Defaults to "check answer" if required audio does not exist + if(!file.exists(audio)) { + audio = ApplicationInfo.getAudioFilePath( + looseVoices[bonus.checkAnswer]) + } + if(!audioVoices.play(audio)) if(looseSound) audioEffects.play(looseSound) start() animation.start() } SequentialAnimation { id: animation NumberAnimation { target: bonus property: "opacity" from: 0; to: 1.0 duration: 1000 easing.type: Easing.InOutQuad } NumberAnimation { target: bonus property: "opacity" from: 1.0; to: 0 duration: 500 easing.type: Easing.InOutQuad } onStopped: { bonus.stop() isWin ? win() : loose() } } + File { + id: file + } // It is useful to launch the bonus after a delay to let the children // appreciate the completed level Timer { id: timer interval: 500 onTriggered: isWin ? bonus._good() : bonus._bad() } }