moved FX app to client dir

This commit is contained in:
FrancescoPedretti
2026-04-13 15:09:10 +02:00
parent 9e8b0475fd
commit f79b511843
10 changed files with 9 additions and 806 deletions

View File

@@ -0,0 +1,681 @@
package client;
import Server.*;
import Server.Automaton.ActionResult;
import Server.Automaton.Game;
import Server.Cards.*;
import javafx.animation.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
public class DeckGridAppFX extends Application {
private static final Logger logger = LogManager.getLogger(DeckGridAppFX.class);
private static final int IMG_HEIGHT=200;
private PDDocument documentFront;
private PDDocument documentBack;
private PDFRenderer pdfRendererFront;
private PDFRenderer pdfRendererBack;
private int totalCards = 0;
// Contenitori Layout
private HBox topMenu;
private HBox topRow;
private HBox centerRow;
private HBox bottomRow;
private HBox playersArea; // NUOVO: Area per i giocatori
private Button btnRefresh;
private Button btnTop;
private Button btnBottom;
private Button btnChooseOffering;
private Button btnMsg;
// --------------------------------
@Override
public void start(Stage primaryStage) {
List<Player> players = new ArrayList<>();
//players.add(new Player("Yellow", TotemColor.YELLOW));
players.add(new Player("Blue", TotemColor.BLUE));
//players.add(new Player("Purple", TotemColor.PURPLE));
players.add(new Player("Red", TotemColor.RED));
//players.add(new Player("Green", TotemColor.GREEN));
Game game = new Game(players);
InputStream frontStream = getClass().getResourceAsStream("/files/Cards_total_front_PROMO.pdf");
InputStream backStream = getClass().getResourceAsStream("/files/Cards_total_back_PROMO.pdf");
InputStream csvStream = getClass().getResourceAsStream("/files/cards.csv");
try {
documentFront = PDDocument.load(frontStream);
pdfRendererFront = new PDFRenderer(documentFront);
documentBack = PDDocument.load(backStream);
pdfRendererBack = new PDFRenderer(documentBack);
} catch ( IOException e) {
throw new RuntimeException(e);
}
//File fileCsv = new File(getClass().getResource("/files/cards.csv").toURI());
game.newGame(csvStream);
game.setEventListener(notification -> {
Platform.runLater(() -> {
drawGameState(game);
switch (notification.event()) {
case TOTEM_PLACEMENT-> showPopup("TOTEM PLACEMENT\n" + (notification.message()!=null?notification.message():""));
case SETUP -> showPopup("SETUP \n" + (notification.message()!=null?notification.message():""));
case ACTION_RESOLUTION -> showPopup("ACTION_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
case END_ACTION -> showPopup("END_ACTION \n" + (notification.message()!=null?notification.message():"" ));
case EVENT_RESOLUTION -> showPopup("EVENT_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
case EXTRA_DRAW -> showPopup("EXTRA_DRAW \n" + (notification.message()!=null?notification.message():"" ));
case GAME_OVER -> showPopup("GAME OVER");
}
});
});
primaryStage.setTitle("Tavolo da Gioco - Board & Players");
btnTop = new Button("Pick Top");
btnBottom = new Button("Pick Bottom");
btnMsg = new Button("Game State");
btnRefresh= new Button("Refresh");
btnChooseOffering= new Button("Choose Offering");
//btnNext.setDisable(true);
topMenu = new HBox(15, btnRefresh, btnMsg);
topMenu.setAlignment(Pos.CENTER);
topMenu.setPadding(new Insets(10));
Label stateLabel = new Label("Game State = " + game.getState() );
stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
stateLabel.setTextFill(Color.DARKBLUE);
//showPopup(topMenu, "STARTING GAME");
btnMsg.setOnAction(e -> {
new Alert(Alert.AlertType.INFORMATION,
game.toString()
).showAndWait();
});
btnRefresh.setOnAction(e -> {
drawGameState(game);
});
topRow = createRowContainer();
centerRow = createRowContainer();
bottomRow = createRowContainer();
// NUOVO: Inizializza l'area giocatori
playersArea = new HBox(30);
playersArea.setAlignment(Pos.CENTER);
playersArea.setPadding(new Insets(20));
VBox tableArea = new VBox(20,
new Label(" "), topRow,
new Label(" "), centerRow,
new Label(" "), bottomRow,
new Label("--- ASSET PLAYERS ---"), playersArea
);
tableArea.setAlignment(Pos.CENTER);
tableArea.setPadding(new Insets(20));
ScrollPane scrollPane = new ScrollPane(tableArea);
scrollPane.setFitToWidth(true);
BorderPane root = new BorderPane();
root.setTop(topMenu);
root.setCenter(scrollPane);
Scene scene = new Scene(root, 1300, 900);
primaryStage.setScene(scene);
primaryStage.show();
drawGameState(game);
}
private HBox createRowContainer() {
HBox row = new HBox(10);
row.setAlignment(Pos.CENTER);
return row;
}
private void drawGameState(Game game) {
if (documentFront == null) return;
topMenu.getChildren().clear();
topRow.getChildren().clear();
centerRow.getChildren().clear();
bottomRow.getChildren().clear();
playersArea.getChildren().clear();
drawTopMenu(topMenu, game);
drawTopRow(topRow, game);
drawTurnTileAndOffering(centerRow, game.getPlayers().size(), game);
drawBottomRow(bottomRow, game );
List<Player> players =game.getPlayers();
renderPlayers(players, game);
logger.info(game.getCurrentPlayer());
}
private void pickTopCardAction(Player player, GameBoard board, int cardid){
Card card = board.getTopRow().stream()
.filter(CharacterCard.class::isInstance)
.map(CharacterCard.class::cast)
.filter(c -> c.getCardId() == cardid)
.findFirst()
.orElse(null);
if (card != null) {
board.getTopRow().remove(card);
CharacterCard charCard = (CharacterCard)card;
player.addCharacterToTribe((CharacterCard) card);
}
}
private void pickBottomCardAction(Player player, GameBoard board, int cardid){
Card card = board.getBottomRow().stream()
.filter(CharacterCard.class::isInstance)
.map(CharacterCard.class::cast)
.filter(c -> c.getCardId() == cardid)
.findFirst()
.orElse(null);
if (card != null) {
board.getBottomRow().remove(card);
player.addCharacterToTribe((CharacterCard) card);
}
}
private void drawBottomRow(HBox row, Game game) {
for (Card c : game.getGameBoard().getBottomRow()) {
ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);
if (cardImage != null) {
// 1. Create a Label for your debugging text
Label debugLabel = new Label("ID: " + c.getCardId());
// 2. Style the label so it's readable over any card background
// (White text with a semi-transparent black background)
debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
"-fx-text-fill: white; " +
"-fx-padding: 3px; " +
"-fx-font-weight: bold;");
// 3. Create a StackPane and add both the image and the text
StackPane cardContainer = new StackPane();
cardContainer.getChildren().addAll(cardImage, debugLabel);
// 4. Align the text wherever you want (e.g., Top-Left corner of the card)
StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
// You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
cardImage.setOnMouseClicked(event -> {
logger.info("Bottom Card clicked");
Player p = game.getCurrentPlayer();
//pickBottomCardAction(p, game.getGameBoard(), c.getCardId());
ActionResult result = game.resolveCardAction(p, false, c.getCardId());
if (!result.isSuccess()) {
new Alert(Alert.AlertType.ERROR,
result.getErrorMessage()
).showAndWait();
}
drawGameState(game);
});
// 5. Add the StackPane (which now holds both) to the row
row.getChildren().add(cardContainer);
}
}
}
private void drawTopMenu(HBox row, Game game) {
//topMenu = new HBox(15, btnChooseOffering, btnAction, btnTop, btnBottom, btnMsg);
//topMenu.setAlignment(Pos.CENTER);
//topMenu.setPadding(new Insets(10));
Label stateLabel = new Label("Game State = " + game.getState() );
stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
stateLabel.setTextFill(Color.DARKBLUE);
row.getChildren().add(stateLabel);
row.getChildren().add(btnRefresh);
row.getChildren().add(btnMsg);
}
private void drawTopRow(HBox row, Game game) {
for (Card c : game.getGameBoard().getTopRow()) {
ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);
if (cardImage != null) {
// 1. Create a Label for your debugging text
Label debugLabel = new Label("ID: " + c.getCardId());
// 2. Style the label so it's readable over any card background
// (White text with a semi-transparent black background)
debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
"-fx-text-fill: white; " +
"-fx-padding: 3px; " +
"-fx-font-weight: bold;");
// 3. Create a StackPane and add both the image and the text
StackPane cardContainer = new StackPane();
cardContainer.getChildren().addAll(cardImage, debugLabel);
// 4. Align the text wherever you want (e.g., Top-Left corner of the card)
StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
// You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
cardImage.setOnMouseClicked(event -> {
logger.info("Card clicked");
Player p = game.getCurrentPlayer();
//pickTopCardAction(p, game.getGameBoard(), c.getCardId());
//game.resolveCardAction(p, true, c.getCardId());
ActionResult result = game.resolveCardAction(p, true, c.getCardId());
if (!result.isSuccess()) {
new Alert(Alert.AlertType.ERROR,
result.getErrorMessage()
).showAndWait();
}
drawGameState(game);
});
// 5. Add the StackPane (which now holds both) to the row
row.getChildren().add(cardContainer);
}
}
}
private StackPane drawTurnTileCardImageWithPlayers(InputStream imgStream, int height, Player[] players) {
try {
Image fxImage = new Image(imgStream);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
StackPane stack = new StackPane(imageView);
VBox playerBox = new VBox(5);
playerBox.setPadding(new Insets(5));
playerBox.setAlignment(Pos.CENTER_RIGHT);
// 🔥 DEBUG background (to SEE the box)
playerBox.setStyle("-fx-background-color: rgba(255,0,0,0.3);");
for (Player p : players) {
if (p==null) continue;
Rectangle rect = new Rectangle(15,15);
rect.setFill(p.getTotemColor().getFxColor());
rect.setStroke(Color.WHITE);
rect.setStrokeWidth(1);
playerBox.getChildren().add(rect);
}
playerBox.setPadding(new Insets(22));
// 🔥 IMPORTANT: bring to front
playerBox.toFront();
// position it clearly
StackPane.setAlignment(playerBox, Pos.CENTER);
stack.getChildren().add(playerBox);
return stack;
} catch (Exception e) {
logger.error("Errore caricamento immagine {}", imgStream, e);
return null;
}
}
private void drawTurnTileAndOffering(HBox row, int n, Game game) {
// Clear row if needed (optional but recommended)
row.getChildren().clear();
// --- LEFT BLOCK (Turn + initial card) ---
VBox turnBox = new VBox(5);
turnBox.setAlignment(Pos.CENTER);
// Turn label
int round = game.getRound(); // adjust if different
Label turnLabel = new Label("Round = " + round);
turnLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
turnLabel.setTextFill(Color.DARKBLUE);
Card first = game.getGameBoard().getCardDeck().getFirstCardForCover();
if (first!=null){
ImageView imgCover = createCardImageFromPdf(first.getCardId(), 120, false);
turnBox.getChildren().addAll(turnLabel, imgCover);
} else turnBox.getChildren().addAll(turnLabel);
// Add FIRST
row.getChildren().add(turnBox);
// --- TURN TILE ---
StackPane pane = drawTurnTileCardImageWithPlayers(
getClass().getResourceAsStream("/files/Start_" + n + "P.png"),
IMG_HEIGHT,
game.getGameBoard().getTurnTile().getPositions()
);
row.getChildren().add(pane);
// --- OFFERINGS ---
for (OfferingTile offering : game.getGameBoard().getOfferingTiles()) {
Player occupant = offering.getOccupant();
StackPane offPane =drawOfferingCardImageWithTotem(
getClass().getResourceAsStream("/files/offering" + offering.getLetter() + ".png"),
IMG_HEIGHT,
occupant, game, offering);
if (offPane != null) {
row.getChildren().add(offPane);
}
}
}
// --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
private void renderPlayers(List<Player> players, Game game) {
playersArea.getChildren().clear();
for (Player player : players) {
boolean active = player.equals(game.getCurrentPlayer());
VBox playerBox = new VBox(10);
playerBox.setPadding(new Insets(15));
playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");
if (active) {
// Strong highlight (gold border + soft background)
playerBox.setStyle(
"-fx-border-color: gold;" +
"-fx-border-width: 3;" +
"-fx-border-radius: 10;" +
"-fx-background-color: linear-gradient(to bottom, #fffbe6, #f9f9f9);" +
"-fx-background-radius: 10;"
);
// Slight scale-up
playerBox.setScaleX(1.05);
playerBox.setScaleY(1.05);
// Glow effect
DropShadow glow = new DropShadow();
glow.setColor(Color.GOLD);
glow.setRadius(20);
playerBox.setEffect(glow);
// Smooth pulse animation (NOT blinking)
Timeline pulse = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(glow.radiusProperty(), 10)
),
new KeyFrame(Duration.seconds(1.2),
new KeyValue(glow.radiusProperty(), 25)
)
);
pulse.setAutoReverse(true);
pulse.setCycleCount(Animation.INDEFINITE);
pulse.play();
}
HBox nameRow = new HBox(8);
nameRow.setAlignment(Pos.CENTER_LEFT);
Rectangle rect = new Rectangle(12, 12);
TotemColor color = player.getTotemColor();
rect.setFill(color.getFxColor());
rect.setStroke(Color.BLACK);
rect.setStrokeWidth(1);
Label nameLbl = new Label(player.getNickname());
nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));
if (active) {
Label turnLbl = new Label(" ▶ Your Turn");
turnLbl.setTextFill(Color.GOLDENROD);
turnLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
nameRow.getChildren().addAll(rect, nameLbl, turnLbl);
} else {
nameRow.getChildren().addAll(rect, nameLbl);
}
// Risorse Cibo e Soldi
Label statsLbl = new Label("🍖 Food: " + player.getFoodTokens() + " | 💰 Points: " + player.getPrestigePoints());
statsLbl.setTextFill(Color.DARKRED);
statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
playerBox.getChildren().addAll(nameRow, statsLbl);
Map<CharacterType, List<CharacterCard>> groupedCards = player.getPlayerTribe().getCharacters().stream()
.collect(Collectors.groupingBy(CharacterCard::getCharacterType));
// Itera sui gruppi creati e genera la grafica
groupedCards.forEach((type, cardsOfType) -> {
Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
HBox cardImagesRow = new HBox(5);
for (Card c : cardsOfType) {
// Carte più piccole (90px) per l'area giocatore
ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
if (img != null) cardImagesRow.getChildren().add(img);
}
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
});
Map<Era, List<BuildingCard>> groupedBuildCards = player.getPlayerTribe().getBuildingCard().stream()
.collect(Collectors.groupingBy(BuildingCard::getEra));
groupedBuildCards.forEach((type, cardsOfType) -> {
Label typeLbl = new Label("Build Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
HBox cardImagesRow = new HBox(5);
for (Card c : cardsOfType) {
// Carte più piccole (90px) per l'area giocatore
ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
if (img != null) cardImagesRow.getChildren().add(img);
}
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
//playerBox.getChildren().addAll(nameRow, statsLbl);
});
playersArea.getChildren().add(playerBox);
}
}
private ImageView createCardImageFromPdf(int pageIndex, int height, boolean front) {
try {
BufferedImage bim =null;
if (front) bim = pdfRendererFront.renderImageWithDPI(pageIndex-1, 100);
else bim = pdfRendererBack.renderImageWithDPI(pageIndex-1, 100);
Image fxImage = SwingFXUtils.toFXImage(bim, null);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
return imageView;
} catch (IOException e) {
logger.error("Errore rendering pagina {}", pageIndex, e);
return null;
}
}
private StackPane drawOfferingCardImageWithTotem(InputStream imagePath, int height, Player occupant, Game game, OfferingTile offering) {
try {
Image fxImage = new Image(imagePath);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
imageView.setOnMouseClicked(event -> {
int idx = game.getGameBoard().getOfferingTiles().indexOf(offering);
game.placeTotem(game.getCurrentPlayer(), idx);
logger.info(" PLAYER {} choose {} ", game.getCurrentPlayer().getNickname() , offering );
drawGameState(game);
});
StackPane stack = new StackPane(imageView);
if(occupant!=null){
Rectangle rect = new Rectangle(25, 25);
rect.setFill(occupant.getTotemColor().getFxColor());
rect.setStroke(Color.WHITE);
rect.setStrokeWidth(1);
StackPane.setAlignment(rect, Pos.TOP_CENTER);
rect.setTranslateY(20);
StackPane.setMargin(rect, new Insets(5));
stack.getChildren().add(rect);
}
return stack;
} catch (Exception e) {
logger.error("Errore caricamento immagine {}", imagePath, e);
return null;
}
}
private ImageView createCardImage(InputStream imagePath, int height) {
try {
Image fxImage = new Image(imagePath);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
return imageView;
} catch (Exception e) {
logger.error("Errore caricamento immagine {}", imagePath, e);
return null;
}
}
private void showPopup(String message) {
Platform.runLater(() -> {
Label label = new Label("" + message); // icon improves readability
label.setWrapText(true);
label.setMaxWidth(400);
label.setStyle(
"-fx-background-color: rgba(20,20,20,0.9);" +
"-fx-text-fill: white;" +
"-fx-font-size: 20px;" +
"-fx-font-weight: bold;" +
"-fx-padding: 15 25 15 25;" +
"-fx-background-radius: 12;" +
"-fx-border-radius: 12;" +
"-fx-border-color: #00c3ff;"
);
label.setOpacity(0); // start invisible
topMenu.getChildren().add(label);
// Fade IN
FadeTransition fadeIn = new FadeTransition(Duration.millis(250), label);
fadeIn.setFromValue(0);
fadeIn.setToValue(1);
// Stay visible
PauseTransition pause = new PauseTransition(Duration.seconds(4));
// Fade OUT
FadeTransition fadeOut = new FadeTransition(Duration.millis(400), label);
fadeOut.setFromValue(1.0);
fadeOut.setToValue(0.0);
fadeOut.setOnFinished(f -> topMenu.getChildren().remove(label));
fadeIn.play();
fadeIn.setOnFinished(e -> pause.play());
pause.setOnFinished(e -> fadeOut.play());
});
}
private void showPopupNo(String message) {
Platform.runLater(() -> {
Label label = new Label(message);
label.setStyle(
"-fx-background-color: rgba(0,0,0,0.75);" +
"-fx-text-fill: white;" +
"-fx-font-size: 28px;" +
"-fx-font-weight: bold;" +
"-fx-padding: 20 40 20 40;" +
"-fx-background-radius: 12;"
);
label.setMouseTransparent(true);
topMenu.getChildren().add(label);
PauseTransition pause = new PauseTransition(Duration.seconds(5));
pause.setOnFinished(e -> {
FadeTransition fade = new FadeTransition(Duration.millis(400), label);
fade.setFromValue(1.0);
fade.setToValue(0.0);
fade.setOnFinished(f -> topMenu.getChildren().remove(label));
fade.play();
});
pause.play();
});
}
@Override
public void stop() throws Exception {
if (documentFront != null) documentFront.close();
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}