Fixed: SidePanel Render, Selective Render, Fixed TUI "It's not your turn"

This commit is contained in:
rubenpirreram
2026-05-28 00:25:19 +02:00
parent 5e8242bacf
commit b54b2370ed
2 changed files with 241 additions and 176 deletions
@@ -52,6 +52,10 @@ public class MainFXMLController {
@FXML private Button skipBtn;
@FXML private Button detailsBtn;
@FXML private Label infoText;
private final Map<String, Label> foodLabels = new HashMap<>();
private final Map<String, Label> prestigeLabels = new HashMap<>();
private final Map<String, VBox> playerCards = new HashMap<>();
private final Map<String, List<ImageView>> iconViews = new HashMap<>();
private Popup popup;
private HBox popupCards;
@@ -107,15 +111,36 @@ public class MainFXMLController {
// ==== RENDER ====
public void render() {
if(!isError) {
if(foodLabels.isEmpty())
buildSidePanel();
if(isError)
{
updateSidePanel();
isError=false;
return;
}
if(controller.miniModel.lastEvent==null ||controller.miniModel.lastEvent.getEventType().equals(EventType.TOTEM_CHOICE) || controller.miniModel.lastEvent.getEventType().equals(EventType.NEXT_ROUND)) {
renderMyHand();
renderUpper();
renderBoard();
renderLower();
updateSidePanel();
return;
}
if(controller.miniModel.lastEvent.getEventType().equals(EventType.DRAW_UPPER_TRIBE) || controller.miniModel.lastEvent.getEventType().equals(EventType.DRAW_UPPER_BUILD))
{
renderUpper();
}
if(controller.miniModel.lastEvent.getEventType().equals(EventType.DRAW_LOWER_TRIBE) || controller.miniModel.lastEvent.getEventType().equals(EventType.DRAW_LOWER_BUILD))
{
renderLower();
}
if(controller.miniModel.lastEvent.getUsername().equals(controller.myUsername))
{
renderMyHand();
}
renderSidePanel();
isError=false;
renderBoard();
updateSidePanel();
}
@@ -163,6 +188,113 @@ public class MainFXMLController {
// ==== ELEMENTS ====
private VBox buildPlayerCard(Player player) {
VBox card = new VBox(3);
VBox.setMargin(card, new Insets(15, 15, 0, 15));
card.getStyleClass().add("player-card");
card.setPadding(new Insets(4));
// Riga 1: totem + nome
HBox headerRow = new HBox(8);
headerRow.setAlignment(Pos.CENTER);
ImageView totem = new ImageView(loadImage("/GUIImages/Totems/totem_"
+ player.totem.toString().toLowerCase(Locale.ROOT) + ".png"));
totem.setFitHeight(20);
totem.setPreserveRatio(true);
Label usernameLabel = new Label(player.getUserName());
usernameLabel.getStyleClass().add("label-medium");
usernameLabel.setStyle("-fx-text-fill: #711423;");
if (player.getUserName().equals(controller.myUsername)) {
usernameLabel.setText(usernameLabel.getText() + " (you)");
}
if (controller.miniModel.disconnectedPlayers.contains(player.getUserName())) {
card.getStyleClass().add("player-card-crashed");
}
headerRow.getChildren().addAll(totem, usernameLabel);
// Riga 2: cibo + prestigio + building
HBox statsRow = new HBox(10);
statsRow.setAlignment(Pos.CENTER);
HBox foodBox = new HBox(4);
foodBox.setAlignment(Pos.CENTER);
ImageView foodIcon = new ImageView(loadImage("/GUIImages/Icons/Food.png"));
foodIcon.setFitHeight(20);
foodIcon.setPreserveRatio(true);
Label foodLabel = new Label(String.valueOf(player.getFoodValue()));
foodLabel.getStyleClass().add("label-small");
foodLabels.put(player.getUserName(), foodLabel);
foodBox.getChildren().addAll(foodIcon, foodLabel);
HBox prestigeBox = new HBox(1);
prestigeBox.setAlignment(Pos.CENTER);
ImageView PPIcon = new ImageView(loadImage("/GUIImages/Icons/PrestigePoint.png"));
PPIcon.setFitHeight(35);
PPIcon.setPreserveRatio(true);
Label prestigeLabel = new Label(String.valueOf(player.getPrestigeValue()));
prestigeLabel.getStyleClass().add("label-small");
prestigeLabels.put(player.getUserName(), prestigeLabel);
prestigeBox.getChildren().addAll(PPIcon, prestigeLabel);
ImageView buildingIcon = new ImageView(loadImage("/GUIImages/Icons/Building.png"));
buildingIcon.setFitWidth(28);
buildingIcon.setFitHeight(28);
buildingIcon.setPreserveRatio(true);
buildingIcon.setOnMouseClicked(e -> {
ArrayList<PlayableCard> cards = getPlayerCards(player.getUserName(), "building");
if (!cards.isEmpty()) openPopup(cards);
});
addHoverZoom(buildingIcon);
statsRow.getChildren().addAll(foodBox, prestigeBox, buildingIcon);
// Righe 3-4: icone carte
HBox iconsRow1 = new HBox(6);
iconsRow1.setSpacing(20);
iconsRow1.setAlignment(Pos.CENTER);
HBox iconsRow2 = new HBox(6);
iconsRow2.setAlignment(Pos.CENTER);
iconsRow2.setSpacing(20);
String[][] iconTypes = {
{"Artist", "artists"},
{"Gatherer", "gatherers"},
{"Inventor", "inventors"},
{"Builder", "builders"},
{"Shaman", "shamans"},
{"Hunter", "hunters"}
};
List<ImageView> icons = new ArrayList<>();
// aggiungo anche building come prima icona della lista per updateSidePanel
icons.add(buildingIcon);
for (int i = 0; i < iconTypes.length; i++) {
String iconName = iconTypes[i][0];
String fieldName = iconTypes[i][1];
ImageView icon = new ImageView(loadImage("/GUIImages/Icons/" + iconName + ".png"));
icon.setFitWidth(28);
icon.setFitHeight(28);
icon.setPreserveRatio(true);
icon.setOnMouseClicked(e -> {
ArrayList<PlayableCard> cards = getPlayerCards(player.getUserName(), fieldName);
if (!cards.isEmpty()) openPopup(cards);
});
addHoverZoom(icon);
icons.add(icon);
if (i < 3) iconsRow1.getChildren().add(icon);
else iconsRow2.getChildren().add(icon);
}
iconViews.put(player.getUserName(), icons);
card.getChildren().addAll(headerRow, statsRow, iconsRow1, iconsRow2);
return card;
}
private StackPane createOrder(String num) {
ImageView img = new ImageView(loadImage("/GUIImages/Orders/order-" + num + ".png"));
img.fitHeightProperty().bind(board.sceneProperty().get().heightProperty().subtract(56).divide(4).multiply(0.94));
@@ -217,36 +349,73 @@ public class MainFXMLController {
// ==== GROUPS ====
private void renderUpper() {
upperList.setSpacing(14);
upperList.setAlignment(Pos.CENTER);
upperList.getChildren().clear();
int i = 0;
for (TribeCard card : controller.miniModel.upperListTribeCards) {
final int index = i;
StackPane img = createCard(card, true, true, upperList);
img.setOnMouseClicked(e -> controller.drawUpperTribeCard(index));
upperList.getChildren().add(img);
i++;
private void buildSidePanel() {
infoText.setText("Round: "+Integer.toString(controller.miniModel.currentState.getRound()) + "" + controller.miniModel.currentState.getGameStage().toString());
VBox playerList = new VBox();
playerList.setFillWidth(true);
for (Player p : controller.miniModel.players.values()) {
VBox card = buildPlayerCard(p);
playerCards.put(p.getUserName(), card);
playerList.getChildren().add(card);
}
drawUpperBuilding();
playerSide.setContent(playerList);
}
private void renderLower() {
lowerList.setSpacing(14);
lowerList.setAlignment(Pos.CENTER);
lowerList.getChildren().clear();
private void updateSidePanel() {
infoText.setText("Round: "+Integer.toString(controller.miniModel.currentState.getRound()) + "" + controller.miniModel.currentState.getGameStage().toString());
String current = controller.miniModel.currentState.getCurrentPlayer().getUserName();
// ordine: building, artists, gatherers, inventors, builders, shamans, hunters
String[] fields = {"building", "artists", "gatherers", "inventors", "builders", "shamans", "hunters"};
int i = 0;
for (TribeCard card : controller.miniModel.lowerListTribeCards) {
final int index = i;
StackPane img = createCard(card, true, true, lowerList);
img.setOnMouseClicked(e -> controller.drawLowerTribeCard(index));
lowerList.getChildren().add(img);
i++;
for (Player p : controller.miniModel.players.values()) {
String u = p.getUserName();
foodLabels.get(u).setText(String.valueOf(p.getFoodValue()));
prestigeLabels.get(u).setText(String.valueOf(p.getPrestigeValue()));
List<ImageView> icons = iconViews.get(u);
for (int i = 0; i < icons.size(); i++) {
boolean empty = getPlayerCards(u, fields[i]).isEmpty();
icons.get(i).setOpacity(empty ? 0.3 : 1.0);
icons.get(i).setEffect(empty ? new ColorAdjust() : null);
}
// aggiorna current player highlight
VBox card = playerCards.get(u);
if(controller.miniModel.disconnectedPlayers.contains(u))
{
card.getStyleClass().clear();
card.getStyleClass().add("player-card-crashed");
}
else {
card.getStyleClass().clear();
card.getStyleClass().add("player-card");
}
if (u.equals(current)) {
card.setStyle("-fx-effect: dropshadow(gaussian, #fff8dc, 15, 0.10, 0, 0)");
ScaleTransition st = new ScaleTransition(Duration.millis(150), card);
st.setToX(1.1);
st.setToY(1.1);
st.play();
} else {
ScaleTransition st = new ScaleTransition(Duration.millis(150), card);
st.setToX(1.0);
st.setToY(1.0);
st.play();
card.setStyle("");
}
// animazione shake su errore
if (isError && u.equals(controller.myUsername)) {
TranslateTransition tt = new TranslateTransition(Duration.millis(56), card);
tt.setFromX(0);
tt.setByX(10);
tt.setCycleCount(6);
tt.setAutoReverse(true);
tt.play();
}
}
drawLowerBuilding();
}
private void drawUpperBuilding() {
@@ -299,137 +468,6 @@ public class MainFXMLController {
}
}
private void renderPlayer(Player player,VBox target) {
VBox card = new VBox(3);
VBox.setMargin(card, new Insets(15,15,0,15));
card.getStyleClass().add("player-card");
card.setPadding(new Insets(4));
if(player.getUserName().equals(controller.miniModel.currentState.getCurrentPlayer().getUserName())) {
ScaleTransition scaleUp = new ScaleTransition(Duration.millis(150), card);
scaleUp.setToX(1.1);
scaleUp.setToY(1.1);
scaleUp.play();
card.setStyle("-fx-effect: dropshadow(gaussian, #fff8dc, 15, 0.10, 0, 0)");
}
// Riga 1: totem + nome
HBox headerRow = new HBox(8);
headerRow.setAlignment(Pos.CENTER);
ImageView totem = new ImageView(loadImage("/GUIImages/Totems/totem_" + player.totem.toString().toLowerCase(Locale.ROOT) + ".png"));
totem.setFitHeight(20);
totem.setPreserveRatio(true);
Label usernameLabel = new Label(player.getUserName());
usernameLabel.getStyleClass().add("label-medium");
usernameLabel.setStyle("-fx-text-fill: #711423;");
if (player.getUserName().equals(controller.myUsername)) {
usernameLabel.setText(usernameLabel.getText() + " (you)");
if(isError) {
TranslateTransition tt = new TranslateTransition(Duration.millis(56), card);
tt.setFromX(0);
tt.setByX(10);
tt.setCycleCount(6);
tt.setAutoReverse(true);
tt.play();
}
}
if (controller.miniModel.disconnectedPlayers.contains(player.getUserName())) {
card.getStyleClass().add("player-card-crashed");
}
headerRow.getChildren().addAll(totem, usernameLabel);
// Riga 2: cibo + prestigio + building
HBox statsRow = new HBox(10);
statsRow.setAlignment(Pos.CENTER);
HBox foodBox = new HBox(4);
foodBox.setAlignment(Pos.CENTER);
ImageView foodIcon = new ImageView(loadImage("/GUIImages/Icons/Food.png"));
foodIcon.setFitHeight(20);
foodIcon.setPreserveRatio(true);
Label foodLabel = new Label(String.valueOf(player.getFoodValue()));
foodLabel.getStyleClass().add("label-small");
foodBox.getChildren().addAll(foodIcon, foodLabel);
HBox prestigeBox = new HBox(1);
prestigeBox.setAlignment(Pos.CENTER);
ImageView PPIcon = new ImageView(loadImage("/GUIImages/Icons/PrestigePoint.png"));
PPIcon.setFitHeight(35);
PPIcon.setPreserveRatio(true);
Label prestigeLabel = new Label(String.valueOf(player.getPrestigeValue()));
prestigeLabel.getStyleClass().add("label-small");
prestigeBox.getChildren().addAll(PPIcon, prestigeLabel);
ImageView icon = new ImageView();
icon.setFitWidth(28);
icon.setFitHeight(28);
icon.setPreserveRatio(true);
icon.setImage(loadImage("/GUIImages/Icons/Building.png"));
ArrayList<PlayableCard> cards = getPlayerCards(player.getUserName(), "building");
if (cards.isEmpty()) {
ColorAdjust grayscale = new ColorAdjust();
icon.setEffect(grayscale);
icon.setOpacity(0.3);
} else {
icon.setOnMouseClicked(e -> openPopup(new ArrayList<>(cards)));
addHoverZoom(icon);
}
statsRow.getChildren().addAll(foodBox, prestigeBox,icon);
// Righe 3-4: icone carte
HBox iconsRow1 = new HBox(6);
iconsRow1.setSpacing(20);
iconsRow1.setAlignment(Pos.CENTER);
HBox iconsRow2 = new HBox(6);
iconsRow2.setAlignment(Pos.CENTER);
iconsRow2.setSpacing(20);
renderIcons(iconsRow1, iconsRow2, player);
card.getChildren().addAll(headerRow, statsRow, iconsRow1, iconsRow2);
target.getChildren().add(card);
}
private void renderIcons(HBox iconsRow1, HBox iconsRow2, Player player) {
String[][] iconTypes = {
{"Artist", "artists"},
{"Gatherer", "gatherers"},
{"Inventor", "inventors"},
{"Builder", "builders"},
{"Shaman", "shamans"},
{"Hunter", "hunters"}
};
for (int i = 0; i < iconTypes.length; i++) {
String iconName = iconTypes[i][0];
String fieldName = iconTypes[i][1];
ImageView icon = new ImageView();
icon.setFitWidth(28);
icon.setFitHeight(28);
icon.setPreserveRatio(true);
icon.setImage(loadImage("/GUIImages/Icons/" + iconName + ".png"));
ArrayList<PlayableCard> cards = getPlayerCards(player.getUserName(), fieldName);
if (cards.isEmpty()) {
ColorAdjust grayscale = new ColorAdjust();
icon.setEffect(grayscale);
icon.setOpacity(0.3);
} else {
icon.setOnMouseClicked(e -> openPopup(new ArrayList<>(cards)));
addHoverZoom(icon);
}
if (i < 3) iconsRow1.getChildren().add(icon);
else iconsRow2.getChildren().add(icon);
}
}
private ArrayList<PlayableCard> getPlayerCards(String username, String type) {
Player p = controller.miniModel.players.get(username);
return switch (type) {
@@ -446,6 +484,38 @@ public class MainFXMLController {
// ==== ABSOLUTES ====
private void renderUpper() {
upperList.setSpacing(14);
upperList.setAlignment(Pos.CENTER);
upperList.getChildren().clear();
int i = 0;
for (TribeCard card : controller.miniModel.upperListTribeCards) {
final int index = i;
StackPane img = createCard(card, true, true, upperList);
img.setOnMouseClicked(e -> controller.drawUpperTribeCard(index));
upperList.getChildren().add(img);
i++;
}
drawUpperBuilding();
}
private void renderLower() {
lowerList.setSpacing(14);
lowerList.setAlignment(Pos.CENTER);
lowerList.getChildren().clear();
int i = 0;
for (TribeCard card : controller.miniModel.lowerListTribeCards) {
final int index = i;
StackPane img = createCard(card, true, true, lowerList);
img.setOnMouseClicked(e -> controller.drawLowerTribeCard(index));
lowerList.getChildren().add(img);
i++;
}
drawLowerBuilding();
}
private void renderBackground() {
BackgroundSize size = new BackgroundSize(
BackgroundSize.AUTO, BackgroundSize.AUTO,
@@ -569,19 +639,9 @@ public class MainFXMLController {
drawMyHandList(me.buildingCards);
}
private void renderSidePanel() {
infoText.setText("Round: "+Integer.toString(controller.miniModel.currentState.getRound()) + "" + controller.miniModel.currentState.getGameStage().toString());
VBox playerList = new VBox();
playerList.setFillWidth(true);
for (Player p : controller.miniModel.players.values()) {
renderPlayer(p, playerList);
}
playerSide.setContent(playerList);
}
// ==== POPUP ====
public void initPopup() {
private void initPopup() {
popupCards = new HBox(10);
popupCards.setAlignment(Pos.CENTER);
popupCards.setPadding(new Insets(10));
@@ -610,7 +670,6 @@ public class MainFXMLController {
popup.setY(window.getY() + (window.getHeight() - popup.getHeight()) / 2);
}
// Popup sola visualizzazione (mano, icone sidepanel)
private void openPopup(ArrayList<? extends PlayableCard> cardList) {
popupCards.getChildren().clear();
for (PlayableCard card : cardList) {
@@ -619,7 +678,6 @@ public class MainFXMLController {
showPopup();
}
// Popup edifici upper: click sulla carta → pesca e chiude
private void openPopupUpperBuilding(ArrayList<BuildingCard> cardList) {
popupCards.getChildren().clear();
for (int i = 0; i < cardList.size(); i++) {
@@ -634,7 +692,6 @@ public class MainFXMLController {
showPopup();
}
// Popup edifici lower: click sulla carta → pesca e chiude
private void openPopupLowerBuilding(ArrayList<BuildingCard> cardList) {
popupCards.getChildren().clear();
for (int i = 0; i < cardList.size(); i++) {
@@ -660,6 +717,7 @@ public class MainFXMLController {
popupCards.getChildren().add(wrapper);
showPopup();
}
private StackPane createCardPopup(PlayableCard card, boolean withZoom, boolean withShadow) {
ImageView img = new ImageView(loadImage("/GUIImages/Fronts/card-" + card.getIdIMG() + ".png"));
img.setFitHeight(200);
@@ -238,6 +238,13 @@ public class TUI implements IView {
public void showError(ErrorType error,String message) {
clearTerminal();
render();
if(error.equals(ErrorType.WRONG_ACTION))
{
if(!model.currentState.getCurrentPlayer().getUserName().equals(username)){
System.out.println("It's not your turn");
return;
}
}
System.out.println(message);
if(error==ErrorType.SERVER_CRASHED)
System.out.println("Press any key to continue");
@@ -361,8 +368,8 @@ public class TUI implements IView {
BuildTableLower.addRow(i+"-"+model.lowerListBuildingCards.get(i).toString());
}
}
stringUpperListTribe.forEach(x->TribeTableUpper.addRow(x));
stringLowerListTribe.forEach(x->TribeTableLower.addRow(x));
stringUpperListTribe.forEach(TribeTableUpper::addRow);
stringLowerListTribe.forEach(TribeTableLower::addRow);
offerTrack.addRow(stringUpOffer);
offerTrack.addRow(stringDownOffer);
return "CURRENT STATE\n"+model.currentState+"\n"+model.orderLogicCard.toString()+"\n"+ AsciiTable.sideBySide(Arrays.stream((TribeTableUpper.build().split("\n"))).toList(), Arrays.stream(BuildTableUpper.build().split("\n")).toList(),2)+"\n"+offerTrack.build()+"\n"+AsciiTable.sideBySide(List.of(TribeTableLower.build().split("\n")), Arrays.stream(BuildTableLower.build().split("\n")).toList(),2);