diff --git a/src/main/java/org/example/TravelGameController.java b/src/main/java/org/example/TravelGameController.java index 20e42c5e..ce626dd8 100644 --- a/src/main/java/org/example/TravelGameController.java +++ b/src/main/java/org/example/TravelGameController.java @@ -231,6 +231,9 @@ public void onRoll(ActionEvent actionEvent) { } doMove(current.getId(), selectedMove); + // om spelaren blev eliminerad kan listan ha ändrats + if (players.isEmpty() || wonGame) return; + if (currentPlayerIndex >= players.size()) currentPlayerIndex = 0; awaitingMoveChoice = false; selectedMove = null; @@ -504,7 +507,21 @@ private int clampToGrid(int v) { return Math.max(0, Math.min(GRID_SIZE - 1, v)); } + public void startMockJourney() { + logList.getItems().add("📜 Demo path!"); + if (players.isEmpty()) return; + + Traveler current = players.get(currentPlayerIndex); + int px = clampToGrid(current.getPlayerPosX()); + int py = clampToGrid(current.getPlayerPosY()); + List journeyPath = new ArrayList<>(); + journeyPath.add(new int[]{px, py}); + journeyPath.add(new int[]{10, 15}); + journeyPath.add(new int[]{25, 20}); + journeyPath.add(new int[]{40, 45}); + visualizer.animateJourney(journeyPath, drawingPane.getWidth(), drawingPane.getHeight()); + } private void doMove(Long travelerId, PossibleMoves chosen) { if (wonGame) return; @@ -548,12 +565,35 @@ private void doMove(Long travelerId, PossibleMoves chosen) { } syncHudAndMap(); + } catch (IllegalStateException e) { + // typiskt: "traveler cannot afford this move" + if (tx.isActive()) tx.rollback(); + + String msg = e.getMessage() != null ? e.getMessage() : "okänt fel"; + if (msg.toLowerCase().contains("cannot afford")) { + eliminatePlayer(em.find(Traveler.class, travelerId), "har inte råd att resa"); + return; // viktigt så vi inte re-throwar och crashar + } + + logList.getItems().add("⚠️ kunde inte genomföra drag: " + msg); + // resetta state så man kan försöka igen utan att fastna + awaitingMoveChoice = false; + selectedMove = null; + rollButton.setText("ROLL"); + movesBox.getChildren().clear(); + shownMoves = List.of(); + moveButtons.clear(); + lastClickedDestination = null; + cycleIndex = 0; + syncHudAndMap(); + } catch (RuntimeException e) { if (tx.isActive()) tx.rollback(); throw e; } finally { rollButton.setDisable(false); } + } private void doesPlayerWin() { @@ -566,6 +606,57 @@ private void doesPlayerWin() { } } + private void eliminatePlayer(Traveler t, String reason) { + if (t == null) return; + + String name = safeName(t); + logList.getItems().add("💀 " + name + " är ute ur spelet: " + reason); + + int removedIndex = currentPlayerIndex; + + // ta bort ur listan + players.removeIf(p -> p.getId() != null && p.getId().equals(t.getId())); + + // om inga spelare kvar → stoppa + if (players.isEmpty()) { + wonGame = true; + logList.getItems().add("🏁 inga spelare kvar."); + return; + } + + // justera index så att vi landar på en giltig spelare + if (removedIndex >= players.size()) { + currentPlayerIndex = 0; + } else { + currentPlayerIndex = removedIndex % players.size(); + } + + // reset UI-state så det inte blir kvar från den spelaren + awaitingMoveChoice = false; + selectedMove = null; + rollButton.setText("ROLL"); + movesBox.getChildren().clear(); + shownMoves = List.of(); + moveButtons.clear(); + lastClickedDestination = null; + cycleIndex = 0; + + syncHudAndMap(); + } + + + + private List distinctLocationsById(List locations) { + java.util.Map byId = new java.util.LinkedHashMap<>(); + for (Location l : locations) { + if (l != null && l.getId() != null) { + byId.putIfAbsent(l.getId(), l); + } + } + return new java.util.ArrayList<>(byId.values()); + } + + private void highlightSelectedMoveButton(Button selected) { for (var node : movesBox.getChildren()) { if (node instanceof Button b) b.setStyle(""); @@ -617,6 +708,7 @@ private void doContinueJourney(Long travelerId) { if (!managed.isTravelling() && !wonGame) { currentPlayerIndex = (currentPlayerIndex + 1) % players.size(); } + syncHudAndMap(); } catch (RuntimeException e) { if (tx.isActive()) tx.rollback();