Add: Added Coverage Screenshots And htlmReport.

This commit is contained in:
GabrieleRadice
2026-06-19 21:50:38 +02:00
parent 6aca188aa3
commit d78b8bbd93
316 changed files with 86329 additions and 0 deletions
@@ -0,0 +1,413 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > FireParticleSystem</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.View.GUI</a>
</div>
<h1>Coverage Summary for Class: FireParticleSystem (it.polimi.ingsw.gc14.View.GUI)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">FireParticleSystem</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/6)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/31)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/50)
</span>
</td>
</tr>
<tr>
<td class="name">FireParticleSystem$1</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/2)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/2)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/4)
</span>
</td>
</tr>
<tr>
<td class="name">FireParticleSystem$Particle</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/7)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/40)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/69)
</span>
</td>
</tr>
<tr>
<td class="name"><strong>Total</strong></td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/15)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/73)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/123)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.View.GUI;
&nbsp;
&nbsp;import javafx.animation.AnimationTimer;
&nbsp;import javafx.scene.Scene;
&nbsp;import javafx.scene.layout.Pane;
&nbsp;import javafx.scene.paint.Color;
&nbsp;import javafx.scene.shape.Circle;
&nbsp;
&nbsp;import java.util.ArrayList;
&nbsp;import java.util.Iterator;
&nbsp;import java.util.List;
&nbsp;import java.util.Random;
&nbsp;
&nbsp;//TODO ALL JAVADOC
&nbsp;public class FireParticleSystem {
&nbsp;
&nbsp; private static final int MAX_PARTICLES = 30;
&nbsp; private static final long SPAWN_INTERVAL_NS = 100_000_000L;
&nbsp; private static final long FRAME_INTERVAL_NS = 1_000_000_000L / 30; // 30 fps cap
&nbsp; private static final int STEPS_PER_FRAME = 3; // physics steps per frame → 3× speed
&nbsp;
&nbsp; private final Pane pane;
&nbsp; private final Scene scene;
<b class="nc">&nbsp; private final List&lt;Particle&gt; particles = new ArrayList&lt;&gt;();</b>
<b class="nc">&nbsp; private final Random rnd = new Random();</b>
&nbsp; private AnimationTimer timer;
<b class="nc">&nbsp; private long lastSpawn = 0;</b>
<b class="nc">&nbsp; private long lastFrame = 0;</b>
<b class="nc">&nbsp; private boolean warmedUp = false;</b>
&nbsp;
<b class="nc">&nbsp; public FireParticleSystem(Scene scene) {</b>
<b class="nc">&nbsp; this.scene = scene;</b>
<b class="nc">&nbsp; pane = new Pane();</b>
<b class="nc">&nbsp; pane.setMouseTransparent(true);</b>
<b class="nc">&nbsp; pane.setPickOnBounds(false);</b>
&nbsp; }
&nbsp;
&nbsp; /** Returns the transparent overlay pane that holds all particle nodes. */
<b class="nc">&nbsp; public Pane getPane() { return pane; }</b>
&nbsp;
&nbsp; /** Starts the animation timer. */
&nbsp; public void start() {
<b class="nc">&nbsp; timer = new AnimationTimer() {</b>
&nbsp; @Override
&nbsp; public void handle(long now) {
<b class="nc">&nbsp; if (now - lastFrame &lt; FRAME_INTERVAL_NS) return;</b>
<b class="nc">&nbsp; lastFrame = now;</b>
<b class="nc">&nbsp; tick(now);</b>
&nbsp; }
&nbsp; };
<b class="nc">&nbsp; timer.start();</b>
&nbsp; }
&nbsp;
&nbsp; /** Stops the animation timer. */
&nbsp; public void stop() {
<b class="nc">&nbsp; if (timer != null) timer.stop();</b>
&nbsp; }
&nbsp;
&nbsp; private void tick(long now) {
<b class="nc">&nbsp; double w = scene.getWidth();</b>
<b class="nc">&nbsp; double h = scene.getHeight();</b>
<b class="nc">&nbsp; if (w == 0 || h == 0) return;</b>
&nbsp;
<b class="nc">&nbsp; if (!warmedUp) {</b>
<b class="nc">&nbsp; warmedUp = true;</b>
<b class="nc">&nbsp; for (int i = 0; i &lt; MAX_PARTICLES; i++) {</b>
<b class="nc">&nbsp; Particle p = spawnParticle(w, h);</b>
<b class="nc">&nbsp; int advance = rnd.nextInt(800) + 100;</b>
<b class="nc">&nbsp; for (int f = 0; f &lt; advance; f++) p.update();</b>
<b class="nc">&nbsp; if (!p.isDead(w, h)) {</b>
<b class="nc">&nbsp; p.addTo(pane);</b>
<b class="nc">&nbsp; p.updateVisual(w, h);</b>
<b class="nc">&nbsp; particles.add(p);</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
<b class="nc">&nbsp; if (now - lastSpawn &gt; SPAWN_INTERVAL_NS &amp;&amp; particles.size() &lt; MAX_PARTICLES) {</b>
<b class="nc">&nbsp; lastSpawn = now;</b>
<b class="nc">&nbsp; int count = rnd.nextInt(2) + 1;</b>
<b class="nc">&nbsp; for (int i = 0; i &lt; count &amp;&amp; particles.size() &lt; MAX_PARTICLES; i++) {</b>
<b class="nc">&nbsp; Particle p = spawnParticle(w, h);</b>
<b class="nc">&nbsp; p.addTo(pane);</b>
<b class="nc">&nbsp; particles.add(p);</b>
&nbsp; }
&nbsp; }
&nbsp;
<b class="nc">&nbsp; Iterator&lt;Particle&gt; it = particles.iterator();</b>
<b class="nc">&nbsp; while (it.hasNext()) {</b>
<b class="nc">&nbsp; Particle p = it.next();</b>
<b class="nc">&nbsp; for (int s = 0; s &lt; STEPS_PER_FRAME; s++) p.update();</b>
<b class="nc">&nbsp; if (p.isDead(w, h)) {</b>
<b class="nc">&nbsp; p.removeFrom(pane);</b>
<b class="nc">&nbsp; it.remove();</b>
&nbsp; } else {
<b class="nc">&nbsp; p.updateVisual(w, h);</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; private Particle spawnParticle(double w, double h) {
<b class="nc">&nbsp; int corner = rnd.nextInt(4);</b>
<b class="nc">&nbsp; double margin = 0.12;</b>
&nbsp; double x, y;
<b class="nc">&nbsp; switch (corner) {</b>
<b class="nc">&nbsp; case 0 -&gt; { x = rnd.nextDouble() * w * margin; y = h - rnd.nextDouble() * h * margin; }</b>
<b class="nc">&nbsp; case 1 -&gt; { x = w - rnd.nextDouble() * w * margin; y = h - rnd.nextDouble() * h * margin; }</b>
<b class="nc">&nbsp; case 2 -&gt; { x = rnd.nextDouble() * w * margin; y = rnd.nextDouble() * h * margin; }</b>
<b class="nc">&nbsp; default -&gt; { x = w - rnd.nextDouble() * w * margin; y = rnd.nextDouble() * h * margin; }</b>
&nbsp; }
<b class="nc">&nbsp; return new Particle(x, y, w, h, corner, rnd);</b>
&nbsp; }
&nbsp;
&nbsp; // ── Particle ──────────────────────────────────────────────────────────────
&nbsp;
&nbsp; private static class Particle {
&nbsp;
&nbsp; double x, y, vx, vy;
&nbsp; double life;
&nbsp; double size;
&nbsp; double wobblePhase, wobbleSpeed, wobbleAmp;
&nbsp; final int type;
&nbsp; final Random rnd;
&nbsp;
&nbsp; final Circle core;
&nbsp; final Circle glow; // non-null only for embers
&nbsp;
<b class="nc">&nbsp; Particle(double x, double y, double w, double h, int corner, Random rnd) {</b>
<b class="nc">&nbsp; this.x = x;</b>
<b class="nc">&nbsp; this.y = y;</b>
<b class="nc">&nbsp; this.life = 1.0;</b>
<b class="nc">&nbsp; this.rnd = rnd;</b>
<b class="nc">&nbsp; this.type = weightedType(rnd);</b>
&nbsp;
<b class="nc">&nbsp; double targetX = (corner == 0 || corner == 2) ? w * 0.75 : w * 0.25;</b>
<b class="nc">&nbsp; double targetY = (corner == 0 || corner == 1) ? h * 0.25 : h * 0.75;</b>
<b class="nc">&nbsp; double baseAngle = Math.atan2(targetY - y, targetX - x);</b>
<b class="nc">&nbsp; double angle = baseAngle + (rnd.nextDouble() - 0.5) * (Math.PI / 3.5);</b>
&nbsp;
&nbsp; double speed;
<b class="nc">&nbsp; if (type == 0) { // ember</b>
<b class="nc">&nbsp; speed = 0.5 + rnd.nextDouble() * 0.7;</b>
<b class="nc">&nbsp; size = rnd.nextDouble() * 3 + 2;</b>
<b class="nc">&nbsp; glow = new Circle(size * 2.2);</b>
<b class="nc">&nbsp; core = new Circle(size / 2);</b>
<b class="nc">&nbsp; } else if (type == 1) { // spark</b>
<b class="nc">&nbsp; speed = 1.5 + rnd.nextDouble() * 2.0;</b>
<b class="nc">&nbsp; size = rnd.nextDouble() * 1.5 + 0.5;</b>
<b class="nc">&nbsp; glow = null;</b>
<b class="nc">&nbsp; core = new Circle(size / 2);</b>
&nbsp; } else { // dust
<b class="nc">&nbsp; speed = 0.2 + rnd.nextDouble() * 0.35;</b>
<b class="nc">&nbsp; size = rnd.nextDouble() * 8 + 5;</b>
<b class="nc">&nbsp; glow = null;</b>
<b class="nc">&nbsp; core = new Circle(size);</b>
<b class="nc">&nbsp; core.setFill(Color.color(0.55, 0.38, 0.22));</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; vx = Math.cos(angle) * speed;</b>
<b class="nc">&nbsp; vy = Math.sin(angle) * speed;</b>
<b class="nc">&nbsp; wobblePhase = rnd.nextDouble() * Math.PI * 2;</b>
<b class="nc">&nbsp; wobbleSpeed = 0.025 + rnd.nextDouble() * 0.04;</b>
<b class="nc">&nbsp; wobbleAmp = 0.1 + rnd.nextDouble() * 0.4;</b>
&nbsp; }
&nbsp;
&nbsp; private static int weightedType(Random rnd) {
<b class="nc">&nbsp; double r = rnd.nextDouble();</b>
<b class="nc">&nbsp; if (r &lt; 0.55) return 0;</b>
<b class="nc">&nbsp; if (r &lt; 0.78) return 1;</b>
<b class="nc">&nbsp; return 2;</b>
&nbsp; }
&nbsp;
&nbsp; void addTo(Pane pane) {
<b class="nc">&nbsp; if (glow != null) pane.getChildren().add(glow);</b>
<b class="nc">&nbsp; pane.getChildren().add(core);</b>
&nbsp; }
&nbsp;
&nbsp; void removeFrom(Pane pane) {
<b class="nc">&nbsp; pane.getChildren().remove(core);</b>
<b class="nc">&nbsp; if (glow != null) pane.getChildren().remove(glow);</b>
&nbsp; }
&nbsp;
&nbsp; void update() {
<b class="nc">&nbsp; wobblePhase += wobbleSpeed;</b>
<b class="nc">&nbsp; vx += Math.sin(wobblePhase) * wobbleAmp * 0.05;</b>
<b class="nc">&nbsp; vy += Math.cos(wobblePhase) * wobbleAmp * 0.02;</b>
<b class="nc">&nbsp; x += vx;</b>
<b class="nc">&nbsp; y += vy;</b>
<b class="nc">&nbsp; if (type == 1) life -= 0.008 + rnd.nextDouble() * 0.006;</b>
&nbsp; }
&nbsp;
&nbsp; boolean isDead(double w, double h) {
<b class="nc">&nbsp; double pad = size * 4;</b>
<b class="nc">&nbsp; return x &lt; -pad || x &gt; w + pad || y &lt; -pad || y &gt; h + pad</b>
&nbsp; || (type == 1 &amp;&amp; life &lt;= 0);
&nbsp; }
&nbsp;
&nbsp; void updateVisual(double w, double h) {
<b class="nc">&nbsp; double edge = Math.min(w, h) * 0.05;</b>
<b class="nc">&nbsp; double fadeX = Math.min(x / edge, Math.min((w - x) / edge, 1.0));</b>
<b class="nc">&nbsp; double fadeY = Math.min(y / edge, Math.min((h - y) / edge, 1.0));</b>
<b class="nc">&nbsp; double posAlpha = Math.max(0, Math.min(fadeX, fadeY));</b>
<b class="nc">&nbsp; double lifeAlpha = (type == 1) ? Math.min(life * 2.0, 1.0) : 1.0;</b>
<b class="nc">&nbsp; double alpha = posAlpha * lifeAlpha;</b>
&nbsp;
<b class="nc">&nbsp; core.setTranslateX(x);</b>
<b class="nc">&nbsp; core.setTranslateY(y);</b>
&nbsp;
<b class="nc">&nbsp; if (type == 0) {</b>
<b class="nc">&nbsp; double dist = Math.sqrt((x - w/2) * (x - w/2) + (y - h/2) * (y - h/2));</b>
<b class="nc">&nbsp; double hotness = Math.max(0, 1.0 - dist / (Math.sqrt(w * w + h * h) * 0.4));</b>
<b class="nc">&nbsp; double g = 0.3 + hotness * 0.5;</b>
<b class="nc">&nbsp; core.setFill(Color.color(1.0, g, 0.0));</b>
<b class="nc">&nbsp; core.setOpacity(alpha * 0.88);</b>
<b class="nc">&nbsp; glow.setTranslateX(x);</b>
<b class="nc">&nbsp; glow.setTranslateY(y);</b>
<b class="nc">&nbsp; glow.setFill(Color.color(1.0, g * 0.35, 0.0));</b>
<b class="nc">&nbsp; glow.setOpacity(alpha * 0.10);</b>
<b class="nc">&nbsp; } else if (type == 1) {</b>
<b class="nc">&nbsp; double brightness = Math.min(life * 2.5, 1.0);</b>
<b class="nc">&nbsp; core.setFill(Color.color(1.0, brightness * 0.85 + 0.15, brightness * 0.15));</b>
<b class="nc">&nbsp; core.setOpacity(alpha);</b>
&nbsp; } else {
<b class="nc">&nbsp; core.setOpacity(alpha * 0.20);</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>
@@ -0,0 +1,398 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > GUI</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.View.GUI</a>
</div>
<h1>Coverage Summary for Class: GUI (it.polimi.ingsw.gc14.View.GUI)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">GUI</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/16)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/22)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/116)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.View.GUI;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.ClientController;
&nbsp;import it.polimi.ingsw.gc14.ErrorType;
&nbsp;import it.polimi.ingsw.gc14.Model.MiniModel;
&nbsp;import it.polimi.ingsw.gc14.View.IView;
&nbsp;import javafx.animation.FadeTransition;
&nbsp;import javafx.application.Application;
&nbsp;import javafx.application.Platform;
&nbsp;import javafx.fxml.FXMLLoader;
&nbsp;import javafx.geometry.Rectangle2D;
&nbsp;import javafx.scene.Parent;
&nbsp;import javafx.scene.Scene;
&nbsp;import javafx.scene.input.KeyCode;
&nbsp;import javafx.scene.input.KeyEvent;
&nbsp;import javafx.scene.input.KeyCombination;
&nbsp;import javafx.scene.layout.StackPane;
&nbsp;import javafx.scene.media.Media;
&nbsp;import javafx.scene.media.MediaPlayer;
&nbsp;import javafx.scene.paint.Color;
&nbsp;import javafx.scene.shape.Rectangle;
&nbsp;import javafx.stage.Screen;
&nbsp;import javafx.stage.Stage;
&nbsp;import javafx.util.Duration;
&nbsp;
&nbsp;import static it.polimi.ingsw.gc14.Model.GamePackage.GameStages.ENDED;
&nbsp;import static it.polimi.ingsw.gc14.Model.GamePackage.GameStages.TOTEM_CHOICE;
&nbsp;
&nbsp;/**
&nbsp; * JavaFX-based GUI implementation of {@link it.polimi.ingsw.gc14.View.IView IView}.
&nbsp; *
&nbsp; * &lt;p&gt;Manages the primary stage and switches between the login, totem choice,
&nbsp; * main game, and leaderboard scenes based on the current game state.
&nbsp; */
<b class="nc">&nbsp;public class GUI extends Application implements IView {</b>
&nbsp; //TODO
&nbsp; private Stage primaryStage;
&nbsp; //TODO
&nbsp; private volatile MiniModel miniModel;
&nbsp; //TODO
&nbsp; private FXMLLoader loaderLogin;
&nbsp; //TODO
&nbsp; private Scene loginScene;
&nbsp; //TODO
&nbsp; private LoginFXMLController controllerLogin;
&nbsp; //TODO
&nbsp; private FXMLLoader loaderTotem;
&nbsp; //TODO
&nbsp; private Scene totemScene;
&nbsp; //TODO
&nbsp; private TotemFXMLController controllerTotem;
&nbsp; //TODO
&nbsp; private FXMLLoader loaderMain;
&nbsp;
&nbsp; //TODO
&nbsp; private Scene mainScene;
&nbsp; //TODO
&nbsp; private MainFXMLController controllerMain;
&nbsp; //TODO
&nbsp; private FXMLLoader loaderLeaderboard;
&nbsp; //TODO
&nbsp; private Scene leaderboardScene;
&nbsp; //TODO
&nbsp; private LeaderboardFXMLController controllerLeaderboard;
&nbsp;
&nbsp; //TODO
&nbsp; private ClientController controller;
&nbsp; //TODO
<b class="nc">&nbsp; private boolean autoReenterFullscreen = true;</b>
&nbsp; //TODO
<b class="nc">&nbsp; private boolean isFading = false;</b>
&nbsp; //TODO
&nbsp; private MediaPlayer bgMusic;
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * JavaFX entry point: loads all FXML scenes, wires up controllers,
&nbsp; * configures fullscreen behaviour, and shows the login scene.
&nbsp; *
&nbsp; * @param stage the primary stage provided by the JavaFX runtime.
&nbsp; * @throws Exception if any FXML resource cannot be loaded.
&nbsp; */
&nbsp; @Override
&nbsp; public void start(Stage stage) throws Exception {
<b class="nc">&nbsp; this.primaryStage = stage;</b>
<b class="nc">&nbsp; loaderLogin = new FXMLLoader(getClass().getResource(&quot;/GUIScene/login.fxml&quot;));</b>
<b class="nc">&nbsp; loginScene = new Scene(loaderLogin.load());</b>
<b class="nc">&nbsp; controllerLogin = loaderLogin.getController();</b>
<b class="nc">&nbsp; controllerLogin.setController(controller);</b>
&nbsp;
&nbsp;
<b class="nc">&nbsp; loaderTotem = new FXMLLoader(getClass().getResource(&quot;/GUIScene/totem.fxml&quot;));</b>
<b class="nc">&nbsp; totemScene = new Scene(loaderTotem.load());</b>
<b class="nc">&nbsp; controllerTotem = loaderTotem.getController();</b>
<b class="nc">&nbsp; controllerTotem.setController(controller);</b>
&nbsp;
&nbsp;
<b class="nc">&nbsp; loaderMain = new FXMLLoader(getClass().getResource(&quot;/GUIScene/main.fxml&quot;));</b>
<b class="nc">&nbsp; mainScene = new Scene(loaderMain.load());</b>
<b class="nc">&nbsp; controllerMain = loaderMain.getController();</b>
<b class="nc">&nbsp; controllerMain.setController(controller);</b>
&nbsp;
<b class="nc">&nbsp; loaderLeaderboard = new FXMLLoader(getClass().getResource(&quot;/GUIScene/standing.fxml&quot;));</b>
<b class="nc">&nbsp; leaderboardScene = new Scene(loaderLeaderboard.load());</b>
<b class="nc">&nbsp; controllerLeaderboard = loaderLeaderboard.getController();</b>
<b class="nc">&nbsp; controllerLeaderboard.setController(controller, () -&gt; {</b>
<b class="nc">&nbsp; controllerLogin.updateLoginButton(true);</b>
<b class="nc">&nbsp; controllerLogin.showError(&quot;&quot;);</b>
<b class="nc">&nbsp; fadeToScene(loginScene);</b>
&nbsp; }, loginScene);
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
<b class="nc">&nbsp; wrapScene(loginScene);</b>
<b class="nc">&nbsp; wrapScene(totemScene);</b>
<b class="nc">&nbsp; wrapScene(mainScene);</b>
<b class="nc">&nbsp; wrapScene(leaderboardScene);</b>
&nbsp;
<b class="nc">&nbsp; FireParticleSystem fireParticles = new FireParticleSystem(mainScene);</b>
<b class="nc">&nbsp; ((StackPane) mainScene.getRoot()).getChildren().add(fireParticles.getPane());</b>
<b class="nc">&nbsp; fireParticles.start();</b>
&nbsp;
<b class="nc">&nbsp; primaryStage.setScene(loginScene);</b>
<b class="nc">&nbsp; Rectangle2D tmp = Screen.getPrimary().getVisualBounds();</b>
<b class="nc">&nbsp; primaryStage.setWidth(tmp.getWidth());</b>
<b class="nc">&nbsp; primaryStage.setHeight(tmp.getHeight());</b>
<b class="nc">&nbsp; primaryStage.setX(tmp.getMinX());</b>
<b class="nc">&nbsp; primaryStage.setY(tmp.getMinY());</b>
<b class="nc">&nbsp; primaryStage.setResizable(false);</b>
<b class="nc">&nbsp; primaryStage.setFullScreenExitHint(&quot;&quot;);</b>
<b class="nc">&nbsp; primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);</b>
&nbsp;
<b class="nc">&nbsp; primaryStage.fullScreenProperty().addListener((obs, was, isNow) -&gt; {</b>
<b class="nc">&nbsp; if (!isNow &amp;&amp; autoReenterFullscreen) {</b>
<b class="nc">&nbsp; Platform.runLater(() -&gt; primaryStage.setFullScreen(true));</b>
&nbsp; }
&nbsp; });
&nbsp;
<b class="nc">&nbsp; primaryStage.addEventFilter(KeyEvent.KEY_PRESSED, e -&gt; {</b>
<b class="nc">&nbsp; if (e.getCode() == KeyCode.ESCAPE) {</b>
<b class="nc">&nbsp; autoReenterFullscreen = false;</b>
<b class="nc">&nbsp; primaryStage.setFullScreen(false);</b>
<b class="nc">&nbsp; e.consume();</b>
<b class="nc">&nbsp; } else if (e.getCode() == KeyCode.F11) {</b>
<b class="nc">&nbsp; autoReenterFullscreen = true;</b>
<b class="nc">&nbsp; primaryStage.setFullScreen(true);</b>
<b class="nc">&nbsp; e.consume();</b>
&nbsp; }
&nbsp; });
&nbsp;
<b class="nc">&nbsp; primaryStage.setTitle(&quot;Mesos&quot;);</b>
<b class="nc">&nbsp; primaryStage.setOnCloseRequest(e -&gt; {</b>
<b class="nc">&nbsp; Platform.exit();</b>
<b class="nc">&nbsp; System.exit(0);</b>
&nbsp; });
<b class="nc">&nbsp; primaryStage.show();</b>
<b class="nc">&nbsp; Platform.runLater(() -&gt; primaryStage.setFullScreen(true));</b>
&nbsp;
&nbsp; try {
<b class="nc">&nbsp; String musicUrl = getClass().getResource(&quot;/Audio/Music.mp3&quot;).toExternalForm();</b>
<b class="nc">&nbsp; bgMusic = new MediaPlayer(new Media(musicUrl));</b>
<b class="nc">&nbsp; bgMusic.setCycleCount(MediaPlayer.INDEFINITE);</b>
<b class="nc">&nbsp; bgMusic.setVolume(0.4);</b>
<b class="nc">&nbsp; bgMusic.play();</b>
&nbsp; } catch (Exception e) {
<b class="nc">&nbsp; System.err.println(&quot;Audio unavailable: &quot; + e.getMessage());</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Updates the local mini model reference used by the GUI.
&nbsp; *
&nbsp; * @param miniModel the latest mini model received from the server.
&nbsp; */
&nbsp; @Override
&nbsp; public void setModel(MiniModel miniModel) {
<b class="nc">&nbsp; this.miniModel = miniModel;</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Injects the client controller into this GUI.
&nbsp; *
&nbsp; * @param controller the client controller to use.
&nbsp; */
&nbsp; public void setController(ClientController controller) {
<b class="nc">&nbsp; this.controller=controller;</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Re-renders the GUI on the JavaFX application thread, switching to the
&nbsp; * appropriate scene based on the current game stage.
&nbsp; */
&nbsp; public void render() {
<b class="nc">&nbsp; Platform.runLater(() -&gt; {</b>
<b class="nc">&nbsp; if (miniModel == null) return;</b>
<b class="nc">&nbsp; if (controller.getMiniModel().currentState.getGameStage() == TOTEM_CHOICE) {</b>
<b class="nc">&nbsp; controllerTotem.render();</b>
<b class="nc">&nbsp; fadeToScene(totemScene);</b>
<b class="nc">&nbsp; } else if (controller.getMiniModel().currentState.getGameStage() == ENDED) {</b>
<b class="nc">&nbsp; controllerLeaderboard.render();</b>
<b class="nc">&nbsp; fadeToScene(leaderboardScene);</b>
&nbsp; } else {
<b class="nc">&nbsp; controllerMain.render();</b>
<b class="nc">&nbsp; fadeToScene(mainScene);</b>
&nbsp; }
&nbsp; });
&nbsp; }
&nbsp; //TODO
&nbsp; private void wrapScene(Scene scene) {
<b class="nc">&nbsp; Parent root = scene.getRoot();</b>
<b class="nc">&nbsp; scene.setRoot(new StackPane(root));</b>
&nbsp; }
&nbsp; //TODO
&nbsp; private void fadeToScene(Scene newScene) {
<b class="nc">&nbsp; Scene currentScene = primaryStage.getScene();</b>
<b class="nc">&nbsp; if (currentScene == newScene || isFading) return;</b>
<b class="nc">&nbsp; isFading = true;</b>
&nbsp;
<b class="nc">&nbsp; StackPane currentRoot = (StackPane) currentScene.getRoot();</b>
<b class="nc">&nbsp; Rectangle overlay = new Rectangle();</b>
<b class="nc">&nbsp; overlay.setFill(Color.BLACK);</b>
<b class="nc">&nbsp; overlay.setOpacity(0);</b>
<b class="nc">&nbsp; overlay.widthProperty().bind(currentRoot.widthProperty());</b>
<b class="nc">&nbsp; overlay.heightProperty().bind(currentRoot.heightProperty());</b>
<b class="nc">&nbsp; currentRoot.getChildren().add(overlay);</b>
&nbsp;
<b class="nc">&nbsp; FadeTransition fadeOut = new FadeTransition(Duration.millis(500), overlay);</b>
<b class="nc">&nbsp; fadeOut.setFromValue(0);</b>
<b class="nc">&nbsp; fadeOut.setToValue(1);</b>
<b class="nc">&nbsp; fadeOut.setOnFinished(e -&gt; {</b>
<b class="nc">&nbsp; currentRoot.getChildren().remove(overlay);</b>
&nbsp;
<b class="nc">&nbsp; StackPane newRoot = (StackPane) newScene.getRoot();</b>
<b class="nc">&nbsp; Rectangle newOverlay = new Rectangle();</b>
<b class="nc">&nbsp; newOverlay.setFill(Color.BLACK);</b>
<b class="nc">&nbsp; newOverlay.setOpacity(1);</b>
<b class="nc">&nbsp; newOverlay.widthProperty().bind(newRoot.widthProperty());</b>
<b class="nc">&nbsp; newOverlay.heightProperty().bind(newRoot.heightProperty());</b>
<b class="nc">&nbsp; newRoot.getChildren().add(newOverlay);</b>
&nbsp;
<b class="nc">&nbsp; primaryStage.setScene(newScene);</b>
&nbsp;
<b class="nc">&nbsp; FadeTransition fadeIn = new FadeTransition(Duration.millis(500), newOverlay);</b>
<b class="nc">&nbsp; fadeIn.setFromValue(1);</b>
<b class="nc">&nbsp; fadeIn.setToValue(0);</b>
<b class="nc">&nbsp; fadeIn.setOnFinished(ev -&gt; {</b>
<b class="nc">&nbsp; newRoot.getChildren().remove(newOverlay);</b>
<b class="nc">&nbsp; isFading = false;</b>
&nbsp; });
<b class="nc">&nbsp; fadeIn.play();</b>
&nbsp; });
<b class="nc">&nbsp; fadeOut.play();</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Displays an error on the JavaFX application thread.
&nbsp; *
&nbsp; * &lt;p&gt;If the server crashed, returns to the login scene and re-enables the login button.
&nbsp; * Otherwise delegates to the active scene&#39;s error display.
&nbsp; *
&nbsp; * @param error the error type.
&nbsp; * @param message a human-readable description of the error.
&nbsp; */
&nbsp; @Override
&nbsp; public void showError(ErrorType error,String message) {
<b class="nc">&nbsp; Platform.runLater(() -&gt; {</b>
<b class="nc">&nbsp; if (error == ErrorType.SERVER_CRASHED) {</b>
<b class="nc">&nbsp; controllerLogin.updateLoginButton(true);</b>
<b class="nc">&nbsp; controllerLogin.showError(error);</b>
<b class="nc">&nbsp; fadeToScene(loginScene);</b>
&nbsp; } else {
<b class="nc">&nbsp; if (miniModel == null) return;</b>
<b class="nc">&nbsp; controllerMain.setError(true);</b>
<b class="nc">&nbsp; controllerMain.render();</b>
&nbsp; }
&nbsp; });
&nbsp; }
&nbsp;
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>
@@ -0,0 +1,498 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > LeaderboardFXMLController</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.View.GUI</a>
</div>
<h1>Coverage Summary for Class: LeaderboardFXMLController (it.polimi.ingsw.gc14.View.GUI)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">LeaderboardFXMLController</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/19)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/34)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/182)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.View.GUI;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.ClientController;
&nbsp;import it.polimi.ingsw.gc14.Model.Player;
&nbsp;import javafx.animation.ScaleTransition;
&nbsp;import it.polimi.ingsw.gc14.Model.PlayableCard;
&nbsp;import javafx.event.Event;
&nbsp;import javafx.fxml.FXML;
&nbsp;import javafx.scene.Scene;
&nbsp;import javafx.scene.control.ScrollPane;
&nbsp;import javafx.stage.Popup;
&nbsp;import javafx.geometry.Insets;
&nbsp;import javafx.geometry.Pos;
&nbsp;import javafx.scene.Cursor;
&nbsp;import javafx.scene.Node;
&nbsp;import javafx.scene.control.Label;
&nbsp;import javafx.scene.effect.DropShadow;
&nbsp;import javafx.scene.image.Image;
&nbsp;import javafx.scene.image.ImageView;
&nbsp;import javafx.scene.input.MouseEvent;
&nbsp;import javafx.scene.layout.*;
&nbsp;import javafx.scene.paint.Color;
&nbsp;import javafx.scene.paint.LinearGradient;
&nbsp;import javafx.scene.paint.Stop;
&nbsp;import javafx.scene.paint.CycleMethod;
&nbsp;import javafx.scene.text.Font;
&nbsp;import javafx.stage.Stage;
&nbsp;import javafx.stage.Window;
&nbsp;import javafx.util.Duration;
&nbsp;
&nbsp;import java.util.*;
&nbsp;
&nbsp;/**
&nbsp; * FXML controller for the end-of-game leaderboard scene.
&nbsp; *
&nbsp; * &lt;p&gt;Displays the final player rankings and a winner/game-over banner.
&nbsp; */
<b class="nc">&nbsp;public class LeaderboardFXMLController {</b>
&nbsp;
&nbsp; @FXML private StackPane rootPane;
&nbsp; @FXML private VBox mainVBox;
&nbsp; @FXML private VBox rankingList;
&nbsp; // title label removed; outcome text is added dynamically in render()
&nbsp; //TODO
&nbsp; private ClientController controller;
&nbsp; //TODO
&nbsp; private Popup popup;
&nbsp; //TODO
&nbsp; private VBox popupContent;
&nbsp; //TODO
<b class="nc">&nbsp; private static final Map&lt;String, Image&gt; imageCache = new HashMap&lt;&gt;();</b>
&nbsp; //TODO
&nbsp; private Scene loginScene;
&nbsp;
&nbsp; //TODO
&nbsp; private Runnable action;
&nbsp;
&nbsp; /** Returns a cached {@link Image} for the given classpath {@code path}, loading it on first access. */
&nbsp; private Image loadImage(String path) {
<b class="nc">&nbsp; return imageCache.computeIfAbsent(path,</b>
<b class="nc">&nbsp; p -&gt; new Image(getClass().getResourceAsStream(p)));</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /**
&nbsp; * Injects the client controller, a post-game action, and the login scene reference.
&nbsp; *
&nbsp; * @param controller the client controller.
&nbsp; * @param action the action to run when the player returns to the login screen.
&nbsp; * @param loginScene the login scene to show on exit.
&nbsp; */
&nbsp; public void setController(ClientController controller, Runnable action, Scene loginScene) {
<b class="nc">&nbsp; this.controller = controller;</b>
<b class="nc">&nbsp; this.loginScene = loginScene;</b>
<b class="nc">&nbsp; this.action = action;</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /** Initializes the scene: loads fonts, sets up the background, and initializes the popup. */
&nbsp; @FXML
&nbsp; public void initialize() {
<b class="nc">&nbsp; Font.loadFont(getClass().getResourceAsStream(&quot;/Fonts/InknutAntiqua-Regular.ttf&quot;), 14);</b>
<b class="nc">&nbsp; mainVBox.sceneProperty().addListener((obs, oldScene, newScene) -&gt; {</b>
<b class="nc">&nbsp; if (newScene != null) newScene.getRoot().applyCss();</b>
&nbsp; });
<b class="nc">&nbsp; renderBackground();</b>
<b class="nc">&nbsp; initPopup();</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /** Populates the ranking list with the final player standings and shows the outcome banner. */
&nbsp; public void render() {
<b class="nc">&nbsp; rankingList.getChildren().clear();</b>
<b class="nc">&nbsp; if(!controller.getMiniModel().standingPlayers.isEmpty())</b>
&nbsp; {
<b class="nc">&nbsp; List&lt;Player&gt; sorted = controller.getMiniModel().standingPlayers;</b>
&nbsp;
<b class="nc">&nbsp; boolean iWon = sorted.get(0).getUserName().equals(controller.getMyUsername());</b>
&nbsp;
&nbsp; // Label grande Winner / Game Over
<b class="nc">&nbsp; Label outcomeLabel = new Label(iWon ? &quot;\uD83C\uDFC6 WINNER! \uD83C\uDFC6&quot; : &quot;GAME OVER&quot;);</b>
<b class="nc">&nbsp; outcomeLabel.setStyle(</b>
&nbsp; &quot;-fx-font-size: 64px;&quot; +
&nbsp; &quot;-fx-font-weight: bold;&quot; +
<b class="nc">&nbsp; &quot;-fx-text-fill: &quot; + (iWon ? &quot;#FFD700;&quot; : &quot;#FF4444;&quot;)</b>
&nbsp; );
<b class="nc">&nbsp; DropShadow glow = new DropShadow();</b>
<b class="nc">&nbsp; glow.setColor(iWon ? Color.rgb(255, 200, 0, 0.95) : Color.rgb(220, 0, 0, 0.95));</b>
<b class="nc">&nbsp; glow.setRadius(35);</b>
<b class="nc">&nbsp; glow.setSpread(0.35);</b>
<b class="nc">&nbsp; outcomeLabel.setEffect(glow);</b>
<b class="nc">&nbsp; rankingList.getChildren().add(outcomeLabel);</b>
&nbsp;
<b class="nc">&nbsp; Region sep = new Region();</b>
<b class="nc">&nbsp; sep.setPrefHeight(16);</b>
<b class="nc">&nbsp; rankingList.getChildren().add(sep);</b>
&nbsp;
<b class="nc">&nbsp; for (int i = 0; i &lt; sorted.size(); i++) {</b>
<b class="nc">&nbsp; rankingList.getChildren().add(createPlayerRow(i + 1, sorted.get(i)));</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== EFFECTS ====
&nbsp; /** Scales {@code node} to 1.02× on hover and sets a hand cursor. */
&nbsp; private void addHoverZoom(Node node) {
<b class="nc">&nbsp; ScaleTransition scaleUp = new ScaleTransition(Duration.millis(150), node);</b>
<b class="nc">&nbsp; scaleUp.setToX(1.02);</b>
<b class="nc">&nbsp; scaleUp.setToY(1.02);</b>
<b class="nc">&nbsp; ScaleTransition scaleDown = new ScaleTransition(Duration.millis(150), node);</b>
<b class="nc">&nbsp; scaleDown.setToX(1.0);</b>
<b class="nc">&nbsp; scaleDown.setToY(1.0);</b>
<b class="nc">&nbsp; node.addEventHandler(MouseEvent.MOUSE_ENTERED, e -&gt; { scaleUp.play(); node.setCursor(Cursor.HAND); });</b>
<b class="nc">&nbsp; node.addEventHandler(MouseEvent.MOUSE_EXITED, e -&gt; { scaleDown.play(); node.setCursor(Cursor.DEFAULT); });</b>
&nbsp; }
&nbsp;
&nbsp; /** Applies a static drop-shadow to {@code node}. */
&nbsp; private void addShadow(Node node) {
<b class="nc">&nbsp; DropShadow shadow = new DropShadow();</b>
<b class="nc">&nbsp; shadow.setColor(Color.rgb(0, 0, 0, 0.6));</b>
<b class="nc">&nbsp; shadow.setRadius(12);</b>
<b class="nc">&nbsp; shadow.setOffsetX(3);</b>
<b class="nc">&nbsp; shadow.setOffsetY(3);</b>
<b class="nc">&nbsp; node.setEffect(shadow);</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== ROW ====
&nbsp; /** Builds a styled leaderboard row showing rank, totem, username, stats, and prestige for {@code player}. */
&nbsp; private HBox createPlayerRow(int position, Player player) {
<b class="nc">&nbsp; HBox row = new HBox(20);</b>
<b class="nc">&nbsp; row.setAlignment(Pos.CENTER_LEFT);</b>
<b class="nc">&nbsp; row.setPadding(new Insets(14, 28, 14, 28));</b>
<b class="nc">&nbsp; row.setMaxWidth(Double.MAX_VALUE);</b>
&nbsp;
<b class="nc">&nbsp; LinearGradient gradient = new LinearGradient(</b>
&nbsp; 0, 0, 1, 0, true, CycleMethod.NO_CYCLE,
<b class="nc">&nbsp; new Stop(0.0, Color.rgb(0, 0, 0, 0.75)),</b>
<b class="nc">&nbsp; new Stop(0.5, Color.rgb(0, 0, 0, 0.60)),</b>
<b class="nc">&nbsp; new Stop(1.0, Color.rgb(0, 0, 0, 0.75))</b>
&nbsp; );
<b class="nc">&nbsp; row.setBackground(new Background(new BackgroundFill(</b>
&nbsp; gradient,
&nbsp; new CornerRadii(12),
&nbsp; Insets.EMPTY
&nbsp; )));
<b class="nc">&nbsp; addShadow(row);</b>
<b class="nc">&nbsp; addHoverZoom(row);</b>
&nbsp;
<b class="nc">&nbsp; Label posLabel = new Label(position + &quot;°&quot;);</b>
<b class="nc">&nbsp; posLabel.setMinWidth(55);</b>
<b class="nc">&nbsp; String medalColor = switch (position) {</b>
<b class="nc">&nbsp; case 1 -&gt; &quot;#FFD700&quot;; // oro</b>
<b class="nc">&nbsp; case 2 -&gt; &quot;#C0C0C0&quot;; // argento</b>
<b class="nc">&nbsp; case 3 -&gt; &quot;#CD7F32&quot;; // bronzo</b>
<b class="nc">&nbsp; default -&gt; &quot;#EEEEEE&quot;;</b>
<b class="nc">&nbsp; };</b>
<b class="nc">&nbsp; posLabel.setStyle(&quot;-fx-font-size: 28px; -fx-font-weight: bold; -fx-text-fill: &quot; + medalColor + &quot;;&quot;);</b>
&nbsp;
&nbsp;
<b class="nc">&nbsp; ImageView totem = new ImageView(loadImage(</b>
<b class="nc">&nbsp; &quot;/GUIImages/Totems/totem_&quot; + player.getTotem().toString().toLowerCase(Locale.ROOT) + &quot;.png&quot;));</b>
<b class="nc">&nbsp; totem.setFitHeight(55);</b>
<b class="nc">&nbsp; totem.setPreserveRatio(true);</b>
&nbsp;
<b class="nc">&nbsp; Label nameLabel = new Label(player.getUserName());</b>
<b class="nc">&nbsp; nameLabel.setStyle(</b>
&nbsp; &quot;-fx-font-size: 22px; -fx-font-weight: bold; -fx-text-fill: &quot; +
<b class="nc">&nbsp; (player.getUserName().equals(controller.getMyUsername()) ? &quot;#ff6b6b;&quot; : &quot;#FFFFFF;&quot;)</b>
&nbsp; );
<b class="nc">&nbsp; HBox.setHgrow(nameLabel, Priority.ALWAYS);</b>
&nbsp;
<b class="nc">&nbsp; HBox stats = createStatsBox(player);</b>
&nbsp;
<b class="nc">&nbsp; HBox ppBox = new HBox(6);</b>
<b class="nc">&nbsp; ppBox.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; ImageView ppIcon = new ImageView(loadImage(&quot;/GUIImages/Icons/PrestigePoint.png&quot;));</b>
<b class="nc">&nbsp; ppIcon.setFitHeight(45);</b>
<b class="nc">&nbsp; ppIcon.setPreserveRatio(true);</b>
<b class="nc">&nbsp; Label ppLabel = new Label(String.valueOf(player.getPrestigeValue()));</b>
<b class="nc">&nbsp; ppLabel.setStyle(&quot;-fx-font-size: 28px; -fx-font-weight: bold; -fx-text-fill: #FFD700;&quot;);</b>
<b class="nc">&nbsp; ppBox.getChildren().addAll(ppIcon, ppLabel);</b>
&nbsp;
<b class="nc">&nbsp; row.getChildren().addAll(posLabel, totem, nameLabel, stats, ppBox);</b>
<b class="nc">&nbsp; row.setOnMouseClicked(e -&gt; openPlayerPopup(player));</b>
<b class="nc">&nbsp; return row;</b>
&nbsp; }
&nbsp;
&nbsp; /** Creates a compact icon+count stats strip for all card types of {@code player}. */
&nbsp; private HBox createStatsBox(Player player) {
<b class="nc">&nbsp; HBox stats = new HBox(14);</b>
<b class="nc">&nbsp; stats.setAlignment(Pos.CENTER);</b>
&nbsp;
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Food.png&quot;, String.valueOf(player.getFoodValue()), 26));</b>
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Artist.png&quot;, String.valueOf(player.getArtists().size()), 26));</b>
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Gatherer.png&quot;, String.valueOf(player.getGatherers().size()), 26));</b>
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Inventor.png&quot;, String.valueOf(player.getInventors().size()), 26));</b>
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Builder.png&quot;, String.valueOf(player.getBuilders().size()), 26));</b>
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Shaman.png&quot;, String.valueOf(player.getShamans().size()), 26));</b>
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Hunter.png&quot;, String.valueOf(player.getHunters().size()), 26));</b>
<b class="nc">&nbsp; stats.getChildren().add(createStatItem(&quot;/GUIImages/Icons/Building.png&quot;, String.valueOf(player.getBuildingCards().size()), 26));</b>
&nbsp;
<b class="nc">&nbsp; return stats;</b>
&nbsp; }
&nbsp;
&nbsp; /** Creates a single icon + label widget for one stat type. */
&nbsp; private HBox createStatItem(String iconPath, String value, double iconHeight) {
<b class="nc">&nbsp; HBox box = new HBox(4);</b>
<b class="nc">&nbsp; box.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; ImageView icon = new ImageView(loadImage(iconPath));</b>
<b class="nc">&nbsp; icon.setFitHeight(iconHeight);</b>
<b class="nc">&nbsp; icon.setPreserveRatio(true);</b>
<b class="nc">&nbsp; Label label = new Label(value);</b>
<b class="nc">&nbsp; label.setStyle(&quot;-fx-font-size: 16px; -fx-text-fill: #FFFFFF; -fx-font-weight: bold;&quot;);</b>
<b class="nc">&nbsp; DropShadow textShadow = new DropShadow();</b>
<b class="nc">&nbsp; textShadow.setColor(Color.rgb(0, 0, 0, 0.9));</b>
<b class="nc">&nbsp; textShadow.setRadius(4);</b>
<b class="nc">&nbsp; label.setEffect(textShadow);</b>
<b class="nc">&nbsp; box.getChildren().addAll(icon, label);</b>
<b class="nc">&nbsp; return box;</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== POPUP ====
&nbsp; /** Creates and configures the auto-hiding player-detail popup. */
&nbsp; private void initPopup() {
<b class="nc">&nbsp; popupContent = new VBox(12);</b>
<b class="nc">&nbsp; popupContent.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; popupContent.setPadding(new Insets(16));</b>
<b class="nc">&nbsp; popupContent.setStyle(</b>
&nbsp; &quot;-fx-background-color: rgba(20,10,5,0.97);&quot; +
&nbsp; &quot;-fx-border-color: #8B4513;&quot; +
&nbsp; &quot;-fx-border-width: 3;&quot; +
&nbsp; &quot;-fx-border-radius: 14;&quot; +
&nbsp; &quot;-fx-background-radius: 14;&quot;
&nbsp; );
&nbsp;
<b class="nc">&nbsp; popup = new Popup();</b>
<b class="nc">&nbsp; popup.getContent().add(popupContent);</b>
<b class="nc">&nbsp; popup.setAutoHide(true);</b>
<b class="nc">&nbsp; popup.addEventHandler(Event.ANY, e -&gt; {</b>
<b class="nc">&nbsp; if (popup.getScene() != null) popup.getScene().setFill(Color.TRANSPARENT);</b>
&nbsp; });
&nbsp; }
&nbsp;
&nbsp; /** Opens the player-detail popup showing all cards held by {@code player}, grouped by type. */
&nbsp; private void openPlayerPopup(Player player) {
<b class="nc">&nbsp; popupContent.getChildren().clear();</b>
&nbsp;
<b class="nc">&nbsp; HBox header = new HBox(10);</b>
<b class="nc">&nbsp; header.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; ImageView totem = new ImageView(loadImage(</b>
<b class="nc">&nbsp; &quot;/GUIImages/Totems/totem_&quot; + player.getTotem().toString().toLowerCase(Locale.ROOT) + &quot;.png&quot;));</b>
<b class="nc">&nbsp; totem.setFitHeight(30);</b>
<b class="nc">&nbsp; totem.setPreserveRatio(true);</b>
<b class="nc">&nbsp; Label nameLabel = new Label(player.getUserName());</b>
<b class="nc">&nbsp; nameLabel.setStyle(&quot;-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: #FFD700;&quot;);</b>
<b class="nc">&nbsp; header.getChildren().addAll(totem, nameLabel);</b>
<b class="nc">&nbsp; popupContent.getChildren().add(header);</b>
&nbsp;
<b class="nc">&nbsp; String[][] types = {</b>
&nbsp; {&quot;Artist&quot;, &quot;artists&quot;},
&nbsp; {&quot;Gatherer&quot;, &quot;gatherers&quot;},
&nbsp; {&quot;Inventor&quot;, &quot;inventors&quot;},
&nbsp; {&quot;Builder&quot;, &quot;builders&quot;},
&nbsp; {&quot;Shaman&quot;, &quot;shamans&quot;},
&nbsp; {&quot;Hunter&quot;, &quot;hunters&quot;},
&nbsp; {&quot;Building&quot;, &quot;buildingCards&quot;}
&nbsp; };
&nbsp;
<b class="nc">&nbsp; VBox grid = new VBox(6);</b>
<b class="nc">&nbsp; grid.setAlignment(Pos.CENTER_LEFT);</b>
<b class="nc">&nbsp; boolean hasAnyCard = false;</b>
&nbsp;
<b class="nc">&nbsp; for (String[] type : types) {</b>
<b class="nc">&nbsp; String typeName = type[0];</b>
<b class="nc">&nbsp; String field = type[1];</b>
<b class="nc">&nbsp; ArrayList&lt;PlayableCard&gt; cards = getPlayerCards(player.getUserName(), field);</b>
<b class="nc">&nbsp; if (cards.isEmpty()) continue;</b>
<b class="nc">&nbsp; hasAnyCard = true;</b>
&nbsp;
<b class="nc">&nbsp; HBox row = new HBox(6);</b>
<b class="nc">&nbsp; row.setAlignment(Pos.CENTER_LEFT);</b>
&nbsp;
<b class="nc">&nbsp; ImageView typeIcon = new ImageView(loadImage(&quot;/GUIImages/Icons/&quot;+typeName+&quot;.png&quot;));</b>
<b class="nc">&nbsp; typeIcon.setFitHeight(22);</b>
<b class="nc">&nbsp; typeIcon.setPreserveRatio(true);</b>
<b class="nc">&nbsp; row.getChildren().add(typeIcon);</b>
&nbsp;
<b class="nc">&nbsp; for (PlayableCard card : cards) {</b>
<b class="nc">&nbsp; ImageView img = new ImageView(loadImage(&quot;/GUIImages/Fronts/card-&quot; + card.getIdIMG() + &quot;.png&quot;));</b>
<b class="nc">&nbsp; img.setFitHeight(230);</b>
<b class="nc">&nbsp; img.setPreserveRatio(true);</b>
<b class="nc">&nbsp; img.setCursor(Cursor.HAND);</b>
<b class="nc">&nbsp; row.getChildren().add(img);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; grid.getChildren().add(row);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; if (!hasAnyCard) {</b>
<b class="nc">&nbsp; Label empty = new Label(&quot;No cards&quot;);</b>
<b class="nc">&nbsp; empty.setStyle(&quot;-fx-font-size: 14px; -fx-text-fill: #888888;&quot;);</b>
<b class="nc">&nbsp; grid.getChildren().add(empty);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; ScrollPane scrollPane = new ScrollPane(grid);</b>
<b class="nc">&nbsp; scrollPane.setFitToWidth(true);</b>
<b class="nc">&nbsp; scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);</b>
<b class="nc">&nbsp; scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);</b>
<b class="nc">&nbsp; scrollPane.setStyle(&quot;-fx-background: transparent; -fx-background-color: transparent;&quot;);</b>
&nbsp;
<b class="nc">&nbsp; Window window = rootPane.getScene().getWindow();</b>
<b class="nc">&nbsp; scrollPane.setMaxHeight(window.getHeight() * 0.8); // cap scroll area to 80% of window height</b>
<b class="nc">&nbsp; popupContent.getChildren().add(scrollPane);</b>
<b class="nc">&nbsp; popup.show(window, 0, 0);</b>
<b class="nc">&nbsp; popup.getScene().setFill(Color.TRANSPARENT);</b>
<b class="nc">&nbsp; popup.setX(window.getX() + (window.getWidth() - popup.getWidth()) / 2);</b>
<b class="nc">&nbsp; popup.setY(window.getY() + (window.getHeight() - popup.getHeight()) / 2);</b>
&nbsp; }
&nbsp;
&nbsp; /** Returns a copy of the named card collection for {@code username}. */
&nbsp; private ArrayList&lt;PlayableCard&gt; getPlayerCards(String username, String type) {
<b class="nc">&nbsp; Player p = controller.getMiniModel().players.get(username);</b>
<b class="nc">&nbsp; return switch (type) {</b>
<b class="nc">&nbsp; case &quot;artists&quot; -&gt; new ArrayList&lt;&gt;(p.getArtists());</b>
<b class="nc">&nbsp; case &quot;gatherers&quot; -&gt; new ArrayList&lt;&gt;(p.getGatherers());</b>
<b class="nc">&nbsp; case &quot;inventors&quot; -&gt; new ArrayList&lt;&gt;(p.getInventors());</b>
<b class="nc">&nbsp; case &quot;builders&quot; -&gt; new ArrayList&lt;&gt;(p.getBuilders());</b>
<b class="nc">&nbsp; case &quot;shamans&quot; -&gt; new ArrayList&lt;&gt;(p.getShamans());</b>
<b class="nc">&nbsp; case &quot;hunters&quot; -&gt; new ArrayList&lt;&gt;(p.getHunters());</b>
<b class="nc">&nbsp; case &quot;buildingCards&quot; -&gt; new ArrayList&lt;&gt;(p.getBuildingCards());</b>
<b class="nc">&nbsp; default -&gt; new ArrayList&lt;&gt;();</b>
&nbsp; };
&nbsp; }
&nbsp;
&nbsp; // ==== BACKGROUND ====
&nbsp; /** Sets the full-cover background image on the root pane. */
&nbsp; private void renderBackground() {
<b class="nc">&nbsp; BackgroundSize size = new BackgroundSize(</b>
&nbsp; BackgroundSize.AUTO, BackgroundSize.AUTO,
&nbsp; false, false, true, true
&nbsp; );
<b class="nc">&nbsp; rootPane.setBackground(new Background(new BackgroundImage(</b>
<b class="nc">&nbsp; loadImage(&quot;/GUIImages/Background.png&quot;),</b>
&nbsp; BackgroundRepeat.NO_REPEAT,
&nbsp; BackgroundRepeat.NO_REPEAT,
&nbsp; BackgroundPosition.CENTER,
&nbsp; size
&nbsp; )));
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== ACTIONS ====
&nbsp; /** Disconnects the client and invokes the post-game action to return to the login scene. */
&nbsp; @FXML
&nbsp; private void onNewGame() {
<b class="nc">&nbsp; controller.disconnect();</b>
<b class="nc">&nbsp; action.run();</b>
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>
@@ -0,0 +1,329 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > LoginFXMLController</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.View.GUI</a>
</div>
<h1>Coverage Summary for Class: LoginFXMLController (it.polimi.ingsw.gc14.View.GUI)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">LoginFXMLController</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/14)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/14)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/63)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.View.GUI;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.ClientController;
&nbsp;import it.polimi.ingsw.gc14.ErrorType;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkConfig;
&nbsp;import it.polimi.ingsw.gc14.Network.InterfaceResolver;
&nbsp;import it.polimi.ingsw.gc14.Network.RMI.Client.RMIClient;
&nbsp;import it.polimi.ingsw.gc14.Network.TCP.Client.TCPClient;
&nbsp;import javafx.animation.*;
&nbsp;import javafx.application.Platform;
&nbsp;import javafx.css.PseudoClass;
&nbsp;import javafx.fxml.FXML;
&nbsp;import javafx.geometry.Rectangle2D;
&nbsp;import javafx.scene.control.*;
&nbsp;import javafx.scene.image.Image;
&nbsp;import javafx.scene.image.ImageView;
&nbsp;import javafx.scene.layout.*;
&nbsp;import javafx.scene.paint.Color;
&nbsp;import javafx.stage.Screen;
&nbsp;import javafx.util.Duration;
&nbsp;
&nbsp;import java.net.*;
&nbsp;import java.util.Enumeration;
&nbsp;
&nbsp;/**
&nbsp; * FXML controller for the login scene.
&nbsp; *
&nbsp; * &lt;p&gt;Handles username/IP/player-count input, protocol selection (TCP/RMI),
&nbsp; * and initiates the connection to the server.
&nbsp; */
<b class="nc">&nbsp;public class LoginFXMLController {</b>
&nbsp; //TODO
&nbsp; @FXML private ImageView backgroundImage;
&nbsp; //TODO
&nbsp; @FXML private TextField campoNome;
&nbsp; //TODO
&nbsp; @FXML private TextField campoNumPlayers;
&nbsp; //TODO
&nbsp; @FXML private TextField campoIP;
&nbsp; //TODO
&nbsp; @FXML private Button btnAccedi;
&nbsp; //TODO
&nbsp; @FXML private Label labelErrore;
&nbsp; //TODO
&nbsp; @FXML private VBox formPanel;
&nbsp; //TODO
&nbsp; @FXML private StackPane protocolToggle;
&nbsp; //TODO
&nbsp; @FXML private Region toggleThumb;
&nbsp; //TODO
&nbsp; @FXML private Label labelRMI;
&nbsp; //TODO
&nbsp; @FXML private Label labelTCP;
&nbsp; //TODO
<b class="nc">&nbsp; private boolean isRMI = true;</b>
&nbsp; //TODO
&nbsp; private ClientController controller;
&nbsp; //TODO
<b class="nc">&nbsp; private final PseudoClass activeProtocolPseudo = PseudoClass.getPseudoClass(&quot;active-protocol&quot;);</b>
&nbsp;
&nbsp; /**
&nbsp; * Injects the client controller into this FXML controller.
&nbsp; *
&nbsp; * @param controller the client controller to use.
&nbsp; */
&nbsp; public void setController(ClientController controller) {
<b class="nc">&nbsp; this.controller = controller;</b>
&nbsp; }
&nbsp;
&nbsp; /** Initializes the scene: loads background, sets up animations, and binds input listeners. */
&nbsp; @FXML
&nbsp; public void initialize() {
&nbsp; // Background setup
<b class="nc">&nbsp; Image img = new Image(getClass().getResourceAsStream(&quot;/GUIImages/BackgroundLogin.png&quot;));</b>
<b class="nc">&nbsp; backgroundImage.setImage(img);</b>
<b class="nc">&nbsp; Rectangle2D screenBounds = Screen.getPrimary().getBounds();</b>
<b class="nc">&nbsp; backgroundImage.setFitWidth(screenBounds.getWidth());</b>
<b class="nc">&nbsp; backgroundImage.setFitHeight(screenBounds.getHeight());</b>
&nbsp;
&nbsp; // Protocol toggle configuration
<b class="nc">&nbsp; protocolToggle.setOnMouseClicked(e -&gt; switchProtocol());</b>
<b class="nc">&nbsp; protocolToggle.setStyle(protocolToggle.getStyle() + &quot; -fx-cursor: hand;&quot;);</b>
&nbsp;
<b class="nc">&nbsp; labelRMI.pseudoClassStateChanged(activeProtocolPseudo, true);</b>
<b class="nc">&nbsp; labelTCP.pseudoClassStateChanged(activeProtocolPseudo, false);</b>
&nbsp;
&nbsp; // Login Action
<b class="nc">&nbsp; btnAccedi.setOnAction(e -&gt; onAccediClick());</b>
&nbsp; }
&nbsp;
&nbsp; /** Toggles the selected protocol between RMI and TCP, animating the toggle thumb and updating label styles. */
&nbsp; private void switchProtocol() {
<b class="nc">&nbsp; isRMI = !isRMI;</b>
&nbsp;
<b class="nc">&nbsp; TranslateTransition tt = new TranslateTransition(Duration.millis(200), toggleThumb);</b>
<b class="nc">&nbsp; tt.setToX(isRMI ? 0 : 110);</b>
<b class="nc">&nbsp; tt.play();</b>
&nbsp;
&nbsp; // pseudoClassStateChanged preserves layout properties unlike direct style mutation
<b class="nc">&nbsp; labelRMI.pseudoClassStateChanged(activeProtocolPseudo, isRMI);</b>
<b class="nc">&nbsp; labelTCP.pseudoClassStateChanged(activeProtocolPseudo, !isRMI);</b>
&nbsp; }
&nbsp;
&nbsp; /** Validates form input and starts a background thread to connect to the server. */
&nbsp; @FXML
&nbsp; private void onAccediClick() {
<b class="nc">&nbsp; String name = campoNome.getText().trim();</b>
<b class="nc">&nbsp; String ip = campoIP.getText().trim();</b>
&nbsp;
<b class="nc">&nbsp; if (name.isEmpty()) {</b>
<b class="nc">&nbsp; showError(&quot;Please select a name.&quot;);</b>
&nbsp; return;
&nbsp; }
&nbsp;
&nbsp; int numPlayers;
&nbsp; try {
<b class="nc">&nbsp; numPlayers = Integer.parseInt(campoNumPlayers.getText().trim());</b>
&nbsp; } catch (NumberFormatException e) {
<b class="nc">&nbsp; showError(&quot;Invalid number of players.&quot;);</b>
&nbsp; return;
&nbsp; }
&nbsp;
<b class="nc">&nbsp; updateLoginButton(false);</b>
<b class="nc">&nbsp; controller.setMyUsername(name);</b>
&nbsp;
<b class="nc">&nbsp; new Thread(() -&gt; {</b>
&nbsp; try {
<b class="nc">&nbsp; String localInterface = InterfaceResolver.resolveLocalInterface(ip);</b>
<b class="nc">&nbsp; System.setProperty(&quot;java.rmi.server.hostname&quot;, localInterface);</b>
<b class="nc">&nbsp; connect(name, ip, numPlayers, localInterface);</b>
&nbsp; } catch (Exception ex) {
<b class="nc">&nbsp; Platform.runLater(() -&gt; {</b>
<b class="nc">&nbsp; showError(&quot;Network error: &quot; + ex.getMessage());</b>
<b class="nc">&nbsp; updateLoginButton(true);</b>
&nbsp; });
&nbsp; }
<b class="nc">&nbsp; }).start();</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Enables or disables the login button and updates its visual style.
&nbsp; *
&nbsp; * @param enabled {@code true} to enable the button, {@code false} to disable it.
&nbsp; */
&nbsp; public void updateLoginButton(boolean enabled) {
<b class="nc">&nbsp; btnAccedi.setDisable(!enabled);</b>
<b class="nc">&nbsp; btnAccedi.setStyle(</b>
&nbsp; &quot;-fx-font-family: &#39;Cinzel&#39;; -fx-font-size: 12; -fx-font-weight: bold;&quot; +
&nbsp; &quot;-fx-letter-spacing: 4; -fx-text-fill: #0c0601;&quot; +
&nbsp; &quot;-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);&quot; +
&nbsp; &quot;-fx-padding: 13 48 13 48; -fx-background-radius: 2;&quot; +
<b class="nc">&nbsp; &quot;-fx-opacity: &quot; + (enabled ? &quot;1.0&quot; : &quot;0.3&quot;) + &quot;;&quot; +</b>
<b class="nc">&nbsp; &quot;-fx-cursor: &quot; + (enabled ? &quot;hand&quot; : &quot;default&quot;) + &quot;;&quot;</b>
&nbsp; );
&nbsp; }
&nbsp;
&nbsp;
&nbsp;
&nbsp;
&nbsp; /** Connects to the server using the selected protocol and transitions to the waiting state on success. */
&nbsp; private void connect(String nome, String ip, int numPlayers, String localInterface) {
<b class="nc">&nbsp; if (isRMI) {</b>
<b class="nc">&nbsp; RMIClient client = new RMIClient(controller, ip, NetworkConfig.RMI_PORT, localInterface);</b>
<b class="nc">&nbsp; ErrorType serverResponse = client.connect(nome, numPlayers);</b>
<b class="nc">&nbsp; if (serverResponse == null) {</b>
<b class="nc">&nbsp; controller.setClient(client);</b>
<b class="nc">&nbsp; Platform.runLater(() -&gt; showSuccess(&quot;Connected! Waiting for other players…&quot;));</b>
&nbsp; } else {
<b class="nc">&nbsp; Platform.runLater(() -&gt; {</b>
<b class="nc">&nbsp; showError(serverResponse);</b>
<b class="nc">&nbsp; updateLoginButton(true);</b>
&nbsp; });
&nbsp; }
&nbsp; } else {
<b class="nc">&nbsp; TCPClient client = new TCPClient(controller, ip, NetworkConfig.TCP_PORT, NetworkConfig.HEARTBEAT_PORT);</b>
<b class="nc">&nbsp; ErrorType serverResponse = client.connect(nome, numPlayers);</b>
<b class="nc">&nbsp; if (serverResponse == null) {</b>
<b class="nc">&nbsp; controller.setClient(client);</b>
<b class="nc">&nbsp; Platform.runLater(() -&gt; showSuccess(&quot;Connected! Waiting for other players…&quot;));</b>
&nbsp; } else {
<b class="nc">&nbsp; Platform.runLater(() -&gt; {</b>
<b class="nc">&nbsp; showError(serverResponse);</b>
<b class="nc">&nbsp; updateLoginButton(true);</b>
&nbsp; });
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp; /**
&nbsp; * Displays an error from an {@link ErrorType} constant in the login form label.
&nbsp; *
&nbsp; * @param errorType the error to display.
&nbsp; */
&nbsp; public void showError(ErrorType errorType) {
<b class="nc">&nbsp; labelErrore.setTextFill(Color.web(&quot;#e05050&quot;));</b>
<b class="nc">&nbsp; labelErrore.setText(errorType.toString());</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Displays an arbitrary error message in the login form label.
&nbsp; *
&nbsp; * @param msg the error message to display.
&nbsp; */
&nbsp; public void showError(String msg) {
<b class="nc">&nbsp; labelErrore.setTextFill(Color.web(&quot;#e05050&quot;));</b>
<b class="nc">&nbsp; labelErrore.setText(msg);</b>
&nbsp; }
&nbsp;
&nbsp; /** Displays a success message in green in the login form label. */
&nbsp; private void showSuccess(String msg) {
<b class="nc">&nbsp; labelErrore.setTextFill(Color.web(&quot;#6fcf8a&quot;));</b>
<b class="nc">&nbsp; labelErrore.setText(msg);</b>
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>
@@ -0,0 +1,902 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > MainFXMLController</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.View.GUI</a>
</div>
<h1>Coverage Summary for Class: MainFXMLController (it.polimi.ingsw.gc14.View.GUI)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">MainFXMLController</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/50)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/124)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/426)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.View.GUI;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.ClientController;
&nbsp;import it.polimi.ingsw.gc14.Model.*;
&nbsp;import it.polimi.ingsw.gc14.Model.Cards.BuildingCard;
&nbsp;import it.polimi.ingsw.gc14.Model.Cards.TribeCard;
&nbsp;import it.polimi.ingsw.gc14.Model.Orders.OrderPlayer;
&nbsp;import it.polimi.ingsw.gc14.Network.EventType;
&nbsp;import it.polimi.ingsw.gc14.Network.NetworkEvent;
&nbsp;import javafx.animation.ScaleTransition;
&nbsp;import javafx.animation.TranslateTransition;
&nbsp;import javafx.application.Platform;
&nbsp;import javafx.beans.value.ChangeListener;
&nbsp;import javafx.beans.value.ObservableValue;
&nbsp;import javafx.event.Event;
&nbsp;import javafx.fxml.FXML;
&nbsp;import javafx.geometry.Bounds;
&nbsp;import javafx.geometry.Insets;
&nbsp;import javafx.geometry.Pos;
&nbsp;import javafx.geometry.Rectangle2D;
&nbsp;import javafx.scene.Cursor;
&nbsp;import javafx.scene.Node;
&nbsp;import javafx.scene.Scene;
&nbsp;import javafx.scene.control.Button;
&nbsp;import javafx.scene.control.Label;
&nbsp;import javafx.scene.control.ScrollPane;
&nbsp;import javafx.scene.effect.ColorAdjust;
&nbsp;import javafx.scene.effect.DropShadow;
&nbsp;import javafx.scene.image.*;
&nbsp;import javafx.scene.input.MouseEvent;
&nbsp;import javafx.scene.layout.*;
&nbsp;import javafx.scene.paint.Color;
&nbsp;import javafx.scene.shape.*;
&nbsp;import javafx.scene.text.Font;
&nbsp;import javafx.stage.Popup;
&nbsp;import javafx.stage.Screen;
&nbsp;import javafx.stage.Window;
&nbsp;import javafx.util.Duration;
&nbsp;
&nbsp;import java.util.*;
&nbsp;
&nbsp;/**
&nbsp; * FXML controller for the main game scene.
&nbsp; *
&nbsp; * &lt;p&gt;Renders the board (upper/lower tribe and building card rows), player stats,
&nbsp; * the player&#39;s hand, and action buttons (skip, details).
&nbsp; */
<b class="nc">&nbsp;public class MainFXMLController {</b>
&nbsp; //TODO
&nbsp; @FXML private GridPane leftGrid;
&nbsp; //TODO
&nbsp; @FXML private ScrollPane playerSide;
&nbsp; //TODO
&nbsp; @FXML private HBox mainHBox;
&nbsp; //TODO
&nbsp; @FXML private ImageView backgroundImage;
&nbsp; //TODO
&nbsp; @FXML private HBox board;
&nbsp; //TODO
&nbsp; @FXML private HBox upperList;
&nbsp; //TODO
&nbsp; @FXML private HBox lowerList;
&nbsp; //TODO
&nbsp; @FXML private HBox myHand;
&nbsp; //TODO
&nbsp; @FXML private Button skipBtn;
&nbsp; //TODO
&nbsp; @FXML private Button detailsBtn;
&nbsp; //TODO
&nbsp; @FXML private Label infoText;
&nbsp; //TODO
<b class="nc">&nbsp; private final Map&lt;String, Label&gt; foodLabels = new HashMap&lt;&gt;();</b>
&nbsp; //TODO
<b class="nc">&nbsp; private final Map&lt;String, Label&gt; prestigeLabels = new HashMap&lt;&gt;();</b>
&nbsp; //TODO
<b class="nc">&nbsp; private final Map&lt;String, VBox&gt; playerCards = new HashMap&lt;&gt;();</b>
&nbsp; //TODO
<b class="nc">&nbsp; private final Map&lt;String, List&lt;ImageView&gt;&gt; iconViews = new HashMap&lt;&gt;();</b>
&nbsp; //TODO
&nbsp; private Popup popup;
&nbsp; //TODO
&nbsp; private HBox popupCards;
&nbsp; //TODO
&nbsp; private ClientController controller;
&nbsp;
&nbsp; /** {@code true} when the last received event was an error response. */
&nbsp; private boolean isError;
&nbsp;
&nbsp; /** Sets the error flag; called by {@link GUI} before delegating to {@link #render()}. */
<b class="nc">&nbsp; public void setError(boolean error) { this.isError = error; }</b>
&nbsp;
&nbsp; // ==== IMAGE CACHE ====
<b class="nc">&nbsp; private static final Map&lt;String, Image&gt; imageCache = new HashMap&lt;&gt;();</b>
&nbsp;
&nbsp; /** Returns a cached {@link Image} for the given classpath {@code path}, loading it on first access. */
&nbsp; private Image loadImage(String path) {
<b class="nc">&nbsp; return imageCache.computeIfAbsent(path,</b>
<b class="nc">&nbsp; p -&gt; new Image(getClass().getResourceAsStream(p)));</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Injects the client controller and wires up the skip-turn button action.
&nbsp; *
&nbsp; * @param controller the client controller to use.
&nbsp; */
&nbsp; public void setController(ClientController controller) {
<b class="nc">&nbsp; this.controller = controller;</b>
<b class="nc">&nbsp; skipBtn.setOnAction(e -&gt; controller.skipTurn());</b>
&nbsp; }
&nbsp;
&nbsp; /** Initializes the scene: loads fonts, sets up the popup, and registers input listeners. */
&nbsp; @FXML
&nbsp; public void initialize() {
<b class="nc">&nbsp; Font.loadFont(getClass().getResourceAsStream(&quot;/Fonts/InknutAntiqua-Regular.ttf&quot;), 14);</b>
&nbsp;
<b class="nc">&nbsp; addHoverZoom(skipBtn);</b>
<b class="nc">&nbsp; addHoverZoom(detailsBtn);</b>
<b class="nc">&nbsp; popup = new Popup();</b>
<b class="nc">&nbsp; mainHBox.sceneProperty().addListener((obs, oldScene, newScene) -&gt; {</b>
<b class="nc">&nbsp; if (newScene != null) {</b>
<b class="nc">&nbsp; newScene.addEventFilter(MouseEvent.MOUSE_PRESSED, e -&gt; {</b>
<b class="nc">&nbsp; if (popup.isShowing()) popup.hide();</b>
&nbsp; });
<b class="nc">&nbsp; newScene.getRoot().applyCss();</b>
<b class="nc">&nbsp; newScene.windowProperty().addListener((o2, ow, nw) -&gt; {</b>
<b class="nc">&nbsp; if (nw != null) {</b>
<b class="nc">&nbsp; nw.focusedProperty().addListener((o3, wf, nf) -&gt; {</b>
<b class="nc">&nbsp; if (!nf) popup.hide();</b>
&nbsp; });
&nbsp; }
&nbsp; });
&nbsp; }
&nbsp; else {
<b class="nc">&nbsp; popup.hide();</b>
&nbsp; }
&nbsp; });
<b class="nc">&nbsp; initPopup();</b>
<b class="nc">&nbsp; detailsBtn.setOnAction(e -&gt; openDetailsPopup());</b>
<b class="nc">&nbsp; renderBackground();</b>
<b class="nc">&nbsp; isError=false;</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== RENDER ====
&nbsp; /** Incrementally re-renders only the parts of the scene affected by the last network event. */
&nbsp; public void render() {
<b class="nc">&nbsp; if(foodLabels.isEmpty() || controller.getMiniModel().lastEvent == null</b>
<b class="nc">&nbsp; || controller.getMiniModel().lastEvent.getEventType().equals(EventType.TOTEM_CHOICE))</b>
<b class="nc">&nbsp; buildSidePanel();</b>
<b class="nc">&nbsp; if(isError)</b>
&nbsp; {
<b class="nc">&nbsp; updateSidePanel();</b>
<b class="nc">&nbsp; isError=false;</b>
&nbsp; return;
&nbsp; }
<b class="nc">&nbsp; if(controller.getMiniModel().lastEvent==null ||controller.getMiniModel().lastEvent.getEventType().equals(EventType.TOTEM_CHOICE) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.NEXT_ROUND)) {</b>
<b class="nc">&nbsp; renderMyHand();</b>
<b class="nc">&nbsp; renderUpper();</b>
<b class="nc">&nbsp; renderBoard();</b>
<b class="nc">&nbsp; renderLower();</b>
<b class="nc">&nbsp; updateSidePanel();</b>
&nbsp; return;
&nbsp; }
<b class="nc">&nbsp; if(controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_UPPER_TRIBE) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_UPPER_BUILD))</b>
&nbsp; {
<b class="nc">&nbsp; renderUpper();</b>
&nbsp; }
<b class="nc">&nbsp; if(controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_LOWER_TRIBE) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_LOWER_BUILD))</b>
&nbsp; {
<b class="nc">&nbsp; renderLower();</b>
&nbsp; }
<b class="nc">&nbsp; if(controller.getMiniModel().lastEvent.getUsername().equals(controller.getMyUsername()))</b>
&nbsp; {
<b class="nc">&nbsp; renderMyHand();</b>
&nbsp; }
<b class="nc">&nbsp; renderBoard();</b>
<b class="nc">&nbsp; updateSidePanel();</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== EFFECTS ====
&nbsp; /** Binds a rounded-rectangle clip to {@code img} so its corners are cropped. */
&nbsp; private void addClip(ImageView img) {
<b class="nc">&nbsp; Rectangle clip = new Rectangle();</b>
<b class="nc">&nbsp; clip.setArcWidth(20);</b>
<b class="nc">&nbsp; clip.setArcHeight(20);</b>
<b class="nc">&nbsp; img.setClip(clip);</b>
<b class="nc">&nbsp; clip.heightProperty().bind(img.layoutBoundsProperty().map(b -&gt; b.getHeight()));</b>
<b class="nc">&nbsp; clip.widthProperty().bind(img.layoutBoundsProperty().map(b -&gt; b.getWidth()));</b>
&nbsp; }
&nbsp;
&nbsp; /** Scales {@code node} to 1.1× on hover and restores on exit; sets a hand cursor. */
&nbsp; private void addHoverZoom(Node node) {
<b class="nc">&nbsp; ScaleTransition scaleUp = new ScaleTransition(Duration.millis(150), node);</b>
<b class="nc">&nbsp; scaleUp.setToX(1.1);</b>
<b class="nc">&nbsp; scaleUp.setToY(1.1);</b>
<b class="nc">&nbsp; ScaleTransition scaleDown = new ScaleTransition(Duration.millis(150), node);</b>
<b class="nc">&nbsp; scaleDown.setToX(1.0);</b>
<b class="nc">&nbsp; scaleDown.setToY(1.0);</b>
<b class="nc">&nbsp; node.addEventHandler(MouseEvent.MOUSE_ENTERED, e -&gt; { scaleUp.play(); node.setCursor(Cursor.HAND); });</b>
<b class="nc">&nbsp; node.addEventHandler(MouseEvent.MOUSE_EXITED, e -&gt; { scaleDown.play(); node.setCursor(Cursor.DEFAULT); });</b>
&nbsp; }
&nbsp;
&nbsp; /** Applies a drop-shadow to {@code node} that intensifies on hover. */
&nbsp; private void addShadow(Node node) {
<b class="nc">&nbsp; DropShadow shadow = new DropShadow();</b>
<b class="nc">&nbsp; shadow.setColor(Color.rgb(0, 0, 0, 0.3));</b>
<b class="nc">&nbsp; shadow.setRadius(6);</b>
<b class="nc">&nbsp; shadow.setOffsetX(2);</b>
<b class="nc">&nbsp; shadow.setOffsetY(2);</b>
<b class="nc">&nbsp; node.setEffect(shadow);</b>
<b class="nc">&nbsp; node.addEventHandler(MouseEvent.MOUSE_ENTERED, e -&gt; {</b>
<b class="nc">&nbsp; shadow.setRadius(16);</b>
<b class="nc">&nbsp; shadow.setOffsetX(4);</b>
<b class="nc">&nbsp; shadow.setOffsetY(4);</b>
<b class="nc">&nbsp; shadow.setColor(Color.rgb(0, 0, 0, 0.5));</b>
&nbsp; });
<b class="nc">&nbsp; node.addEventHandler(MouseEvent.MOUSE_EXITED, e -&gt; {</b>
<b class="nc">&nbsp; shadow.setRadius(6);</b>
<b class="nc">&nbsp; shadow.setOffsetX(2);</b>
<b class="nc">&nbsp; shadow.setOffsetY(2);</b>
<b class="nc">&nbsp; shadow.setColor(Color.rgb(0, 0, 0, 0.3));</b>
&nbsp; });
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== ELEMENTS ====
&nbsp;
&nbsp; /** Builds the side-panel card widget for {@code player} with totem, stats, and card-type icons. */
&nbsp; private VBox buildPlayerCard(Player player) {
<b class="nc">&nbsp; VBox card = new VBox(3);</b>
<b class="nc">&nbsp; VBox.setMargin(card, new Insets(15, 15, 0, 15));</b>
<b class="nc">&nbsp; card.getStyleClass().add(&quot;player-card&quot;);</b>
<b class="nc">&nbsp; card.setPadding(new Insets(4));</b>
&nbsp;
<b class="nc">&nbsp; HBox headerRow = new HBox(8);</b>
<b class="nc">&nbsp; headerRow.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; ImageView totem = new ImageView(loadImage(&quot;/GUIImages/Totems/totem_&quot;</b>
<b class="nc">&nbsp; + player.getTotem().toString().toLowerCase(Locale.ROOT) + &quot;.png&quot;));</b>
<b class="nc">&nbsp; totem.setFitHeight(20);</b>
<b class="nc">&nbsp; totem.setPreserveRatio(true);</b>
<b class="nc">&nbsp; Label usernameLabel = new Label(player.getUserName());</b>
<b class="nc">&nbsp; usernameLabel.getStyleClass().add(&quot;label-medium&quot;);</b>
<b class="nc">&nbsp; usernameLabel.setStyle(&quot;-fx-text-fill: #711423;&quot;);</b>
<b class="nc">&nbsp; if (player.getUserName().equals(controller.getMyUsername())) {</b>
<b class="nc">&nbsp; usernameLabel.setText(usernameLabel.getText() + &quot; (you)&quot;);</b>
&nbsp; }
<b class="nc">&nbsp; if (controller.getMiniModel().disconnectedPlayers.contains(player.getUserName())) {</b>
<b class="nc">&nbsp; card.getStyleClass().add(&quot;player-card-crashed&quot;);</b>
&nbsp; }
<b class="nc">&nbsp; headerRow.getChildren().addAll(totem, usernameLabel);</b>
&nbsp;
<b class="nc">&nbsp; HBox statsRow = new HBox(10);</b>
<b class="nc">&nbsp; statsRow.setAlignment(Pos.CENTER);</b>
&nbsp;
<b class="nc">&nbsp; HBox foodBox = new HBox(4);</b>
<b class="nc">&nbsp; foodBox.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; ImageView foodIcon = new ImageView(loadImage(&quot;/GUIImages/Icons/Food.png&quot;));</b>
<b class="nc">&nbsp; foodIcon.setFitHeight(20);</b>
<b class="nc">&nbsp; foodIcon.setPreserveRatio(true);</b>
<b class="nc">&nbsp; Label foodLabel = new Label(String.valueOf(player.getFoodValue()));</b>
<b class="nc">&nbsp; foodLabel.getStyleClass().add(&quot;label-small&quot;);</b>
<b class="nc">&nbsp; foodLabels.put(player.getUserName(), foodLabel);</b>
<b class="nc">&nbsp; foodBox.getChildren().addAll(foodIcon, foodLabel);</b>
&nbsp;
<b class="nc">&nbsp; HBox prestigeBox = new HBox(1);</b>
<b class="nc">&nbsp; prestigeBox.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; ImageView ppIcon = new ImageView(loadImage(&quot;/GUIImages/Icons/PrestigePoint.png&quot;));</b>
<b class="nc">&nbsp; ppIcon.setFitHeight(35);</b>
<b class="nc">&nbsp; ppIcon.setPreserveRatio(true);</b>
<b class="nc">&nbsp; Label prestigeLabel = new Label(String.valueOf(player.getPrestigeValue()));</b>
<b class="nc">&nbsp; prestigeLabel.getStyleClass().add(&quot;label-small&quot;);</b>
<b class="nc">&nbsp; prestigeLabels.put(player.getUserName(), prestigeLabel);</b>
<b class="nc">&nbsp; prestigeBox.getChildren().addAll(ppIcon, prestigeLabel);</b>
&nbsp;
<b class="nc">&nbsp; ImageView buildingIcon = new ImageView(loadImage(&quot;/GUIImages/Icons/Building.png&quot;));</b>
<b class="nc">&nbsp; buildingIcon.setFitWidth(28);</b>
<b class="nc">&nbsp; buildingIcon.setFitHeight(28);</b>
<b class="nc">&nbsp; buildingIcon.setPreserveRatio(true);</b>
<b class="nc">&nbsp; buildingIcon.setOnMouseClicked(e -&gt; {</b>
<b class="nc">&nbsp; ArrayList&lt;PlayableCard&gt; cards = getPlayerCards(player.getUserName(), &quot;building&quot;);</b>
<b class="nc">&nbsp; if (!cards.isEmpty()) openPopup(cards);</b>
&nbsp; });
<b class="nc">&nbsp; addHoverZoom(buildingIcon);</b>
&nbsp;
<b class="nc">&nbsp; statsRow.getChildren().addAll(foodBox, prestigeBox, buildingIcon);</b>
&nbsp;
<b class="nc">&nbsp; HBox iconsRow1 = new HBox(6);</b>
<b class="nc">&nbsp; iconsRow1.setSpacing(20);</b>
<b class="nc">&nbsp; iconsRow1.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; HBox iconsRow2 = new HBox(6);</b>
<b class="nc">&nbsp; iconsRow2.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; iconsRow2.setSpacing(20);</b>
&nbsp;
<b class="nc">&nbsp; String[][] iconTypes = {</b>
&nbsp; {&quot;Artist&quot;, &quot;artists&quot;},
&nbsp; {&quot;Gatherer&quot;, &quot;gatherers&quot;},
&nbsp; {&quot;Inventor&quot;, &quot;inventors&quot;},
&nbsp; {&quot;Builder&quot;, &quot;builders&quot;},
&nbsp; {&quot;Shaman&quot;, &quot;shamans&quot;},
&nbsp; {&quot;Hunter&quot;, &quot;hunters&quot;}
&nbsp; };
&nbsp;
<b class="nc">&nbsp; List&lt;ImageView&gt; icons = new ArrayList&lt;&gt;();</b>
&nbsp; // building must be index 0; updateSidePanel iterates icons in [&quot;building&quot;,&quot;artists&quot;,...] order
<b class="nc">&nbsp; icons.add(buildingIcon);</b>
&nbsp;
<b class="nc">&nbsp; for (int i = 0; i &lt; iconTypes.length; i++) {</b>
<b class="nc">&nbsp; String iconName = iconTypes[i][0];</b>
<b class="nc">&nbsp; String fieldName = iconTypes[i][1];</b>
&nbsp;
<b class="nc">&nbsp; ImageView icon = new ImageView(loadImage(&quot;/GUIImages/Icons/&quot; + iconName + &quot;.png&quot;));</b>
<b class="nc">&nbsp; icon.setFitWidth(28);</b>
<b class="nc">&nbsp; icon.setFitHeight(28);</b>
<b class="nc">&nbsp; icon.setPreserveRatio(true);</b>
<b class="nc">&nbsp; icon.setOnMouseClicked(e -&gt; {</b>
<b class="nc">&nbsp; ArrayList&lt;PlayableCard&gt; cards = getPlayerCards(player.getUserName(), fieldName);</b>
<b class="nc">&nbsp; if (!cards.isEmpty()) openPopup(cards);</b>
&nbsp; });
<b class="nc">&nbsp; addHoverZoom(icon);</b>
<b class="nc">&nbsp; icons.add(icon);</b>
&nbsp;
<b class="nc">&nbsp; if (i &lt; 3) iconsRow1.getChildren().add(icon);</b>
<b class="nc">&nbsp; else iconsRow2.getChildren().add(icon);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; iconViews.put(player.getUserName(), icons);</b>
&nbsp;
<b class="nc">&nbsp; card.getChildren().addAll(headerRow, statsRow, iconsRow1, iconsRow2);</b>
<b class="nc">&nbsp; return card;</b>
&nbsp; }
&nbsp;
&nbsp; /** Creates a {@link StackPane} containing the order card image for a game of {@code num} players. */
&nbsp; private StackPane createOrder(String num) {
<b class="nc">&nbsp; ImageView img = new ImageView(loadImage(&quot;/GUIImages/Orders/order-&quot; + num + &quot;.png&quot;));</b>
<b class="nc">&nbsp; img.fitHeightProperty().bind(board.sceneProperty().get().heightProperty().subtract(56).divide(4).multiply(0.94));</b>
<b class="nc">&nbsp; img.setPreserveRatio(true);</b>
<b class="nc">&nbsp; addClip(img);</b>
<b class="nc">&nbsp; return new StackPane(img);</b>
&nbsp; }
&nbsp;
&nbsp; /** Creates a {@link StackPane} for a board slot, optionally overlaying the occupying player&#39;s totem. */
&nbsp; private StackPane createSlot(Slot slot, boolean withZoom, boolean withShadow) {
<b class="nc">&nbsp; ImageView img = new ImageView(loadImage(&quot;/GUIImages/Fronts/card-&quot; + slot.getSlotId() + &quot;.png&quot;));</b>
<b class="nc">&nbsp; img.setPreserveRatio(true);</b>
<b class="nc">&nbsp; img.fitHeightProperty().bind(board.sceneProperty().get().heightProperty().subtract(56).divide(4).multiply(0.94));</b>
<b class="nc">&nbsp; addClip(img);</b>
<b class="nc">&nbsp; StackPane wrapper = new StackPane(img);</b>
&nbsp;
<b class="nc">&nbsp; Player player = controller.getMiniModel().slotPlayerMap.get(slot);</b>
<b class="nc">&nbsp; if (player != null &amp;&amp; controller.getMiniModel().getPositionByUsername(player.getUserName()) != -1) {</b>
<b class="nc">&nbsp; ImageView totem = new ImageView(loadImage(&quot;/GUIImages/Totems/totem_&quot; + player.getTotem().toString().toLowerCase(Locale.ROOT) + &quot;.png&quot;));</b>
<b class="nc">&nbsp; totem.fitHeightProperty().bind(img.fitHeightProperty().multiply(0.332));</b>
<b class="nc">&nbsp; totem.setPreserveRatio(true);</b>
<b class="nc">&nbsp; StackPane.setAlignment(totem, Pos.TOP_LEFT);</b>
<b class="nc">&nbsp; img.fitHeightProperty().addListener((obs, ov, nv) -&gt; {</b>
<b class="nc">&nbsp; StackPane.setMargin(totem, new Insets(0, 0, 0, 0.224 * nv.doubleValue()));</b>
&nbsp; });
<b class="nc">&nbsp; if (img.getFitHeight() &gt; 0) {</b>
<b class="nc">&nbsp; StackPane.setMargin(totem, new Insets(0, 0, 0, 0.224 * img.getFitHeight()));</b>
&nbsp; }
<b class="nc">&nbsp; wrapper.getChildren().add(totem);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; if (withShadow) addShadow(wrapper);</b>
<b class="nc">&nbsp; if (withZoom) addHoverZoom(wrapper);</b>
<b class="nc">&nbsp; return wrapper;</b>
&nbsp; }
&nbsp;
&nbsp; /** Creates a {@link StackPane} for a playable card, scaling its height to a fraction of {@code parent}&#39;s scene. */
&nbsp; private StackPane createCard(PlayableCard card, boolean withZoom, boolean withShadow, Region parent) {
<b class="nc">&nbsp; ImageView img = new ImageView(loadImage(&quot;/GUIImages/Fronts/card-&quot; + card.getIdIMG() + &quot;.png&quot;));</b>
<b class="nc">&nbsp; img.setPreserveRatio(true);</b>
<b class="nc">&nbsp; if(controller.getMiniModel().players.size()==5)</b>
&nbsp; {
<b class="nc">&nbsp; img.fitHeightProperty().bind(parent.getScene().heightProperty().subtract(56).divide(4).multiply(0.85));</b>
&nbsp; }
&nbsp; else
<b class="nc">&nbsp; img.fitHeightProperty().bind(parent.getScene().heightProperty().subtract(56).divide(4).multiply(0.90));</b>
<b class="nc">&nbsp; addClip(img);</b>
<b class="nc">&nbsp; StackPane wrapper = new StackPane(img);</b>
<b class="nc">&nbsp; if (withShadow) addShadow(wrapper);</b>
<b class="nc">&nbsp; if (withZoom) addHoverZoom(wrapper);</b>
<b class="nc">&nbsp; return wrapper;</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp;
&nbsp; // ==== GROUPS ====
&nbsp; /** Builds the full side panel from scratch, creating a player card for each connected player. */
&nbsp; private void buildSidePanel() {
<b class="nc">&nbsp; infoText.setText(&quot;Round: &quot;+Integer.toString(controller.getMiniModel().currentState.getRound()) + &quot;&quot; + controller.getMiniModel().currentState.getGameStage().toString());</b>
<b class="nc">&nbsp; VBox playerList = new VBox();</b>
<b class="nc">&nbsp; playerList.setFillWidth(true);</b>
<b class="nc">&nbsp; for (Player p : controller.getMiniModel().players.values()) {</b>
<b class="nc">&nbsp; VBox card = buildPlayerCard(p);</b>
<b class="nc">&nbsp; playerCards.put(p.getUserName(), card);</b>
<b class="nc">&nbsp; playerList.getChildren().add(card);</b>
&nbsp; }
<b class="nc">&nbsp; playerSide.setContent(playerList);</b>
&nbsp; }
&nbsp;
&nbsp; /** Updates food/prestige labels, current-player highlight, card-icon opacity, and plays error shake if needed. */
&nbsp; private void updateSidePanel() {
<b class="nc">&nbsp; infoText.setText(&quot;Round: &quot;+Integer.toString(controller.getMiniModel().currentState.getRound()) + &quot;&quot; + controller.getMiniModel().currentState.getGameStage().toString());</b>
<b class="nc">&nbsp; if (controller.getMiniModel().currentState.getCurrentPlayer() == null) return;</b>
<b class="nc">&nbsp; String current = controller.getMiniModel().currentState.getCurrentPlayer().getUserName();</b>
&nbsp; // ordine: building, artists, gatherers, inventors, builders, shamans, hunters
<b class="nc">&nbsp; String[] fields = {&quot;building&quot;, &quot;artists&quot;, &quot;gatherers&quot;, &quot;inventors&quot;, &quot;builders&quot;, &quot;shamans&quot;, &quot;hunters&quot;};</b>
&nbsp;
<b class="nc">&nbsp; for (Player p : controller.getMiniModel().players.values()) {</b>
<b class="nc">&nbsp; String u = p.getUserName();</b>
&nbsp;
<b class="nc">&nbsp; foodLabels.get(u).setText(String.valueOf(p.getFoodValue()));</b>
<b class="nc">&nbsp; prestigeLabels.get(u).setText(String.valueOf(p.getPrestigeValue()));</b>
&nbsp;
<b class="nc">&nbsp; List&lt;ImageView&gt; icons = iconViews.get(u);</b>
<b class="nc">&nbsp; for (int i = 0; i &lt; icons.size(); i++) {</b>
<b class="nc">&nbsp; boolean empty = getPlayerCards(u, fields[i]).isEmpty();</b>
<b class="nc">&nbsp; icons.get(i).setOpacity(empty ? 0.3 : 1.0);</b>
<b class="nc">&nbsp; icons.get(i).setEffect(empty ? new ColorAdjust() : null);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; VBox card = playerCards.get(u);</b>
<b class="nc">&nbsp; if(controller.getMiniModel().disconnectedPlayers.contains(u))</b>
&nbsp; {
<b class="nc">&nbsp; card.getStyleClass().clear();</b>
<b class="nc">&nbsp; card.getStyleClass().add(&quot;player-card-crashed&quot;);</b>
&nbsp; }
&nbsp; else {
<b class="nc">&nbsp; card.getStyleClass().clear();</b>
<b class="nc">&nbsp; card.getStyleClass().add(&quot;player-card&quot;);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; if (u.equals(current)) {</b>
<b class="nc">&nbsp; card.setStyle(&quot;-fx-effect: dropshadow(gaussian, #fff8dc, 15, 0.10, 0, 0)&quot;);</b>
<b class="nc">&nbsp; ScaleTransition st = new ScaleTransition(Duration.millis(150), card);</b>
<b class="nc">&nbsp; st.setToX(1.1);</b>
<b class="nc">&nbsp; st.setToY(1.1);</b>
<b class="nc">&nbsp; st.play();</b>
&nbsp; } else {
<b class="nc">&nbsp; ScaleTransition st = new ScaleTransition(Duration.millis(150), card);</b>
<b class="nc">&nbsp; st.setToX(1.0);</b>
<b class="nc">&nbsp; st.setToY(1.0);</b>
<b class="nc">&nbsp; st.play();</b>
<b class="nc">&nbsp; card.setStyle(&quot;&quot;);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; if (isError &amp;&amp; u.equals(controller.getMyUsername())) {</b>
<b class="nc">&nbsp; TranslateTransition tt = new TranslateTransition(Duration.millis(56), card);</b>
<b class="nc">&nbsp; tt.setFromX(0);</b>
<b class="nc">&nbsp; tt.setByX(10);</b>
<b class="nc">&nbsp; tt.setCycleCount(6);</b>
<b class="nc">&nbsp; tt.setAutoReverse(true);</b>
<b class="nc">&nbsp; tt.play();</b>
&nbsp; }
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /** Renders the top card of the upper building stack with a count badge; clicking opens the selection popup. */
&nbsp; private void drawUpperBuilding() {
<b class="nc">&nbsp; ArrayList&lt;BuildingCard&gt; cardList = new ArrayList&lt;&gt;(controller.getMiniModel().upperListBuildingCards);</b>
<b class="nc">&nbsp; if (!cardList.isEmpty()) {</b>
<b class="nc">&nbsp; StackPane img = createCard(cardList.getLast(), true, true, upperList);</b>
<b class="nc">&nbsp; Label label = new Label(String.valueOf(cardList.size()));</b>
<b class="nc">&nbsp; label.setTranslateY(-56);</b>
<b class="nc">&nbsp; label.setTranslateX(10);</b>
<b class="nc">&nbsp; label.getStyleClass().add(&quot;label-large&quot;);</b>
<b class="nc">&nbsp; StackPane.setAlignment(label, Pos.TOP_RIGHT);</b>
<b class="nc">&nbsp; img.getChildren().add(label);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; openPopupUpperBuilding(cardList));</b>
<b class="nc">&nbsp; upperList.getChildren().add(img);</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /** Renders the top card of the lower building stack with a count badge; clicking opens the selection popup. */
&nbsp; private void drawLowerBuilding() {
<b class="nc">&nbsp; ArrayList&lt;BuildingCard&gt; cardList = new ArrayList&lt;&gt;(controller.getMiniModel().lowerListBuildingCards);</b>
<b class="nc">&nbsp; if (!cardList.isEmpty()) {</b>
<b class="nc">&nbsp; StackPane img = createCard(cardList.getLast(), true, true, lowerList);</b>
<b class="nc">&nbsp; Label label = new Label(String.valueOf(cardList.size()));</b>
<b class="nc">&nbsp; label.setTranslateY(-56);</b>
<b class="nc">&nbsp; label.setTranslateX(10);</b>
<b class="nc">&nbsp; label.getStyleClass().add(&quot;label-large&quot;);</b>
<b class="nc">&nbsp; StackPane.setAlignment(label, Pos.TOP_RIGHT);</b>
<b class="nc">&nbsp; img.getChildren().add(label);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; openPopupLowerBuilding(cardList));</b>
<b class="nc">&nbsp; lowerList.getChildren().add(img);</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /** Renders a single card-type pile in the hand area, or an invisible placeholder if the list is empty. */
&nbsp; private void drawMyHandList(ArrayList&lt;? extends PlayableCard&gt; cardList) {
<b class="nc">&nbsp; if (!cardList.isEmpty()) {</b>
<b class="nc">&nbsp; StackPane img = createCard(cardList.getLast(), true, true, myHand);</b>
<b class="nc">&nbsp; Label label = new Label(String.valueOf(cardList.size()));</b>
<b class="nc">&nbsp; label.setTranslateY(-56);</b>
<b class="nc">&nbsp; label.setTranslateX(10);</b>
<b class="nc">&nbsp; label.getStyleClass().add(&quot;label-large&quot;);</b>
<b class="nc">&nbsp; StackPane.setAlignment(label, Pos.TOP_RIGHT);</b>
<b class="nc">&nbsp; img.getChildren().add(label);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; openPopup(new ArrayList&lt;&gt;(cardList)));</b>
<b class="nc">&nbsp; myHand.getChildren().add(img);</b>
&nbsp; } else {
<b class="nc">&nbsp; Region placeholder = new Region();</b>
<b class="nc">&nbsp; placeholder.prefHeightProperty().bind(myHand.getScene().heightProperty().subtract(56).divide(4).multiply(0.90));</b>
<b class="nc">&nbsp; placeholder.prefWidthProperty().bind(placeholder.prefHeightProperty().multiply(0.675));</b>
<b class="nc">&nbsp; placeholder.setStyle(&quot;-fx-background-color: transparent;&quot;);</b>
<b class="nc">&nbsp; myHand.getChildren().add(placeholder);</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /** Returns a copy of the named card collection for {@code username}. */
&nbsp; private ArrayList&lt;PlayableCard&gt; getPlayerCards(String username, String type) {
<b class="nc">&nbsp; Player p = controller.getMiniModel().players.get(username);</b>
<b class="nc">&nbsp; return switch (type) {</b>
<b class="nc">&nbsp; case &quot;artists&quot; -&gt; new ArrayList&lt;&gt;(p.getArtists());</b>
<b class="nc">&nbsp; case &quot;gatherers&quot; -&gt; new ArrayList&lt;&gt;(p.getGatherers());</b>
<b class="nc">&nbsp; case &quot;inventors&quot; -&gt; new ArrayList&lt;&gt;(p.getInventors());</b>
<b class="nc">&nbsp; case &quot;builders&quot; -&gt; new ArrayList&lt;&gt;(p.getBuilders());</b>
<b class="nc">&nbsp; case &quot;shamans&quot; -&gt; new ArrayList&lt;&gt;(p.getShamans());</b>
<b class="nc">&nbsp; case &quot;hunters&quot; -&gt; new ArrayList&lt;&gt;(p.getHunters());</b>
<b class="nc">&nbsp; case &quot;building&quot; -&gt; new ArrayList&lt;&gt;(p.getBuildingCards());</b>
<b class="nc">&nbsp; default -&gt; new ArrayList&lt;&gt;();</b>
&nbsp; };
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== ABSOLUTES ====
&nbsp; /** Clears and rebuilds the upper tribe row with click handlers for drawing, then appends the upper building stack. */
&nbsp; private void renderUpper() {
<b class="nc">&nbsp; upperList.setSpacing(14);</b>
<b class="nc">&nbsp; upperList.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; upperList.getChildren().clear();</b>
&nbsp;
<b class="nc">&nbsp; int i = 0;</b>
<b class="nc">&nbsp; for (TribeCard card : controller.getMiniModel().upperListTribeCards) {</b>
<b class="nc">&nbsp; final int index = i;</b>
<b class="nc">&nbsp; StackPane img = createCard(card, true, true, upperList);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; controller.drawUpperTribeCard(index));</b>
<b class="nc">&nbsp; upperList.getChildren().add(img);</b>
<b class="nc">&nbsp; i++;</b>
&nbsp; }
<b class="nc">&nbsp; drawUpperBuilding();</b>
&nbsp; }
&nbsp;
&nbsp; /** Clears and rebuilds the lower tribe row with click handlers for drawing, then appends the lower building stack. */
&nbsp; private void renderLower() {
<b class="nc">&nbsp; lowerList.setSpacing(14);</b>
<b class="nc">&nbsp; lowerList.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; lowerList.getChildren().clear();</b>
&nbsp;
<b class="nc">&nbsp; int i = 0;</b>
<b class="nc">&nbsp; for (TribeCard card : controller.getMiniModel().lowerListTribeCards) {</b>
<b class="nc">&nbsp; final int index = i;</b>
<b class="nc">&nbsp; StackPane img = createCard(card, true, true, lowerList);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; controller.drawLowerTribeCard(index));</b>
<b class="nc">&nbsp; lowerList.getChildren().add(img);</b>
<b class="nc">&nbsp; i++;</b>
&nbsp; }
<b class="nc">&nbsp; drawLowerBuilding();</b>
&nbsp; }
&nbsp;
&nbsp; /** Sets the full-cover background image on the main HBox container. */
&nbsp; private void renderBackground() {
<b class="nc">&nbsp; BackgroundSize size = new BackgroundSize(</b>
&nbsp; BackgroundSize.AUTO, BackgroundSize.AUTO,
&nbsp; false, false, true, true
&nbsp; );
<b class="nc">&nbsp; mainHBox.setBackground(new Background(new BackgroundImage(</b>
<b class="nc">&nbsp; loadImage(&quot;/GUIImages/Background.png&quot;),</b>
&nbsp; BackgroundRepeat.NO_REPEAT,
&nbsp; BackgroundRepeat.NO_REPEAT,
&nbsp; BackgroundPosition.CENTER,
&nbsp; size
&nbsp; )));
&nbsp; }
&nbsp;
&nbsp; /** Clears the board area and orchestrates deck, order card, and slot-map rendering. */
&nbsp; private void renderBoard() {
<b class="nc">&nbsp; board.setSpacing(14);</b>
<b class="nc">&nbsp; board.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; board.getChildren().clear();</b>
<b class="nc">&nbsp; renderDeck();</b>
<b class="nc">&nbsp; renderOrder();</b>
<b class="nc">&nbsp; renderSlotMap();</b>
&nbsp; }
&nbsp;
&nbsp; /** Adds the era-specific deck back image to the board. */
&nbsp; private void renderDeck() {
<b class="nc">&nbsp; String path = switch (controller.getMiniModel().currentState.getEra()) {</b>
<b class="nc">&nbsp; case 1 -&gt; &quot;/GUIImages/Backs/back-001.png&quot;;</b>
<b class="nc">&nbsp; case 2 -&gt; &quot;/GUIImages/Backs/back-030.png&quot;;</b>
<b class="nc">&nbsp; case 3 -&gt; &quot;/GUIImages/Backs/back-058.png&quot;;</b>
<b class="nc">&nbsp; default -&gt; &quot;/GUIImages/Backs/back-001.png&quot;;</b>
<b class="nc">&nbsp; };</b>
<b class="nc">&nbsp; ImageView back = new ImageView(loadImage(path));</b>
<b class="nc">&nbsp; back.fitHeightProperty().bind(board.sceneProperty().get().heightProperty().subtract(56).divide(4).multiply(0.94));</b>
<b class="nc">&nbsp; back.setPreserveRatio(true);</b>
<b class="nc">&nbsp; addClip(back);</b>
<b class="nc">&nbsp; addShadow(back);</b>
<b class="nc">&nbsp; addHoverZoom(back);</b>
<b class="nc">&nbsp; board.getChildren().add(back);</b>
&nbsp; }
&nbsp;
&nbsp; /** Renders the order card and overlays each player&#39;s totem at their proportional position. */
&nbsp; private void renderOrder() {
<b class="nc">&nbsp; int numPlayers = controller.getMiniModel().players.size();</b>
<b class="nc">&nbsp; StackPane card = createOrder(Integer.toString(numPlayers));</b>
&nbsp;
<b class="nc">&nbsp; Map&lt;Integer, double[]&gt; slotPositions = Map.of(</b>
<b class="nc">&nbsp; 2, new double[]{0.229, 0.413},</b>
<b class="nc">&nbsp; 3, new double[]{0.183, 0.367, 0.548},</b>
<b class="nc">&nbsp; 4, new double[]{0.142, 0.316, 0.503, 0.690},</b>
<b class="nc">&nbsp; 5, new double[]{0.066, 0.251, 0.433, 0.617, 0.802}</b>
&nbsp; );
<b class="nc">&nbsp; double[] positions = slotPositions.get(numPlayers);</b>
<b class="nc">&nbsp; if (positions == null) return;</b>
&nbsp;
<b class="nc">&nbsp; Pane overlay = new Pane();</b>
<b class="nc">&nbsp; overlay.setPickOnBounds(false);</b>
<b class="nc">&nbsp; overlay.setMouseTransparent(true);</b>
&nbsp;
<b class="nc">&nbsp; for (int i = 0; i &lt; controller.getMiniModel().orderLogicCard.getPlayerList().size(); i++) {</b>
<b class="nc">&nbsp; OrderPlayer op = controller.getMiniModel().orderLogicCard.getPlayerList().get(i);</b>
&nbsp;
<b class="nc">&nbsp; ImageView totem = new ImageView(loadImage(</b>
<b class="nc">&nbsp; &quot;/GUIImages/Totems/totem_&quot; + op.getPlayer().getTotem().toString().toLowerCase(Locale.ROOT) + &quot;.png&quot;</b>
&nbsp; ));
<b class="nc">&nbsp; totem.setPreserveRatio(true);</b>
&nbsp;
<b class="nc">&nbsp; if (op.isPlayed()) {</b>
<b class="nc">&nbsp; totem.setVisible(false);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; final double yRatio = positions[i];</b>
&nbsp;
<b class="nc">&nbsp; overlay.prefHeightProperty().bind(card.heightProperty());</b>
<b class="nc">&nbsp; overlay.prefWidthProperty().bind(card.widthProperty());</b>
&nbsp;
<b class="nc">&nbsp; totem.fitHeightProperty().bind(card.heightProperty().multiply(0.323));</b>
&nbsp;
<b class="nc">&nbsp; card.heightProperty().addListener((obs, ov, nv) -&gt; {</b>
<b class="nc">&nbsp; totem.setLayoutY(nv.doubleValue() * (yRatio-0.196));</b>
&nbsp; });
<b class="nc">&nbsp; card.widthProperty().addListener((obs, ov, nv) -&gt; {</b>
<b class="nc">&nbsp; totem.setLayoutX(nv.doubleValue() * 0.364);</b>
&nbsp; });
&nbsp;
&nbsp; // initialize immediately if already laid out
<b class="nc">&nbsp; if (card.getHeight() &gt; 0) totem.setLayoutY(card.getHeight() *(yRatio-0.196));</b>
<b class="nc">&nbsp; if (card.getWidth() &gt; 0) totem.setLayoutX(card.getWidth() * 0.364);</b>
&nbsp;
<b class="nc">&nbsp; overlay.getChildren().add(totem);</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; card.getChildren().add(overlay);</b>
<b class="nc">&nbsp; board.getChildren().add(card);</b>
&nbsp; }
&nbsp;
&nbsp; /** Adds a clickable slot widget for each entry in the slot-player map. */
&nbsp; private void renderSlotMap() {
<b class="nc">&nbsp; int i = 0;</b>
<b class="nc">&nbsp; for (Map.Entry&lt;Slot, Player&gt; entry : controller.getMiniModel().slotPlayerMap.entrySet()) {</b>
<b class="nc">&nbsp; Slot slot = entry.getKey();</b>
<b class="nc">&nbsp; final int index = i;</b>
<b class="nc">&nbsp; StackPane img = createSlot(slot, true, true);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; controller.slotChoice(index));</b>
<b class="nc">&nbsp; board.getChildren().add(img);</b>
<b class="nc">&nbsp; i++;</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /** Clears and re-renders all seven card-type piles in the local player&#39;s hand area. */
&nbsp; private void renderMyHand() {
<b class="nc">&nbsp; myHand.getChildren().clear();</b>
<b class="nc">&nbsp; myHand.setSpacing(14);</b>
<b class="nc">&nbsp; myHand.setAlignment(Pos.CENTER);</b>
&nbsp;
<b class="nc">&nbsp; Player me = controller.getMiniModel().players.get(controller.getMyUsername());</b>
<b class="nc">&nbsp; drawMyHandList(me.getArtists());</b>
<b class="nc">&nbsp; drawMyHandList(me.getGatherers());</b>
<b class="nc">&nbsp; drawMyHandList(me.getInventors());</b>
<b class="nc">&nbsp; drawMyHandList(me.getBuilders());</b>
<b class="nc">&nbsp; drawMyHandList(me.getShamans());</b>
<b class="nc">&nbsp; drawMyHandList(me.getHunters());</b>
<b class="nc">&nbsp; drawMyHandList(me.getBuildingCards());</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; // ==== POPUP ====
&nbsp; /** Creates and configures the shared card-preview popup container. */
&nbsp; private void initPopup() {
<b class="nc">&nbsp; popupCards = new HBox(10);</b>
<b class="nc">&nbsp; popupCards.setAlignment(Pos.CENTER);</b>
<b class="nc">&nbsp; popupCards.setPadding(new Insets(10));</b>
<b class="nc">&nbsp; popupCards.setStyle(</b>
&nbsp; &quot;-fx-background-color: #b42224;&quot; +
&nbsp; &quot;-fx-border-color: black;&quot; +
&nbsp; &quot;-fx-border-width: 2;&quot; +
&nbsp; &quot;-fx-border-radius: 10;&quot; +
&nbsp; &quot;-fx-background-radius: 10;&quot;
&nbsp; );
&nbsp;
<b class="nc">&nbsp; popup.getContent().add(popupCards);</b>
<b class="nc">&nbsp; popup.setAutoHide(false);</b>
<b class="nc">&nbsp; popup.addEventHandler(Event.ANY, e -&gt; {</b>
<b class="nc">&nbsp; if (popup.getScene() != null) {</b>
<b class="nc">&nbsp; popup.getScene().setFill(Color.TRANSPARENT);</b>
&nbsp; }
&nbsp; });
&nbsp; }
&nbsp;
&nbsp; /** Centers the popup over the current window and makes it visible. */
&nbsp; private void showPopup() {
<b class="nc">&nbsp; Window window = myHand.getScene().getWindow();</b>
<b class="nc">&nbsp; popup.show(window, 0, 0);</b>
<b class="nc">&nbsp; popup.getScene().setFill(Color.TRANSPARENT);</b>
<b class="nc">&nbsp; popup.setX(window.getX() + (window.getWidth() - popup.getWidth()) / 2);</b>
<b class="nc">&nbsp; popup.setY(window.getY() + (window.getHeight() - popup.getHeight()) / 2);</b>
&nbsp; }
&nbsp;
&nbsp; /** Populates the popup with non-clickable card images and shows it. */
&nbsp; private void openPopup(ArrayList&lt;? extends PlayableCard&gt; cardList) {
<b class="nc">&nbsp; popupCards.getChildren().clear();</b>
<b class="nc">&nbsp; for (PlayableCard card : cardList) {</b>
<b class="nc">&nbsp; popupCards.getChildren().add(createCardPopup(card, false, false));</b>
&nbsp; }
<b class="nc">&nbsp; showPopup();</b>
&nbsp; }
&nbsp;
&nbsp; /** Populates the popup with clickable upper-building cards; clicking one draws it via the controller. */
&nbsp; private void openPopupUpperBuilding(ArrayList&lt;BuildingCard&gt; cardList) {
<b class="nc">&nbsp; popupCards.getChildren().clear();</b>
<b class="nc">&nbsp; for (int i = 0; i &lt; cardList.size(); i++) {</b>
<b class="nc">&nbsp; final int index = i;</b>
<b class="nc">&nbsp; StackPane img = createCardPopup(cardList.get(i), true, false);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; {</b>
<b class="nc">&nbsp; popup.hide();</b>
<b class="nc">&nbsp; controller.drawUpperBuildingCard(index);</b>
&nbsp; });
<b class="nc">&nbsp; popupCards.getChildren().add(img);</b>
&nbsp; }
<b class="nc">&nbsp; showPopup();</b>
&nbsp; }
&nbsp;
&nbsp; /** Populates the popup with clickable lower-building cards; clicking one draws it via the controller. */
&nbsp; private void openPopupLowerBuilding(ArrayList&lt;BuildingCard&gt; cardList) {
<b class="nc">&nbsp; popupCards.getChildren().clear();</b>
<b class="nc">&nbsp; for (int i = 0; i &lt; cardList.size(); i++) {</b>
<b class="nc">&nbsp; final int index = i;</b>
<b class="nc">&nbsp; StackPane img = createCardPopup(cardList.get(i), true, false);</b>
<b class="nc">&nbsp; img.setOnMouseClicked(e -&gt; {</b>
<b class="nc">&nbsp; popup.hide();</b>
<b class="nc">&nbsp; controller.drawLowerBuildingCard(index);</b>
&nbsp; });
<b class="nc">&nbsp; popupCards.getChildren().add(img);</b>
&nbsp; }
<b class="nc">&nbsp; showPopup();</b>
&nbsp; }
&nbsp;
&nbsp; /** Shows the deck details/rules card in the popup. */
&nbsp; private void openDetailsPopup() {
<b class="nc">&nbsp; popupCards.getChildren().clear();</b>
<b class="nc">&nbsp; ImageView img = new ImageView(loadImage(&quot;/GUIImages/Backs/back-118.png&quot;));</b>
<b class="nc">&nbsp; img.setFitHeight(290);</b>
<b class="nc">&nbsp; img.setPreserveRatio(true);</b>
<b class="nc">&nbsp; addClip(img);</b>
<b class="nc">&nbsp; StackPane wrapper = new StackPane(img);</b>
<b class="nc">&nbsp; addShadow(wrapper);</b>
<b class="nc">&nbsp; popupCards.getChildren().add(wrapper);</b>
<b class="nc">&nbsp; showPopup();</b>
&nbsp; }
&nbsp;
&nbsp; /** Creates a fixed-height {@link StackPane} card widget suitable for use inside the popup. */
&nbsp; private StackPane createCardPopup(PlayableCard card, boolean withZoom, boolean withShadow) {
<b class="nc">&nbsp; ImageView img = new ImageView(loadImage(&quot;/GUIImages/Fronts/card-&quot; + card.getIdIMG() + &quot;.png&quot;));</b>
<b class="nc">&nbsp; img.setFitHeight(200);</b>
<b class="nc">&nbsp; img.setPreserveRatio(true);</b>
<b class="nc">&nbsp; addClip(img);</b>
<b class="nc">&nbsp; StackPane wrapper = new StackPane(img);</b>
<b class="nc">&nbsp; if (withShadow) addShadow(wrapper);</b>
<b class="nc">&nbsp; if (withZoom) addHoverZoom(wrapper);</b>
<b class="nc">&nbsp; return wrapper;</b>
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>
@@ -0,0 +1,345 @@
<!DOCTYPE html>
<html id="htmlId">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Coverage Report > TotemFXMLController</title>
<style type="text/css">
@import "../../css/coverage.css";
@import "../../css/idea.min.css";
</style>
<script type="text/javascript" src="../../js/highlight.min.js"></script>
<script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>
<body>
<div class="content">
<div class="breadCrumbs">
Current scope: <a href="../../index.html">all classes</a>
<span class="separator">|</span>
<a href="../index.html">it.polimi.ingsw.gc14.View.GUI</a>
</div>
<h1>Coverage Summary for Class: TotemFXMLController (it.polimi.ingsw.gc14.View.GUI)</h1>
<table class="coverageStats">
<tr>
<th class="name">Class</th>
<th class="coverageStat
">
Class, %
</th>
<th class="coverageStat
">
Method, %
</th>
<th class="coverageStat
">
Branch, %
</th>
<th class="coverageStat
">
Line, %
</th>
</tr>
<tr>
<td class="name">TotemFXMLController</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/1)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/16)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/24)
</span>
</td>
<td class="coverageStat">
<span class="percent">
0%
</span>
<span class="absValue">
(0/90)
</span>
</td>
</tr>
</table>
<br/>
<br/>
<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package it.polimi.ingsw.gc14.View.GUI;
&nbsp;
&nbsp;import it.polimi.ingsw.gc14.Controller.ClientController;
&nbsp;import it.polimi.ingsw.gc14.Model.Totems;
&nbsp;import javafx.animation.*;
&nbsp;import javafx.fxml.FXML;
&nbsp;import javafx.geometry.Pos;
&nbsp;import javafx.geometry.Rectangle2D;
&nbsp;import javafx.scene.Cursor;
&nbsp;import javafx.scene.control.Button;
&nbsp;import javafx.scene.control.Label;
&nbsp;import javafx.scene.image.Image;
&nbsp;import javafx.scene.image.ImageView;
&nbsp;import javafx.scene.layout.*;
&nbsp;import javafx.stage.Screen;
&nbsp;import javafx.util.Duration;
&nbsp;
&nbsp;import java.util.Locale;
&nbsp;
&nbsp;/**
&nbsp; * FXML controller for the totem selection scene.
&nbsp; *
&nbsp; * &lt;p&gt;Displays the available totems and lets the current player choose one;
&nbsp; * other players see a waiting banner.
&nbsp; */
<b class="nc">&nbsp;public class TotemFXMLController {</b>
&nbsp; //TODO
&nbsp; @FXML private ImageView backgroundImage;
&nbsp; //TODO
&nbsp; @FXML private HBox mainHBox;
&nbsp; //TODO
&nbsp; @FXML private Label turnBanner;
&nbsp; //TODO
&nbsp; @FXML private Button confirmButton;
&nbsp;
&nbsp; //TODO
&nbsp; private ClientController controller;
&nbsp; //TODO
<b class="nc">&nbsp; private int selectedIndex = -1;</b>
&nbsp; //TODO
<b class="nc">&nbsp; private StackPane selectedFrame = null;</b>
&nbsp;
&nbsp; /**
&nbsp; * Injects the client controller into this FXML controller.
&nbsp; *
&nbsp; * @param controller the client controller to use.
&nbsp; */
&nbsp; public void setController(ClientController controller) {
<b class="nc">&nbsp; this.controller = controller;</b>
&nbsp; }
&nbsp;
&nbsp; /** Initializes the scene: sets the background image to fill the screen. */
&nbsp; @FXML
&nbsp; public void initialize() {
<b class="nc">&nbsp; Image img = new Image(getClass().getResourceAsStream(&quot;/GUIImages/Background.png&quot;));</b>
<b class="nc">&nbsp; backgroundImage.setImage(img);</b>
<b class="nc">&nbsp; Rectangle2D screenBounds = Screen.getPrimary().getBounds();</b>
<b class="nc">&nbsp; backgroundImage.setFitWidth(screenBounds.getWidth());</b>
<b class="nc">&nbsp; backgroundImage.setFitHeight(screenBounds.getHeight());</b>
&nbsp; }
&nbsp;
&nbsp; /**
&nbsp; * Renders the totem selection cards with entry animations.
&nbsp; * Disables interaction for players who are not the current chooser.
&nbsp; */
&nbsp; public void render() {
<b class="nc">&nbsp; mainHBox.getChildren().clear();</b>
<b class="nc">&nbsp; selectedIndex = -1;</b>
<b class="nc">&nbsp; selectedFrame = null;</b>
&nbsp;
<b class="nc">&nbsp; if (controller.getMiniModel().currentState.getCurrentPlayer() == null) return;</b>
<b class="nc">&nbsp; String chooser = controller.getMiniModel().currentState.getCurrentPlayer().getUserName();</b>
<b class="nc">&nbsp; boolean myTurn = chooser.equals(controller.getMyUsername());</b>
&nbsp;
<b class="nc">&nbsp; updateBanner(chooser, myTurn);</b>
<b class="nc">&nbsp; updateConfirmButton(false);</b>
&nbsp;
<b class="nc">&nbsp; for (int i = 0; i &lt; controller.getMiniModel().availableTotems.size(); i++) {</b>
<b class="nc">&nbsp; Totems totem = controller.getMiniModel().availableTotems.get(i);</b>
<b class="nc">&nbsp; VBox card = buildCard(totem, i, myTurn);</b>
<b class="nc">&nbsp; mainHBox.getChildren().add(card);</b>
&nbsp;
<b class="nc">&nbsp; card.setOpacity(0);</b>
<b class="nc">&nbsp; card.setTranslateY(14);</b>
<b class="nc">&nbsp; PauseTransition delay = new PauseTransition(Duration.millis(80 + i * 65));</b>
<b class="nc">&nbsp; delay.setOnFinished(e -&gt; {</b>
<b class="nc">&nbsp; FadeTransition ft = new FadeTransition(Duration.millis(320), card);</b>
<b class="nc">&nbsp; ft.setToValue(1);</b>
<b class="nc">&nbsp; TranslateTransition tt = new TranslateTransition(Duration.millis(320), card);</b>
<b class="nc">&nbsp; tt.setToY(0);</b>
<b class="nc">&nbsp; new ParallelTransition(ft, tt).play();</b>
&nbsp; });
<b class="nc">&nbsp; delay.play();</b>
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /** Updates the turn banner text and style based on whether it is the local player&#39;s turn. */
&nbsp; private void updateBanner(String chooser, boolean myTurn) {
<b class="nc">&nbsp; if (myTurn) {</b>
<b class="nc">&nbsp; turnBanner.setText(&quot;✦ It&#39;s your turn to choose ✦&quot;);</b>
<b class="nc">&nbsp; turnBanner.setStyle(</b>
&nbsp; &quot;-fx-font-family: &#39;Cinzel&#39;; -fx-font-size: 13; -fx-font-weight: &#39;bold&#39;; -fx-letter-spacing: 3;&quot; +
&nbsp; &quot;-fx-text-fill: #000000;&quot; +
&nbsp; &quot;-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);&quot; +
&nbsp; &quot;-fx-border-color: #000000; -fx-border-width: 1;&quot; +
&nbsp; &quot;-fx-border-radius: 3; -fx-background-radius: 3;&quot; +
&nbsp; &quot;-fx-padding: 10 28 10 28;&quot; +
&nbsp; &quot;-fx-effect: dropshadow(gaussian, rgba(244,192,90,0.3), 18, 0, 0, 0);&quot;
&nbsp; );
&nbsp; } else {
<b class="nc">&nbsp; turnBanner.setText(&quot;Waiting for &quot; + chooser + &quot; to choose his totem…&quot;);</b>
<b class="nc">&nbsp; turnBanner.setStyle(</b>
&nbsp; &quot;-fx-font-family: &#39;Cinzel&#39;; -fx-font-size: 11; -fx-letter-spacing: 2;&quot; +
&nbsp; &quot;-fx-text-fill: #000000;&quot; +
&nbsp; &quot;-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);&quot; +
&nbsp; &quot;-fx-border-color: #000000; -fx-border-width: 1;&quot; +
&nbsp; &quot;-fx-border-radius: 3; -fx-background-radius: 3;&quot; +
&nbsp; &quot;-fx-padding: 10 28 10 28;&quot;
&nbsp; );
&nbsp; }
&nbsp; }
&nbsp;
&nbsp; /** Builds a totem card widget; if {@code interactive}, wires click/hover handlers and selection logic. */
&nbsp; private VBox buildCard(Totems totem, int idx, boolean interactive) {
<b class="nc">&nbsp; ImageView img = new ImageView(new Image(getClass().getResourceAsStream(&quot;/GUIImages/Totems/totem_&quot; + String.valueOf(totem).toLowerCase(Locale.ROOT) + &quot;.png&quot;)));</b>
<b class="nc">&nbsp; img.setFitHeight(150);</b>
<b class="nc">&nbsp; img.setPreserveRatio(true);</b>
&nbsp;
<b class="nc">&nbsp; StackPane frame = new StackPane(img);</b>
<b class="nc">&nbsp; frame.setPrefSize(100, 145);</b>
<b class="nc">&nbsp; frame.setBackground(Background.EMPTY);</b>
<b class="nc">&nbsp; img.setStyle(frameStyle(false));</b>
&nbsp;
<b class="nc">&nbsp; Region base = new Region();</b>
<b class="nc">&nbsp; base.setPrefSize(100, 10);</b>
&nbsp;
<b class="nc">&nbsp; Label name = new Label(capitalize(totem));</b>
<b class="nc">&nbsp; name.setStyle(nameStyle(false));</b>
&nbsp;
<b class="nc">&nbsp; VBox card = new VBox(4, frame, base, name);</b>
<b class="nc">&nbsp; card.setAlignment(Pos.CENTER);</b>
&nbsp;
<b class="nc">&nbsp; if (!interactive) {</b>
<b class="nc">&nbsp; card.setOpacity(0.35);</b>
<b class="nc">&nbsp; card.setCursor(Cursor.DEFAULT);</b>
<b class="nc">&nbsp; return card;</b>
&nbsp; }
&nbsp;
<b class="nc">&nbsp; card.setCursor(Cursor.HAND);</b>
&nbsp;
<b class="nc">&nbsp; card.setOnMouseEntered(e -&gt; {</b>
<b class="nc">&nbsp; if (frame != selectedFrame) {</b>
<b class="nc">&nbsp; ScaleTransition st = new ScaleTransition(Duration.millis(140), card);</b>
<b class="nc">&nbsp; st.setToX(1.06); st.setToY(1.06); st.play();</b>
&nbsp; }
&nbsp; });
<b class="nc">&nbsp; card.setOnMouseExited(e -&gt; {</b>
<b class="nc">&nbsp; if (frame != selectedFrame) {</b>
<b class="nc">&nbsp; ScaleTransition st = new ScaleTransition(Duration.millis(140), card);</b>
<b class="nc">&nbsp; st.setToX(1.0); st.setToY(1.0); st.play();</b>
&nbsp; }
&nbsp; });
&nbsp;
<b class="nc">&nbsp; card.setOnMouseClicked(e -&gt; {</b>
<b class="nc">&nbsp; mainHBox.getChildren().forEach(n -&gt; {</b>
<b class="nc">&nbsp; if (n instanceof VBox v) {</b>
<b class="nc">&nbsp; StackPane f = (StackPane) v.getChildren().get(0);</b>
<b class="nc">&nbsp; Label l = (Label) v.getChildren().get(2);</b>
<b class="nc">&nbsp; f.setStyle(frameStyle(false));</b>
<b class="nc">&nbsp; l.setStyle(nameStyle(false));</b>
<b class="nc">&nbsp; ScaleTransition st = new ScaleTransition(Duration.millis(130), v);</b>
<b class="nc">&nbsp; st.setToX(1.0); st.setToY(1.0); st.play();</b>
&nbsp; }
&nbsp; });
&nbsp;
<b class="nc">&nbsp; frame.setStyle(frameStyle(true));</b>
<b class="nc">&nbsp; name.setStyle(nameStyle(true));</b>
<b class="nc">&nbsp; selectedFrame = frame;</b>
<b class="nc">&nbsp; selectedIndex = idx;</b>
&nbsp;
<b class="nc">&nbsp; ScaleTransition pop = new ScaleTransition(Duration.millis(160), card);</b>
<b class="nc">&nbsp; pop.setToX(1.08); pop.setToY(1.08);</b>
<b class="nc">&nbsp; pop.setAutoReverse(true); pop.setCycleCount(2); pop.play();</b>
&nbsp;
<b class="nc">&nbsp; updateConfirmButton(true);</b>
&nbsp; });
&nbsp;
<b class="nc">&nbsp; return card;</b>
&nbsp; }
&nbsp;
&nbsp; /** Enables or disables the confirm button and updates its visual opacity to reflect the state. */
&nbsp; private void updateConfirmButton(boolean enabled) {
<b class="nc">&nbsp; confirmButton.setDisable(!enabled);</b>
<b class="nc">&nbsp; confirmButton.setStyle(</b>
&nbsp; &quot;-fx-font-family: &#39;Cinzel&#39;; -fx-font-size: 12; -fx-font-weight: bold;&quot; +
&nbsp; &quot;-fx-letter-spacing: 4; -fx-text-fill: #0c0601;&quot; +
&nbsp; &quot;-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);&quot; +
&nbsp; &quot;-fx-padding: 12 48 12 48; -fx-background-radius: 2;&quot; +
<b class="nc">&nbsp; &quot;-fx-opacity: &quot; + (enabled ? &quot;1.0&quot; : &quot;0.3&quot;) + &quot;;&quot; +</b>
<b class="nc">&nbsp; &quot;-fx-cursor: &quot; + (enabled ? &quot;hand&quot; : &quot;default&quot;) + &quot;;&quot;</b>
&nbsp; );
&nbsp; }
&nbsp;
&nbsp; /** Returns the CSS style string for the totem card frame, highlighting it when {@code selected}. */
&nbsp; private String frameStyle(boolean selected) {
<b class="nc">&nbsp; return selected ? &quot;-fx-effect: dropshadow(gaussian, #ffffff, 20, 0.33, 0, 0);&quot; : &quot;&quot;;</b>
&nbsp; }
&nbsp;
&nbsp; /** Returns the CSS style string for the totem name label, brightening it when {@code selected}. */
&nbsp; private String nameStyle(boolean selected) {
&nbsp; return &quot;-fx-font-family: &#39;Cinzel&#39;; -fx-font-size: 10; -fx-letter-spacing: 2;&quot; +
<b class="nc">&nbsp; &quot;-fx-text-fill: &quot; + (selected ? &quot;#FFD700&quot; : &quot;#ffffff&quot;) + &quot;;&quot;;</b>
&nbsp; }
&nbsp;
&nbsp; /** Returns the totem name with only the first letter capitalised. */
&nbsp; private String capitalize(Totems t) {
<b class="nc">&nbsp; String s = t.name().toLowerCase(Locale.ROOT);</b>
<b class="nc">&nbsp; return Character.toUpperCase(s.charAt(0)) + s.substring(1);</b>
&nbsp; }
&nbsp;
&nbsp;
&nbsp; /** Submits the selected totem index to the controller when the confirm button is clicked. */
&nbsp; @FXML
&nbsp; private void onConfirm() {
<b class="nc">&nbsp; if (selectedIndex &gt;= 0) {</b>
<b class="nc">&nbsp; controller.totemChoice(selectedIndex);</b>
&nbsp; }
&nbsp; }
&nbsp;}
</code>
</pre>
</div>
<script type="text/javascript">
(function() {
var msie = false, msie9 = false;
/*@cc_on
msie = true;
@if (@_jscript_version >= 9)
msie9 = true;
@end
@*/
if (!msie || msie && msie9) {
hljs.highlightAll()
hljs.initLineNumbersOnLoad();
}
})();
</script>
<div class="footer">
<div style="float:right;">generated on 2026-06-14 21:53</div>
</div>
</body>
</html>