Skip to content

Commit

Permalink
Merge pull request #50 from TDT4240-14/BugFix
Browse files Browse the repository at this point in the history
Bug fix
  • Loading branch information
benjamls authored Mar 30, 2026
2 parents 760ae59 + 9e7992c commit 96800a0
Show file tree
Hide file tree
Showing 9 changed files with 507 additions and 223 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,19 @@ private int getInt(DataSnapshot snapshot, String key) {
if (val instanceof Integer) return (Integer) val;
return 0;
}

@Override
public void unconfirmSetup(String gameId, boolean isWhite, Runnable onSuccess, Callback<String> onError) {
String player = isWhite ? "white" : "black";
DatabaseReference gameRef = db.child("games").child(gameId);

// Clear the ready flag for this player and reset bothReady
gameRef.child("setup").child(player).removeValue()
.addOnSuccessListener(v -> {
gameRef.child("bothReady").removeValue()
.addOnSuccessListener(v2 -> onSuccess.run())
.addOnFailureListener(e -> onError.call(e.getMessage()));
})
.addOnFailureListener(e -> onError.call(e.getMessage()));
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// FirebaseAPI.java - Fix unconfirmSetup signature
package com.group14.regicidechess.database;

import com.group14.regicidechess.model.Lobby;
Expand Down Expand Up @@ -39,6 +40,11 @@ public interface FirebaseAPI {
*/
void confirmSetup(String gameId, boolean isWhite, int[][] board, Runnable onSuccess);

/**
* Unconfirms the setup, marking this player as not ready.
*/
void unconfirmSetup(String gameId, boolean isWhite, Runnable onSuccess, Callback<String> onError);

/** Fetches the opponent's stored board layout. */
void getOpponentBoard(String gameId, boolean localIsWhite, Callback<int[][]> onBoard);

Expand Down Expand Up @@ -79,4 +85,4 @@ void listenForHeartbeat(String gameId, boolean listenForWhite, long timeoutMs,
interface Callback<T> {
void call(T value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ public void render(float delta) {
stage.act(delta);
stage.draw();

// Draw a semi-transparent dimmer behind any visible overlay
if (overlayManager.isAnyOverlayVisible()) {
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Expand All @@ -217,7 +216,7 @@ public void render(float delta) {
sr.end();
sr.dispose();
Gdx.gl.glDisable(GL20.GL_BLEND);
stage.draw(); // redraw so overlay card appears above dimmer
stage.draw();
}
}

Expand Down Expand Up @@ -295,7 +294,6 @@ private void executeMove(Vector2 from, Vector2 to) {
ChessPiece movingPiece = inMatchState.getBoard().getPieceAt(from);
if (movingPiece == null) { deselect(); return; }

// Execute locally — this also switches the turn inside InMatchState
ChessPiece captured = inMatchState.executeMove(from, to);
deselect();

Expand All @@ -305,7 +303,6 @@ private void executeMove(Vector2 from, Vector2 to) {
overlayManager.showGameOver(true);

} else if (isPawnPromotion(movingPiece, to)) {
// Do NOT save yet — wait for the player to pick a promotion piece
pendingPromotionMove = new Move(from, to, movingPiece, localPlayer);
overlayManager.showPromotion();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public interface HostUIListener {
private Label statusLabel;
private TextButton createBtn;
private TextButton startBtn;
private Table sliderSection;

public LobbyHostUI(Skin skin, HostUIListener listener) {
this.skin = skin;
Expand Down Expand Up @@ -82,19 +83,21 @@ public void changed(ChangeEvent event, Actor actor) {
}
});

// Layout
container.add(buildRow(boardSizeLabel, boardSizeValueLabel))
// Sliders grouped so they can be hidden after lobby is created
sliderSection = new Table();
sliderSection.add(buildRow(boardSizeLabel, boardSizeValueLabel))
.expandX().fillX().padBottom(8).row();
container.add(boardSlider)
sliderSection.add(boardSlider)
.width(LobbyScreenConfig.SLIDER_WIDTH)
.height(LobbyScreenConfig.SLIDER_HEIGHT)
.padBottom(24).row();
container.add(buildRow(budgetLabel, budgetValueLabel))
sliderSection.add(buildRow(budgetLabel, budgetValueLabel))
.expandX().fillX().padBottom(8).row();
container.add(budgetSlider)
sliderSection.add(budgetSlider)
.width(LobbyScreenConfig.SLIDER_WIDTH)
.height(LobbyScreenConfig.SLIDER_HEIGHT)
.padBottom(32).row();
container.add(sliderSection).expandX().fillX().row();
container.add(createBtn)
.width(LobbyScreenConfig.BUTTON_WIDTH)
.height(LobbyScreenConfig.BUTTON_HEIGHT)
Expand Down Expand Up @@ -166,6 +169,7 @@ public void showCreatingState() {
}

public void showLobbyCreated(String gameId) {
sliderSection.setVisible(false);
createBtn.setVisible(false);
startBtn.setVisible(true);
startBtn.setDisabled(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// File: core/src/main/java/com/group14/regicidechess/screens/setup/SetupBoardInputHandler.java
// SetupBoardInputHandler.java - Updated version
package com.group14.regicidechess.screens.setup;

import com.group14.regicidechess.model.Player;
Expand All @@ -14,6 +14,7 @@ public class SetupBoardInputHandler {
public interface BoardActionListener {
void onPiecePlaced(ChessPiece piece, int col, int row);
void onPieceRemoved(int col, int row);
void onPieceReplaced(ChessPiece oldPiece, ChessPiece newPiece, int col, int row);
void onInvalidPlacement(String reason);
void onStateChanged();
}
Expand All @@ -23,18 +24,32 @@ public interface BoardActionListener {
private final SetupPaletteWidget palette;
private final Player localPlayer;
private final BoardActionListener listener;
private final boolean isLocked;

public SetupBoardInputHandler(SetupState setupState, SetupBoardRenderer renderer,
SetupPaletteWidget palette, Player localPlayer,
BoardActionListener listener) {
this(setupState, renderer, palette, localPlayer, listener, false);
}

public SetupBoardInputHandler(SetupState setupState, SetupBoardRenderer renderer,
SetupPaletteWidget palette, Player localPlayer,
BoardActionListener listener, boolean isLocked) {
this.setupState = setupState;
this.renderer = renderer;
this.palette = palette;
this.localPlayer = localPlayer;
this.listener = listener;
this.isLocked = isLocked;
}

public boolean handleTap(float worldX, float worldY) {
// If locked, ignore all board interactions
if (isLocked) {
listener.onInvalidPlacement("Setup is locked. Unconfirm to make changes.");
return false;
}

float cellSize = renderer.getCellSize();
float boardLeft = renderer.getBoardLeft();
float boardBottom = renderer.getBoardBottom();
Expand All @@ -49,26 +64,62 @@ public boolean handleTap(float worldX, float worldY) {

ChessPiece existing = setupState.getBoard().getPieceAt(col, row);

if (existing != null) {
// Remove existing piece
if (palette.hasSelection()) {
ChessPiece newPiece = palette.createSelectedPiece(localPlayer);

if (existing != null) {
// Check if it's the same piece type
boolean isSameType = existing.getClass().equals(newPiece.getClass());

if (isSameType) {
// Same piece type = remove
setupState.removePiece(col, row);
listener.onPieceRemoved(col, row);
// Keep selection for future placements
listener.onStateChanged();
} else {
// Different piece type = replace
// Check if we can afford the new piece after refunding the old one
int costDiff = newPiece.getPointCost() - existing.getPointCost();

if (costDiff <= setupState.getPlayer().getBudgetRemaining()) {
// First remove old piece (this refunds its cost)
setupState.removePiece(col, row);
// Then place new piece (this spends its cost)
boolean success = setupState.placePiece(newPiece, col, row);

if (success) {
listener.onPieceReplaced(existing, newPiece, col, row);
palette.afterPiecePlaced();
listener.onStateChanged();
} else {
// If placement fails, put the old piece back
setupState.placePiece(existing, col, row);
listener.onInvalidPlacement("Cannot replace piece - check budget and placement rules.");
}
} else {
listener.onInvalidPlacement("Not enough budget to replace with " + newPiece.getTypeName() +
" (needs " + costDiff + " more)");
}
}
} else {
// Empty square - normal placement
boolean success = setupState.placePiece(newPiece, col, row);

if (success) {
listener.onPiecePlaced(newPiece, col, row);
palette.afterPiecePlaced();
listener.onStateChanged();
} else {
String reason = getPlacementFailureReason(newPiece);
listener.onInvalidPlacement(reason);
}
}
} else if (existing != null) {
// No piece selected, clicking on existing piece = remove
setupState.removePiece(col, row);
listener.onPieceRemoved(col, row);
listener.onStateChanged();
} else if (palette.hasSelection()) {
ChessPiece piece = palette.createSelectedPiece(localPlayer);
boolean success = setupState.placePiece(piece, col, row);

if (success) {
// IMPORTANT: Do NOT clear selection - keep it so player can place more
// palette.afterPiecePlaced() is called but we don't clear selection
// The palette's keepSelectionAfterPlace flag is true by default
palette.afterPiecePlaced(); // This won't clear if keepSelectionAfterPlace is true
listener.onPiecePlaced(piece, col, row);
listener.onStateChanged();
} else {
String reason = getPlacementFailureReason(piece);
listener.onInvalidPlacement(reason);
}
}

return true;
Expand All @@ -87,4 +138,7 @@ private boolean kingIsOnBoard() {
}
return false;
}

public void setLocked(boolean locked) {
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// File: core/src/main/java/com/group14/regicidechess/screens/setup/SetupFlowController.java
// SetupFlowController.java - Updated version
package com.group14.regicidechess.screens.setup;

import com.badlogic.gdx.Gdx;
Expand All @@ -17,6 +17,8 @@ public interface FlowListener {
void onBothReady();
void onOpponentBoardFetched(Board finalBoard, Player opponent);
void onError(String message);
void onUnconfirmSuccess();
void onUnconfirmError(String message);
}

private final String gameId;
Expand All @@ -26,6 +28,7 @@ public interface FlowListener {
private final BoardFetchRetryPolicy retryPolicy;

private int boardFetchRetries = 0;
private boolean isConfirmed = false;

public SetupFlowController(String gameId, Player localPlayer, SetupState setupState, FlowListener listener) {
this(gameId, localPlayer, setupState, listener, new BoardFetchRetryPolicy());
Expand All @@ -41,6 +44,11 @@ public SetupFlowController(String gameId, Player localPlayer, SetupState setupSt
}

public void confirm() {
if (isConfirmed) {
listener.onError("Setup already confirmed!");
return;
}

// Check UI readiness (King placed)
if (!setupState.isReadyForConfirm()) {
listener.onError("Place your King before confirming.");
Expand All @@ -53,6 +61,7 @@ public void confirm() {
return;
}

isConfirmed = true;
listener.onUploadComplete();

int[][] encoded = SetupBoardCodec.encode(setupState.getBoard());
Expand All @@ -63,6 +72,31 @@ public void confirm() {
this::listenForBothReady
);
}

public void unconfirm() {
if (!isConfirmed) {
listener.onError("Setup not confirmed yet!");
return;
}

// Reset the player's ready flag
setupState.getPlayer().resetReady();
isConfirmed = false;

DatabaseManager.getInstance().getApi().unconfirmSetup(
gameId,
localPlayer.isWhite(),
() -> Gdx.app.postRunnable(() -> {
listener.onUnconfirmSuccess();
}),
error -> Gdx.app.postRunnable(() -> {
listener.onUnconfirmError(error);
// Revert flag if unconfirm fails
isConfirmed = true;
setupState.setReady();
})
);
}

private void listenForBothReady() {
DatabaseManager.getInstance().getApi().listenForBothSetupReady(
Expand Down Expand Up @@ -106,4 +140,8 @@ private void fetchOpponentBoard() {
})
);
}

public boolean isConfirmed() {
return isConfirmed;
}
}
Loading

0 comments on commit 96800a0

Please sign in to comment.