diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a688c31..10ec71b 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -33,6 +33,10 @@ android {
}
}
+base {
+ archivesName.set("OcheCompanion")
+}
+
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
diff --git a/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java b/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java
index d8c86bc..ff59e88 100644
--- a/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java
+++ b/app/src/main/java/com/aldo/apps/ochecompanion/GameActivity.java
@@ -123,6 +123,12 @@ public class GameActivity extends BaseActivity {
*/
private boolean mIsTurnOver = false;
+ /**
+ * Flag indicating the current turn resulted in a bust.
+ * Used to prevent UI from subtracting bust darts from the score display.
+ */
+ private boolean mIsBustedTurn = false;
+
/**
* Helper class to track dart hit details for statistics.
*/
@@ -183,6 +189,11 @@ public class GameActivity extends BaseActivity {
* Visible when score ≤170 and route is available.
*/
private LinearLayout layoutCheckoutSuggestion;
+
+ /**
+ * The Container for displaying the current score.
+ */
+ private LinearLayout mScoreContainer;
/**
* Button for selecting single (1×) multiplier.
@@ -199,6 +210,16 @@ public class GameActivity extends BaseActivity {
*/
private View btnTriple;
+ /**
+ * Overlay to be shown when the player busted.
+ */
+ private View mBustOverlay;
+
+ /**
+ * The Button to submit a turn.
+ */
+ private MaterialButton mSubmitTurnBtn;
+
/**
* The Button to open the stats view.
*/
@@ -329,6 +350,7 @@ public class GameActivity extends BaseActivity {
outState.putInt("startingScore", mStartingScore);
outState.putString("matchUuid", mMatchUuid);
outState.putBoolean("isTurnOver", mIsTurnOver);
+ outState.putBoolean("isBustedTurn", mIsBustedTurn);
// Save current turn darts
int[] dartsArray = new int[mCurrentTurnDarts.size()];
@@ -382,6 +404,7 @@ public class GameActivity extends BaseActivity {
mStartingScore = savedInstanceState.getInt("startingScore", DartsConstants.DEFAULT_GAME_SCORE);
mMatchUuid = savedInstanceState.getString("matchUuid");
mIsTurnOver = savedInstanceState.getBoolean("isTurnOver", false);
+ mIsBustedTurn = savedInstanceState.getBoolean("isBustedTurn", false);
// Restore current turn darts
mCurrentTurnDarts.clear();
@@ -443,6 +466,9 @@ public class GameActivity extends BaseActivity {
tvDartPills[0] = findViewById(R.id.tvDart1);
tvDartPills[1] = findViewById(R.id.tvDart2);
tvDartPills[2] = findViewById(R.id.tvDart3);
+ mBustOverlay = findViewById(R.id.bust_pulse_overlay);
+ mSubmitTurnBtn = findViewById(R.id.btnSubmitTurn);
+ mScoreContainer = findViewById(R.id.scoreContainer);
glKeyboard = findViewById(R.id.glKeyboard);
@@ -458,7 +484,7 @@ public class GameActivity extends BaseActivity {
});
- findViewById(R.id.btnSubmitTurn).setOnClickListener(v -> submitTurn());
+ mSubmitTurnBtn.setOnClickListener(v -> submitTurn());
findViewById(R.id.btnUndoDart).setOnClickListener(v -> undoLastDart());
}
@@ -540,10 +566,11 @@ public class GameActivity extends BaseActivity {
updateTurnIndicators();
mIsTurnOver = true;
+ mIsBustedTurn = true;
if (mIsAudioEnabled) {
mSoundEngine.playBustedSound();
}
- Toast.makeText(this, "BUST!", Toast.LENGTH_SHORT).show();
+ triggerBustSequence();
// In a pro interface, we usually wait for "Submit" or auto-submit after a short delay
} else if (scoreAfterDart == 0 && isDouble) {
// VICTORY CONDITION
@@ -571,6 +598,39 @@ public class GameActivity extends BaseActivity {
setMultiplier(DartsConstants.MULTIPLIER_SINGLE);
}
+ private void triggerBustSequence() {
+ mIsTurnOver = true;
+ mIsBustedTurn = true;
+
+ // Visual feedback: Shake scoreboard
+ Animation shake = AnimationUtils.loadAnimation(this, R.anim.shake);
+ mScoreContainer.startAnimation(shake);
+
+ // Change scoreboard to Red
+ mScoreContainer.setBackgroundColor(Color.parseColor("#33FF3B30"));
+ tvScorePrimary.setTextColor(ContextCompat.getColor(this, R.color.double_red));
+
+ // Pulsing red overlay on input
+ mBustOverlay.setVisibility(View.VISIBLE);
+ Animation pulse = new AlphaAnimation(0.2f, 0.5f);
+ pulse.setDuration(500);
+ pulse.setRepeatMode(Animation.REVERSE);
+ pulse.setRepeatCount(Animation.INFINITE);
+ mBustOverlay.startAnimation(pulse);
+
+ mSubmitTurnBtn.setText("NEXT PLAYER (BUST)");
+ updateTurnIndicators();
+ }
+
+ private void resetVisuals() {
+ mScoreContainer.setBackgroundColor(ContextCompat.getColor(this, R.color.surface_primary));
+ tvScorePrimary.setTextColor(ContextCompat.getColor(this, R.color.volt_green));
+ mBustOverlay.clearAnimation();
+ mBustOverlay.setVisibility(View.GONE);
+ mSubmitTurnBtn.setText(R.string.txt_game_btn_submit);
+ }
+
+
/**
* Handler for Bull button tap. Delegates to onNumberTap with base value 25.
*
@@ -659,6 +719,7 @@ public class GameActivity extends BaseActivity {
* Updates player score (unless bust), rotates to next player, resets turn state.
*/
private void submitTurn() {
+ resetVisuals();
// Don't submit if no darts thrown
if (mCurrentTurnDarts.isEmpty()) return;
@@ -694,10 +755,9 @@ public class GameActivity extends BaseActivity {
}
}
- // Re-check logic for non-double finish or score of 1
- int lastDartValue = mCurrentTurnDarts.get(mCurrentTurnDarts.size() - 1);
- // Note: this check is redundant but safe for manual "Submit" actions
- boolean isBust = (finalScore < 0 || finalScore == 1 || (finalScore == 0 && !isFinishDart(mCurrentTurnDarts.size() - 1)));
+ // Use the mIsBustedTurn flag that was set in onNumberTap when the bust was detected
+ // This is more reliable than recalculating since it tracks the actual multiplier used
+ boolean isBust = mIsBustedTurn;
// Update score only if not bust
if (!isBust) {
@@ -717,6 +777,7 @@ public class GameActivity extends BaseActivity {
mCurrentTurnDarts.clear();
mCurrentTurnDartHits.clear();
mIsTurnOver = false;
+ mIsBustedTurn = false;
// Update UI for next player
updateUI();
@@ -756,10 +817,12 @@ public class GameActivity extends BaseActivity {
// Allow turn to continue
mIsTurnOver = false;
+ mIsBustedTurn = false;
// Update displays
updateTurnIndicators();
updateUI();
+ resetVisuals();
}
}
@@ -782,8 +845,11 @@ public class GameActivity extends BaseActivity {
tvLegAvg.setText(String.format("AVG: %.1f", avg));
// Calculate current target (remaining score minus current turn darts)
+ // If it's a busted turn, don't subtract the bust darts from the display
int turnPointsSoFar = 0;
- for (int d : mCurrentTurnDarts) turnPointsSoFar += d;
+ if (!mIsBustedTurn) {
+ for (int d : mCurrentTurnDarts) turnPointsSoFar += d;
+ }
int currentTarget = active.remainingScore - turnPointsSoFar;
int dartsRemaining = 3 - mCurrentTurnDarts.size();
diff --git a/app/src/main/res/layout/activity_game.xml b/app/src/main/res/layout/activity_game.xml
index b7b1dbc..9c06492 100644
--- a/app/src/main/res/layout/activity_game.xml
+++ b/app/src/main/res/layout/activity_game.xml
@@ -157,6 +157,15 @@
+
+
@@ -198,7 +208,6 @@
android:id="@+id/player_stats_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="15dp"
android:visibility="gone"/>
\ No newline at end of file