Added rudimentary "Bust" Animation

This commit is contained in:
Alexander Doerflinger
2026-02-03 10:38:54 +01:00
parent cd0ee3ed4d
commit 939b0425b0
3 changed files with 87 additions and 8 deletions

View File

@@ -33,6 +33,10 @@ android {
} }
} }
base {
archivesName.set("OcheCompanion")
}
dependencies { dependencies {
implementation(libs.appcompat) implementation(libs.appcompat)
implementation(libs.material) implementation(libs.material)

View File

@@ -123,6 +123,12 @@ public class GameActivity extends BaseActivity {
*/ */
private boolean mIsTurnOver = false; 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. * Helper class to track dart hit details for statistics.
*/ */
@@ -184,6 +190,11 @@ public class GameActivity extends BaseActivity {
*/ */
private LinearLayout layoutCheckoutSuggestion; private LinearLayout layoutCheckoutSuggestion;
/**
* The Container for displaying the current score.
*/
private LinearLayout mScoreContainer;
/** /**
* Button for selecting single (1×) multiplier. * Button for selecting single (1×) multiplier.
*/ */
@@ -199,6 +210,16 @@ public class GameActivity extends BaseActivity {
*/ */
private View btnTriple; 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. * The Button to open the stats view.
*/ */
@@ -329,6 +350,7 @@ public class GameActivity extends BaseActivity {
outState.putInt("startingScore", mStartingScore); outState.putInt("startingScore", mStartingScore);
outState.putString("matchUuid", mMatchUuid); outState.putString("matchUuid", mMatchUuid);
outState.putBoolean("isTurnOver", mIsTurnOver); outState.putBoolean("isTurnOver", mIsTurnOver);
outState.putBoolean("isBustedTurn", mIsBustedTurn);
// Save current turn darts // Save current turn darts
int[] dartsArray = new int[mCurrentTurnDarts.size()]; int[] dartsArray = new int[mCurrentTurnDarts.size()];
@@ -382,6 +404,7 @@ public class GameActivity extends BaseActivity {
mStartingScore = savedInstanceState.getInt("startingScore", DartsConstants.DEFAULT_GAME_SCORE); mStartingScore = savedInstanceState.getInt("startingScore", DartsConstants.DEFAULT_GAME_SCORE);
mMatchUuid = savedInstanceState.getString("matchUuid"); mMatchUuid = savedInstanceState.getString("matchUuid");
mIsTurnOver = savedInstanceState.getBoolean("isTurnOver", false); mIsTurnOver = savedInstanceState.getBoolean("isTurnOver", false);
mIsBustedTurn = savedInstanceState.getBoolean("isBustedTurn", false);
// Restore current turn darts // Restore current turn darts
mCurrentTurnDarts.clear(); mCurrentTurnDarts.clear();
@@ -443,6 +466,9 @@ public class GameActivity extends BaseActivity {
tvDartPills[0] = findViewById(R.id.tvDart1); tvDartPills[0] = findViewById(R.id.tvDart1);
tvDartPills[1] = findViewById(R.id.tvDart2); tvDartPills[1] = findViewById(R.id.tvDart2);
tvDartPills[2] = findViewById(R.id.tvDart3); 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); 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()); findViewById(R.id.btnUndoDart).setOnClickListener(v -> undoLastDart());
} }
@@ -540,10 +566,11 @@ public class GameActivity extends BaseActivity {
updateTurnIndicators(); updateTurnIndicators();
mIsTurnOver = true; mIsTurnOver = true;
mIsBustedTurn = true;
if (mIsAudioEnabled) { if (mIsAudioEnabled) {
mSoundEngine.playBustedSound(); 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 // In a pro interface, we usually wait for "Submit" or auto-submit after a short delay
} else if (scoreAfterDart == 0 && isDouble) { } else if (scoreAfterDart == 0 && isDouble) {
// VICTORY CONDITION // VICTORY CONDITION
@@ -571,6 +598,39 @@ public class GameActivity extends BaseActivity {
setMultiplier(DartsConstants.MULTIPLIER_SINGLE); 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. * 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. * Updates player score (unless bust), rotates to next player, resets turn state.
*/ */
private void submitTurn() { private void submitTurn() {
resetVisuals();
// Don't submit if no darts thrown // Don't submit if no darts thrown
if (mCurrentTurnDarts.isEmpty()) return; if (mCurrentTurnDarts.isEmpty()) return;
@@ -694,10 +755,9 @@ public class GameActivity extends BaseActivity {
} }
} }
// Re-check logic for non-double finish or score of 1 // Use the mIsBustedTurn flag that was set in onNumberTap when the bust was detected
int lastDartValue = mCurrentTurnDarts.get(mCurrentTurnDarts.size() - 1); // This is more reliable than recalculating since it tracks the actual multiplier used
// Note: this check is redundant but safe for manual "Submit" actions boolean isBust = mIsBustedTurn;
boolean isBust = (finalScore < 0 || finalScore == 1 || (finalScore == 0 && !isFinishDart(mCurrentTurnDarts.size() - 1)));
// Update score only if not bust // Update score only if not bust
if (!isBust) { if (!isBust) {
@@ -717,6 +777,7 @@ public class GameActivity extends BaseActivity {
mCurrentTurnDarts.clear(); mCurrentTurnDarts.clear();
mCurrentTurnDartHits.clear(); mCurrentTurnDartHits.clear();
mIsTurnOver = false; mIsTurnOver = false;
mIsBustedTurn = false;
// Update UI for next player // Update UI for next player
updateUI(); updateUI();
@@ -756,10 +817,12 @@ public class GameActivity extends BaseActivity {
// Allow turn to continue // Allow turn to continue
mIsTurnOver = false; mIsTurnOver = false;
mIsBustedTurn = false;
// Update displays // Update displays
updateTurnIndicators(); updateTurnIndicators();
updateUI(); updateUI();
resetVisuals();
} }
} }
@@ -782,8 +845,11 @@ public class GameActivity extends BaseActivity {
tvLegAvg.setText(String.format("AVG: %.1f", avg)); tvLegAvg.setText(String.format("AVG: %.1f", avg));
// Calculate current target (remaining score minus current turn darts) // 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; int turnPointsSoFar = 0;
if (!mIsBustedTurn) {
for (int d : mCurrentTurnDarts) turnPointsSoFar += d; for (int d : mCurrentTurnDarts) turnPointsSoFar += d;
}
int currentTarget = active.remainingScore - turnPointsSoFar; int currentTarget = active.remainingScore - turnPointsSoFar;
int dartsRemaining = 3 - mCurrentTurnDarts.size(); int dartsRemaining = 3 - mCurrentTurnDarts.size();

View File

@@ -157,6 +157,15 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<View
android:id="@+id/bust_pulse_overlay"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#4DFF3B30"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/trackerBar"
app:layout_constraintBottom_toBottomOf="parent" />
<FrameLayout <FrameLayout
android:id="@+id/dimmer" android:id="@+id/dimmer"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -185,6 +194,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/winner_text" app:layout_constraintTop_toBottomOf="@id/winner_text"
android:layout_marginTop="15dp"
style="@style/Widget.Oche_Button_Primary" style="@style/Widget.Oche_Button_Primary"
android:visibility="gone"/> android:visibility="gone"/>
@@ -198,7 +208,6 @@
android:id="@+id/player_stats_view" android:id="@+id/player_stats_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:visibility="gone"/> android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>