Warning, /education/gcompris/src/activities/checkers/Checkers.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - checkers.qml 0002 * 0003 * SPDX-FileCopyrightText: 2017 Johnny Jazeix <jazeix@gmail.com> 0004 * 0005 * Authors: 0006 * Johnny Jazeix <jazeix@gmail.com> 0007 * Timothée Giet <animtim@gmail.com> (big layout refactoring) 0008 * 0009 * SPDX-License-Identifier: GPL-3.0-or-later 0010 */ 0011 import QtQuick 2.12 0012 import GCompris 1.0 0013 0014 import "../../core" 0015 import "." 0016 import "checkers.js" as Activity 0017 0018 ActivityBase { 0019 id: activity 0020 0021 property bool acceptClick: true 0022 property bool twoPlayers: false 0023 // difficultyByLevel means that at level 1 computer is bad better at last level 0024 property bool difficultyByLevel: true 0025 0026 onStart: focus = true 0027 onStop: {} 0028 0029 pageComponent: Image { 0030 id: background 0031 anchors.fill: parent 0032 source: "qrc:/gcompris/src/activities/chess/resource/background-wood.svg" 0033 signal start 0034 signal stop 0035 0036 Component.onCompleted: { 0037 activity.start.connect(start) 0038 activity.stop.connect(stop) 0039 } 0040 0041 // Add here the QML items you need to access in javascript 0042 QtObject { 0043 id: items 0044 property Item main: activity.main 0045 property GCSfx audioEffects: activity.audioEffects 0046 property alias background: background 0047 property int currentLevel: activity.currentLevel 0048 property alias bonus: bonus 0049 property var barHeightAddon: ApplicationSettings.isBarHidden ? textMessage.height : bar.height 0050 property bool isPortrait: (background.height >= background.width) 0051 property int cellSize: boardBg.width * 0.098 0052 property var fen: activity.fen 0053 property bool twoPlayer: activity.twoPlayers 0054 property bool difficultyByLevel: activity.difficultyByLevel 0055 property var positions 0056 property var pieces: pieces 0057 property var squares: squares 0058 property var history 0059 property var redo_stack 0060 property alias redoTimer: redoTimer 0061 property int from 0062 property bool blackTurn 0063 property bool gameOver 0064 property var movesToDo: [] 0065 property string message 0066 property alias trigComputerMove: trigComputerMove 0067 // Used to stop piece animation on board resize; set to true on board resize, and to false on any action that triggers a piece move 0068 property bool noPieceAnimation: false 0069 0070 property int numberOfCases: 10 0071 } 0072 0073 onStart: { Activity.start(items) } 0074 onStop: { Activity.stop() } 0075 0076 GCText { 0077 id: textMessage 0078 z: 20 0079 color: "white" 0080 anchors.horizontalCenter: parent.horizontalCenter 0081 width: parent.width 0082 fontSize: smallSize 0083 text: items.message 0084 horizontalAlignment: Text.AlignHCenter 0085 wrapMode: TextEdit.WordWrap 0086 } 0087 0088 Grid { 0089 id: controls 0090 z: 20 0091 spacing: (boardBg.width - items.cellSize * 3) / 3 0092 columns: items.isPortrait ? 3 : 1 0093 horizontalItemAlignment: Grid.AlignHCenter 0094 verticalItemAlignment: Grid.AlignVCenter 0095 0096 GCButton { 0097 id: undo 0098 height: items.cellSize 0099 width: items.cellSize 0100 text: ""; 0101 theme: "noStyle" 0102 onClicked: Activity.undo() 0103 enabled: (items.history && items.history.length > 0) ? true : false 0104 opacity: enabled ? 1 : 0 0105 Image { 0106 source: 'qrc:/gcompris/src/activities/chess/resource/undo.svg' 0107 height: items.cellSize 0108 width: items.cellSize 0109 sourceSize.height: items.cellSize 0110 fillMode: Image.PreserveAspectFit 0111 } 0112 Behavior on opacity { 0113 PropertyAnimation { 0114 easing.type: Easing.InQuad 0115 duration: 200 0116 } 0117 } 0118 } 0119 0120 GCButton { 0121 id: redo 0122 height: items.cellSize 0123 width: items.cellSize 0124 text: ""; 0125 theme: "noStyle" 0126 onClicked: { 0127 Activity.redo() 0128 } 0129 enabled: items.redo_stack.length > 0 && acceptClick ? 1 : 0 0130 opacity: enabled 0131 Image { 0132 source: 'qrc:/gcompris/src/activities/chess/resource/redo.svg' 0133 height: items.cellSize 0134 width: items.cellSize 0135 sourceSize.height: items.cellSize 0136 fillMode: Image.PreserveAspectFit 0137 } 0138 Behavior on opacity { 0139 PropertyAnimation { 0140 easing.type: Easing.InQuad 0141 duration: 200 0142 } 0143 } 0144 } 0145 0146 GCButton { 0147 height: items.cellSize 0148 width: items.cellSize 0149 text: ""; 0150 theme: "noStyle" 0151 enabled: items.twoPlayer 0152 opacity: enabled 0153 Image { 0154 source: 'qrc:/gcompris/src/activities/chess/resource/turn.svg' 0155 height: items.cellSize 0156 width: items.cellSize 0157 sourceSize.height: items.cellSize 0158 fillMode: Image.PreserveAspectFit 0159 } 0160 onClicked: chessboard.swap() 0161 } 0162 } 0163 0164 Rectangle { 0165 id: layoutArea 0166 width: background.width 0167 height: background.height - textMessage.height - items.barHeightAddon * 1.1 0168 opacity: 0 0169 anchors.horizontalCenter: background.horizontalCenter 0170 } 0171 0172 Rectangle { 0173 id: controlsArea 0174 anchors.left: background.left 0175 anchors.right: boardBg.left 0176 anchors.top: boardBg.top 0177 anchors.bottom: boardBg.bottom 0178 opacity: 0 0179 } 0180 0181 states: [ 0182 State { 0183 name: "portraitLayout"; when: items.isPortrait 0184 PropertyChanges { 0185 target: layoutArea 0186 width: background.width * 0.86 0187 height: background.height - textMessage.height - bar.height * 1.1 0188 } 0189 PropertyChanges { 0190 target: controls 0191 width:layoutArea.width 0192 height: items.cellSize * 1.2 0193 anchors.leftMargin: controls.spacing * 0.5 0194 anchors.topMargin: 0 0195 } 0196 PropertyChanges { 0197 target: boardBg 0198 anchors.verticalCenterOffset: items.cellSize * -0.6 0199 } 0200 AnchorChanges { 0201 target: layoutArea 0202 anchors.top: controls.bottom 0203 } 0204 AnchorChanges { 0205 target: controls 0206 anchors.top: textMessage.bottom 0207 anchors.horizontalCenter: undefined 0208 anchors.left: boardBg.left 0209 } 0210 }, 0211 State { 0212 name: "horizontalLayout"; when: !items.isPortrait 0213 PropertyChanges { 0214 target: layoutArea 0215 width: background.width 0216 height: background.height - textMessage.height - items.barHeightAddon * 1.1 0217 } 0218 PropertyChanges { 0219 target: controls 0220 width: items.cellSize * 1.2 0221 height: layoutArea.height 0222 anchors.leftMargin: 0 0223 anchors.topMargin: controls.spacing * 0.5 0224 } 0225 PropertyChanges { 0226 target: boardBg 0227 anchors.verticalCenterOffset: 0 0228 } 0229 AnchorChanges { 0230 target: layoutArea 0231 anchors.top: textMessage.bottom 0232 } 0233 AnchorChanges { 0234 target: controls 0235 anchors.top: controlsArea.top 0236 anchors.horizontalCenter: controlsArea.horizontalCenter 0237 anchors.left: undefined 0238 } 0239 } 0240 ] 0241 0242 Rectangle { 0243 id: boardBg 0244 width: Math.min(layoutArea.width, layoutArea.height) 0245 height: boardBg.width 0246 anchors.centerIn: layoutArea 0247 z: 09 0248 color: "#2E1B0C" 0249 onWidthChanged: items.noPieceAnimation = true 0250 0251 // The chessboard 0252 GridView { 0253 id: chessboard 0254 cellWidth: items.cellSize 0255 cellHeight: items.cellSize 0256 width: items.cellSize * items.numberOfCases 0257 height: items.cellSize * items.numberOfCases 0258 interactive: false 0259 keyNavigationWraps: true 0260 model: items.numberOfCases*items.numberOfCases 0261 layoutDirection: Qt.RightToLeft 0262 delegate: square 0263 rotation: 180 0264 z: 10 0265 anchors.centerIn: boardBg 0266 Component { 0267 id: square 0268 Image { 0269 source: index % 2 + (Math.floor(index / items.numberOfCases) % 2) == 1 ? 0270 Activity.url + 'checkers-white.svg' : Activity.url + 'checkers-black.svg'; 0271 width: items.cellSize 0272 height: items.cellSize 0273 } 0274 } 0275 0276 Behavior on rotation { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1400 } } 0277 0278 function swap() { 0279 items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/flip.wav') 0280 if(chessboard.rotation == 180) 0281 chessboard.rotation = 0 0282 else 0283 chessboard.rotation = 180 0284 } 0285 } 0286 } 0287 0288 0289 Repeater { 0290 id: squares 0291 model: items.positions 0292 delegate: square 0293 parent: chessboard 0294 0295 DropArea { 0296 id: square 0297 x: items.cellSize * (9 - pos % items.numberOfCases) + spacing / 2 0298 y: items.cellSize * Math.floor(pos / items.numberOfCases) + spacing / 2 0299 width: items.cellSize - spacing 0300 height: items.cellSize - spacing 0301 z: 1 0302 keys: acceptMove ? ['acceptMe'] : ['sorryNo'] 0303 property bool acceptMove: false 0304 property bool jumpable: false 0305 property int pos: modelData.pos 0306 property int spacing: 6 * ApplicationInfo.ratio 0307 Rectangle { 0308 id: possibleMove 0309 anchors.fill: parent 0310 color: parent.containsDrag ? '#803ACAFF' : 'transparent' 0311 border.width: parent.acceptMove || parent.jumpable ? 5 : 0 0312 border.color: parent.acceptMove ? '#FF808080' : '#C0808080' 0313 radius: parent.acceptMove ? width*0.5 : 0 0314 z: 1 0315 } 0316 } 0317 0318 function getSquareAt(pos) { 0319 for(var i=0; i < squares.count; i++) { 0320 if(squares.itemAt(i).pos === pos) 0321 return squares.itemAt(i) 0322 } 0323 return undefined 0324 } 0325 } 0326 0327 Repeater { 0328 id: pieces 0329 model: items.positions 0330 delegate: piece 0331 parent: chessboard 0332 0333 Piece { 0334 id: piece 0335 sourceSize.width: items.cellSize 0336 width: items.cellSize - spacing 0337 height: items.cellSize - spacing 0338 source: img ? Activity.url + img + '.svg' : '' 0339 img: modelData.img 0340 x: items.cellSize * (items.numberOfCases - 1 - pos % items.numberOfCases) + spacing / 2 0341 y: items.cellSize * Math.floor(pos / items.numberOfCases) + spacing / 2 0342 z: 1 0343 pos: modelData.pos 0344 newPos: modelData.pos 0345 rotation: - chessboard.rotation 0346 0347 property int spacing: 6 * ApplicationInfo.ratio 0348 0349 Drag.active: dragArea.drag.active 0350 Drag.hotSpot.x: width / 2 0351 Drag.hotSpot.y: height / 2 0352 0353 MouseArea { 0354 id: dragArea 0355 anchors.fill: parent 0356 enabled: !items.gameOver && !items.trigComputerMove.running 0357 drag.target: ((items.blackTurn && !parent.isWhite) || (!items.blackTurn && parent.isWhite)) ? 0358 parent : null 0359 onPressed: { 0360 piece.Drag.keys = ['acceptMe'] 0361 parent.z = 100 0362 0363 if(parent.isWhite == 1 && !items.blackTurn || 0364 parent.isWhite == 0 && items.blackTurn) { 0365 items.from = parent.newPos 0366 Activity.showPossibleMoves(items.from) 0367 } else if(items.from != -1 && squares.getSquareAt(parent.newPos)['acceptMove']) { 0368 Activity.moveTo(items.from, parent.newPos) 0369 } 0370 } 0371 onReleased: { 0372 // If no target or move not possible, we reset the position 0373 if(!piece.Drag.target || (items.from != -1 && !Activity.moveTo(items.from, piece.Drag.target.pos))) { 0374 var pos = parent.pos 0375 // Force recalc of the old x,y position 0376 parent.pos = -1 0377 if(pieces.getPieceAt(pos)) 0378 pieces.getPieceAt(pos).move(pos) 0379 } 0380 } 0381 } 0382 } 0383 0384 function moveTo(from, to, moves) { 0385 items.movesToDo.push({"from": from, "to": to, "move": moves}); 0386 if(items.movesToDo.length == 1) { 0387 moveInternal(); 0388 } 0389 } 0390 0391 function moveInternal() { 0392 var moveToDo = items.movesToDo[0] 0393 var from = moveToDo.from; 0394 var to = moveToDo.to; 0395 var moves = moveToDo.move; 0396 0397 var fromPiece = getPieceAt(from) 0398 var toPiece = getPieceAt(to) 0399 0400 if(moves.jumps.length != 0) 0401 items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/smudge.wav') 0402 else 0403 items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') 0404 0405 toPiece.hide(from) 0406 movingPiece = fromPiece 0407 0408 if(moves.jumps.length !== 0) { 0409 listJumps = moves.jumps 0410 } 0411 else { 0412 // create the move if needed 0413 listJumps = [Activity.viewPosToEngine(from), Activity.viewPosToEngine(to)] 0414 } 0415 } 0416 0417 function promotion(to) { 0418 var toPiece = getPieceAt(to) 0419 toPiece.promotion() 0420 } 0421 0422 function getPieceAt(pos) { 0423 for(var i=0; i < pieces.count; i++) { 0424 if(pieces.itemAt(i).newPos === pos) 0425 return pieces.itemAt(i) 0426 } 0427 return undefined 0428 } 0429 } 0430 0431 property var movingPiece: undefined 0432 property var listJumps 0433 property bool restartNeeded: false 0434 onListJumpsChanged: { 0435 if(listJumps.length >= 2) { 0436 if(!animationTimer.isRunning) 0437 animationTimer.restart(); 0438 else 0439 restartNeeded = true; 0440 } 0441 } 0442 0443 SequentialAnimation { 0444 id: animationTimer 0445 onRunningChanged: { 0446 if(!running && restartNeeded) 0447 start() 0448 } 0449 ScriptAction { 0450 script: { 0451 restartNeeded = false 0452 var to = Activity.engineToViewPos(listJumps[1]) 0453 movingPiece.move(to) 0454 } 0455 } 0456 PauseAnimation { duration: 200 } 0457 ScriptAction { 0458 script: { 0459 listJumps.shift() 0460 // only shifting does not trigger the onChanged 0461 var tmp = listJumps 0462 listJumps = tmp 0463 // only change player once all the jumps have been done 0464 if(listJumps.length === 1) { 0465 0466 items.movesToDo.shift() 0467 if(items.movesToDo.length > 0) { 0468 pieces.moveInternal() 0469 } 0470 else { 0471 Activity.refresh() 0472 movingPiece = undefined 0473 } 0474 } 0475 } 0476 } 0477 } 0478 0479 Timer { 0480 id: trigComputerMove 0481 repeat: false 0482 interval: 400 0483 onTriggered: Activity.randomMove() 0484 } 0485 0486 // Use to redo the computer move after the user move 0487 Timer { 0488 id: redoTimer 0489 repeat: false 0490 interval: 400 0491 onTriggered: { 0492 acceptClick = true; 0493 Activity.moveByEngine(move) 0494 } 0495 property var move 0496 0497 function moveByEngine(engineMove) { 0498 move = engineMove 0499 redoTimer.start() 0500 } 0501 } 0502 0503 DialogHelp { 0504 id: dialogHelp 0505 onClose: home() 0506 } 0507 0508 Bar { 0509 id: bar 0510 level: items.currentLevel + 1 0511 content: BarEnumContent { value: help | home | (items.twoPlayer ? 0 : level) | 0512 (items.twoPlayer && !items.gameOver ? 0 : reload) } 0513 0514 onHelpClicked: { 0515 displayDialog(dialogHelp) 0516 } 0517 onPreviousLevelClicked: Activity.previousLevel() 0518 onNextLevelClicked: Activity.nextLevel() 0519 onHomeClicked: activity.home() 0520 onReloadClicked: { 0521 trigComputerMove.stop() 0522 Activity.initLevel() 0523 } 0524 } 0525 0526 Bonus { 0527 id: bonus 0528 } 0529 } 0530 }