From 459e45802836366ea60cbe88ec9d7434679a85d2 Mon Sep 17 00:00:00 2001 From: "fl.du.pr Grens" Date: Fri, 16 May 2025 17:53:01 +0200 Subject: [PATCH] feat: popups --- .../java/chess/view/DDDrender/DDDView.java | 593 +++++++++++------- 1 file changed, 366 insertions(+), 227 deletions(-) diff --git a/app/src/main/java/chess/view/DDDrender/DDDView.java b/app/src/main/java/chess/view/DDDrender/DDDView.java index 607f804..8a80341 100644 --- a/app/src/main/java/chess/view/DDDrender/DDDView.java +++ b/app/src/main/java/chess/view/DDDrender/DDDView.java @@ -3,18 +3,18 @@ package chess.view.DDDrender; import java.io.IOException; import java.util.List; import java.util.function.Consumer; +import java.util.function.Function; -import chess.controller.commands.MoveCommand; +import chess.controller.commands.*; import chess.controller.event.GameListener; import imgui.ImGui; +import imgui.type.ImBoolean; import org.joml.Vector2f; import org.joml.Vector3f; import chess.controller.Command; import chess.controller.Command.CommandResult; import chess.controller.CommandExecutor; -import chess.controller.commands.GetAllowedMovesPieceCommand; -import chess.controller.commands.GetPieceAtCommand; import chess.controller.event.GameAdaptator; import chess.model.Color; import chess.model.Coordinate; @@ -26,256 +26,395 @@ import chess.view.DDDrender.world.World; public class DDDView extends GameAdaptator implements GameListener { - private static final Vector3f BLACK = new Vector3f(0.3f, 0.3f, 0.3f); - private static final Vector3f WHITE = new Vector3f(1.0f, 1.0f, 1.0f); + private static final Vector3f BLACK = new Vector3f(0.3f, 0.3f, 0.3f); + private static final Vector3f WHITE = new Vector3f(1.0f, 1.0f, 1.0f); - private final CommandExecutor commandExecutor; - private final Window window; - private final World world; - private BoardEntity boardEntity; - private final Camera camera; - private Coordinate click = null; + private final CommandExecutor commandExecutor; + private final Window window; + private final World world; + private BoardEntity boardEntity; + private final Camera camera; + private Coordinate click = null; - private static final float animationTime = 1.5f; // in seconds - private static final int animationTurns = 1; + private static final float animationTime = 1.5f; // in seconds + private static final int animationTurns = 1; - public DDDView(CommandExecutor commandExecutor) { - this.commandExecutor = commandExecutor; - this.world = new World(); - this.camera = new Camera(); - this.window = new Window(new Renderer(), this.world, this.camera); - } + private float moveProgress = 0.0f; - private void cancelClick(){ - this.click=null; - } - private void setClick(Coordinate coordinate) {this.click=coordinate;} - private CommandResult sendCommand(Command command) { - return this.commandExecutor.executeCommand(command); - } + private String waitingPopup = null; + private final ImBoolean popupOpened = new ImBoolean(false); - // Invoked when a cell is clicked - private void onCellClick(Coordinate coordinate) { - if (this.click == null){ // case: first click - GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); - if (sendCommand(movesCommand) == CommandResult.NotAllowed) { // case: invalid piece to move - System.out.println("Nothing to do here."); - return; - } - List allowedMoves = movesCommand.getDestinations(); - if (allowedMoves.isEmpty()) { // case: no movement possible for piece - System.out.println("This piece cannot be moved at the moment."); - return; - } - setClick(coordinate); - previewMoves(coordinate); - System.out.println("First click on " + coordinate); - return; - } - // case: second click - GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(this.click); - if (sendCommand(movesCommand) == CommandResult.NotAllowed) { // case: invalid piece to move - cancelPreview(this.click); - System.out.println("Nothing to do here."); - cancelClick(); - return; - } - List allowedMoves = movesCommand.getDestinations(); - if (allowedMoves.isEmpty()) { // case: no movement possible for piece - cancelPreview(this.click); - System.out.println("This piece cannot be moved at the moment."); - cancelClick(); - return; - } - if (allowedMoves.contains(coordinate)){ // case: valid attempt to move - System.out.println("Move on " + coordinate); - cancelPreview(this.click); - Command.CommandResult result = sendCommand(new MoveCommand(new Move(click, coordinate))); - cancelClick(); - return; - } - if (!(coordinate == this.click)) { - System.out.println("New click on " + coordinate); // cases: invalid move, selecting another piece - cancelPreview(this.click); - previewMoves(coordinate); - setClick(coordinate); - return; - } - System.out.println("Cancelling click."); // case: cancelling previous click - cancelClick(); - } + public DDDView(CommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + this.world = new World(); + this.camera = new Camera(); + this.window = new Window(new Renderer(), this.world, this.camera); + } - private void previewMoves(Coordinate coordinate){ - Piece p = pieceAt(coordinate); - if (p == null) { - return; - } - GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); - if (sendCommand(movesCommand) == CommandResult.NotAllowed) { - return; - } - this.boardEntity.setCellColor(coordinate, new Vector3f(1, 0, 0)); - this.world.getPiece(coordinate).setColor(new Vector3f(1, 0, 0)); - List allowedMoves = movesCommand.getDestinations(); - if (allowedMoves.isEmpty()) - return; - for (Coordinate destCoord : allowedMoves) { - this.boardEntity.setCellColor(destCoord, new Vector3f(1, 1, 0)); - } - } + private void cancelClick() { + this.click = null; + } - // Invoked when a cell is hovered - private void onCellEnter(Coordinate coordinate) { - if (this.click == null){ - // small test turning a cell red when hovered - this.boardEntity.setCellColor(coordinate, new Vector3f(1, 0, 0)); - Piece p = pieceAt(coordinate); - if (p == null) - return; - this.world.getPiece(coordinate).setColor(new Vector3f(1, 0, 0)); - GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); - if (sendCommand(movesCommand) == CommandResult.NotAllowed) - return ; - List allowedMoves = movesCommand.getDestinations(); - if (allowedMoves.isEmpty()) - return ; - for (Coordinate destCoord : allowedMoves) { - this.boardEntity.setCellColor(destCoord, new Vector3f(1, 0, 0)); - } - } - } + private void setClick(Coordinate coordinate) { + this.click = coordinate; + } - // Invoked when a cell is not hovered anymore - private void onCellExit(Coordinate coordinate) { - if (this.click == null){ - this.boardEntity.resetCellColor(coordinate); - Piece p = pieceAt(coordinate); - if (p == null) - return; - this.world.getPiece(coordinate).setColor(p.getColor() == Color.White ? WHITE : BLACK); - GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); - if (sendCommand(movesCommand) == CommandResult.NotAllowed) - return ; - List allowedMoves = movesCommand.getDestinations(); - if (allowedMoves.isEmpty()) - return ; - for (Coordinate destCoord : allowedMoves) { - this.boardEntity.resetCellColor(destCoord); - } - } - } + private CommandResult sendCommand(Command command) { + return this.commandExecutor.executeCommand(command); + } - private void cancelPreview(Coordinate coordinate){ - this.boardEntity.resetCellColor(coordinate); - Piece p = pieceAt(coordinate); - if (p == null) - return; - this.world.getPiece(coordinate).setColor(p.getColor() == Color.White ? WHITE : BLACK); - GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); - if (sendCommand(movesCommand) == CommandResult.NotAllowed) - return ; - List allowedMoves = movesCommand.getDestinations(); - if (allowedMoves.isEmpty()) - return ; - for (Coordinate destCoord : allowedMoves) { - this.boardEntity.resetCellColor(destCoord); - } - } + // Invoked when a cell is clicked + private void onCellClick(Coordinate coordinate) { + if (this.click == null) { // case: first click + GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); + if (sendCommand(movesCommand) == CommandResult.NotAllowed) { // case: invalid piece to move + System.out.println("Nothing to do here."); + return; + } + List allowedMoves = movesCommand.getDestinations(); + if (allowedMoves.isEmpty()) { // case: no movement possible for piece + System.out.println("This piece cannot be moved at the moment."); + return; + } + setClick(coordinate); + previewMoves(coordinate); + System.out.println("First click on " + coordinate); + return; + } + // case: second click + GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(this.click); + if (sendCommand(movesCommand) == CommandResult.NotAllowed) { // case: invalid piece to move + cancelPreview(this.click); + System.out.println("Nothing to do here."); + cancelClick(); + return; + } + List allowedMoves = movesCommand.getDestinations(); + if (allowedMoves.isEmpty()) { // case: no movement possible for piece + cancelPreview(this.click); + System.out.println("This piece cannot be moved at the moment."); + cancelClick(); + return; + } + if (allowedMoves.contains(coordinate)) { // case: valid attempt to move + System.out.println("Move on " + coordinate); + cancelPreview(this.click); + Command.CommandResult result = sendCommand(new MoveCommand(new Move(click, coordinate))); + cancelClick(); + return; + } + if (!(coordinate == this.click)) { + System.out.println("New click on " + coordinate); // cases: invalid move, selecting another piece + cancelPreview(this.click); + previewMoves(coordinate); + setClick(coordinate); + return; + } + System.out.println("Cancelling click."); // case: cancelling previous click + cancelClick(); + } - private Piece pieceAt(Coordinate pos) { - GetPieceAtCommand cmd = new GetPieceAtCommand(pos); - this.commandExecutor.executeCommand(cmd); - return cmd.getPiece(); - } + private void previewMoves(Coordinate coordinate) { + Piece p = pieceAt(coordinate); + if (p == null) { + return; + } + GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); + if (sendCommand(movesCommand) == CommandResult.NotAllowed) { + return; + } + this.boardEntity.setCellColor(coordinate, new Vector3f(1, 0, 0)); + this.world.getPiece(coordinate).setColor(new Vector3f(1, 0, 0)); + List allowedMoves = movesCommand.getDestinations(); + if (allowedMoves.isEmpty()) + return; + for (Coordinate destCoord : allowedMoves) { + this.boardEntity.setCellColor(destCoord, new Vector3f(1, 1, 0)); + } + } - @Override - public void onGameStart() { - this.window.scheduleTask(() -> { - try { - initBoard(); - } catch (IOException e) { - e.printStackTrace(); - } - // start listening to mouse events - this.window.OnCellClick.connect(this::onCellClick); - this.window.OnCellEnter.connect(this::onCellEnter); - this.window.OnCellExit.connect(this::onCellExit); - this.window.OnImGuiTopRender.connect(this::onHeaderRender); - this.window.OnImGuiBottomRender.connect(this::onFooterRender); - }); - } + // Invoked when a cell is hovered + private void onCellEnter(Coordinate coordinate) { + if (this.click == null) { + // small test turning a cell red when hovered + this.boardEntity.setCellColor(coordinate, new Vector3f(1, 0, 0)); + Piece p = pieceAt(coordinate); + if (p == null) + return; + this.world.getPiece(coordinate).setColor(new Vector3f(1, 0, 0)); + GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); + if (sendCommand(movesCommand) == CommandResult.NotAllowed) + return; + List allowedMoves = movesCommand.getDestinations(); + if (allowedMoves.isEmpty()) + return; + for (Coordinate destCoord : allowedMoves) { + this.boardEntity.setCellColor(destCoord, new Vector3f(1, 0, 0)); + } + } + } - private void onHeaderRender() { - ImGui.text("FPS : " + ImGui.getIO().getFramerate()); - } + // Invoked when a cell is not hovered anymore + private void onCellExit(Coordinate coordinate) { + if (this.click == null) { + this.boardEntity.resetCellColor(coordinate); + Piece p = pieceAt(coordinate); + if (p == null) + return; + PieceEntity pEntity = this.world.getPiece(coordinate); + if (pEntity == null) + return; - private void onFooterRender() { - ImGui.button("Coucou"); - } + pEntity.setColor(p.getColor() == Color.White ? WHITE : BLACK); + GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); + if (sendCommand(movesCommand) == CommandResult.NotAllowed) + return; + List allowedMoves = movesCommand.getDestinations(); + if (allowedMoves.isEmpty()) + return; + for (Coordinate destCoord : allowedMoves) { + this.boardEntity.resetCellColor(destCoord); + } + } + } - private void initBoard() throws IOException { - for (int i = 0; i < Coordinate.VALUE_MAX; i++) { - for (int j = 0; j < Coordinate.VALUE_MAX; j++) { - Coordinate pos = new Coordinate(i, j); - Piece piece = pieceAt(pos); - if (piece == null) - continue; + private void cancelPreview(Coordinate coordinate) { + this.boardEntity.resetCellColor(coordinate); + Piece p = pieceAt(coordinate); + if (p == null) + return; + this.world.getPiece(coordinate).setColor(p.getColor() == Color.White ? WHITE : BLACK); + GetAllowedMovesPieceCommand movesCommand = new GetAllowedMovesPieceCommand(coordinate); + if (sendCommand(movesCommand) == CommandResult.NotAllowed) + return; + List allowedMoves = movesCommand.getDestinations(); + if (allowedMoves.isEmpty()) + return; + for (Coordinate destCoord : allowedMoves) { + this.boardEntity.resetCellColor(destCoord); + } + } - Vector2f pieceBoardPos = DDDPlacement.coordinatesToVector(pos); - Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y()); + private Piece pieceAt(Coordinate pos) { + GetPieceAtCommand cmd = new GetPieceAtCommand(pos); + this.commandExecutor.executeCommand(cmd); + return cmd.getPiece(); + } - PieceEntity entity = new PieceEntity(piece, pieceWorldPos, - piece.getColor() == Color.White ? WHITE : BLACK, - piece.getColor() == Color.White ? 0.0f : (float) Math.PI); + @Override + public void onGameStart() { + this.window.scheduleTask(() -> { + try { + initBoard(); + } catch (IOException e) { + e.printStackTrace(); + } + // start listening to mouse events + this.window.OnCellClick.connect(this::onCellClick); + this.window.OnCellEnter.connect(this::onCellEnter); + this.window.OnCellExit.connect(this::onCellExit); + this.window.OnImGuiTopRender.connect(this::onHeaderRender); + this.window.OnImGuiBottomRender.connect(this::onFooterRender); + }); + } - this.world.addPiece(entity, pos); - } - } - this.boardEntity = new BoardEntity(); - this.world.addEntity(this.boardEntity); - } + private void onHeaderRender() { + ImGui.text("FPS : " + ImGui.getIO().getFramerate()); + } - @Override - public void onMove(Move move) { - if(move.getDeadPieceCoords() != null) { - this.world.ejectPiece(move.getDeadPieceCoords()); - } - Vector2f pieceBoardPos = DDDPlacement.coordinatesToVector(move.getFinish()); - Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y()); - this.world.getPiece(move.getStart()).setPosition(pieceWorldPos); - this.world.movePiece(this.world.getPiece(move.getStart()), move); - cameraRotate(); - } + private void onFooterRender() { + if (ImGui.button("Roque")) { + sendCommand(new CastlingCommand(false)); + } + ImGui.sameLine(); + if (ImGui.button("Grand Roque")) { + sendCommand(new CastlingCommand(true)); + } + ImGui.sameLine(); + if (ImGui.button("Annuler le coup précédent")) { + sendCommand(new UndoCommand()); + } + openPopup(); + renderPopups(); + } - private void cameraTick(float delta) { - int oddAnimationTurn = (2 * (animationTurns-1)) + 1; - final float angle = (float) Math.PI; - this.camera.setRotateAngle(this.camera.getRotateAngle() + angle * delta * oddAnimationTurn / animationTime); - } + private void openPopup() { + if (waitingPopup != null) { + ImGui.openPopup(waitingPopup); + waitingPopup = null; + } + } - public void cameraRotate() { - float end = this.camera.getRotateAngle() + (float) Math.PI; - Consumer rotationConsumer = this::cameraTick; - this.window.addRegularTask(rotationConsumer); + private void initBoard() throws IOException { + for (int i = 0; i < Coordinate.VALUE_MAX; i++) { + for (int j = 0; j < Coordinate.VALUE_MAX; j++) { + Coordinate pos = new Coordinate(i, j); + Piece piece = pieceAt(pos); + if (piece == null) + continue; + + Vector2f pieceBoardPos = DDDPlacement.coordinatesToVector(pos); + Vector3f pieceWorldPos = new Vector3f(pieceBoardPos.x(), 0, pieceBoardPos.y()); + + PieceEntity entity = new PieceEntity(piece, pieceWorldPos, + piece.getColor() == Color.White ? WHITE : BLACK, + piece.getColor() == Color.White ? 0.0f : (float) Math.PI); + + this.world.addPiece(entity, pos); + } + } + this.boardEntity = new BoardEntity(); + this.world.addEntity(this.boardEntity); + } + + /** + * @param begin begin + * @param middle control point + * @param end end + * @param t between 0 and 1 + * @return the point + */ + private Vector3f bezierCurve(Vector3f begin, Vector3f middle, Vector3f end, float t) { + return begin.mul((1.0f - t) * (1.0f - t)).add(middle.mul(2.0f * t * (1.0f - t))).add(end.mul(t * t)); + } + + private Function lagrangeInterpolation(Vector3f begin, Vector3f middle, Vector3f end) { + return (t) -> { + return t+1.0f; + }; + } + + private void pieceTick(float progress, PieceEntity piece, Move move) { + float height = 1; // how much the piece is raised + Vector2f pieceStartBoard = DDDPlacement.coordinatesToVector(move.getStart()); + Vector2f pieceDestinationBoard = DDDPlacement.coordinatesToVector(move.getFinish()); + Vector3f start = new Vector3f(pieceStartBoard.x(), 0, pieceStartBoard.y()); + Vector3f top = new Vector3f(pieceDestinationBoard.x() - pieceStartBoard.x(), height, pieceDestinationBoard.y() - pieceStartBoard.y()); + Vector3f end = new Vector3f(pieceDestinationBoard.x(), 0, pieceDestinationBoard.y()); + + piece.setPosition(bezierCurve(start, top, end, progress)); + } + + private void move3DPiece(Move move) { + Vector2f pieceDestBoardPos = DDDPlacement.coordinatesToVector(move.getFinish()); + Vector3f pieceDestWorldPos = new Vector3f(pieceDestBoardPos.x(), 0, pieceDestBoardPos.y()); + + final PieceEntity pEntity = this.world.getPiece(move.getStart()); + final Move pMove = move; + + this.moveProgress = 0.0f; + + Consumer moveConsumer = (delta) -> { + this.moveProgress += delta; + pieceTick(this.moveProgress, pEntity, pMove); + }; + + this.window.addRegularTask(moveConsumer); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + this.window.removeRegularTask(moveConsumer); + + if (move.getDeadPieceCoords() != null) { + this.world.ejectPiece(move.getDeadPieceCoords()); + } + pEntity.setPosition(pieceDestWorldPos); + + this.world.movePiece(pEntity, move); + } + + @Override + public void onMove(Move move) { + move3DPiece(move); + cameraRotate(); + } + + private void cameraTick(float delta) { + int oddAnimationTurn = (2 * (animationTurns - 1)) + 1; + final float angle = (float) Math.PI; + this.camera.setRotateAngle(this.camera.getRotateAngle() + angle * delta * oddAnimationTurn / animationTime); + } + + private void cameraRotate() { + float end = this.camera.getRotateAngle() + (float) Math.PI; + Consumer rotationConsumer = this::cameraTick; + this.window.addRegularTask(rotationConsumer); try { Thread.sleep((long) (animationTime * 1000)); } catch (InterruptedException e) { throw new RuntimeException(e); } - this.window.removeRegularTask(rotationConsumer); - this.camera.setRotateAngle(end); - } + this.window.removeRegularTask(rotationConsumer); + this.camera.setRotateAngle(end); + } - public void run() { - this.window.run(); + public void run() { + this.window.run(); - // free OpenGL resources - try { - this.window.close(); - this.world.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } + // free OpenGL resources + try { + this.window.close(); + this.world.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private void renderPopup(String title, String text) { + if(ImGui.beginPopupModal(title, popupOpened)){ + ImGui.text(text); + ImGui.endPopup(); + } + } + + private void renderPopups() { + renderPopup("Check","Your king is in check"); + renderPopup("Checkmate", "Checkmate, it's a win!"); + renderPopup("Promotion", "Please promote your pawn."); + renderPopup("Pat", "It's a pat!"); + renderPopup("Tie", "It's a tie!"); + renderPopup("White surrender", "The white player has surrendered!"); + renderPopup("Black surrender", "The black player has surrendered!"); + renderPopup("White victory", "The white player has won !"); + renderPopup("Black victory", "The black player has won !"); + renderPopup("End", "End of the game, thank you for playing!"); + } + + private void openPopup(String title) { + this.popupOpened.set(true); + this.waitingPopup = title; + } + + @Override + public void onKingInCheck(){ + openPopup("Check"); + } + + @Override + public void onDraw() { + openPopup("Tie"); + } + + @Override + public void onKingInMat() { + openPopup("Checkmate"); + } + + @Override + public void onGameEnd() { + openPopup("End"); + } + + @Override + public void onPatSituation() { + openPopup("Pat"); + } + + @Override + public void onSurrender(Color color) { + openPopup(color == Color.White ? "White surrender": "Black surrender"); + openPopup(color == Color.White ? "Black victory" : "White victory"); + } }