/***************************************************************************** ** ** ** 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/tetris.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 }