Merge lp:~ken-vandine/dropping-letters/toolbar into lp:dropping-letters

Proposed by Ken VanDine
Status: Merged
Approved by: Alan Pope 🍺🐧🐱 πŸ¦„
Approved revision: 29
Merged at revision: 24
Proposed branch: lp:~ken-vandine/dropping-letters/toolbar
Merge into: lp:dropping-letters
Diff against target: 1291 lines (+608/-602)
5 files modified
debian/changelog (+11/-1)
debian/control (+2/-2)
debian/install (+2/-1)
dropping-letters.qml (+592/-597)
dropping-letters.qmlproject (+1/-1)
To merge this branch: bzr merge lp:~ken-vandine/dropping-letters/toolbar
Reviewer Review Type Date Requested Status
Alan Pope 🍺🐧🐱 πŸ¦„ (community) Approve
Review via email: mp+162706@code.launchpad.net

Commit message

* Use a toolbar for volume and new game instead of the small items on the
  bottombar
* Add HUD support

Description of the change

* Use a toolbar for volume and new game instead of the small items on the
  bottombar
* Add HUD support

To post a comment you must log in.
28. By Ken VanDine

Use a toolbar for volume and new game instead of the small items on the
bottombar

29. By Ken VanDine

added New game to HUD

Revision history for this message
Alan Pope 🍺🐧🐱 πŸ¦„ (popey) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/changelog'
--- debian/changelog 2013-05-01 05:01:59 +0000
+++ debian/changelog 2013-05-07 05:11:28 +0000
@@ -1,4 +1,14 @@
1dropping-letters (0.1) UNRELEASED; urgency=low1dropping-letters (0.1.1) UNRELEASED; urgency=low
2
3 * Use a toolbar for volume and new game instead of the small items on the
4 bottombar
5 * Add HUD support
6 * debian/control
7 - depend on qtdeclarative5-hud1.0
8
9 -- Ken VanDine <ken.vandine@canonical.com> Wed, 01 May 2013 18:00:42 -0700
10
11dropping-letters (0.1) raring; urgency=low
212
3 * Initial Release.13 * Initial Release.
414
515
=== modified file 'debian/control'
--- debian/control 2013-05-01 05:01:59 +0000
+++ debian/control 2013-05-07 05:11:28 +0000
@@ -5,11 +5,11 @@
5Build-Depends: debhelper (>= 9.0.0)5Build-Depends: debhelper (>= 9.0.0)
6Standards-Version: 3.9.46Standards-Version: 3.9.4
7Homepage: http://launchpad.net/dropping-letters7Homepage: http://launchpad.net/dropping-letters
8Vcs-Bzr: https://code.launchpad.net/~sil/dropping-letters8Vcs-Bzr: https://code.launchpad.net/~dropping-letters-devs/dropping-letters/trunk
99
10Package: dropping-letters10Package: dropping-letters
11Architecture: all11Architecture: all
12Depends: ${misc:Depends},qtdeclarative5-localstorage-plugin,qtdeclarative5-particles-plugin,qtdeclarative5-qtmultimedia-plugin,qtdeclarative5-qtquick2-plugin,qtdeclarative5-ubuntu-ui-toolkit-plugin|qt-components-ubuntu,qtdeclarative5-window-plugin,qtdeclarative5-xmllistmodel-plugin,qmlscene12Depends: ${misc:Depends},qtdeclarative5-hud1.0,qtdeclarative5-localstorage-plugin,qtdeclarative5-particles-plugin,qtdeclarative5-qtmultimedia-plugin,qtdeclarative5-qtquick2-plugin,qtdeclarative5-ubuntu-ui-toolkit-plugin|qt-components-ubuntu,qtdeclarative5-window-plugin,qtdeclarative5-xmllistmodel-plugin,qmlscene
13Description: Dropping Letters game13Description: Dropping Letters game
14 Build up words from falling letters to score14 Build up words from falling letters to score
15 points. Don't let the letters build up to the15 points. Don't let the letters build up to the
1616
=== modified file 'debian/install'
--- debian/install 2013-05-01 05:01:59 +0000
+++ debian/install 2013-05-07 05:11:28 +0000
@@ -1,5 +1,6 @@
1dropping-letters-ubuntu.qml /usr/share/dropping-letters1dropping-letters.qml /usr/share/dropping-letters
2volume-sprite.png /usr/share/dropping-letters2volume-sprite.png /usr/share/dropping-letters
3new.png /usr/share/dropping-letters
3test_load_binarydict.js /usr/share/dropping-letters4test_load_binarydict.js /usr/share/dropping-letters
480921__justinbw__buttonchime02up.ogg /usr/share/dropping-letters580921__justinbw__buttonchime02up.ogg /usr/share/dropping-letters
5audio-volume-high.svg /usr/share/dropping-letters6audio-volume-high.svg /usr/share/dropping-letters
67
=== renamed file 'dropping-letters-ubuntu.qml' => 'dropping-letters.qml'
--- dropping-letters-ubuntu.qml 2013-05-01 06:10:15 +0000
+++ dropping-letters.qml 2013-05-07 05:11:28 +0000
@@ -5,6 +5,7 @@
5import QtQuick.LocalStorage 2.05import QtQuick.LocalStorage 2.0
6import Ubuntu.Components 0.16import Ubuntu.Components 0.1
7import Ubuntu.Components.Popups 0.17import Ubuntu.Components.Popups 0.1
8import Ubuntu.HUD 1.0 as HUD
89
9MainView {10MainView {
10 id: mainView11 id: mainView
@@ -12,617 +13,598 @@
12 width: units.gu(45) // 48 * 7 + 2 * 7 + 213 width: units.gu(45) // 48 * 7 + 2 * 7 + 2
13 height: units.gu(65) // 48 * 10 + 2 * 10 + 5014 height: units.gu(65) // 48 * 10 + 2 * 10 + 50
1415
15 Flipable {16
16 id: flipable17 function resetGame () {
17 property bool flipped: false18 lm.clear();
18 property variant db: null19 for (var i=0; i<7; i++) {
19 anchors.fill: parent20 lm.append({'letters': [] });
2021 }
21 transform: Rotation {22 accum.text = "";
22 id: rotation23 droptimer.tickCount = 0;
23 origin.x: flipable.width/224 flipable.flipped = true;
24 origin.y: flipable.height/225 }
25 axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis26
26 angle: 0 // the default angle27 Page {
27 }28 tools: ToolbarActions {
28 property int minChromeHeight: 5029 Action {
2930 text: i18n.tr("Volume")
30 Component.onCompleted: {31 iconSource: (volume.audible ? "audio-volume-high-symbolic.svg" : "audio-volume-muted-symbolic.svg")
31 var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);32 onTriggered: {
32 db.transaction(function(tx) {33 volume.audible = !volume.audible;
33 // Create the database if it doesn't already exist34 }
34 tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(score INT, time TEXT)');35 }
35 var res = tx.executeSql('SELECT score from Scores order by score DESC LIMIT 1');36 Action {
36 if (res.rows.length === 0) {37 text: i18n.tr("New game")
37 bestscore.updateScore(0);38 iconSource: ("new.png")
38 } else {39 onTriggered: {
39 bestscore.updateScore(res.rows.item(0).score);40 resetGame();
40 }41 }
41 });42 }
42 }43 }
4344
44 states: [45 Item {
45 State {46 id: volume
46 name: "back"47 property int frame: 0
47 PropertyChanges { target: rotation; angle: 180 }48 property bool reverse: false
48 when: flipable.flipped49 property bool audible: true
49 },50 }
50 State {51
51 name: "front"52 Flipable {
52 when: !flipable.flipped53 id: flipable
53 }54 property bool flipped: false
54 ]55 property variant db: null
5556 anchors.fill: parent
56 transitions: [57
57 Transition {58 transform: Rotation {
58 NumberAnimation { target: rotation; property: "angle"; duration: 200 }59 id: rotation
59 }60 origin.x: flipable.width/2
60 ]61 origin.y: flipable.height/2
6162 axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
62 front: Rectangle {63 angle: 0 // the default angle
63 id: entry64 }
64 color: "#efefea"65 property int minChromeHeight: 50
65 width: flipable.width // 48 * 7 + 2 * 7 + 266
66 height: flipable.height // 48 * 10 + 2 * 10 + 5067 Component.onCompleted: {
6768 var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
68 Column {69 db.transaction(function(tx) {
69 id: logo70 // Create the database if it doesn't already exist
70 anchors.centerIn: parent71 tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(score INT, time TEXT)');
71 //spacing: units.dp(2) // 272 var res = tx.executeSql('SELECT score from Scores order by score DESC LIMIT 1');
72 Repeater {73 if (res.rows.length === 0) {
73 id: titleRowRepeater74 bestscore.updateScore(0);
74 model: ["DROP", "PING", "LETT", "ERS "]75 } else {
75 Row {76 bestscore.updateScore(res.rows.item(0).score);
76 id: titleRow77 }
77 //spacing: units.dp(2) // 278 });
78 property int lineIndex: index79 }
79 property string modelString: modelData80
80 Repeater {81 states: [
81 id: titleColRepeater82 State {
82 model: modelData.split("")83 name: "back"
83 Rectangle {84 PropertyChanges { target: rotation; angle: 180 }
84 id: titleletter85 when: flipable.flipped
85 width: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing86 },
86 height: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing87 State {
87 color: Qt.hsla(210/360, 0.84, 0.25 + ((4-parent.lineIndex) * 0.05) + ((4-index) * 0.05));88 name: "front"
88 //radius: "medium"89 when: !flipable.flipped
89 Label {90 }
90 text: modelData91 ]
91 anchors.centerIn: parent92
92 fontSize: "x-large"93 transitions: [
93 font.bold: true94 Transition {
94 color: "#ffffff"95 NumberAnimation { target: rotation; property: "angle"; duration: 200 }
95 }96 }
96 states: [97 ]
97 State { name: "showing"; when: flipable.state == "front"98
98 PropertyChanges { target: titleletter; y: 0 }99 front: Rectangle {
99 },100 id: entry
100 State { name: "notshowing"; when: flipable.state != "front"
101 PropertyChanges { target: titleletter; y: -units.gu(300) }
102 }
103 ]
104 Behavior on y {
105 SequentialAnimation {
106 PauseAnimation {
107 duration: (35 * parent.modelString.length * (4-parent.lineIndex)) + (
108 35 * index) }
109 NumberAnimation { easing.type: Easing.OutQuart }
110 }
111 }
112 }
113 }
114 }
115 }
116 }
117 Label {
118 id: playbutton
119 text: "play"
120 anchors.top: logo.bottom
121 anchors.topMargin: units.gu(2)
122 anchors.horizontalCenter: logo.horizontalCenter
123 fontSize: "x-large"
124 color: "#222222"
125 }
126 Label {
127 id: bestscore
128 property int bestsofar: 0
129 text: "..."
130 anchors.top: playbutton.bottom
131 anchors.topMargin: units.gu(2)
132 anchors.horizontalCenter: logo.horizontalCenter
133 fontSize: "large"
134 color: "#222222"
135 function updateScore(score) {
136 if (score >= bestscore.bestsofar) {
137 bestscore.text = "Best score: " + score;
138 bestscore.bestsofar = score;
139 }
140 }
141 }
142 MouseArea {
143 anchors.fill: parent
144 enabled: !flipable.flipped
145 onClicked: {
146 lm.clear();
147 for (var i=0; i<7; i++) {
148 lm.append({'letters': [] });
149 }
150 accum.text = "";
151 droptimer.tickCount = 0;
152 flipable.flipped = true
153 }
154 }
155
156 Rectangle {
157 id: helpbutton
158 width: units.gu(5)
159 height: units.gu(5)
160 radius: units.gu(1)
161 Label {
162 text: "?"
163 anchors.centerIn: parent
164 }
165 anchors.top: parent.top
166 anchors.right: parent.right
167 anchors.margins: units.gu(2)
168 MouseArea {
169 anchors.fill: parent
170 onClicked: {
171 PopupUtils.open(helppop, helpbutton)
172 }
173 }
174 }
175 Component {
176 id: helppop
177 Popover {
178 id: helpsheet
179 Rectangle {
180 anchors.top: parent.top
181 anchors.left: parent.left
182 anchors.right: parent.right
183 height: units.gu(15)
184 border.color: "#ddd"
185 radius: 2
186 Label {
187 anchors.fill: parent
188 anchors.margins: units.gu(1)
189 text: "game by <a href='http://twitter.com/sil'>@sil</a><br>music: <a href='http://incompetech.com/music/royalty-free/index.html?isrc=USUAN1200076'>Easy Lemon</a> from Kevin McCloud (incompetech.com)<br>sounds: freesound from <a href='http://www.freesound.org/people/kantouth/sounds/106727/'>kantouth</a> / <a href='http://www.freesound.org/people/tictacshutup/sounds/407/'>tictacshutup</a> / <a href='http://www.freesound.org/people/dj-chronos/sounds/45137/'>dj-chronos</a> / <a href='http://www.freesound.org/people/justinbw/sounds/80921/'>justinbw</a>"
190 wrapMode: Text.WordWrap
191 textFormat: Text.StyledText
192 onLinkActivated: Qt.openUrlExternally(link)
193 }
194 }
195 }
196 }
197
198 }
199
200 back: Rectangle {
201 id: main
202 color: "#58585A"
203 width: flipable.width // 48 * 7 + 2 * 7 + 2
204 height: flipable.height // 48 * 10 + 2 * 10 + 50
205 property var selectedItems: []
206 /*
207 obtained with:
208 import re; fp=open('/usr/share/dict/words');
209 shorts=[re.sub('[^a-zA-Z]','',x.lower().strip()) for x in fp if len(x)<7];
210 from collections import Counter; c=Counter(''.join(shorts)); least=c.most_common()[-1][1];
211 print dict([(x.upper(),c[x]/least) for x in c])
212 */
213 property variant letterFreqs: {
214 'A': 66, 'C': 22, 'B': 19, 'E': 72, 'D': 28, 'G': 18, 'F': 12, 'I': 41, 'H': 20,
215 'K': 14, 'J': 4, 'M': 22, 'L': 40, 'O': 48, 'N': 35, 'Q': 1, 'P': 22, 'S': 75,
216 'R': 43, 'U': 26, 'T': 37, 'W': 13, 'V': 7, 'Y': 19, 'X': 3, 'Z': 4
217 }
218 property variant letterScores: {
219 "A":1,"B":3,"C":3,"D":2,"E":1,"F":4,"G":2,"H":4,"I":1,"J":8,"K":5,"L":1,
220 "M":3,"N":1,"O":1,"P":3,"Q":10,"R":1,"S":1,"T":1,"U":1,"V":4,"W":4,"X":8,
221 "Y":4,"Z":10
222 }
223 property int score: 0
224
225 function getRandomWeightedLetter() {
226 // could work out sumOfWeights once, but this is easier to understand
227 var sumOfWeights = 0;
228 for (var k in main.letterFreqs) { sumOfWeights += main.letterFreqs[k]; }
229 var selection = Math.floor(Math.random() * sumOfWeights);
230 for (var k in main.letterFreqs) {
231 if (selection < main.letterFreqs[k]) return k;
232 selection -= main.letterFreqs[k];
233 }
234 }
235
236 Audio {
237 id: music
238 source: "Easy_Lemon_60_second.ogg"
239 autoPlay: true
240 onStopped: music.play()
241 muted: !volume.audible
242 }
243
244 Audio {
245 id: click
246 source: "407__tictacshutup__click-1-off-click.ogg"
247 autoLoad: true
248 muted: !volume.audible
249 }
250
251 Audio {
252 id: success
253 source: "80921__justinbw__buttonchime02up.ogg"
254 autoLoad: true
255 muted: !volume.audible
256 }
257
258 Audio {
259 id: failure
260 source: "106727__kantouth__cartoon-bing-low.ogg"
261 autoLoad: true
262 muted: !volume.audible
263 }
264
265 Audio {
266 id: gameover
267 source: "45137__dj-chronos__dark-church-bell.ogg"
268 autoLoad: true
269 onStopped: {
270 flipable.flipped = false
271 }
272 muted: !volume.audible
273 }
274
275 Rectangle {
276 id: bottombar
277 anchors.bottom: main.bottom
278 anchors.left: main.left
279 anchors.right: main.right
280 height: (main.height - (game.squaresize * 10 + 11)) / 2
281 z: 2
282 color: Qt.hsla(210/360, 0.84, 0.2)
283
284 Rectangle {
285 id: mainscore
286 anchors.right: quitgame.left
287 anchors.top: bottombar.top
288 anchors.bottom: bottombar.bottom
289 anchors.left: volume.right
290 z: 2
291 color: Qt.rgba(0,0,0,0)
292 Label {
293 anchors.centerIn: parent
294 fontSize: "x-large"
295 text: "" + main.score
296 color: "white"
297 }
298 }
299
300 Rectangle {
301 id: quitgame
302 width: bottombar.height
303 height: bottombar.height
304 color: Qt.rgba(0,0,0,0)
305 anchors.right: bottombar.right
306 anchors.top: bottombar.top
307 z: 3
308
309 Label {
310 anchors.centerIn: parent
311 text: "\u00d7"
312 fontSize: "x-large"
313 color: "white"
314 MouseArea {
315 anchors.fill: parent
316 onClicked: {
317 flipable.flipped = false;
318 }
319 }
320 }
321 }
322
323 Rectangle {
324 id: volume
325 width: bottombar.height
326 height: bottombar.height + 1
327 color: Qt.rgba(0,0,0,0)
328 anchors.left: bottombar.left
329 anchors.bottom: bottombar.bottom
330 z: 3
331 clip: true
332 property int frame: 0
333 property bool reverse: false
334 property bool audible: true
335 Image {
336 id: image
337 sourceSize.width: parent.height * 0.8
338 sourceSize.height: parent.height * 0.8
339 anchors.centerIn: parent
340 source: volume.audible ? "audio-volume-high-symbolic.svg" : "audio-volume-muted-symbolic.svg";
341 }
342 MouseArea {
343 anchors.fill: parent
344 onClicked: {
345 volume.audible = !volume.audible
346 }
347 }
348 }
349 }
350
351 Rectangle {
352 id: topbar
353 color: "blue"
354 anchors.top: main.top
355 anchors.left: main.left
356 anchors.right: main.right
357 height: (main.height - (game.squaresize * 10 + 11)) / 2
358 z: 2
359
360 Rectangle {
361 anchors.fill: topbar
362 z: 2
363 color: accum.text == "" ? "#efefea" : (accum.isValid ? Qt.hsla(124/360, 0.83, 0.45) : Qt.hsla(0, 0.83, 0.65))
364 id: suggestedWordContainer
365 Label {
366 id: accum
367 anchors.centerIn: parent
368 text: ""
369 color: "white"
370 fontSize: "x-large"
371 font.bold: true
372 property bool isValid: false
373 function findBinaryWord( word ) {
374 var bd = Wordlist.wordlist;
375 var l = word.length;
376 // Don't search if there's nothing to look through
377 if ( !bd[l] ) {
378 return false;
379 }
380 // Get the number of words in the dictionary bin
381 var words = bd[l].length / l,
382 // The low point from where we're starting the binary search
383 low = 0,
384 // The max high point
385 high = words - 1,
386 // And the precise middle of the search
387 mid = Math.floor( words / 2 );
388 // We continue to look until we reach a final word
389 while ( high >= low ) {
390 // Grab the word at our current position
391 var found = bd[l].substr( l * mid, l );
392 // If we've found the word, stop now
393 if ( word === found ) {
394 return true;
395 }
396 // Otherwise, compare
397 // If we're too high, move lower
398 if ( word < found ) {
399 high = mid - 1;
400 // If we're too low, go higher
401 } else {
402 low = mid + 1;
403 }
404 // And find the new search point
405 mid = Math.floor( (low + high) / 2 );
406 }
407 // Nothing was found
408 return false;
409 }
410 onTextChanged: {
411 accum.isValid = findBinaryWord(accum.text);
412 }
413 }
414 function processSuggestedWord() {
415 if (accum.isValid) {
416 success.play();
417 var thisscore = 0, wordlength = accum.text.length;
418 accum.text = "";
419
420 // tell the boxes to destroy themselves
421 main.selectedItems.forEach(function(b) {
422 thisscore += main.letterScores[b.containedLetter];
423 b.state = "dead";
424 })
425 main.selectedItems = [];
426 main.score += thisscore * wordlength;
427 scoredisplay.text = "" + (thisscore * wordlength);
428 showscoredisplay.start();
429 } else {
430 failure.play();
431 accum.text = "";
432 main.selectedItems.forEach(function(b) { b.selected = false; })
433 main.selectedItems = [];
434 }
435 }
436 focus: true
437 Keys.onReturnPressed: {
438 suggestedWordContainer.processSuggestedWord();
439 }
440 MouseArea {
441 anchors.fill: parent
442 onClicked: { suggestedWordContainer.processSuggestedWord(); }
443 }
444 }
445
446
447 }
448
449 Label {
450 id: scoredisplay
451 anchors.centerIn: parent
452 z: 3
453 fontSize: "x-large"
454 text: "200"
455 color: "red"
456 opacity: 0
457 }
458
459 ParallelAnimation {
460 id: showscoredisplay
461 NumberAnimation {
462 property: "scale"
463 from: 0.1
464 to: 8.0
465 duration: 400
466 target: scoredisplay
467 }
468 SequentialAnimation {
469 NumberAnimation {
470 property: "opacity"
471 from: 0
472 to: 1.0
473 duration: 20
474 target: scoredisplay
475 }
476 NumberAnimation {
477 property: "opacity"
478 from: 1.0
479 to: 0
480 duration: 380
481 target: scoredisplay
482 }
483 }
484 }
485
486
487 Timer {
488 id: droptimer
489 repeat: true
490 running: flipable.flipped
491 interval: 2000
492 property int tickCount: 0
493 triggeredOnStart: true
494 onTriggered: {
495 tickCount += 5
496 droptimer.interval = 2000 - tickCount
497 var idx = Math.round(Math.random() * (lm.count - 1));
498 lm.get(idx).letters.append({ letter: main.getRandomWeightedLetter() });
499 if (lm.get(idx).letters.count >= 10) {
500 droptimer.stop();
501 gameover.play();
502 var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
503 db.transaction(function(tx) {
504 tx.executeSql("insert into Scores values (?,?)", [main.score, 0]);
505 bestscore.updateScore(main.score);
506 });
507 }
508 }
509 }
510
511 ListModel {
512 id: lm
513 }
514
515
516 Rectangle {
517 id: game
518 property int squaresize: Math.min((flipable.width) / 7, (flipable.height - (flipable.minChromeHeight * 2)) / 10)
519 anchors.top: topbar.bottom
520 anchors.bottom: bottombar.top
521 anchors.left: main.left
522 anchors.right: main.right
523 scale: -1
524 color: "#efefea"101 color: "#efefea"
525 Row {102 width: flipable.width // 48 * 7 + 2 * 7 + 2
526 anchors.horizontalCenter: game.horizontalCenter103 height: flipable.height // 48 * 10 + 2 * 10 + 50
527 anchors.top: game.top104
528 anchors.topMargin: 1105 Column {
529 //spacing: 1106 id: logo
107 anchors.centerIn: parent
108 //spacing: units.dp(2) // 2
530 Repeater {109 Repeater {
531 model: lm110 id: titleRowRepeater
532 Column {111 model: ["DROP", "PING", "LETT", "ERS "]
533 //spacing: 1112 Row {
534 property int idx: index113 id: titleRow
535 width: game.squaresize114 //spacing: units.dp(2) // 2
536 height: game.height115 property int lineIndex: index
537 add: Transition {116 property string modelString: modelData
538 NumberAnimation { properties: "y"; easing.type: Easing.OutBounce; duration: 1000 }
539 }
540 move: Transition {
541 NumberAnimation { properties: "y"; easing.type: Easing.OutBounce }
542 }
543 Repeater {117 Repeater {
544 model: letters118 id: titleColRepeater
119 model: modelData.split("")
545 Rectangle {120 Rectangle {
546 id: box121 id: titleletter
547 property bool selected: false122 width: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing
548 property int idx: index123 height: Math.max(flipable.width * 0.4, flipable.height * 0.4) / titleRowRepeater.count - titleRowRepeater.count * titleRow.spacing
549 property string containedLetter: letter124 color: Qt.hsla(210/360, 0.84, 0.25 + ((4-parent.lineIndex) * 0.05) + ((4-index) * 0.05));
550 color: {
551 if (lm.get(parent.idx).letters.count >= 10) {
552 return "red";
553 } else if (!selected) {
554 return Qt.hsla(210/360, 0.84, 0.25 + (idx * 0.05));
555 } else if (accum.isValid) {
556 return Qt.hsla(124/360, 0.83, 0.45); // "#93cc98";
557 } else {
558 return Qt.hsla(0, 0.83, 0.65); // "#cc9598"
559 }
560 }
561 //radius: "medium"125 //radius: "medium"
562 scale: -1
563 width: game.squaresize
564 height: game.squaresize
565 y: game.height + game.squaresize
566 z: 5
567 Label {126 Label {
127 text: modelData
568 anchors.centerIn: parent128 anchors.centerIn: parent
569 text: letter129 fontSize: "x-large"
570 fontSize: "large"
571 font.bold: true130 font.bold: true
572 color: "#ffffff"131 color: "#ffffff"
573 }132 }
574 MouseArea {133 states: [
575 anchors.fill: parent134 State { name: "showing"; when: flipable.state == "front"
576 onClicked: {135 PropertyChanges { target: titleletter; y: 0 }
577 if (!box.selected) {136 },
578 box.selected = true;137 State { name: "notshowing"; when: flipable.state != "front"
579 accum.text += letter;138 PropertyChanges { target: titleletter; y: -units.gu(300) }
580 click.play();139 }
581 main.selectedItems[main.selectedItems.length] = box;140 ]
141 Behavior on y {
142 SequentialAnimation {
143 PauseAnimation {
144 duration: (35 * parent.modelString.length * (4-parent.lineIndex)) + (
145 35 * index) }
146 NumberAnimation { easing.type: Easing.OutQuart }
147 }
148 }
149 }
150 }
151 }
152 }
153 }
154 Label {
155 id: playbutton
156 text: i18n.tr("Play")
157 anchors.top: logo.bottom
158 anchors.topMargin: units.gu(2)
159 anchors.horizontalCenter: logo.horizontalCenter
160 fontSize: "x-large"
161 color: "#222222"
162 MouseArea {
163 anchors.fill: parent
164 enabled: !flipable.flipped
165 onClicked: {
166 resetGame();
167 }
168 }
169 }
170 Label {
171 id: bestscore
172 property int bestsofar: 0
173 text: "..."
174 anchors.top: playbutton.bottom
175 anchors.topMargin: units.gu(2)
176 anchors.horizontalCenter: logo.horizontalCenter
177 fontSize: "large"
178 color: "#222222"
179 function updateScore(score) {
180 if (score >= bestscore.bestsofar) {
181 bestscore.text = "Best score: " + score;
182 bestscore.bestsofar = score;
183 }
184 }
185 }
186
187 Rectangle {
188 id: helpbutton
189 width: units.gu(5)
190 height: units.gu(5)
191 radius: units.gu(1)
192 Label {
193 text: "?"
194 anchors.centerIn: parent
195 }
196 anchors.top: parent.top
197 anchors.right: parent.right
198 anchors.margins: units.gu(2)
199 MouseArea {
200 anchors.fill: parent
201 onClicked: {
202 PopupUtils.open(helppop, helpbutton)
203 }
204 }
205 }
206 Component {
207 id: helppop
208 Popover {
209 id: helpsheet
210 Rectangle {
211 anchors.top: parent.top
212 anchors.left: parent.left
213 anchors.right: parent.right
214 height: units.gu(15)
215 border.color: "#ddd"
216 radius: 2
217 Label {
218 anchors.fill: parent
219 anchors.margins: units.gu(1)
220 text: "game by <a href='http://twitter.com/sil'>@sil</a><br>music: <a href='http://incompetech.com/music/royalty-free/index.html?isrc=USUAN1200076'>Easy Lemon</a> from Kevin McCloud (incompetech.com)<br>sounds: freesound from <a href='http://www.freesound.org/people/kantouth/sounds/106727/'>kantouth</a> / <a href='http://www.freesound.org/people/tictacshutup/sounds/407/'>tictacshutup</a> / <a href='http://www.freesound.org/people/dj-chronos/sounds/45137/'>dj-chronos</a> / <a href='http://www.freesound.org/people/justinbw/sounds/80921/'>justinbw</a>"
221 wrapMode: Text.WordWrap
222 textFormat: Text.StyledText
223 onLinkActivated: Qt.openUrlExternally(link)
224 }
225 }
226 }
227 }
228
229 }
230
231 back: Rectangle {
232 id: main
233 color: "#58585A"
234 width: flipable.width // 48 * 7 + 2 * 7 + 2
235 height: flipable.height // 48 * 10 + 2 * 10 + 50
236 property var selectedItems: []
237 /*
238 obtained with:
239 import re; fp=open('/usr/share/dict/words');
240 shorts=[re.sub('[^a-zA-Z]','',x.lower().strip()) for x in fp if len(x)<7];
241 from collections import Counter; c=Counter(''.join(shorts)); least=c.most_common()[-1][1];
242 print dict([(x.upper(),c[x]/least) for x in c])
243 */
244 property variant letterFreqs: {
245 'A': 66, 'C': 22, 'B': 19, 'E': 72, 'D': 28, 'G': 18, 'F': 12, 'I': 41, 'H': 20,
246 'K': 14, 'J': 4, 'M': 22, 'L': 40, 'O': 48, 'N': 35, 'Q': 1, 'P': 22, 'S': 75,
247 'R': 43, 'U': 26, 'T': 37, 'W': 13, 'V': 7, 'Y': 19, 'X': 3, 'Z': 4
248 }
249 property variant letterScores: {
250 "A":1,"B":3,"C":3,"D":2,"E":1,"F":4,"G":2,"H":4,"I":1,"J":8,"K":5,"L":1,
251 "M":3,"N":1,"O":1,"P":3,"Q":10,"R":1,"S":1,"T":1,"U":1,"V":4,"W":4,"X":8,
252 "Y":4,"Z":10
253 }
254 property int score: 0
255
256 function getRandomWeightedLetter() {
257 // could work out sumOfWeights once, but this is easier to understand
258 var sumOfWeights = 0;
259 for (var k in main.letterFreqs) { sumOfWeights += main.letterFreqs[k]; }
260 var selection = Math.floor(Math.random() * sumOfWeights);
261 for (var k in main.letterFreqs) {
262 if (selection < main.letterFreqs[k]) return k;
263 selection -= main.letterFreqs[k];
264 }
265 }
266
267 Audio {
268 id: music
269 source: "Easy_Lemon_60_second.ogg"
270 autoPlay: true
271 onStopped: music.play()
272 muted: !volume.audible
273 }
274
275 Audio {
276 id: click
277 source: "407__tictacshutup__click-1-off-click.ogg"
278 autoLoad: true
279 muted: !volume.audible
280 }
281
282 Audio {
283 id: success
284 source: "80921__justinbw__buttonchime02up.ogg"
285 autoLoad: true
286 muted: !volume.audible
287 }
288
289 Audio {
290 id: failure
291 source: "106727__kantouth__cartoon-bing-low.ogg"
292 autoLoad: true
293 muted: !volume.audible
294 }
295
296 Audio {
297 id: gameover
298 source: "45137__dj-chronos__dark-church-bell.ogg"
299 autoLoad: true
300 onStopped: {
301 flipable.flipped = false
302 }
303 muted: !volume.audible
304 }
305
306 Rectangle {
307 id: bottombar
308 anchors.bottom: main.bottom
309 anchors.left: main.left
310 anchors.right: main.right
311 height: (main.height - (game.squaresize * 10 + 11)) / 2
312 z: 2
313 color: Qt.hsla(210/360, 0.84, 0.2)
314
315 Rectangle {
316 id: mainscore
317 anchors {
318 centerIn: bottombar
319 bottom: bottombar.bottom
320 }
321 z: 2
322 color: Qt.rgba(0,0,0,0)
323 Label {
324 anchors.centerIn: parent
325 fontSize: "x-large"
326 text: "" + main.score
327 color: "white"
328 }
329 }
330 }
331
332 Rectangle {
333 id: topbar
334 color: "blue"
335 anchors.top: main.top
336 anchors.left: main.left
337 anchors.right: main.right
338 height: (main.height - (game.squaresize * 10 + 11)) / 2
339 z: 2
340
341 Rectangle {
342 anchors.fill: topbar
343 z: 2
344 color: accum.text == "" ? "#efefea" : (accum.isValid ? Qt.hsla(124/360, 0.83, 0.45) : Qt.hsla(0, 0.83, 0.65))
345 id: suggestedWordContainer
346 Label {
347 id: accum
348 anchors.centerIn: parent
349 text: ""
350 color: "white"
351 fontSize: "x-large"
352 font.bold: true
353 property bool isValid: false
354 function findBinaryWord( word ) {
355 var bd = Wordlist.wordlist;
356 var l = word.length;
357 // Don't search if there's nothing to look through
358 if ( !bd[l] ) {
359 return false;
360 }
361 // Get the number of words in the dictionary bin
362 var words = bd[l].length / l,
363 // The low point from where we're starting the binary search
364 low = 0,
365 // The max high point
366 high = words - 1,
367 // And the precise middle of the search
368 mid = Math.floor( words / 2 );
369 // We continue to look until we reach a final word
370 while ( high >= low ) {
371 // Grab the word at our current position
372 var found = bd[l].substr( l * mid, l );
373 // If we've found the word, stop now
374 if ( word === found ) {
375 return true;
376 }
377 // Otherwise, compare
378 // If we're too high, move lower
379 if ( word < found ) {
380 high = mid - 1;
381 // If we're too low, go higher
382 } else {
383 low = mid + 1;
384 }
385 // And find the new search point
386 mid = Math.floor( (low + high) / 2 );
387 }
388 // Nothing was found
389 return false;
390 }
391 onTextChanged: {
392 accum.isValid = findBinaryWord(accum.text);
393 }
394 }
395 function processSuggestedWord() {
396 if (accum.isValid) {
397 success.play();
398 var thisscore = 0, wordlength = accum.text.length;
399 accum.text = "";
400
401 // tell the boxes to destroy themselves
402 main.selectedItems.forEach(function(b) {
403 thisscore += main.letterScores[b.containedLetter];
404 b.state = "dead";
405 })
406 main.selectedItems = [];
407 main.score += thisscore * wordlength;
408 scoredisplay.text = "" + (thisscore * wordlength);
409 showscoredisplay.start();
410 } else {
411 failure.play();
412 accum.text = "";
413 main.selectedItems.forEach(function(b) { b.selected = false; })
414 main.selectedItems = [];
415 }
416 }
417 focus: true
418 Keys.onReturnPressed: {
419 suggestedWordContainer.processSuggestedWord();
420 }
421 MouseArea {
422 anchors.fill: parent
423 onClicked: { suggestedWordContainer.processSuggestedWord(); }
424 }
425 }
426
427
428 }
429
430 Label {
431 id: scoredisplay
432 anchors.centerIn: parent
433 z: 3
434 fontSize: "x-large"
435 text: "200"
436 color: "red"
437 opacity: 0
438 }
439
440 ParallelAnimation {
441 id: showscoredisplay
442 NumberAnimation {
443 property: "scale"
444 from: 0.1
445 to: 8.0
446 duration: 400
447 target: scoredisplay
448 }
449 SequentialAnimation {
450 NumberAnimation {
451 property: "opacity"
452 from: 0
453 to: 1.0
454 duration: 20
455 target: scoredisplay
456 }
457 NumberAnimation {
458 property: "opacity"
459 from: 1.0
460 to: 0
461 duration: 380
462 target: scoredisplay
463 }
464 }
465 }
466
467
468 Timer {
469 id: droptimer
470 repeat: true
471 running: flipable.flipped
472 interval: 2000
473 property int tickCount: 0
474 triggeredOnStart: true
475 onTriggered: {
476 tickCount += 5
477 droptimer.interval = 2000 - tickCount
478 var idx = Math.round(Math.random() * (lm.count - 1));
479 lm.get(idx).letters.append({ letter: main.getRandomWeightedLetter() });
480 if (lm.get(idx).letters.count >= 10) {
481 droptimer.stop();
482 gameover.play();
483 var db = LocalStorage.openDatabaseSync("dropping-letters", "1.0", "Dropping Letters", 1000);
484 db.transaction(function(tx) {
485 tx.executeSql("insert into Scores values (?,?)", [main.score, 0]);
486 bestscore.updateScore(main.score);
487 });
488 }
489 }
490 }
491
492 ListModel {
493 id: lm
494 }
495
496
497 Rectangle {
498 id: game
499 property int squaresize: Math.min((flipable.width) / 7, (flipable.height - (flipable.minChromeHeight * 2)) / 10)
500 anchors.top: topbar.bottom
501 anchors.bottom: bottombar.top
502 anchors.left: main.left
503 anchors.right: main.right
504 scale: -1
505 color: "#efefea"
506 Row {
507 anchors.horizontalCenter: game.horizontalCenter
508 anchors.top: game.top
509 anchors.topMargin: 1
510 //spacing: 1
511 Repeater {
512 model: lm
513 Column {
514 //spacing: 1
515 property int idx: index
516 width: game.squaresize
517 height: game.height
518 add: Transition {
519 NumberAnimation { properties: "y"; easing.type: Easing.OutBounce; duration: 1000 }
520 }
521 move: Transition {
522 NumberAnimation { properties: "y"; easing.type: Easing.OutBounce }
523 }
524 Repeater {
525 model: letters
526 Rectangle {
527 id: box
528 property bool selected: false
529 property int idx: index
530 property string containedLetter: letter
531 color: {
532 if (lm.get(parent.idx).letters.count >= 10) {
533 return "red";
534 } else if (!selected) {
535 return Qt.hsla(210/360, 0.84, 0.25 + (idx * 0.05));
536 } else if (accum.isValid) {
537 return Qt.hsla(124/360, 0.83, 0.45); // "#93cc98";
582 } else {538 } else {
583 if (box === main.selectedItems[main.selectedItems.length - 1]) {539 return Qt.hsla(0, 0.83, 0.65); // "#cc9598"
584 main.selectedItems.pop(main.selectedItems.length - 1);540 }
585 box.selected = false;541 }
586 accum.text = accum.text.substr(0, accum.text.length - 1);542 //radius: "medium"
543 scale: -1
544 width: game.squaresize
545 height: game.squaresize
546 y: game.height + game.squaresize
547 z: 5
548 Label {
549 anchors.centerIn: parent
550 text: letter
551 fontSize: "large"
552 font.bold: true
553 color: "#ffffff"
554 }
555 MouseArea {
556 anchors.fill: parent
557 onClicked: {
558 if (!box.selected) {
559 box.selected = true;
560 accum.text += letter;
561 click.play();
562 main.selectedItems[main.selectedItems.length] = box;
563 } else {
564 if (box === main.selectedItems[main.selectedItems.length - 1]) {
565 main.selectedItems.pop(main.selectedItems.length - 1);
566 box.selected = false;
567 accum.text = accum.text.substr(0, accum.text.length - 1);
568 }
587 }569 }
588 }570 }
589 }571 }
590 }572 Behavior on opacity {
591 Behavior on opacity {573 SequentialAnimation {
592 SequentialAnimation {574 ScriptAction { script: pulseEmitter.burst(1000); }
593 ScriptAction { script: pulseEmitter.burst(1000); }575 NumberAnimation { properties:"opacity"; duration: 500 }
594 NumberAnimation { properties:"opacity"; duration: 500 }576 ScriptAction { script: lm.get(box.parent.idx).letters.remove(box.idx); }
595 ScriptAction { script: lm.get(box.parent.idx).letters.remove(box.idx); }577 }
596 }578 }
597 }579 states: [
598 states: [580 State { name: "alive" },
599 State { name: "alive" },581 State {
600 State {582 name: "dead"
601 name: "dead"583 PropertyChanges { target: box; opacity: 0 }
602 PropertyChanges { target: box; opacity: 0 }584 }
603 }585 ]
604 ]586 ParticleSystem {
605 ParticleSystem {587 id: particles
606 id: particles588 width: flipable.width / 2
607 width: flipable.width / 2589 height: flipable.width / 2
608 height: flipable.width / 2590 anchors.centerIn: parent
609 anchors.centerIn: parent591 clip: false
610 clip: false592 ImageParticle {
611 ImageParticle {593 source: "redStar.png"
612 source: "redStar.png"594 alpha: 0
613 alpha: 0595 colorVariation: 0.6
614 colorVariation: 0.6596 }
615 }597 Emitter {
616 Emitter {598 id: pulseEmitter
617 id: pulseEmitter599 x: parent.width/2
618 x: parent.width/2600 y: parent.height/2
619 y: parent.height/2601 emitRate: 2000
620 emitRate: 2000602 lifeSpan: 500
621 lifeSpan: 500603 enabled: false
622 enabled: false604 velocity: AngleDirection { magnitude: 256; angleVariation: 360; magnitudeVariation: 200; }
623 velocity: AngleDirection { magnitude: 256; angleVariation: 360; magnitudeVariation: 200; }605 size: parent.width / 8
624 size: parent.width / 8606 sizeVariation: 8
625 sizeVariation: 8607 }
626 }608 }
627 }609 }
628 }610 }
@@ -633,4 +615,17 @@
633 }615 }
634 }616 }
635 }617 }
618 HUD.HUD {
619 applicationIdentifier: "dropping-letters"
620 HUD.Context {
621 HUD.QuitAction {
622 onTriggered: Qt.quit()
623 }
624 HUD.Action {
625 label: i18n.tr("New game")
626 keywords: "Reset;Start;New"
627 onTriggered: resetGame()
628 }
629 }
630 }
636}631}
637632
=== modified file 'dropping-letters.qmlproject'
--- dropping-letters.qmlproject 2013-05-01 05:13:52 +0000
+++ dropping-letters.qmlproject 2013-05-07 05:11:28 +0000
@@ -3,7 +3,7 @@
3import QmlProject 1.13import QmlProject 1.1
44
5Project {5Project {
6 mainFile: "dropping-letters-ubuntu.qml"6 mainFile: "dropping-letters.qml"
7 7
8 /* Include .qml, .js, and image files from current directory and subdirectories */8 /* Include .qml, .js, and image files from current directory and subdirectories */
9 QmlFiles {9 QmlFiles {
1010
=== added file 'new.png'
11Binary files new.png 1970-01-01 00:00:00 +0000 and new.png 2013-05-07 05:11:28 +0000 differ11Binary files new.png 1970-01-01 00:00:00 +0000 and new.png 2013-05-07 05:11:28 +0000 differ

Subscribers

People subscribed via source and target branches