Warning, /education/gcompris/src/core/Bonus.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - Bonus.qml 0002 * 0003 * SPDX-FileCopyrightText: 2014 Bruno Coudoin <bruno.coudoin@gcompris.net> 0004 * 0005 * Authors: 0006 * Bruno Coudoin <bruno.coudoin@gcompris.net> 0007 * 0008 * SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 import QtQuick 2.12 0011 import GCompris 1.0 0012 0013 // Requires the global property in the scope: 0014 // property GCAudio audioEffects, audioVoices 0015 0016 /** 0017 * A QML component providing user feedback upon winning/loosing. 0018 * @ingroup components 0019 * 0020 * Usually triggered by an activity when a user has won/lost a level via the 0021 * @ref good / @ref bad methods. Bonus then provides visual and auditive 0022 * feedback to the user and emits the @ref win / @ref loose signals when 0023 * finished. 0024 * 0025 * Maintains a list of possible audio voice resources to be played back 0026 * upon winning/loosing events, and selects randomly from them when triggered. 0027 * 0028 * @inherit QtQuick.Image 0029 */ 0030 Image { 0031 id: bonus 0032 0033 /** 0034 * type:string 0035 * Url of the audio resource to be used as winning sound. 0036 */ 0037 property string winSound: url + "sounds/bonus.wav" 0038 0039 /** 0040 * type:string 0041 * Url of the audio resource to be used as loosing sound. 0042 */ 0043 property string looseSound 0044 0045 /** 0046 * type:int 0047 * Interval in milliseconds after which the bonus will be played (default is 500ms) 0048 */ 0049 property alias interval: timer.interval 0050 0051 /** 0052 * Emitted when the bonus starts 0053 */ 0054 signal start 0055 0056 /** 0057 * Emitted when the bonus stops 0058 */ 0059 signal stop 0060 0061 /** 0062 * Emitted when the win event is over. 0063 * 0064 * After the animation has finished. 0065 */ 0066 signal win 0067 0068 /** 0069 * Emitted when the loose event is over. 0070 * 0071 * After the animation has finished. 0072 */ 0073 signal loose 0074 0075 Connections { 0076 target: activity 0077 onStop: haltBonus(); 0078 } 0079 0080 /// @cond INTERNAL_DOCS 0081 property string url: "qrc:/gcompris/src/core/resource/" 0082 property bool isWin: false 0083 property var winVoices: [ 0084 "voices-$CA/$LOCALE/misc/congratulation.$CA", 0085 "voices-$CA/$LOCALE/misc/great.$CA", 0086 "voices-$CA/$LOCALE/misc/good.$CA", 0087 "voices-$CA/$LOCALE/misc/awesome.$CA", 0088 "voices-$CA/$LOCALE/misc/fantastic.$CA", 0089 "voices-$CA/$LOCALE/misc/waytogo.$CA", 0090 "voices-$CA/$LOCALE/misc/super.$CA", 0091 "voices-$CA/$LOCALE/misc/perfect.$CA" 0092 ] 0093 property var looseVoices: [ 0094 "voices-$CA/$LOCALE/misc/try_again.$CA", 0095 "voices-$CA/$LOCALE/misc/check_answer.$CA" 0096 ] 0097 readonly property int tryAgain: 0 0098 readonly property int checkAnswer: 1 0099 /// @endcond 0100 0101 property int nextAudioIndex: 0 0102 /** 0103 * type:bool 0104 * True between the moment we have the win/lose signal emitted and the 0105 * bonus image is no more displayed 0106 */ 0107 property bool isPlaying: animation.running || timer.running 0108 visible: true 0109 opacity: 0 0110 anchors.fill: parent 0111 anchors.margins: 50 * ApplicationInfo.ratio 0112 fillMode: Image.PreserveAspectFit 0113 z: 1000 0114 sourceSize.width: parent.width * 0.5 0115 0116 /** 0117 * type:bool 0118 * used in animation onStopped to check if we need to trigger win-loose signals 0119 */ 0120 property bool bonusStopped: false 0121 0122 /** 0123 * Use this when stopping manually the bonus, like when changing level manually 0124 */ 0125 function haltBonus() { 0126 bonusStopped = true; 0127 timer.stop(); 0128 animation.stop(); 0129 bonus.opacity = 0; 0130 bonusStopped = false; 0131 } 0132 0133 /** 0134 * Triggers win feedback. 0135 * 0136 * Tries to play back a voice resource for winning, if not found fall back 0137 * to the winSound. 0138 * 0139 * @param name Type of win image to show. 0140 * Possible values are "flower", "gnu", "lion", "note", "smiley", "tux" 0141 */ 0142 function good(name) { 0143 source = url + "bonus/" + name + "_good.svg" 0144 isWin = true 0145 timer.restart() 0146 } 0147 0148 /** 0149 * Triggers loose feedback. 0150 * 0151 * Tries to play back a voice resource for loosing, if not found fall back 0152 * to the looseSound. 0153 * 0154 * @param name Type of win image to show. 0155 * Possible values are "flower", "gnu", "lion", "note", "smiley", "tux" 0156 */ 0157 function bad(name, audioIndex) { 0158 source = url + "bonus/" + name + "_bad.svg" 0159 isWin = false; 0160 timer.restart() 0161 if(audioIndex) { 0162 nextAudioIndex = audioIndex 0163 } 0164 else { 0165 nextAudioIndex = bonus.tryAgain 0166 } 0167 } 0168 0169 /** 0170 * Private: Triggers win feedback after the timer completion 0171 */ 0172 function _good() { 0173 if(!audioVoices.play( 0174 ApplicationInfo.getAudioFilePath( 0175 winVoices[Math.floor(Math.random()*winVoices.length)]))) 0176 if(winSound) 0177 audioEffects.play(winSound) 0178 0179 start() 0180 animation.start() 0181 } 0182 0183 /** 0184 * Private: Triggers loose feedback after the timer completion. 0185 */ 0186 function _bad(name) { 0187 var audio = ApplicationInfo.getAudioFilePath( 0188 looseVoices[nextAudioIndex]) 0189 // Defaults to "check answer" if required audio does not exist 0190 if(!file.exists(audio)) { 0191 audio = ApplicationInfo.getAudioFilePath( 0192 looseVoices[bonus.checkAnswer]) 0193 } 0194 if(!audioVoices.play(audio)) 0195 if(looseSound) 0196 audioEffects.play(looseSound) 0197 start() 0198 animation.start() 0199 } 0200 0201 SequentialAnimation { 0202 id: animation 0203 NumberAnimation { 0204 target: bonus 0205 property: "opacity" 0206 from: 0; to: 1.0 0207 duration: 1000 0208 easing.type: Easing.InOutQuad 0209 } 0210 NumberAnimation { 0211 target: bonus 0212 property: "opacity" 0213 from: 1.0; to: 0 0214 duration: 500 0215 easing.type: Easing.InOutQuad 0216 } 0217 onStopped: { 0218 bonus.stop(); 0219 if(!bonusStopped) 0220 isWin ? win() : loose(); 0221 } 0222 } 0223 0224 File { 0225 id: file 0226 } 0227 // It is useful to launch the bonus after a delay to let the children 0228 // appreciate the completed level 0229 Timer { 0230 id: timer 0231 interval: 500 0232 onTriggered: isWin ? bonus._good() : bonus._bad() 0233 } 0234 }