commit e24b1d5c015cba0f8e00fb2290db157e4d0d0562 Author: Niels Date: Fri May 30 16:09:44 2025 +0200 Initial commit2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bb7b593 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Use target-compatible line endings as the safe default for cross compilation. +* text=auto eol=lf diff --git a/harbour-tetrafish.desktop b/harbour-tetrafish.desktop new file mode 100644 index 0000000..d5b2aac --- /dev/null +++ b/harbour-tetrafish.desktop @@ -0,0 +1,20 @@ +[Desktop Entry] +Type=Application +X-Nemo-Application-Type=silica-qt5 +Icon=harbour-tetrafish +Exec=harbour-tetrafish +Name=harbour-tetrafish +# translation example: +# your app name in German locale (de) +# +# Remember to comment out the following line, if you do not want to use +# a different app name in German locale (de). +Name[de]=harbour-tetrafish + +[X-Sailjail] +# Replace with your organization as a reverse domain name +OrganizationName=org.myorg +# ApplicationName does not have to be identical to Name +ApplicationName=tetrafish +# Add the required permissions here +Permissions= diff --git a/harbour-tetrafish.pro b/harbour-tetrafish.pro new file mode 100644 index 0000000..d92c969 --- /dev/null +++ b/harbour-tetrafish.pro @@ -0,0 +1,39 @@ +# NOTICE: +# +# Application name defined in TARGET has a corresponding QML filename. +# If name defined in TARGET is changed, the following needs to be done +# to match new name: +# - corresponding QML filename must be changed +# - desktop icon filename must be changed +# - desktop filename must be changed +# - icon definition filename in desktop file must be changed +# - translation filenames have to be changed + +# The name of your application +TARGET = harbour-tetrafish + +CONFIG += sailfishapp + +SOURCES += src/harbour-tetrafish.cpp + +DISTFILES += qml/harbour-tetrafish.qml \ + qml/cover/CoverPage.qml \ + qml/pages/FirstPage.qml \ + qml/pages/SecondPage.qml \ + rpm/harbour-tetrafish.changes.in \ + rpm/harbour-tetrafish.changes.run.in \ + rpm/harbour-tetrafish.spec \ + translations/*.ts \ + harbour-tetrafish.desktop + +SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172 + +# to disable building translations every time, comment out the +# following CONFIG line +CONFIG += sailfishapp_i18n + +# German translation is enabled as an example. If you aren't +# planning to localize your app, remember to comment out the +# following TRANSLATIONS line. And also do not forget to +# modify the localized app name in the the .desktop file. +TRANSLATIONS += translations/harbour-tetrafish-de.ts diff --git a/icons/108x108/harbour-tetrafish.png b/icons/108x108/harbour-tetrafish.png new file mode 100644 index 0000000..ab10628 Binary files /dev/null and b/icons/108x108/harbour-tetrafish.png differ diff --git a/icons/128x128/harbour-tetrafish.png b/icons/128x128/harbour-tetrafish.png new file mode 100644 index 0000000..54375c5 Binary files /dev/null and b/icons/128x128/harbour-tetrafish.png differ diff --git a/icons/172x172/harbour-tetrafish.png b/icons/172x172/harbour-tetrafish.png new file mode 100644 index 0000000..36eee58 Binary files /dev/null and b/icons/172x172/harbour-tetrafish.png differ diff --git a/icons/86x86/harbour-tetrafish.png b/icons/86x86/harbour-tetrafish.png new file mode 100644 index 0000000..ad316d6 Binary files /dev/null and b/icons/86x86/harbour-tetrafish.png differ diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml new file mode 100644 index 0000000..3f9aeba --- /dev/null +++ b/qml/cover/CoverPage.qml @@ -0,0 +1,65 @@ +/* + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Jolla Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +CoverBackground { + Image { + id: tetimg + source: "../data/tetris.png" + fillMode: Image.Stretch + width: parent.width + height: parent.height + opacity: 0.2 + } + Column { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + Image { + id: tetraimg + anchors.horizontalCenter: parent.horizontalCenter + source: "../data/tetrafish.png" + fillMode: Image.PreserveAspectFit + width: parent.width + height: parent.width + // opacity: 0.1 + } + Label { + text: "TetraFish" + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: Theme.fontSizeExtraLarge + color: Theme.highlightColor + } + } +} + + diff --git a/qml/data/Kraku - Tetris part 1.ogg b/qml/data/Kraku - Tetris part 1.ogg new file mode 100644 index 0000000..8b87573 Binary files /dev/null and b/qml/data/Kraku - Tetris part 1.ogg differ diff --git a/qml/data/back2back.wav b/qml/data/back2back.wav new file mode 100644 index 0000000..c769ac4 Binary files /dev/null and b/qml/data/back2back.wav differ diff --git a/qml/data/bonus.wav b/qml/data/bonus.wav new file mode 100644 index 0000000..7368ed2 Binary files /dev/null and b/qml/data/bonus.wav differ diff --git a/qml/data/dot.png b/qml/data/dot.png new file mode 100644 index 0000000..fd3ee53 Binary files /dev/null and b/qml/data/dot.png differ diff --git a/qml/data/down.wav b/qml/data/down.wav new file mode 100644 index 0000000..fd77904 Binary files /dev/null and b/qml/data/down.wav differ diff --git a/qml/data/gravity.wav b/qml/data/gravity.wav new file mode 100644 index 0000000..af51ae1 Binary files /dev/null and b/qml/data/gravity.wav differ diff --git a/qml/data/harbour-tetrafish.png b/qml/data/harbour-tetrafish.png new file mode 100644 index 0000000..0d11b4c Binary files /dev/null and b/qml/data/harbour-tetrafish.png differ diff --git a/qml/data/harddrop.wav b/qml/data/harddrop.wav new file mode 100644 index 0000000..3760ffe Binary files /dev/null and b/qml/data/harddrop.wav differ diff --git a/qml/data/levelup.wav b/qml/data/levelup.wav new file mode 100644 index 0000000..e318af6 Binary files /dev/null and b/qml/data/levelup.wav differ diff --git a/qml/data/move.wav b/qml/data/move.wav new file mode 100644 index 0000000..8278b1e Binary files /dev/null and b/qml/data/move.wav differ diff --git a/qml/data/nobonus.wav b/qml/data/nobonus.wav new file mode 100644 index 0000000..02e7064 Binary files /dev/null and b/qml/data/nobonus.wav differ diff --git a/qml/data/rotate.wav b/qml/data/rotate.wav new file mode 100644 index 0000000..a409d39 Binary files /dev/null and b/qml/data/rotate.wav differ diff --git a/qml/data/softdrop.wav b/qml/data/softdrop.wav new file mode 100644 index 0000000..9cc593d Binary files /dev/null and b/qml/data/softdrop.wav differ diff --git a/qml/data/tetrafish.png b/qml/data/tetrafish.png new file mode 100644 index 0000000..461a91d Binary files /dev/null and b/qml/data/tetrafish.png differ diff --git a/qml/data/tetris.png b/qml/data/tetris.png new file mode 100644 index 0000000..02e2d7a Binary files /dev/null and b/qml/data/tetris.png differ diff --git a/qml/data/tetris.wav b/qml/data/tetris.wav new file mode 100644 index 0000000..6fc9510 Binary files /dev/null and b/qml/data/tetris.wav differ diff --git a/qml/game/Dot.qml b/qml/game/Dot.qml new file mode 100644 index 0000000..6856d3d --- /dev/null +++ b/qml/game/Dot.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +GlassItem { + property int active: 0 // 0 = empty; 1 = active; 2 = inactive + property bool ghost: false + property bool glowing: false + id: dot + width: Theme.paddingLarge + height: width + radius: dots === 0 ? 0.3 : 0 // 0 ? 0.2 : 0 + falloffRadius: dots === 0 ? 0.40 : 2 // 0 ? 0.25 : 2 + color: Theme.highlightColor + Timer { + property bool up + id: glowingTimer + running: glowing + repeat: true + interval: 30 + onTriggered: { + if (opacity === 1){ + up = false + } else if (opacity < 0.5) { + up = true + } + if (up) + opacity += 0.05 + else + opacity -= 0.05 + } + } +} diff --git a/qml/game/Functions.qml b/qml/game/Functions.qml new file mode 100644 index 0000000..795d063 --- /dev/null +++ b/qml/game/Functions.qml @@ -0,0 +1,1175 @@ +import QtQuick 2.2 +import Sailfish.Silica 1.0 +import QtMultimedia 5.6 +import QtQuick.LocalStorage 2.0 + +import "../js/settings.js" as Settings + +Item { + id: functions + property bool backtoback: false + property bool garunning: false + property bool gapaused: false + property bool maenabled: true + property int levelCount: 0 + property int nr: 0 + signal gameRunning(bool running) + signal gameOver() + signal paused(bool pause) + signal bonusStart() + signal bonusEnd() + signal bonusCount(int counts) + signal gameBonus(int bonus,int grav) + signal gameLevel(int level) + signal brickRotate() + signal brickMove(string direction) // not used but could be useful maybe?! + signal brickBottom(bool instant) + signal direction(string dir) + Connections { // Check if app is active or pause + target: Qt.application + onStateChanged: { + if (Qt.application.state === 2 && garunning && !gapaused) pause() + } + } + // New Game: ok! + + onPaused: { + pause ? gapaused = true : gapaused = false + pauseLabel.visible = gapaused + if (musicEnabled && pause) { + pausePlay() + pauseLabel.visible = true + } else { + if (musicEnabled && !pause) musicPlay() + } + } + + onGameRunning: { + garunning = running + if (musicEnabled) running ? musicPlay() : stopPlay() + } + + NumberAnimation{ + id:audiofadeout; + target: playMusic; + property: "volume"; + from:playMusic.volume; + to: 0; + duration:3000 + onStopped: { + playMusic.stop() + playMusic.volume = 0.6 + } + } + + onGameOver: { + + comboTimeout.running = false + countTimer.running = false + garunning = false + functions.gameRunning(false) + bonusLabel.opacity = 0 + // var index = difficulty + mouseArea.enabled = false + downTimer.running = false + highScoreModel.newScore = false + highScoreModel.compareScore(scoreValue) + root.interactive = true + gameOverTimer.running = true + + scoreValue -= 1 + } + + onGameBonus: { + if (sfxEnabled) bonus ? bonuss.play() : gravi.play() // + } + + onBonusStart: { + } + + onBonusEnd: { + if (sfxEnabled) nobonus.play() //nobonus. .play() + bonusLabel.opacity = 0 + } + + onBonusCount: { + bonusLabel.opacity = 0 + bonusLabel.text = counts + bonusLabel.opacity = 0.8 + } + + onBrickRotate: { + if (sfxEnabled) rotate.play() + } + onBrickBottom: { + if (sfxEnabled) instant ? instantBottom.play() : bottom.play() + maenabled = false + } + onGameLevel: { + downSpeed -= downSpeed/9 + infoLabel.text = "LEVEL "+level + infoLabel.opacity = 0.8 + infoTimer.restart() + if (sfxEnabled) levelup.play() + } + + function newGame() { + pullDownMenu.enabled = false + root.interactive = false + mouseArea.enabled = true + downTimer.running = true + pauseVal = false + playMusic.source = musicEnabled ? musicScore : "" //http://nectarine.from-de.com/necta192" : "" + for ( var i = 15; i > 0; i--) + for ( var j = 1; j < 11; j++) { + repeater.itemAt(i*12+j).color = Theme.secondaryColor + repeater.itemAt(i*12+j).active = 0 + repeater.itemAt(i*12+j).opacity = 0.1 + } + + for ( i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + + activeBlock = -1 + futureBlock = -1 + + // Reset score + + level = 1 + downSpeedCalc(level) + scoreValue = 0 + speedValue = 0 + linesTotal = 0 + + functions.gameRunning(true) + generate() + } + function musicPlay() { + if (musicEnabled) playMusic.play() + } + function stopPlay() { + if (musicEnabled) audiofadeout.start() //playMusic.stop() + } + function pausePlay() { + if (musicEnabled) playMusic.pause() + } + + function ghost() { + if ( ghostEnabled) { + var min = 16 + for ( var i = 15; i > 0; i--) + for ( var j = 1; j < 11; j++) { + var index = i*12+j + if (repeater.itemAt(index).ghost) { + repeater.itemAt(index).ghost = !repeater.itemAt(index).ghost + if (repeater.itemAt(index).active === 0) + repeater.itemAt(index).opacity = 0.1 + else + repeater.itemAt(index).opacity = 1 + } + } + for ( i = 15; i > 0; i--) + for ( j = 1; j < 11; j++) { + index = i*12+j + if ( repeater.itemAt(index).active === 1 && repeater.itemAt(index + 12).active === 0 ) + for ( var k = i+1; k < 17; k++) + if((repeater.itemAt(k*12+j).active === 2 || repeater.itemAt(k*12+j).active === 3) && min > k-i-1) { + min = k-i-1 + k = 16 + } + } + for ( i = 15; i > 0; i--) + for ( j = 1; j < 11; j++) { + index = i*12+j + if ( repeater.itemAt(index).active === 1 && repeater.itemAt(index+12*min).active === 0) { + repeater.itemAt(index+12*min).ghost = true + repeater.itemAt(index+12*min).opacity = 0.5 + } + } + } else { + for ( i = 15; i > 0; i--) + for ( j = 1; j < 11; j++) { + index = i*12+j + if (repeater.itemAt(index).ghost) { + repeater.itemAt(index).ghost = !repeater.itemAt(index).ghost + if (repeater.itemAt(index).active === 0) + repeater.itemAt(index).opacity = 0.1 + else + repeater.itemAt(index).opacity = 1 + } + } + } + } + + // Pause: ok! + + function pause() { + pauseVal = !pauseVal + pullDownMenu.enabled = !pullDownMenu.enabled + root.interactive = !root.interactive + mouseArea.enabled = !mouseArea.enabled + downTimer.running = !downTimer.running + if (pauseVal) { + //console.log("pause " + countTimer.count) + ////////////////////////// + functions.paused(true) + //////////////////////////// + comboTimeout.running = false + countTimer.running = false + for (var index = 0; index < 204; index++) { + if ( repeater.itemAt(index).active === 3){ + repeater.itemAt(index).glowing = false + repeater.itemAt(index).opacity = 1 + } + } + } else { functions.paused(false); if ( combo > 1 ) { + comboTimeout.running = true //??? + countTimer.running = true //??? //STARTING COUNTTIMER (PAUSED) + + if (combo > 1) + for ( index = 0; index < 204; index++) + if ( repeater.itemAt(index).active === 3) + repeater.itemAt(index).glowing = true + } + } + } + + + /* function saveGame() { + var values = [activeColor, centerX, centerY, activeBlock, futureBlock, scoreValue, speedValue, level, difficulty] + fileIO.save(1,values) + var active = [] + for (var i = 0; i < 204; i++) + active[i] = repeater.itemAt(i).active + fileIO.save(1, "active", active) + var color = [] + for (i = 0; i < 204; i++) + color[i] = repeater.itemAt(i).color + fileIO.save(1, "color", color) + fileIO.save("count", countTimer.count) + fileIO.save("combo",combo) + fileIO.write("slot1",1) + savedGame = 1 + } + + function loadGame() { + var active = [] + active = fileIO.load(1, "active") + var color = [] + color = fileIO.load(1, "color") + var values = [] + values = fileIO.load(1) + // console.log(values) + + for ( var i = 0; i < 204; i++) { + repeater.itemAt(i).active = active[i] + repeater.itemAt(i).color = color[i] + if (repeater.itemAt(i).active === 0) + repeater.itemAt(i).opacity = 0.1 + else + repeater.itemAt(i).opacity = 1 + } + activeColor = values[0] + centerX = values[1] + centerY = values[2] + activeBlock = values[3] + futureBlock = values[4] + scoreValue = values[5] + speedValue = values[6] + level = values[7] + difficulty = values[8] + countTimer.count = fileIO.read("count") + combo = fileIO.read("combo") + if (combo > 1) { + // console.log("restart " + countTimer.count) + comboTimeout.running = true + countTimer.running = true + //ticktock.play() + for ( index = 0; index < 204; index++) + if ( repeater.itemAt(index).active === 3) + repeater.itemAt(index).glowing = true + } + switch (futureBlock) { + case 0 : + future_l_normal() + break + case 1 : + future_l_reverse() + break + case 2 : + future_s_normal() + break + case 3 : + future_s_reverse() + break + case 4 : + future_t_normal() + break + case 5 : + future_square() + break + case 6 : + future_line() + break + } + pullDownMenu.enabled = false + root.interactive = false + mouseArea.enabled = true + pushUpMenu.enabled = false + downTimer.running = true + pauseVal = false + ghost() + } */ + + // Tetraminos Active: ok! + + function l_normal() { // 0 index = [17,29,41,42] + var index = [17,29,41,42] + centerX = 5 + centerY = 2 + activeColor = "orange" // orange + for ( var i = 0; i < 4; i++) { + repeater.itemAt(index[i]).opacity = 1 + repeater.itemAt(index[i]).color = activeColor + repeater.itemAt(index[i]).active = 1 + } + } + function l_reverse() { // 1 index = [18,30,41,42] + var index = [18,30,41,42] + centerX = 6 + centerY = 2 + activeColor = "blue" // yellow + for ( var i = 0; i < 4; i++) { + repeater.itemAt(index[i]).opacity = 1 + repeater.itemAt(index[i]).color = activeColor + repeater.itemAt(index[i]).active = 1 + } + } + function s_normal() { // 2 index = [17,29,30,42] + var index = [17,29,30,42] + centerX = 5 + centerY = 2 + activeColor = "lime" // red + for ( var i = 0; i < 4; i++) { + repeater.itemAt(index[i]).opacity = 1 + repeater.itemAt(index[i]).color = activeColor + repeater.itemAt(index[i]).active = 1 + } + } + function s_reverse() { // 3 index = [18,29,30,41] + var index = [18,29,30,41] + centerX = 6 + centerY = 2 + activeColor = "red" // green + for ( var i = 0; i < 4; i++) { + repeater.itemAt(index[i]).opacity = 1 + repeater.itemAt(index[i]).color = activeColor + repeater.itemAt(index[i]).active = 1 + } + } + function t_normal() { // 4 index = [17,29,30,41] + var index = [17,29,30,41] + centerX = 5 + centerY = 2 + activeColor = "magenta" //blue + for ( var i = 0; i < 4; i++) { + repeater.itemAt(index[i]).opacity = 1 + repeater.itemAt(index[i]).color = activeColor + repeater.itemAt(index[i]).active = 1 + } + } + function square() { // 5 index = [17,18,29,30] + var index = [17,18,29,30] + centerX = 5.5 + centerY = 1.5 + activeColor = "yellow" // purple (was magenta) + for ( var i = 0; i < 4; i++) { + repeater.itemAt(index[i]).opacity = 1 + repeater.itemAt(index[i]).color = activeColor + repeater.itemAt(index[i]).active = 1 + } + } + function line() { // 6 index = [16,17,18,19] + var index = [16,17,18,19] + centerX = 5 + centerY = 1 + activeColor = "cyan" // pink + for ( var i = 0; i < index.length; i++) { + repeater.itemAt(index[i]).opacity = 1 + repeater.itemAt(index[i]).color = activeColor + repeater.itemAt(index[i]).active = 1 + } + } + + // Tetraminos Future: ok! + + function future_l_normal() { // 0 + var index = [1,5,9,10] + futureColor = "orange" // orange + for ( var i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + for (i = 0; i < 4; i++) { + futureRepeater.itemAt(index[i]).opacity = 1 + futureRepeater.itemAt(index[i]).color = futureColor + } + } + function future_l_reverse() { // 1 + var index = [2,6,9,10] + futureColor = "blue" // yellow + for ( var i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + for (i = 0; i < 4; i++) { + futureRepeater.itemAt(index[i]).opacity = 1 + futureRepeater.itemAt(index[i]).color = futureColor + } + } + function future_s_normal() { // 2 + var index = [1,5,6,10] + futureColor = "lime" // orange + for ( var i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + for (i = 0; i < 4; i++) { + futureRepeater.itemAt(index[i]).opacity = 1 + futureRepeater.itemAt(index[i]).color = futureColor + } + } + function future_s_reverse() { // 3 + var index = [2,5,6,9] + futureColor = "red" // green + for ( var i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + for (i = 0; i < 4; i++) { + futureRepeater.itemAt(index[i]).opacity = 1 + futureRepeater.itemAt(index[i]).color = futureColor + } + } + function future_t_normal() { // 4 + var index = [1,5,6,9] + futureColor = "magenta" // blue + for ( var i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + for (i = 0; i < 4; i++) { + futureRepeater.itemAt(index[i]).opacity = 1 + futureRepeater.itemAt(index[i]).color = futureColor + } + } + function future_square() { // 5 + var index = [5,6,9,10] + futureColor = "yellow" // purple + for ( var i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + for (i = 0; i < 4; i++) { + futureRepeater.itemAt(index[i]).opacity = 1 + futureRepeater.itemAt(index[i]).color = futureColor + } + } + function future_line() { // 6 + var index = [4,5,6,7] + futureColor = "cyan" // pink + for ( var i = 0; i < 12; i++) { + futureRepeater.itemAt(i).opacity = 0.1 + futureRepeater.itemAt(i).color = Theme.secondaryColor + } + for (i = 0; i < 4; i++) { + futureRepeater.itemAt(index[i]).opacity = 1 + futureRepeater.itemAt(index[i]).color = futureColor + } + } + + + // Rotation: ok! + + function rotate() { + var x = [], newX = [], originX = [], tempX = [], + y = [], newY = [], originY = [], tempY = [] + + for (var i = 15; i > 0; i--) + for (var j = 1; j < 11; j++) + if (repeater.itemAt(i*12+j).active === 1) { + x[x.length] = j + y[y.length] = i + } + // Traslation + for (i = 0; i < x.length; i++) { + originX[i] = (x[i] - centerX) + originY[i] = (y[i] - centerY) + } + // Rotation + for (i = 0; i < x.length; i++) { + tempX[i] = -originY[i] + tempY[i] = originX[i] + } + // Second Traslation + for (i = 0; i < x.length; i++) { + newX[i] = (tempX[i] + centerX) + newY[i] = (tempY[i] + centerY) + } + + // Empty + for ( i = 0; i < x.length; i++) { + repeater.itemAt( y[i] * 12 + x[i] ).color = Theme.secondaryColor + repeater.itemAt( y[i] * 12 + x[i] ).active = 0 + repeater.itemAt( y[i] * 12 + x[i] ).opacity = 0.1 + } + + // Check if space available + var available = 1 + for ( i = 0; i < x.length; i++) + if (repeater.itemAt( newY[i] * 12 + newX[i] ).active !== 0 ) + available = 0 + + if (available === 1) { + // Refill new dots + for ( i = 0; i < x.length; i++) { + repeater.itemAt( newY[i] * 12 + newX[i] ).color = activeColor + repeater.itemAt( newY[i] * 12 + newX[i] ).active = 1 + repeater.itemAt( newY[i] * 12 + newX[i] ).opacity = 1 + } + ////////////////////////////// + functions.brickRotate() + ///////////////////////////// + } else if (available === 0) { + // Refill old dots + for ( i = 0; i < x.length; i++) { + repeater.itemAt( y[i] * 12 + x[i] ).color = activeColor + repeater.itemAt( y[i] * 12 + x[i] ).active = 1 + repeater.itemAt( y[i] * 12 + x[i] ).opacity = 1 + } + } + ghost() + } + + // Random choice of tetramino: ok! + + function generate() { + + if ( futureBlock === -1 ) { + var rand = Math.floor(Math.random()*7) + activeBlock = rand + switch (activeBlock) { + case 0 : + l_normal() + break + case 1 : + l_reverse() + break + case 2 : + s_normal() + break + case 3 : + s_reverse() + break + case 4 : + t_normal() + break + case 5 : + square() + break + case 6 : + line() + break + case 7 : + generate() + break + } + var futureRand = Math.floor(Math.random()*7) + futureBlock = futureRand + switch (futureBlock) { + case 0 : + future_l_normal() + break + case 1 : + future_l_reverse() + break + case 2 : + future_s_normal() + break + case 3 : + future_s_reverse() + break + case 4 : + future_t_normal() + break + case 5 : + future_square() + break + case 6 : + future_line() + break + case 7 : + generate() + break + } + + } else { + var game = true + activeBlock = futureBlock + switch (activeBlock) { + case 0 : + if ( repeater.itemAt(17).active + repeater.itemAt(29).active + repeater.itemAt(41).active + repeater.itemAt(42).active === 0) + l_normal() + else { + gameOver() + game = false + } + break + case 1 : + if ( repeater.itemAt(18).active + repeater.itemAt(30).active + repeater.itemAt(41).active + repeater.itemAt(42).active === 0) + l_reverse() + else { + gameOver() + game = false + } + break + case 2 : + if ( repeater.itemAt(17).active + repeater.itemAt(29).active + repeater.itemAt(30).active + repeater.itemAt(42).active === 0) + s_normal() + else { + gameOver() + game = false + } + break + case 3 : + if ( repeater.itemAt(18).active + repeater.itemAt(29).active + repeater.itemAt(30).active + repeater.itemAt(41).active === 0) + s_reverse() + else { + gameOver() + game = false + } + break + case 4 : + if ( repeater.itemAt(17).active + repeater.itemAt(29).active + repeater.itemAt(30).active + repeater.itemAt(41).active === 0) + t_normal() + else { + gameOver() + game = false + } + break + case 5 : + if ( repeater.itemAt(17).active + repeater.itemAt(18).active + repeater.itemAt(29).active + repeater.itemAt(30).active === 0) + square() + else { + gameOver() + game = false + } + break + case 6 : + if ( repeater.itemAt(16).active + repeater.itemAt(17).active + repeater.itemAt(18).active + repeater.itemAt(19).active === 0) + line() + else { + gameOver() + game = false + } + break + } + if (game) { + futureRand = Math.floor(Math.random()*7) + futureBlock = futureRand + switch (futureBlock) { + case 0 : + future_l_normal() + break + case 1 : + future_l_reverse() + break + case 2 : + future_s_normal() + break + case 3 : + future_s_reverse() + break + case 4 : + future_t_normal() + break + case 5 : + future_square() + break + case 6 : + future_line() + break + case 7 : + generate() + break + } + } + } + + ghost() + } + + // Game over: ok! + + function saveHighscore(name,point){ + highScoreModel.savePlayerScore(name,point) + } + + // Gravity bonus + + function gravity() { + var count = 0 + for (var i = 15; i > 0; i--) + for ( var j = 10; j > 0; j--) { + var index = i*12+j + if ( repeater.itemAt(index).active === 0) { + for ( var k = 1; k < i; k++) { + if (repeater.itemAt(index-12*k).active === 2) { + repeater.itemAt(index).active = repeater.itemAt(index-12*k).active + repeater.itemAt(index).opacity = repeater.itemAt(index-12*k).opacity + repeater.itemAt(index).color = repeater.itemAt(index-12*k).color + repeater.itemAt(index-12*k).active = 0 + repeater.itemAt(index-12*k).opacity = 0.1 + repeater.itemAt(index-12*k).color = Theme.secondaryColor + break + } + } + } + } + var score = 0 + var lines = [] + for ( i = 1; i < 16; i++) { + var full = 0 + for ( j = 1; j < 11; j++) { + if (repeater.itemAt(i*12+j).active === 2) { + full++ + } + } + if (full === 10) + lines[lines.length] = i + } + score += lines.length*10 + linesTotal = linesTotal+lines.length + scoreValue += score*level//*(level+1) + speedValue += score + linesValue = linesValue + linesValue+lines.length + + if (linesValue >= 10) { + linesValue = 0 + level += 1 + // difficulty -= 0.014 + functions.gameLevel(level) + } + + for ( k = 0; k < lines.length; k++) { + for ( i = lines[k]; i > 0; i--) { + if ( i === 1) { + for ( j = 1; j < 11; j++) { + repeater.itemAt(i*12+j).color = Theme.secondaryColor + repeater.itemAt(i*12+j).active = 0 + repeater.itemAt(i*12+j).opacity = 0.1 + } + } else + for ( j = 1; j < 11; j++) { + if (repeater.itemAt(i*12+j).active !== 3){ + repeater.itemAt(i*12+j).color = repeater.itemAt(i*12+j-12).color + repeater.itemAt(i*12+j).active = repeater.itemAt(i*12+j-12).active + repeater.itemAt(i*12+j).opacity = repeater.itemAt(i*12+j-12).opacity + } + } + } + } + } + + // Down Flow Traslation: ok! + + function flow() { + var down = 1 + for (var i = 190; i > 12; i-- ) + if (repeater.itemAt(i).active === 1 && repeater.itemAt(i+12).active > 1) { + down = 0 + } if ( down === 1 ) { + for (i = 190; i > 12; i-- ) { + if (repeater.itemAt(i).active === 1) { + repeater.itemAt(i+12).color = repeater.itemAt(i).color + repeater.itemAt(i+12).active = repeater.itemAt(i).active + repeater.itemAt(i+12).opacity = repeater.itemAt(i).opacity + repeater.itemAt(i).color = Theme.secondaryColor + repeater.itemAt(i).active = 0 + repeater.itemAt(i).opacity = 0.1 + } + } + centerY += 1 + } else if (down === 0) { + for (i = 190; i > 12; i-- ) + if (repeater.itemAt(i).active === 1) { + repeater.itemAt(i).active = 2 + } + ///////////////////////// + functions.brickBottom(false) + // console.log("FLOW DOWN = 0") + scoreValue += 1*level + //////////////////////// + score() + generate() + } + + } + + // Instant Down + + function instantDown() { + var min = 16 + var down = 1 + for (var i = 15; i > 0; i--) + for ( var j = 1; j < 11; j++) { + var index = i*12+j + if (repeater.itemAt(index).active === 1 && repeater.itemAt(index+12).active > 1) { + down = 0 + } + } + + if (down === 1) { + for ( i = 15; i > 0; i--) + for ( j = 1; j < 11; j++) { + index = i*12+j + if ( repeater.itemAt(index).active === 1 && repeater.itemAt(index + 12).active === 0 ) + for ( var k = i+1; k < 17; k++) + if((repeater.itemAt(k*12+j).active === 2 || repeater.itemAt(k*12+j).active === 3) && min > k-i-1) { + min = k-i-1 + k = 16 + } + } + for ( i = 15; i > 0; i--) + for ( j = 1; j < 11; j++) { + index = i*12+j + if ( repeater.itemAt(index).active === 1 ) { + repeater.itemAt(index+12*min).color = repeater.itemAt(index).color + repeater.itemAt(index+12*min).active = 1 + repeater.itemAt(index+12*min).opacity = 1 + repeater.itemAt(index).color = Theme.secondaryColor + repeater.itemAt(index).active = 0 + repeater.itemAt(index).opacity = 0.1 + } + + } + } + scoreValue += 1*level + ///////////////////////// + functions.brickBottom(true) + /////////////////////////// + flow() + } + + // Down Traslation: ok! + + function down() { + var down = 1 + for (var i = 190; i > 12; i-- ) + if (repeater.itemAt(i).active === 1 && repeater.itemAt(i+12).active > 1){ + down = 0 + scoreValue += 1*level + } if ( down === 1 ) { + for (i = 190; i > 12; i-- ) { + if (repeater.itemAt(i).active === 1) { + repeater.itemAt(i+12).color = repeater.itemAt(i).color + repeater.itemAt(i+12).active = repeater.itemAt(i).active + repeater.itemAt(i+12).opacity = repeater.itemAt(i).opacity + repeater.itemAt(i).color = Theme.secondaryColor + repeater.itemAt(i).active = 0 + repeater.itemAt(i).opacity = 0.1 + } + } + centerY += 1 + } else + flow() + } + + // Left Traslation: ok! + + function left() { + var left = 1 + for (var i = 1; i < 204; i++ ) + if (repeater.itemAt(i).active === 1 && repeater.itemAt(i-1).active > 1) + left = 0 + + if ( left === 1 ) { + for (i = 1; i < 204; i++ ) { + if (repeater.itemAt(i).active === 1) { + repeater.itemAt(i-1).color = repeater.itemAt(i).color + repeater.itemAt(i-1).active = repeater.itemAt(i).active + repeater.itemAt(i-1).opacity = repeater.itemAt(i).opacity + repeater.itemAt(i).color = Theme.secondaryColor + repeater.itemAt(i).active = 0 + repeater.itemAt(i).opacity = 0.1 + } + } + centerX -= 1 + } + ghost() + } + + // Right traslation: ok! + + function right() { + var right = 1 + for (var i = 202; i > 0; i-- ) + if (repeater.itemAt(i).active === 1 && repeater.itemAt(i+1).active > 1) + right = 0 + + if ( right === 1 ) { + for (i = 202; i > 0; i-- ) { + if (repeater.itemAt(i).active === 1) { + repeater.itemAt(i+1).color = repeater.itemAt(i).color + repeater.itemAt(i+1).active = repeater.itemAt(i).active + repeater.itemAt(i+1).opacity = repeater.itemAt(i).opacity + repeater.itemAt(i).color = Theme.secondaryColor + repeater.itemAt(i).active = 0 + repeater.itemAt(i).opacity = 0.1 + } + } + centerX += 1 + } + ghost() + } + + // Score function: + // Set 10 points for each full line: ok! + // Set 100 point bonus for combos: ok! + // Empty full lines and traslate other dots: ok! + // Increase speed each 1000 points! ok! + // Set 1000 point bonus for 4 lines combo: ok! + + function score() { + var score = 0 + var lines = [] + for (var i = 1; i < 16; i++) { + var full = 0 + for (var j = 1; j < 11; j++) { + if (repeater.itemAt(i*12+j).active === 2) { + full++ + } + } + if (full === 10) + lines[lines.length] = i + } + linesTotal=linesTotal+lines.length + linesValue = linesValue+lines.length + + // Bonus + if (lines.length > 0) { + if ( comboTimeout.running ) { + combo += 1 + gravityBreak += 1 + for (var index = 0; index < 204; index++) + if ( repeater.itemAt(index).active === 3) + repeater.itemAt(index).glowing = true + } + countTimer.count = 0 + countTimer.restart() // STARTING COUNTTIMER + comboTimeout.restart() + + if (lines.length === 4) { ////// Fyra i rad bonus grej + if (backtoback) { + if (sfxEnabled) b2b.play() + infoLabel.text = "BACK TO BACK TETRIS\nCOMBO x "+combo + infoLabel.opacity = 0.8 + infoTimer.restart() + score += 1200*level + backtoback=false + } else { + if (sfxEnabled) fourrows.play() + infoLabel.text = "TETRIS\nCOMBO x "+combo + infoLabel.opacity = 0.8 + infoTimer.restart() + score += 800*level + backtoback=true + } + } else if (lines.length === 3) { + if (sfxEnabled) bonuss.play() + backtoback=false + infoLabel.text = "3 LINES\nCOMBO x "+combo + infoLabel.opacity = 0.8 + infoTimer.restart() + score += 500*level + } else if (lines.length === 2) { + if (sfxEnabled) bonuss.play() + backtoback=false + infoLabel.text = "2 LINES\nCOMBO x "+combo + infoLabel.opacity = 0.8 + infoTimer.restart() + score += 300*level + } else if (lines.length === 1) { + if (sfxEnabled) bonuss.play() + backtoback=false + infoLabel.text = "1 LINE\nCOMBO x "+combo + infoLabel.opacity = 0.8 + infoTimer.restart() + score += 100*level + } + } + + scoreValue += score*combo//*(level+1) + speedValue += score + + // Increase Level + + if (linesValue >= 10) { + linesValue = 0 + level += 1 + // difficulty -= 0.014 + functions.gameLevel(level) + functions.gameBonus(combo,1) + functions.gameLevel(level) + } + + // Da rivedere ( forse ora va bene ) + + for (var k = 0; k < lines.length; k++) { + for ( i = lines[k]; i > 0; i--) { + if ( i === 1) { + for ( j = 1; j < 11; j++) { + repeater.itemAt(i*12+j).color = Theme.secondaryColor + repeater.itemAt(i*12+j).active = 0 + repeater.itemAt(i*12+j).opacity = 0.1 + } + } else + for ( j = 1; j < 11; j++) { + if (repeater.itemAt(i*12+j).active !== 3){ + repeater.itemAt(i*12+j).color = repeater.itemAt(i*12+j-12).color + repeater.itemAt(i*12+j).active = repeater.itemAt(i*12+j-12).active + repeater.itemAt(i*12+j).opacity = repeater.itemAt(i*12+j-12).opacity + } + } + + } + } + if ( gravityBreak === 4 ) { // Insert Gravity label + gravityBreak = 0 + functions.gameBonus(0,1) + infoLabel.text = "GRAVITY BONUS\nCOMBO x "+combo + infoLabel.opacity = 0.8 + infoTimer.restart() + functions.gravity() + } + } + + Timer { + id: comboTimeout + running: false + repeat: false + interval: 10000-((level*100)/2) + onTriggered: { + for (var index = 0; index < 204; index++) { + if ( repeater.itemAt(index).active === 3){ + repeater.itemAt(index).glowing = false + repeater.itemAt(index).opacity = 1 + } + } + backtoback = false + combo = 1 + gravityBreak = 1 + running = false + countTimer.running = false + countTimer.count = 0 + } + } + Timer { + id: countTimer + property int count: 0 + + running: false + repeat: true + interval: comboTimeout.interval/11 + + onTriggered: { + var combi + //////////////////////////////////////// + functions.bonusCount(10-count) + ///////////////////////////////////////// + count += 1 + + if (count == 11) { + comboTimeout.stop() + infoLabel.text="LOST COMBO" + infoLabel.opacity = 0.8 + infoTimer.restart() + /////////////////////////////// + functions.bonusEnd() + ///////////////////////////////// + for (var index = 0; index < 204; index++) { + if ( repeater.itemAt(index).active === 3){ + repeater.itemAt(index).glowing = false + repeater.itemAt(index).opacity = 1 + } + } + combo = 1 + gravityBreak = 1 + count = 0 + countTimer.running = false + } + } + + } + SoundEffect { + id: b2b + // volume: 0.9 + source: "../data/back2back.wav" + //loops: SoundEffect.Infinite + } + Audio { + id: countdown + volume: 0.6 + autoLoad: true + //source: "bonus.wav" + } + SoundEffect { + id: rotate + volume: 0.8 + source: "../data/rotate.wav" + } + SoundEffect { + id: bonuss + source: "../data/bonus.wav" + } + SoundEffect { + id: fourrows + source: "../data/tetris.wav" + } + SoundEffect { + id: gravi + source: "../data/gravity.wav" + } + SoundEffect { + id: bottom + // volume: 0.8 + source: "../data/down.wav" + } + SoundEffect { + id: instantBottom + volume: 0.6 + source: "../data/harddrop.wav" + } + SoundEffect { + id: nobonus + source: "../data/nobonus.wav" + } + SoundEffect { + id: levelup + source: "../data/levelup.wav" + } + + + Audio { + id: playMusic + autoPlay: false + autoLoad: true + + loops: Audio.Infinite + source: musicEnabled ? musicScore : "" //http://nectarine.from-de.com/necta192" : "" + volume: 0.6 + } +} // Item End + diff --git a/qml/game/HighScoreModel.qml b/qml/game/HighScoreModel.qml new file mode 100644 index 0000000..40e2ab2 --- /dev/null +++ b/qml/game/HighScoreModel.qml @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.LocalStorage 2.0 + +// Models a high score table. +// +// Use this component like this: +// +// HighScoreModel { +// id: highScores +// game: "MyCoolGame" +// } +// +// Then use either use the top-score properties: +// +// Text { text: "HI: " + highScores.topScore } +// +// or, use the model in a view: +// +// ListView { +// model: highScore +// delegate: Component { +// ... player ... score ... +// } +// } +// +// Add new scores via: +// +// saveScore(newScore) +// +// or: +// +// savePlayerScore(playerName,newScore) +// +// The best maxScore scores added by this method will be retained in an SQL database, +// and presented in the model and in the topScore/topPlayer properties. +// + +ListModel { + id: model + property string game: "" + property int topScore: 0 + property string topPlayer: "" + property int place: 0 + property int maxScores: 10 + property bool newScore: false + + onGameChanged: fillModel() + + function __db() + { + return LocalStorage.openDatabaseSync("HighScoreModel", "1.0", "Generic High Score Functionality for QML", 1000000); + } + function __ensureTables(tx) + { + tx.executeSql('CREATE TABLE IF NOT EXISTS HighScores(game TEXT, score INT, player TEXT)', []); + } + + function compareScore(point) { + __db().transaction( function(tx) { + __ensureTables(tx); + var rs = tx.executeSql("SELECT score,player FROM HighScores WHERE game=? ORDER BY score ASC", [game]); + if (rs.rows.length >= 10) { + if (point > rs.rows.item(0).score) newScore = true; else newScore = false;//return true; else return false; + } else newScore = true + var length = rs.rows.length + if (newScore){ + var i=0 + try { + while (point>rs.rows.item(i).score) i++; + place = (lenght-i)-1 + } catch (e) {place = (length-i)+1} + } + }) + } + + function fillModel() { + __db().transaction( + function(tx) { + __ensureTables(tx); + var rs = tx.executeSql("SELECT score,player FROM HighScores WHERE game=? ORDER BY score DESC", [game]); + model.clear(); + if (rs.rows.length > 0) { + topScore = rs.rows.item(0).score + topPlayer = rs.rows.item(0).player + // console.log('ts 0: '+rs.rows.item(0).score) + //console.log('ts 9: '+rs.rows.item(0).score) + for (var i=0; i maxScores) + tx.executeSql("DELETE FROM HighScores WHERE game=? AND score <= ?", + [game, rs.rows.item(maxScores).score]); + } + } + ) + } + + function savePlayerScore(player,score) { + __db().transaction( + function(tx) { + __ensureTables(tx); + tx.executeSql("INSERT INTO HighScores VALUES(?,?,?)", [game,score,player]); + fillModel(); + } + ) + } + + function saveScore(score) { + savePlayerScore("player",score); + } + + function clearScores() { + __db().transaction( + function(tx) { + tx.executeSql("DELETE FROM HighScores WHERE game=?", [game]); + fillModel(); + } + ) + } + + Component.onCompleted: { fillModel() } +} diff --git a/qml/game/OnlineHighScoreModel.qml b/qml/game/OnlineHighScoreModel.qml new file mode 100644 index 0000000..9c36e13 --- /dev/null +++ b/qml/game/OnlineHighScoreModel.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + +} diff --git a/qml/game/Square.qml b/qml/game/Square.qml new file mode 100644 index 0000000..8be1106 --- /dev/null +++ b/qml/game/Square.qml @@ -0,0 +1,33 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +GlassItem { + property int active: 0 // 0 = empty; 1 = active; 2 = inactive + property bool ghost: false + property bool glowing: false + id: square + width: Theme.paddingLarge*2 + height: width + radius: dots === 0 ? 0.4 : 0 // 0 ? 0.2 : 0 + falloffRadius: dots === 0 ? 0.25 : 2 // 0 ? 0.25 : 2 + color: Theme.secondaryColor + opacity: 0.1 + Timer { + property bool up + id: glowingTimer + running: glowing + repeat: true + interval: 30 + onTriggered: { + if (opacity === 1){ + up = false + } else if (opacity < 0.5) { + up = true + } + if (up) + opacity += 0.05 + else + opacity -= 0.05 + } + } +} diff --git a/qml/harbour-tetrafish.qml b/qml/harbour-tetrafish.qml new file mode 100644 index 0000000..1aecb4d --- /dev/null +++ b/qml/harbour-tetrafish.qml @@ -0,0 +1,40 @@ +/* + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Jolla Ltd nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import "pages" + +ApplicationWindow { + initialPage: Component { GamePage { } } + cover: Qt.resolvedUrl("cover/CoverPage.qml") + allowedOrientations: defaultAllowedOrientations +} + diff --git a/qml/js/settings.js b/qml/js/settings.js new file mode 100644 index 0000000..7b49eb8 --- /dev/null +++ b/qml/js/settings.js @@ -0,0 +1,76 @@ +var db = undefined; +function settings_db_open() { + if (db == undefined) + db = LocalStorage.openDatabaseSync("harbour-tetrafish", "1.0", "StorageDatabase", 100000); + return db; +} +/// GAME SETTINGS +function getValue(setting,def) { + var db = settings_db_open(); + var res=def; + try { + db.transaction(function(tx) { + var rs = tx.executeSql('SELECT value FROM settings WHERE setting=?;', [setting]); + if (rs.rows.length > 0) { + res = rs.rows.item(0).value; + } else { + res = def; + } + }) + } catch (err) { + res = def + }; + return res +} + +function setValue(setting, value) { + var db = settings_db_open(); + var res = ""; + db.transaction(function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS settings(setting TEXT UNIQUE, value TEXT)'); + var rs = tx.executeSql('INSERT OR REPLACE INTO settings VALUES (?,?);', [setting,value]); + if (rs.rowsAffected > 0) { + res = "OK"; + + } else { + res = "Error"; + } + } + ); + return res; +} +/// LOAD/SAVE GAME +function getSave(setting,def) { + var db = settings_db_open(); + var res=def; + try { + db.transaction(function(tx) { + var rs = tx.executeSql('SELECT value FROM savegame WHERE setting=?;', [setting]); + if (rs.rows.length > 0) { + res = rs.rows.item(0).value; + } else { + res = def; + } + }) + } catch (err) { + res = def + }; + return res +} + +function setSave(setting, value) { + var db = settings_db_open(); + var res = ""; + db.transaction(function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS settings(savegame TEXT UNIQUE, value TEXT)'); + var rs = tx.executeSql('INSERT OR REPLACE INTO savegame VALUES (?,?);', [setting,value]); + if (rs.rowsAffected > 0) { + res = "OK"; + + } else { + res = "Error"; + } + } + ); + return res; +} diff --git a/qml/pages/About.qml b/qml/pages/About.qml new file mode 100644 index 0000000..db1a1eb --- /dev/null +++ b/qml/pages/About.qml @@ -0,0 +1,97 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +Page { + id: page + SilicaFlickable { + id: flickable + anchors.fill: page + contentHeight: column.height + Column { + id: column + width: parent.width - Theme.paddingLarge*2 + spacing: 20 + anchors { + left: parent.left + leftMargin: Theme.paddingLarge + } + PageHeader { title: qsTr("TetraFish") } + Label { + width: parent.width + text: qsTr("A remake of SailTris which is a remake of Tetris\n") + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeMedium + horizontalAlignment: Text.AlignJustify + wrapMode: Text.WordWrap + } + + Label { + width: parent.width + text: qsTr("TetraFish is a remake of the game SailTris by Billy Halley, available on OpenRepos. I wanted to contribute to SailTris with some changes and fixes but Billy Halley had stopped he's development on Sailfish and SailTris and so was TetraFish with the kind approval of Billy Halley born. \n\nTetra fish is a common fish in a lot of aquariums and the original game Tetris got it's name from the greek numerical prefix tetra (4) symbolising the 4 segments making up every block in a Tetris game.") + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeMedium + horizontalAlignment: Text.AlignJustify + wrapMode: Text.WordWrap + } + Label { + width: parent.width + text: qsTr("Controls") + color: Theme.highlightColor + anchors.right: parent.right + anchors.rightMargin: Theme.paddingLarge + + font.pixelSize: Theme.fontSizeLarge + horizontalAlignment: Text.AlignRight + wrapMode: Text.WordWrap + } + Label { + width: parent.width + text: qsTr("TetraFish i controlled by multitouch\n\nUse one finger to swipe left, right and down (tap with second finger to rotate while moving)\n\nUse two fingers to swipe down without the ability to swipe left or right (handy to avoid mistakes :-)...)\n\nSwipe down with three fingers to make an instant down\n\nSwipe up with two fingers to pause the game") + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeMedium + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + } + Label { + width: parent.width + text: qsTr("Scoring") + color: Theme.highlightColor + anchors.right: parent.right + anchors.rightMargin: Theme.paddingLarge + + font.pixelSize: Theme.fontSizeLarge + horizontalAlignment: Text.AlignRight + wrapMode: Text.WordWrap + } + Label { + width: parent.width + text: qsTr("Block soft down = 1 x level\nBlock hard down = 2 x level\nBlock instant down = 3 x level\nSingle line clear = 100 x level\ntwo line clear = 300 x level\nthree line clear = 500 x level\nfour line clear (Tetris) = 800 x level\nBack to back Tetris (2 four line clear in a row) = 1200 x level\ncombo bonus = 50 x combo x level\n") + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeMedium + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + } + Label { + width: parent.width + text: qsTr("Credits") + color: Theme.highlightColor + anchors.right: parent.right + anchors.rightMargin: Theme.paddingLarge + + font.pixelSize: Theme.fontSizeLarge + horizontalAlignment: Text.AlignRight + wrapMode: Text.WordWrap + } + Label { + width: parent.width + text: qsTr("Music by kraku\n\nSounds from different sources (should be public domain according to the same sources!)\n\nSailTris by Billy Halley\n\nTetris by Alexey Pajitnov\n\nHighscoreModel by Nokia\n\nPut together and modified with added functions by me (nesnomis)\n\nAll copyrights belong to their respective owners\n\n") + color: Theme.primaryColor + font.pixelSize: Theme.fontSizeMedium + horizontalAlignment: Text.AlignLeft + wrapMode: Text.WordWrap + } + } + + VerticalScrollDecorator { flickable: flickable } + } +} diff --git a/qml/pages/FirstPage.qml b/qml/pages/FirstPage.qml new file mode 100644 index 0000000..9a00fc3 --- /dev/null +++ b/qml/pages/FirstPage.qml @@ -0,0 +1,43 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +Page { + id: page + + // The effective value will be restricted by ApplicationWindow.allowedOrientations + allowedOrientations: Orientation.All + + // To enable PullDownMenu, place our content in a SilicaFlickable + SilicaFlickable { + anchors.fill: parent + + // PullDownMenu and PushUpMenu must be declared in SilicaFlickable, SilicaListView or SilicaGridView + PullDownMenu { + MenuItem { + text: qsTr("Show Page 2") + onClicked: pageStack.animatorPush(Qt.resolvedUrl("SecondPage.qml")) + } + } + + // Tell SilicaFlickable the height of its content. + contentHeight: column.height + + // Place our content in a Column. The PageHeader is always placed at the top + // of the page, followed by our content. + Column { + id: column + + width: page.width + spacing: Theme.paddingLarge + PageHeader { + title: qsTr("UI Template") + } + Label { + x: Theme.horizontalPageMargin + text: qsTr("Hello Sailors") + color: Theme.secondaryHighlightColor + font.pixelSize: Theme.fontSizeExtraLarge + } + } + } +} diff --git a/qml/pages/GamePage.qml b/qml/pages/GamePage.qml new file mode 100644 index 0000000..d7824b6 --- /dev/null +++ b/qml/pages/GamePage.qml @@ -0,0 +1,728 @@ +/***************************************************************************** +** ** +** Created by Antonio Mancini ** +** Contact: ** +** This is a version of classic Tetris game for SailfishOS developed ** +** entirely by me, no copyright infringement intended. ** +** ** +*****************************************************************************/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import QtQuick.LocalStorage 2.0 +import QtQuick.Particles 2.0 +import "../js/settings.js" as Settings +import "../game" + +Page { + id: page + + + HighScoreModel { + id: highScoreModel + game: hsdb + } + + Functions { + id: functions + } + property bool onlineEnabled: Settings.getValue("onlineEnabled",0) + property string hsdb: onlineEnabled ? "online" : "local" + property int dots: Settings.getValue("dots",1) + property int ghostEnabled: Settings.getValue("ghostEnabled",1) + property int musicEnabled: Settings.getValue("musicEnabled",1) + property int sfxEnabled: Settings.getValue("sfxEnabled",1) + property string musicScore: "../data/Kraku - Tetris part 1.ogg" //: Settings.getValue("musicScore","") + property int scoreValue + property int speedValue + property int linesValue + property int level: 1 + property int linesTotal: 0 + property int activeBlock + property int futureBlock: -1 + property int combo: 1 + property int gravityBreak: 1 + property bool pauseVal + property int downSpeed: 2500 + property int comboSpeed: 10000 + + // 0 = l_normal; 1 = l_reverse; + // 2 = s_normal; 3 = s_reverse; + // 4 = t_normal; 5 = square; + // 6 = line + + property variant activeColor + property variant futureColor + + property real centerX + property real centerY + + Timer { + id: gameOverTimer + property int i: 15 + property int j: 10 + property bool clear: true + property bool done: false + interval: 10 + repeat: true + onTriggered : { + if (clear) { + if ( j === 0) { + i-- + j = 10 + } else { + if ( i === 0) { + clear = false + done = true + j = 10 + i = 15 + } else { + var index = i*12+j + repeater.itemAt(index).color = Theme.secondaryHighlightColor//"black"//"transparent"//Theme.secondaryColor + repeater.itemAt(index).opacity = 0.5//0.5 + j-- + } + } + } else { + if ( j === 0) { + i-- + j = 10 + } else { + if ( i === 0) { + running = false + clear = true + if (highScoreModel.newScore) { + highScoreModel.newScore = false + getName.visible = true ; + } else { + pullDownMenu.enabled = true + splash.visible = true + } + i = 15 + j = 10 + } + else { + done = false + index = i*12+j + repeater.itemAt(index).opacity = 0.1 + j-- + } + } + } + } + } + + function downSpeedCalc(level) { + if (level>1){ + for ( var i = 1; i < level; i++) { + var ds = downSpeed/15 + downSpeed -= ds + } + return downSpeed + } + } + + function comboSpeedCalc(level) { + for ( var i = 1; i < level; i++) { + comboSpeed = comboSpeed/30 + } + return comboSpeed+200 + } + + Timer { + id: downTimer + interval: downSpeed+150//interval-(interval/20)+150 //difficulty*(1338*Math.pow(Math.E,-0.07*level)+150) //difficulty*(1338*Math.pow(Math.E,-0.26*level)+150) + repeat: true + running: false + onTriggered: { + functions.flow() + speedValue += 1 + } + } + + SilicaFlickable { + id: root + anchors.fill: page + contentHeight : height + + PushUpMenu { + id:pushUpMenu + enabled: false + visible: false + MenuItem { + id: pauseMenuItem + text: qsTr("Resume") + onClicked: functions.pause() + } + } + + PullDownMenu { + id: pullDownMenu + MenuItem { + text: qsTr("About") + onClicked: pageStack.push("About.qml") + } + + MenuItem { + text: qsTr("Settings") + onClicked: { + var dialog = pageStack.push("SettingsDialog.qml", {"dots": dots, "ghostEnabled": ghostEnabled, "musicEnabled": musicEnabled, "sfxEnabled": sfxEnabled, "musicScore": musicScore, "onlineEnabled": onlineEnabled}) + dialog.accepted.connect(function() { + + dots = dialog.dots + ghostEnabled = dialog.ghostEnabled + musicEnabled = dialog.musicEnabled + sfxEnabled = dialog.sfxEnabled + onlineEnabled = dialog.onlineEnabled + highScoreModel.place = 0 + Settings.setValue("dots",dots) + Settings.setValue("ghostEnabled",ghostEnabled) + Settings.setValue("musicEnabled",musicEnabled) + Settings.setValue("sfxEnabled",sfxEnabled) + Settings.setValue("onlineEnabled",onlineEnabled) + // highScoreModel.game = "local" + }) + } + } + + MenuItem { + text: functions.garunning ? functions.gapaused ? qsTr("Resume") : qsTr("Pause") : qsTr("New Game") + onClicked: { + if (functions.garunning) { + functions.pause() + } else { + + splash.visible = false + highscores.visible = false + functions.newGame() + } + } + } + } + + Label { + id: score + text: qsTr("LEVEL ") + "\n" + qsTr("SCORE ") + "\n" + qsTr("LINES ") + color: Theme.secondaryHighlightColor + font.family: Theme.fontFamilyHeading + font.pixelSize: Theme.fontSizeMedium + anchors { + left: parent.left + leftMargin: Theme.paddingMedium + bottom: futureGrid.bottom + } + } + Label { + id: scoreValues + text: level + "\n" + scoreValue + "\n" + linesTotal + color: Theme.highlightColor + font.family: Theme.fontFamilyHeading + font.bold: true + font.pixelSize: Theme.fontSizeMedium + horizontalAlignment: Text.AlignRight + anchors { + right: bonusPanel.left + rightMargin: Theme.paddingLarge + bottom: futureGrid.bottom + } + } + + Rectangle { + id: bonusPanel + radius: height / 2 + height: futureGrid.height + width: height + color: Theme.highlightDimmerColor + + opacity: bonusLabel.opacity > 0 ? 0.8 : 0//bonusLabel.opacity + anchors { + horizontalCenter: parent.horizontalCenter + top: futureGrid.top + } + } + + Label { + id: bonusLabel + text: ""///functions.garunning ? "" : "PLAY" + color: Theme.highlightColor + font.pixelSize: functions.garunning ? bonusPanel.height - (Theme.paddingMedium * 2) : Theme.fontSizeLarge + font.bold: true + anchors.verticalCenter: bonusPanel.verticalCenter + anchors.horizontalCenter: bonusPanel.horizontalCenter + opacity: 0//functions.garunning ? 0 : 1//infoTimer.running ? 1 : 0 + Behavior on opacity { + FadeAnimator {} + } + } + + Grid { + id: futureGrid + y: (parent.height-rect.height-height)/2 + anchors { + right: parent.right + rightMargin: Theme.paddingLarge + } + columns: 4 + rows: 3 + Repeater { + id: futureRepeater + model: 12 + delegate: Dot {width: Theme.paddingLarge*5/3 ;color: Theme.highlightBackgroundColor; opacity: 0.1} + } + } + + Timer { // EXPERIMENT + id: hssplachTimer + running: splash.visible || highscores.visible && Qt.application.active && !contextMenu.visible? true : false//!functions.garunning && !gameOverTimer.running ? true : false + interval: 8000 + repeat: true + onTriggered: if (highscores.visible) {highscores.visible=false;splash.visible = true;} else {highscores.visible=true;splash.visible=false}//highscores.visible ? splash.visible = true : high.visible = false + } + + Rectangle { + id: rect + width: grid.width + height: grid.height + anchors { + bottom: parent.bottom + } + border.color: "transparent" + color: "black" + opacity: 0.8//0.8 + Grid { + id: grid + columns: 12 + rows: 17 + Repeater { + id: repeater + model: 204 + delegate: Dot {width: page.width/12 ;color: Theme.highlightBackgroundColor; opacity: 0.1} + onItemAdded: { // Bordi "muro"... seriamente, si può fare di meglio, 576 caratteri per una linea è più brutto di questo commento, da aggiungere a newGame() magari + if (index < 12 || index > 191 || index === 0 || index === 12 || + index === 24 || index === 36 || index === 48 || index === 60 || + index === 72 || index === 84 || index === 96 || index === 108 || + index === 120 || index === 132 || index === 144 || index === 156 || + index === 168 || index === 180 || index === 23 || index === 35 || + index === 47 || index === 59 || index === 71 || index === 83 || + index === 95 || index === 107 || index === 119 || index === 131 || + index === 143 || index === 155 || index === 167 || index === 179 || index === 191) + { + itemAt(index).active = 3 + itemAt(index).color = Theme.highlightBackgroundColor + itemAt(index).opacity = 0.5 + } + } + } + } + + /* ParticleSystem { + id: sys + } + + Emitter { + anchors.fill: parent + system: sys + ImageParticle { + anchors.fill: parent + system: sys + source: "../data/dot.png" + clip: true + id: redblip + } + velocity: PointDirection {yVariation: 16; xVariation: 5} + acceleration: PointDirection {y: -16} + lifeSpan: Emitter.InfiniteLife + maximumEmitted: 1000 + } */ + + MultiPointTouchArea { + id: mouseArea + property int offsetX: grid.width/14 + property int offsetY: (grid.height/19) + property int blahX: (parent.width - (grid.width-point1.x))/offsetX + property int blahY: (parent.height - (grid.height-point1.y))/offsetY + property int prevX + property int prevY + property int initX + property int initY + property int inter: 85 + property int plus: 0 + x: 0 + y: 0 + + anchors.fill: grid + anchors.margins: offsetX + mouseEnabled: true + + touchPoints: [ + TouchPoint { id: point1 }, + TouchPoint { id: point2 }, + TouchPoint { id: point3 } + ] + + + onTouchUpdated: { + if (functions.maenabled){ + if (point1.pressed && point2.pressed && point3.pressed){ + if (blahY > prevY+2){ + prevY=blahY + functions.instantDown() + } + } else if (point1.pressed && point2.pressed && !point3.pressed){ + if (blahY > prevY){ + prevY=blahY + functions.down() + } + if (blahY < prevY-2){ + prevY=blahY + functions.pause() + } + } else if (point1.pressed && !point2.pressed && !point3.pressed){ + //if (blahY==prevY) var plus = 0; else plus = 1; + if (blahY > prevY){ + plus = 1; + functions.down() + prevY=blahY + } else { + if (blahX < prevX-plus) { + functions.left() + prevX=blahX + plus = 0 + } + if (blahX > prevX+plus){ + functions.right() + prevX=blahX + plus = 0 + } + } + /* if (blahY > prevY){ + prevY=blahY + functions.down() + } */ + } + // console.log(blahX + " - " + prevX) + } + } + + onReleased: { + if (blahY == initY && blahX == initX && !point1.pressed && !point2.pressed || point1.pressed && !point2.pressed){ + functions.rotate() + } + + functions.maenabled = true + } + + onPressed: { + prevX = blahX + initX = blahX + prevY = blahY + initY = blahY + } + } + Label { + id: gameOverLabel + anchors { + horizontalCenter: rect.horizontalCenter + verticalCenter: rect.verticalCenter + } + style: Text.Outline; styleColor: Theme.secondaryHighlightColor + text: "GAME OVER" + color: Theme.highlightColor + font.pixelSize: Theme.fontSizeHuge + font.bold: true + visible: !functions.garunning && opacity > 0 ? true : false + opacity: gameOverTimer.running ? 1 : 0 + + Behavior on opacity { + FadeAnimator {} + } + + } + Label { + id: pauseLabel + anchors { + horizontalCenter: rect.horizontalCenter + verticalCenter: rect.verticalCenter + } + style: Text.Outline; styleColor: Theme.secondaryHighlightColor + text: "PAUSED" + color: Theme.highlightColor + font.pixelSize: Theme.fontSizeHuge + font.bold: true + visible: false + states: [ + State { when: stateVisible; + PropertyChanges { target: myRect; opacity: 1.0 } + }, + State { when: !stateVisible; + PropertyChanges { target: myRect; opacity: 0.0 } + } + ] + transitions: Transition { + NumberAnimation { property: "opacity"; duration: 300} + } + } + Label { + id: infoLabel + anchors { + verticalCenter: rect.verticalCenter + horizontalCenter: rect.horizontalCenter + } + width: parent.width + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + style: Text.Outline; styleColor: Theme.secondaryHighlightColor; + color: Theme.highlightColor + font.pixelSize: Theme.fontSizeLarge*2 + font.bold: true + opacity: infoTimer.running ? 1 : 0 + + Behavior on opacity { + FadeAnimator {} + } + } + Timer { + id: infoTimer + running: false + repeat: false + interval: 1100 + onTriggered: { + infoLabel.opacity = 0 + } + } + } + + + + Rectangle { + id: highscores + anchors.verticalCenter: rect.verticalCenter + anchors.horizontalCenter: rect.horizontalCenter + width: rect.width - (rect.width/6) + height: rect.height - (rect.height/8)+Theme.paddingSmall + visible: splash.visible || functions.garunning || getName.visible && opacity === 0 ? false : true //!splash.visible && !getName.visible && !functions.garunning + color: "transparent"//Theme.highlightBackgroundColor + opacity: !visible ? 0 : 0.8 + + Behavior on opacity { + FadeAnimator {onStopped: console.log("highscores fade animation klar")} + } + + Label { + id: hs1 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: Theme.paddingLarge + text: "HIGHSCORES ["+highScoreModel.game+"]" + color: Theme.secondaryHighlightColor + font.pixelSize: Theme.fontSizeLarge + font.bold: true + MouseArea { + anchors.fill: hs1 + onClicked: { + !onlineEnabled ? onlineEnabled=true : onlineEnabled=false + console.log("ONLINE: "+highScoreModel.game) + } + } + } + + ListView { + width: parent.width; height: parent.height - hs1.height + enabled: true + anchors.top: hs1.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: Theme.paddingLarge + model: highScoreModel + delegate: ListItem { + id: showDelegate + // menu: contextMenu + highlighted: index == highScoreModel.place-1 ? true : false + width: ListView.view.width + height: menuOpen ? contextMenu.height + pn.height + Theme.paddingSmall : pn.height + Theme.paddingSmall//contextMenu.height + contentItem.height + Theme.paddingMedium : contentItem.height + Theme.paddingMedium + contentHeight: pn.height + Theme.paddingSmall + Text { + id: pn + font.pixelSize: Theme.fontSizeLarge + anchors.left: parent.left + anchors.leftMargin: Theme.paddingMedium + color: Theme.highlightColor + text: index+(1)+"." + } + Text { + id: pnm + font.pixelSize: Theme.fontSizeLarge + anchors.left: pn.right + anchors.leftMargin: Theme.paddingMedium + anchors.top: pn.top + color: Theme.highlightColor + text: player + } + Text { + font.pixelSize: Theme.fontSizeLarge + anchors.right: parent.right + anchors.rightMargin: Theme.paddingMedium + anchors.top: pn.top + color: Theme.highlightColor + text: score + } + + } + } + } + + ContextMenu { + id: contextMenu + width: rect.width - (rect.width/6) + anchors.horizontalCenter: highscores.horizontalCenter + MenuItem { + id:onlineMenu + + visible: true + text: qsTr("Share score online") + onClicked: { + var uuid = generateUUID() + console.log("UUID: "+uuid) + /* internal ? ps(source) : model.id == 0 ? ps(source) : cps(model.id) + radioStation = title + if (icon == "0") picon = "../allradio-data/images/allradio.png" + else if (icon.search(".png")>0) picon = icon.toLowerCase(); // The old save in database + else picon = "../allradio-data/images/"+icon+".png"; + website = (Qt.resolvedUrl(site)) */ + } + } + } + + Rectangle { + id: splash + anchors.verticalCenter: rect.verticalCenter + anchors.horizontalCenter: rect.horizontalCenter + width: rect.width - (rect.width/6) + height: rect.height - (rect.height/8) + Theme.paddingSmall + + visible: functions.garunning || getName.visible || highscores.visible && opacity === 0 ? false : true//true && !functions.garunning//!functions.garunning && !getName.visible && !highscores.visible//&& !gameOverTimer.done ? true : false //&& gameOverTimer.running ? done : + color: "transparent"//Theme.highlightBackgroundColor + opacity: !visible ? 0 : 0.8 + + Behavior on opacity { + FadeAnimator {} + } + Column { + spacing: Theme.paddingLarge + anchors { + horizontalCenter: splash.horizontalCenter + verticalCenter: splash.verticalCenter + } + Label { + id: name + anchors.horizontalCenter: parent.horizontalCenter + text: "TetraFish" + color: Theme.highlightColor + font.pixelSize: Theme.fontSizeHuge + font.bold: true + } + Image { + anchors.horizontalCenter: parent.horizontalCenter + id: tetraFish + width: name.width + height: width + source: "../data/tetrafish.png" + } + + Label { + id: info1 + + anchors.horizontalCenter: parent.horizontalCenter + text: "A TETRIS clone" + color: Theme.secondaryHighlightColor + font.pixelSize: Theme.fontSizeLarge + font.bold: true + } + Label { + id: info2 + anchors.horizontalCenter: parent.horizontalCenter + text: "Presented by NESNOMIS" + color: Theme.secondaryHighlightColor + font.pixelSize: Theme.fontSizeSmall + font.bold: true + } + } + } + Rectangle { + id: getName + anchors.fill: rect + anchors.left: rect.left + anchors.top: rect.top + width: rect.width + height: rect.height + + visible: false//!functions.garunning && !gameOverTimer.done ? true : false //&& gameOverTimer.running ? done : + color: "black"//Theme.highlightBackgroundColor + opacity: !visible ? 0 : 0.8 + + Behavior on opacity { + FadeAnimator {} + } + + Label { + id: congrat + anchors.horizontalCenter: parent.horizontalCenter + text: "CONGRATULATIONS"//+highScoreModel.place + color: Theme.highlightColor + anchors.bottom: namn.top + font.pixelSize: Theme.fontSizeExtraLarge + font.bold: true + } + Label { + id: namn + anchors.horizontalCenter: parent.horizontalCenter + text: "You placed No. "+highScoreModel.place + color: Theme.primaryColor + anchors.bottom: textField.top + anchors.bottomMargin: Theme.paddingLarge * 3 + font.pixelSize: Theme.fontSizeMedium + font.bold: true + } + + TextField { + id: textField + enabled: parent.visible + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + width: parent.width + maximumLength: 8 + focus: true + placeholderText: "ENTER YOUR NAME" + inputMethodHints: Qt.ImhUppercaseOnly | Qt.ImhPreferUppercase | Qt.ImhNoPredictiveText + horizontalAlignment: TextInput.AlignHCenter + font.bold: true + font.pixelSize: Theme.fontSizeHuge + font.capitalization: Font.AllUppercase + EnterKey.iconSource: "image://theme/icon-m-enter" //"image://theme/icon-m-enter-close" + EnterKey.onClicked: { + + getName.visible = false + splash.visible = false + highscores.visible = true + pullDownMenu.enabled = true + root.interactive = true + functions.saveHighscore(text.toUpperCase(),scoreValue) + } + } + } + + } + function generateUUID(){ + var d = new Date().getTime(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random()*16)%16 | 0; + d = Math.floor(d/16); + return (c === 'x' ? r : (r&0x3|0x8)).toString(16); + }); + return uuid; + } + + Component.onCompleted: splash.visible = true +} + diff --git a/qml/pages/SecondPage.qml b/qml/pages/SecondPage.qml new file mode 100644 index 0000000..6dbadf4 --- /dev/null +++ b/qml/pages/SecondPage.qml @@ -0,0 +1,30 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +Page { + id: page + + // The effective value will be restricted by ApplicationWindow.allowedOrientations + allowedOrientations: Orientation.All + + SilicaListView { + id: listView + model: 20 + anchors.fill: parent + header: PageHeader { + title: qsTr("Nested Page") + } + delegate: BackgroundItem { + id: delegate + + Label { + x: Theme.horizontalPageMargin + text: qsTr("Item") + " " + index + anchors.verticalCenter: parent.verticalCenter + color: delegate.highlighted ? Theme.highlightColor : Theme.primaryColor + } + onClicked: console.log("Clicked " + index) + } + VerticalScrollDecorator {} + } +} diff --git a/qml/pages/SettingsDialog.qml b/qml/pages/SettingsDialog.qml new file mode 100644 index 0000000..022cff8 --- /dev/null +++ b/qml/pages/SettingsDialog.qml @@ -0,0 +1,105 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +Dialog { + id: page + property int dots + property int ghostEnabled + property int musicEnabled + property int sfxEnabled + property int highscoreValue + property int onlineEnabled + + SilicaFlickable { + id: flickable + anchors.fill: parent + contentHeight: column.height + + Column { + id: column + width: page.width + spacing: Theme.paddingLarge + DialogHeader { + title: qsTr("Settings") + } + + TextSwitch { + id: dotSwitch + automaticCheck: false + checked: dots ? true : false + text: checked ? qsTr("Squares") : qsTr("Dots") + description: qsTr("Changes the shape of the blocks") + onClicked: { + if (dots === 0) { + dots = 1 + } + else { + dots = 0 + } + } + } + TextSwitch { + id: ghostSwitch + automaticCheck: false + checked: ghostEnabled ? true : false + text: checked ? qsTr("Ghost Enabled") : qsTr("Ghost Disabled") + description: qsTr("Display a hint of where the block will fall") + onClicked: { + if (ghostEnabled === 0) { + ghostEnabled = 1 + } + else { + ghostEnabled = 0 + } + } + } + TextSwitch { + id: sfxSwitch + automaticCheck: false + checked: sfxEnabled ? true : false + text: checked ? qsTr("SoundFX Enabled") : qsTr("SoundFX Disabled") + description: qsTr("Play sound effects in game") + onClicked: { + if (sfxEnabled === 0) { + sfxEnabled = 1 + } + else { + sfxEnabled = 0 + } + } + } + TextSwitch { + id: musicSwitch + automaticCheck: false + checked: musicEnabled ? true : false + text: checked ? qsTr("Music Enabled") : qsTr("Music Disabled") + description: qsTr("Play music in game") + enabled: true /// enable if music should be included?! + onClicked: { + if (musicEnabled === 0) { + musicEnabled = 1 + } + else { + musicEnabled = 0 + } + } + } + /* TextSwitch { + id: onlineSwitch + automaticCheck: false + checked: onlineEnabled ? true : false + text: checked ? qsTr("Online highscores Enabled") : qsTr("Online highscores Disabled") + description: qsTr("Save scores online to compare scores with other players") + enabled: true /// enable if music should be included?! + onClicked: { + if (onlineEnabled === 0) { + onlineEnabled = 1 + } + else { + onlineEnabled = 0 + } + } + } */ + } + } +} diff --git a/rpm/harbour-tetrafish.changes.in b/rpm/harbour-tetrafish.changes.in new file mode 100644 index 0000000..5c15ea0 --- /dev/null +++ b/rpm/harbour-tetrafish.changes.in @@ -0,0 +1,18 @@ +# Rename this file as harbour-tetrafish.changes to include changelog +# entries in your RPM file. +# +# Add new changelog entries following the format below. +# Add newest entries to the top of the list. +# Separate entries from eachother with a blank line. +# +# Alternatively, if your changelog is automatically generated (e.g. with +# the git-change-log command provided with Sailfish OS SDK), create a +# harbour-tetrafish.changes.run script to let mb2 run the required commands for you. + +# * date Author's Name version-release +# - Summary of changes + +* Sun Apr 13 2014 Jack Tar 0.0.1-1 +- Scrubbed the deck +- Hoisted the sails + diff --git a/rpm/harbour-tetrafish.changes.run.in b/rpm/harbour-tetrafish.changes.run.in new file mode 100644 index 0000000..d980bd1 --- /dev/null +++ b/rpm/harbour-tetrafish.changes.run.in @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Rename this file as harbour-tetrafish.changes.run to let sfdk automatically +# generate changelog from well formatted Git commit messages and tag +# annotations. Note that 'sfdk changelog' must be invoked as 'sfdk-changelog' here. + +sfdk-changelog + +# Here are some basic examples how to change from the default behavior. Run +# 'sfdk --help-maintaining' to learn more. + +# Use a subset of tags +#sfdk-changelog --tags refs/tags/my-prefix/* + +# Group entries by minor revision, suppress headlines for patch-level revisions +#sfdk-changelog --dense '/[0-9]+.[0-9+$' + +# Trim very old changes +#sfdk-changelog --since 2014-04-01 +#echo '[ Some changelog entries trimmed for brevity ]' + +# Use the subjects (first lines) of tag annotations when no entry would be +# included for a revision otherwise +#sfdk-changelog --auto-add-annotations diff --git a/rpm/harbour-tetrafish.spec b/rpm/harbour-tetrafish.spec new file mode 100644 index 0000000..13fb1ec --- /dev/null +++ b/rpm/harbour-tetrafish.spec @@ -0,0 +1,41 @@ +Name: harbour-tetrafish + +Summary: TetraFish +Version: 0.0.1 +Release: 1 +License: LICENSE +URL: http://example.org/ +Source0: %{name}-%{version}.tar.bz2 +Requires: sailfishsilica-qt5 >= 0.10.9 +BuildRequires: pkgconfig(sailfishapp) >= 1.0.2 +BuildRequires: pkgconfig(Qt5Core) +BuildRequires: pkgconfig(Qt5Qml) +BuildRequires: pkgconfig(Qt5Quick) +BuildRequires: desktop-file-utils + +%description +Tetris clone + + +%prep +%setup -q -n %{name}-%{version} + +%build + +%qmake5 + +%make_build + + +%install +%qmake5_install + + +desktop-file-install --delete-original --dir %{buildroot}%{_datadir}/applications %{buildroot}%{_datadir}/applications/*.desktop + +%files +%defattr(-,root,root,-) +%{_bindir}/%{name} +%{_datadir}/%{name} +%{_datadir}/applications/%{name}.desktop +%{_datadir}/icons/hicolor/*/apps/%{name}.png diff --git a/src/harbour-tetrafish.cpp b/src/harbour-tetrafish.cpp new file mode 100644 index 0000000..e4b9dd2 --- /dev/null +++ b/src/harbour-tetrafish.cpp @@ -0,0 +1,20 @@ +#ifdef QT_QML_DEBUG +#include +#endif + +#include + +int main(int argc, char *argv[]) +{ + // SailfishApp::main() will display "qml/harbour-tetrafish.qml", if you need more + // control over initialization, you can use: + // + // - SailfishApp::application(int, char *[]) to get the QGuiApplication * + // - SailfishApp::createView() to get a new QQuickView * instance + // - SailfishApp::pathTo(QString) to get a QUrl to a resource file + // - SailfishApp::pathToMainQml() to get a QUrl to the main QML file + // + // To display the view, call "show()" (will show fullscreen on device). + + return SailfishApp::main(argc, argv); +} diff --git a/translations/harbour-tetrafish-de.ts b/translations/harbour-tetrafish-de.ts new file mode 100644 index 0000000..f5a753d --- /dev/null +++ b/translations/harbour-tetrafish-de.ts @@ -0,0 +1,197 @@ + + + + + About + + TetraFish + + + + A remake of SailTris which is a remake of Tetris + + + + + TetraFish is a remake of the game SailTris by Billy Halley, available on OpenRepos. I wanted to contribute to SailTris with some changes and fixes but Billy Halley had stopped he's development on Sailfish and SailTris and so was TetraFish with the kind approval of Billy Halley born. + +Tetra fish is a common fish in a lot of aquariums and the original game Tetris got it's name from the greek numerical prefix tetra (4) symbolising the 4 segments making up every block in a Tetris game. + + + + Controls + + + + TetraFish i controlled by multitouch + +Use one finger to swipe left, right and down (tap with second finger to rotate while moving) + +Use two fingers to swipe down without the ability to swipe left or right (handy to avoid mistakes :-)...) + +Swipe down with three fingers to make an instant down + +Swipe up with two fingers to pause the game + + + + Scoring + + + + Block soft down = 1 x level +Block hard down = 2 x level +Block instant down = 3 x level +Single line clear = 100 x level +two line clear = 300 x level +three line clear = 500 x level +four line clear (Tetris) = 800 x level +Back to back Tetris (2 four line clear in a row) = 1200 x level +combo bonus = 50 x combo x level + + + + + Credits + + + + Music by kraku + +Sounds from different sources (should be public domain according to the same sources!) + +SailTris by Billy Halley + +Tetris by Alexey Pajitnov + +HighscoreModel by Nokia + +Put together and modified with added functions by me (nesnomis) + +All copyrights belong to their respective owners + + + + + + + FirstPage + + Show Page 2 + Zur Seite 2 + + + UI Template + UI-Vorlage + + + Hello Sailors + Hallo Matrosen + + + + GamePage + + Resume + + + + About + + + + Settings + + + + Pause + + + + New Game + + + + LEVEL + + + + SCORE + + + + LINES + + + + Share score online + + + + + SecondPage + + Nested Page + Unterseite + + + Item + Element + + + + SettingsDialog + + Settings + + + + Squares + + + + Dots + + + + Changes the shape of the blocks + + + + Ghost Enabled + + + + Ghost Disabled + + + + Display a hint of where the block will fall + + + + SoundFX Enabled + + + + SoundFX Disabled + + + + Play sound effects in game + + + + Music Enabled + + + + Music Disabled + + + + Play music in game + + + + diff --git a/translations/harbour-tetrafish.ts b/translations/harbour-tetrafish.ts new file mode 100644 index 0000000..81d1517 --- /dev/null +++ b/translations/harbour-tetrafish.ts @@ -0,0 +1,197 @@ + + + + + About + + TetraFish + + + + A remake of SailTris which is a remake of Tetris + + + + + TetraFish is a remake of the game SailTris by Billy Halley, available on OpenRepos. I wanted to contribute to SailTris with some changes and fixes but Billy Halley had stopped he's development on Sailfish and SailTris and so was TetraFish with the kind approval of Billy Halley born. + +Tetra fish is a common fish in a lot of aquariums and the original game Tetris got it's name from the greek numerical prefix tetra (4) symbolising the 4 segments making up every block in a Tetris game. + + + + Controls + + + + TetraFish i controlled by multitouch + +Use one finger to swipe left, right and down (tap with second finger to rotate while moving) + +Use two fingers to swipe down without the ability to swipe left or right (handy to avoid mistakes :-)...) + +Swipe down with three fingers to make an instant down + +Swipe up with two fingers to pause the game + + + + Scoring + + + + Block soft down = 1 x level +Block hard down = 2 x level +Block instant down = 3 x level +Single line clear = 100 x level +two line clear = 300 x level +three line clear = 500 x level +four line clear (Tetris) = 800 x level +Back to back Tetris (2 four line clear in a row) = 1200 x level +combo bonus = 50 x combo x level + + + + + Credits + + + + Music by kraku + +Sounds from different sources (should be public domain according to the same sources!) + +SailTris by Billy Halley + +Tetris by Alexey Pajitnov + +HighscoreModel by Nokia + +Put together and modified with added functions by me (nesnomis) + +All copyrights belong to their respective owners + + + + + + + FirstPage + + Show Page 2 + + + + UI Template + + + + Hello Sailors + + + + + GamePage + + Resume + + + + About + + + + Settings + + + + Pause + + + + New Game + + + + LEVEL + + + + SCORE + + + + LINES + + + + Share score online + + + + + SecondPage + + Nested Page + + + + Item + + + + + SettingsDialog + + Settings + + + + Squares + + + + Dots + + + + Changes the shape of the blocks + + + + Ghost Enabled + + + + Ghost Disabled + + + + Display a hint of where the block will fall + + + + SoundFX Enabled + + + + SoundFX Disabled + + + + Play sound effects in game + + + + Music Enabled + + + + Music Disabled + + + + Play music in game + + + +