diff --git a/src/main/java/org/example/MapVisualizer.java b/src/main/java/org/example/MapVisualizer.java index 501ce430..47b38c39 100644 --- a/src/main/java/org/example/MapVisualizer.java +++ b/src/main/java/org/example/MapVisualizer.java @@ -100,6 +100,46 @@ public void drawPlayers(List players, int currentIndex, double totalWidth } } + public void drawPlayersInterpolated(List players, int currentIndex, double totalWidth, double totalHeight) { + playerLayer.getChildren().clear(); + + double cellWidth = totalWidth / gridSize; + double cellHeight = totalHeight / gridSize; + + for (int i = 0; i < players.size(); i++) { + double[] p = players.get(i); + double gridX = p[0]; + double gridY = p[1]; + + double centerX = (gridX * cellWidth) + (cellWidth / 2); + double centerY = totalHeight - (gridY * cellHeight) - (cellHeight / 2); + + double rPlayer = (cellWidth / 2) * 0.8; + + if (i == currentIndex) { + Color c = colorForPlayer(i); + Circle halo = new Circle(centerX, centerY, rPlayer * 1.25); + halo.setFill(c.deriveColor(0, 1, 1, 0.25)); + halo.setMouseTransparent(true); + playerLayer.getChildren().add(halo); + } + + Circle token = new Circle(centerX, centerY, rPlayer); + token.setFill(colorForPlayer(i)); + + if (i == currentIndex) { + token.setStroke(Color.WHITE); + token.setStrokeWidth(3); + } else { + token.setStroke(Color.BLACK); + token.setStrokeWidth(1.5); + } + + token.setMouseTransparent(true); + playerLayer.getChildren().add(token); + } + } + public void drawDestMarkers(List destinations, double w, double h) { markerLayer.getChildren().clear(); diff --git a/src/main/java/org/example/TravelGameController.java b/src/main/java/org/example/TravelGameController.java index 5fce38f0..d17bdb74 100644 --- a/src/main/java/org/example/TravelGameController.java +++ b/src/main/java/org/example/TravelGameController.java @@ -370,11 +370,56 @@ private void updateGraphics() { redrawAllPathsFromDb(w, h); - List positions = new ArrayList<>(); - for (Traveler t : players) { - positions.add(new int[]{ clampToGrid(t.getPlayerPosX()), clampToGrid(t.getPlayerPosY()) }); + List positions = new ArrayList<>(); + for (Traveler tRef : players) { + Traveler t = em != null && tRef.getId() != null ? em.find(Traveler.class, tRef.getId()) : tRef; + if (t == null) continue; + positions.add(computePlayerGridPos(t)); } - visualizer.drawPlayers(positions, currentPlayerIndex, w, h); + visualizer.drawPlayersInterpolated(positions, currentPlayerIndex, w, h); + } + + private double[] computePlayerGridPos(Traveler t) { + double baseX = clampToGrid(t.getPlayerPosX()); + double baseY = clampToGrid(t.getPlayerPosY()); + + if (!t.isTravelling() || t.getId() == null || em == null) { + return new double[]{baseX, baseY}; + } + + List list = em.createQuery( + "select j from Journey j " + + "join fetch j.locationLink ll " + + "join fetch ll.fromLocation " + + "join fetch ll.toLocation " + + "where j.traveler.id = :id " + + "order by j.turnNumber desc, j.id desc", + Journey.class + ) + .setParameter("id", t.getId()) + .setMaxResults(1) + .getResultList(); + + if (list.isEmpty()) return new double[]{baseX, baseY}; + + Journey j = list.get(0); + LocationLink ll = j.getLocationLink(); + int total = ll.getDistance(); + if (total <= 0) return new double[]{baseX, baseY}; + + Location from = ll.getFromLocation(); + Location to = ll.getToLocation(); + + int remainingAfter = j.getRemainingDistance(); + double fracAfter = (double) (total - remainingAfter) / total; + + double gx = from.getX() + (to.getX() - from.getX()) * fracAfter; + double gy = from.getY() + (to.getY() - from.getY()) * fracAfter; + + gx = Math.max(0.0, Math.min(GRID_SIZE - 1, gx)); + gy = Math.max(0.0, Math.min(GRID_SIZE - 1, gy)); + + return new double[]{gx, gy}; } private void redrawAllPathsFromDb(double w, double h) { @@ -441,21 +486,7 @@ 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; @@ -517,18 +548,6 @@ private void doesPlayerWin() { } } - - 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(""); @@ -537,9 +556,12 @@ private void highlightSelectedMoveButton(Button selected) { } private Location getLocationByName(String name) { - return em.createQuery("select l from Location l where l.name = :n", Location.class) + List list = em.createQuery("select l from Location l where l.name = :n order by l.id", Location.class) .setParameter("n", name) - .getSingleResult(); + .setMaxResults(1) + .getResultList(); + if (list.isEmpty()) throw new IllegalStateException("No location found with name: " + name); + return list.get(0); } private void doContinueJourney(Long travelerId) {