diff --git a/BetButton.qml b/BetButton.qml index 14fbe48..e0000f4 100644 --- a/BetButton.qml +++ b/BetButton.qml @@ -20,17 +20,19 @@ Item { border.width: 0 color: { - if (PlayerAreaModel.focusedPlayer == 0 && rouletteTable.numberOfBets == 1 && border.width != 3) { - "#00000000" - } else if (rouletteTable.numberOfBets < 2 || border.width == 3) { - if ((PlayerAreaModel.focusedPlayer == 0 && rouletteTable.numberOfBets < 1) || PlayerAreaModel.focusedPlayer != 0 || border.width == 3) { - if (tapHandler.pressed) { - "#88999999" - } else if (hoverHandler.hovered) { - "#88EEEEEE" - } else { - "#00000000" - } + /* + * A bit of a mess + * The left side of the OR is checking if the player is croupier, and if it is, if it hasn't made a bet that has to be a number. + * Even if the above isn't true, it still highlights if it's hovering a bet so that it can remove the bet. + * + * The right side of the OR is similar logic but for normal players that can make two bets on any square of the board + */ + if (((PlayerAreaModel.focusedPlayer == 0 && betType == 9) && (rouletteTable.numberOfBets < 1 || border.width == 3)) || + (PlayerAreaModel.focusedPlayer != 0 && (rouletteTable.numberOfBets < 2 || border.width == 3))) { + if (tapHandler.pressed) { + "#88999999" + } else if (hoverHandler.hovered) { + "#88EEEEEE" } else { "#00000000" } @@ -46,9 +48,12 @@ Item { id: tapHandler onTapped: { - if (PlayerAreaModel.focusedPlayer == 0 && rouletteTable.numberOfBets == 1 && parent.border.width != 3) { - - } else if (rouletteTable.numberOfBets < 2 && parent.border.width == 0) { + /* + * Similar logic to what is used for setting the color attribute, but this time only go inside the "if" if a bet hasn't + * been made on the square that was clicked. + */ + if (((PlayerAreaModel.focusedPlayer == 0 && betType == 9) && (rouletteTable.numberOfBets < 1 && parent.border.width != 3)) || + (PlayerAreaModel.focusedPlayer != 0 && (rouletteTable.numberOfBets < 2 && parent.border.width != 3))) { parent.border.width = 3 /* Don't show this dialog for the croupier */ diff --git a/BetInputDialog.qml b/BetInputDialog.qml index e686a1a..e9cff57 100644 --- a/BetInputDialog.qml +++ b/BetInputDialog.qml @@ -17,6 +17,8 @@ Dialog { TextField { id: input focus: true + /* No negative numners or leading 0s, and cap at 99 million */ + validator: RegularExpressionValidator { regularExpression: /[1-9][0-9]{0,7}/ } anchors { left: parent.left diff --git a/CMakeLists.txt b/CMakeLists.txt index 39d49f0..9e7f209 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ qt_add_qml_module(roulette-payout RedBlack.qml EvenOdd.qml LowHigh.qml - + Results.qml ) target_link_libraries(roulette-payout PRIVATE diff --git a/MainWindow.cpp b/MainWindow.cpp index b78ca81..f1c74aa 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -11,7 +11,7 @@ MainWindow::MainWindow(QWindow *parent) { /* * MainWindow is a QQmlApplicationEngine, so a .show() isn't needed - * it will load a Window or ApplicationWindow that will set its own + * It will load a Window or ApplicationWindow that will set its own * visibility status */ load(QUrl("qrc:///Root.qml")); diff --git a/PlayerAreaModel.cpp b/PlayerAreaModel.cpp index ab6a2cd..c6d59e2 100644 --- a/PlayerAreaModel.cpp +++ b/PlayerAreaModel.cpp @@ -1,12 +1,19 @@ #include "PlayerAreaModel.h" +#include PlayerAreaModel::PlayerAreaModel(QObject *parent) { Player defaultValues; + Bet defaultBets; for(int i = 0; i < 8; i++){ players.append(defaultValues); + for (int ii = 0; ii < 2; ii++) { + players[i].bets.append(defaultBets); + } } + /* This is set to avoid segfaults if the starting value is out of bounds */ + focusedPlayer_ = 1; } uint PlayerAreaModel::focusedPlayer() @@ -18,10 +25,10 @@ void PlayerAreaModel::setFocusedPlayer(int n) { focusedPlayer_ = n; Player player = players.at(n); - BetType b1 = player.bet1.betType; - BetType b2 = player.bet2.betType; - uint n1 = player.bet1.betSpot; - uint n2 = player.bet2.betSpot; + BetType b1 = player.bets[0].betType; + BetType b2 = player.bets[1].betType; + uint n1 = player.bets[0].betSpot; + uint n2 = player.bets[1].betSpot; emit focusedPlayerChanged(b1, n1, b2, n2); } @@ -32,51 +39,111 @@ QString PlayerAreaModel::getPlayerName(int n) void PlayerAreaModel::setPlayerName(QString s, int n) { - this->players[n].playerName = s; + players[n].playerName = s; } void PlayerAreaModel::bet(BetType b, int n, int bet) { Player *player = &players[focusedPlayer_]; - if (player->bet1.betType == NONE) { - player->bet1.betType = b; - player->bet1.betValue = bet; - player->bet1.betSpot = n; - if (b == 9 && n <= 35) { - n = 35 - n; + for (int i = 0; i < 2; i++) { + if (player->bets[i].betType == NONE) { + player->bets[i].betType = b; + player->bets[i].betValue = bet; + player->bets[i].betSpot = n; + /* + * This is needed because the visual roulette board is inverted from the + * actual QML grid we use to represent it. + * Here we use 35 because the grid is 0-indexed, but later we use 36 when it is 1-indexed. + */ + if (b == SINGLE && n <= 35) { + n = 35 - n; + } + emit betChanged(b, n, bet); + break; } - emit betChanged(b, n, bet); - } else if (player->bet2.betType == NONE) { - player->bet2.betType = b; - player->bet2.betValue = bet; - player->bet2.betSpot = n; - if (b == 9 && n <= 35) { - n = 35 - n; - } - emit betChanged(b, n, bet); } } void PlayerAreaModel::removeBet(BetType b, int n) { Player *player = &players[focusedPlayer_]; - if (player->bet1.betType != NONE) { - player->bet1.betType = NONE; - if (b == 9 && n <= 35) { - n = 35 - n; + + + for (int i = 0; i < 2; i++) { + if (player->bets[i].betType == b && player->bets[i].betSpot == n) { + player->bets[i].betType = NONE; + if (b == SINGLE && n <= 35) { + n = 35 - n; + } + emit betRemoved(b, n); } - emit betRemoved(b, n); - } else { - player->bet2.betType = NONE; - if (b == 9 && n <= 35) { - n = 35 - n; - } - emit betRemoved(b, n); } } -void PlayerAreaModel::cancelBet(BetType b, int n) { +void PlayerAreaModel::cancelBet(BetType b, int n) +{ emit betCanceled(b, n); } +void PlayerAreaModel::payout() +{ + if (players.at(0).bets[0].betType == NONE) { + fprintf(stderr, "Select where the ball landed\n"); + } else { + int rollSpot = 36 - players.at(0).bets[0].betSpot; + + for (int i = 1; i < 8; i++) { + players[i].payout = 0; + for (int ii = 0; ii < 2; ii++) { + int betType = players.at(i).bets[ii].betType; + int betSpot; + + if (betType != NONE) { + betSpot = players.at(i).bets[ii].betSpot; + + if (betType == SINGLE && betSpot <= 35) { + betSpot = 36 - betSpot; + } else { + betSpot += 1; + } + + if (betType == SINGLE && betSpot == rollSpot) { + players[i].payout += players.at(i).bets[ii].betValue * 21; + } else if (betType == RED && (((int64_t)1 << (rollSpot - 1)) & redNumbers)) { + players[i].payout += players.at(i).bets[ii].betValue * 2; + } else if (betType == BLACK && !(((int64_t)1 << (rollSpot - 1)) & redNumbers)) { + players[i].payout += players.at(i).bets[ii].betValue * 2; + } else if (betType == ODD && (rollSpot % 2)) { + players[i].payout += players.at(i).bets[ii].betValue * 2; + } else if (betType == EVEN && !(rollSpot % 2)) { + players[i].payout += players.at(i).bets[ii].betValue * 2; + } else if (betType == LOW && rollSpot <= 18 ) { + players[i].payout += players.at(i).bets[ii].betValue * 2; + } else if (betType == HIGH && rollSpot >= 19 && rollSpot < 37) { + players[i].payout += players.at(i).bets[ii].betValue * 2; + } else if (betType == DOZEN) { + printf("CHECK %d %d\n", betSpot, rollSpot); + if (rollSpot <= 12 * betSpot && rollSpot >= (12 * (betSpot - 1)) + 1) { + players[i].payout += players.at(i).bets[ii].betValue * 3; + } + } else if (betType == STREET) { + if (rollSpot <= 3 * betSpot && rollSpot >= (3 * (betSpot - 1)) + 1) { + players[i].payout += players.at(i).bets[ii].betValue * 6; + } + } else if (betType == COLUMN) { + if (betSpot == 1 && (((int64_t)1 << (rollSpot - 1)) & column1)) { + players[i].payout += players.at(i).bets[ii].betValue * 3; + } else if (betSpot == 2 && (((int64_t)1 << (rollSpot - 1)) & column2)) { + players[i].payout += players.at(i).bets[ii].betValue * 3; + } else if (betSpot == 3 && (((int64_t)1 << (rollSpot - 1)) & column3)) { + players[i].payout += players.at(i).bets[ii].betValue * 3; + } + } + } + } + + emit results(players.at(i).playerName, i, players.at(i).payout); + } + } +} diff --git a/PlayerAreaModel.h b/PlayerAreaModel.h index 8e1112e..2fc7706 100644 --- a/PlayerAreaModel.h +++ b/PlayerAreaModel.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -10,6 +11,7 @@ class PlayerAreaModel : public QObject { Q_OBJECT + /* The name under which this "global variable" will be known in QML files */ QML_NAMED_ELEMENT(PlayerAreaModel) QML_SINGLETON @@ -42,6 +44,7 @@ signals: void betChanged(BetType b, int n, int bet); void betRemoved(BetType b, int n); void betCanceled(BetType b, uint n); + void results(QString name, int n, int payout); public slots: void setPlayerName(QString s, int n); @@ -49,10 +52,10 @@ public slots: void removeBet(BetType b, int n); QString getPlayerName(int n); void cancelBet(BetType b, int n); + void payout(); private: - struct Bet { uint betValue = 0; BetType betType = NONE; @@ -61,11 +64,17 @@ private: struct Player { QString playerName = ""; - Bet bet1; - Bet bet2; + QList bets; + int payout; }; QList players; int focusedPlayer_; + + /* bitmaps */ + int64_t redNumbers = 45857548629; + int64_t column3 = 9817068105; + int64_t column2 = 19634136210; + int64_t column1 = 39268272420; }; diff --git a/PlayerAreaView.qml b/PlayerAreaView.qml index 493cba7..2edf14d 100644 --- a/PlayerAreaView.qml +++ b/PlayerAreaView.qml @@ -129,7 +129,7 @@ Item { topMargin: 6 } - onTextEdited: PlayerAreaModel.setPlayerName(text, index) + onTextEdited: PlayerAreaModel.setPlayerName(text, index + 1) onActiveFocusChanged: { /* focusReason 0, 1 and 2 are mouse, tab forward, and tab backward, respectively */ if (activeFocus && (focusReason == 0 || focusReason == 1 || focusReason == 2)) { @@ -179,17 +179,17 @@ Item { currentBet1.betType = b currentBet1.n = n if (b < 7) { - currentBet1.text = betTypes[b] + ": " + bet + "g" + currentBet1.text = betTypes[b] + ": " + bet.toLocaleString(Qt.locale(), 'f', 0) + "g" } else { - currentBet1.text = betTypes[b] + " " + (n + 1) + ": " + bet + "g" + currentBet1.text = betTypes[b] + " " + (n + 1) + ": " + bet.toLocaleString(Qt.locale(), 'f', 0) + "g" } } else { currentBet2.betType = b currentBet2.n = n if (b < 7) { - currentBet2.text = betTypes[b] + ": " + bet + "g" + currentBet2.text = betTypes[b] + ": " + bet.toLocaleString(Qt.locale(), 'f', 0) + "g" } else { - currentBet2.text = betTypes[b] + " " + (n + 1) + ": " + bet + "g" + currentBet2.text = betTypes[b] + " " + (n + 1) + ": " + bet.toLocaleString(Qt.locale(), 'f', 0) + "g" } } } diff --git a/Results.qml b/Results.qml new file mode 100644 index 0000000..e66f561 --- /dev/null +++ b/Results.qml @@ -0,0 +1,39 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import roulette + +GridLayout { + rowSpacing: 20 + columnSpacing: 50 + flow: GridLayout.TopToBottom + rows: 3 + + anchors { + /* These random IDs com from Results' parent*/ + left: payoutButton.right + leftMargin: 30 + top: roulette.bottom + topMargin: 20 + } + + + Repeater { + model: 7 + + Text { + text: "" + + Connections { + target: PlayerAreaModel + + function onResults(name, n, payout) { + if (index + 1 == n && name != "") { + text = name + ": " + payout.toLocaleString(Qt.locale(), 'f', 0) + "g" + } + } + } + } + } +} diff --git a/Root.qml b/Root.qml index 655ba4a..a0d819d 100644 --- a/Root.qml +++ b/Root.qml @@ -4,17 +4,19 @@ import QtQuick.Controls import roulette /* this is importing the target "roulette-payout" defined in CMakeLists.txt */ Window { - height: 500 - width: 1000 + maximumHeight: 500 + maximumWidth: 1000 + minimumHeight: 500 + minimumWidth: 1000 title: "Roulette Payout" visible: true Pane { anchors.fill: parent + /* Exists exclusively to remove focus from TextFields by clicking anywhere else on the screen */ MouseArea { anchors.fill: parent - onClicked: focus = true } @@ -32,5 +34,73 @@ Window { right: parent.right } } + + Button { + id: payoutButton + onClicked: PlayerAreaModel.payout() + enabled: false + + anchors { + left: roulette.left + bottom: parent.bottom + bottomMargin: 20 + } + + contentItem: Text { + text: "Payout" + font.pointSize: 15 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + color: { + if (enabled == true) { + "#000000" + } else { + "#555555" + } + } + } + + background: Rectangle { + implicitWidth: 150 + implicitHeight: 60 + opacity: { + if (enabled == true && parent.pressed) { + 0.5 + } else if (enabled == true && parent.hovered) { + 0.2 + } else { + 0.3 + } + } + color: { + if (enabled == true) { + "#AAAAAA" + } else { + "#444444" + } + } + border.width: 0 + } + + Connections { + target: PlayerAreaModel + + function onBetChanged() { + if (PlayerAreaModel.focusedPlayer == 0) { + payoutButton.enabled = true; + } + } + + function onBetRemoved() { + if (PlayerAreaModel.focusedPlayer == 0) { + payoutButton.enabled = false; + } + } + } + } + + Results { + } } } diff --git a/Singles.qml b/Singles.qml index af7f460..39388e4 100644 --- a/Singles.qml +++ b/Singles.qml @@ -8,7 +8,6 @@ Item { LayoutMirroring.enabled: true flow: GridLayout.TopToBottom rows: 3 - //rotation: 180 anchors { right: parent.right diff --git a/StreetBetsModel.cpp b/StreetBetsModel.cpp deleted file mode 100644 index 18a60e6..0000000 --- a/StreetBetsModel.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "StreetBetsModel.h" - -StreetBetsModel::StreetBetsModel(QObject *parent) -{ - -} diff --git a/StreetBetsModel.h b/StreetBetsModel.h deleted file mode 100644 index d1ba119..0000000 --- a/StreetBetsModel.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class StreetBetsModel : public QObject { - Q_OBJECT - QML_ELEMENT - - public: - explicit StreetBetsModel(QObject *parent = nullptr); -};