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 }