FIx: Fixed Coverage Screenshots And htmlRepot.
This commit is contained in:
@@ -0,0 +1,515 @@
|
||||
|
||||
|
||||
|
||||
<!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/5)
|
||||
</span>
|
||||
</td>
|
||||
<td class="coverageStat">
|
||||
<span class="percent">
|
||||
0%
|
||||
</span>
|
||||
<span class="absValue">
|
||||
(0/29)
|
||||
</span>
|
||||
</td>
|
||||
<td class="coverageStat">
|
||||
<span class="percent">
|
||||
0%
|
||||
</span>
|
||||
<span class="absValue">
|
||||
(0/49)
|
||||
</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/14)
|
||||
</span>
|
||||
</td>
|
||||
<td class="coverageStat">
|
||||
<span class="percent">
|
||||
0%
|
||||
</span>
|
||||
<span class="absValue">
|
||||
(0/71)
|
||||
</span>
|
||||
</td>
|
||||
<td class="coverageStat">
|
||||
<span class="percent">
|
||||
0%
|
||||
</span>
|
||||
<span class="absValue">
|
||||
(0/122)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<pre>
|
||||
<code class="sourceCode" id="sourceCode"> package it.polimi.ingsw.gc14.View.GUI;
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Circle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Ambient fire-particle effect rendered on a transparent overlay pane.
|
||||
*
|
||||
* <p>Three particle types float from the screen corners toward the center:
|
||||
* <ul>
|
||||
* <li><b>embers</b> (type 0) — slow, glowing orange circles with a soft halo.</li>
|
||||
* <li><b>sparks</b> (type 1) — fast, short-lived bright particles.</li>
|
||||
* <li><b>dust</b> (type 2) — large, translucent brown drifting circles.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Call {@link #start()} after adding {@link #getPane()} to the scene graph
|
||||
*/
|
||||
public class FireParticleSystem {
|
||||
|
||||
/** Maximum number of live particles at any given time. */
|
||||
private static final int MAX_PARTICLES = 30;
|
||||
/** Minimum nanoseconds between particle spawn batches. */
|
||||
private static final long SPAWN_INTERVAL_NS = 100_000_000L;
|
||||
/** Minimum nanoseconds between rendered frames (30 fps cap). */
|
||||
private static final long FRAME_INTERVAL_NS = 1_000_000_000L / 30;
|
||||
/** Physics update steps applied per rendered frame (multiplies effective speed). */
|
||||
private static final int STEPS_PER_FRAME = 3;
|
||||
|
||||
/** Mouse-transparent overlay pane that hosts all particle nodes. */
|
||||
private final Pane pane;
|
||||
/** The scene whose dimensions are used to position and fade particles. */
|
||||
private final Scene scene;
|
||||
/** Currently live particles. */
|
||||
<b class="nc"> private final List<Particle> particles = new ArrayList<>();</b>
|
||||
/** Shared random source for particle initialization and physics noise. */
|
||||
<b class="nc"> private final Random rnd = new Random();</b>
|
||||
/** Timestamp (ns) of the last particle spawn batch. */
|
||||
<b class="nc"> private long lastSpawn = 0;</b>
|
||||
/** Timestamp (ns) of the last rendered frame. */
|
||||
<b class="nc"> private long lastFrame = 0;</b>
|
||||
/** {@code true} after the first tick pre-populates the particle pool. */
|
||||
<b class="nc"> private boolean warmedUp = false;</b>
|
||||
|
||||
/**
|
||||
* Creates the particle system bound to the given scene.
|
||||
* The internal overlay pane is transparent and mouse-transparent.
|
||||
*
|
||||
* @param scene the scene whose dimensions are used for particle positioning and fading.
|
||||
*/
|
||||
<b class="nc"> public FireParticleSystem(Scene scene) {</b>
|
||||
<b class="nc"> this.scene = scene;</b>
|
||||
<b class="nc"> pane = new Pane();</b>
|
||||
<b class="nc"> pane.setMouseTransparent(true);</b>
|
||||
<b class="nc"> pane.setPickOnBounds(false);</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transparent overlay pane that holds all particle nodes.
|
||||
*
|
||||
* @return the mouse-transparent {@link Pane} overlay.
|
||||
*/
|
||||
<b class="nc"> public Pane getPane() { return pane; }</b>
|
||||
|
||||
/** Starts the animation timer. */
|
||||
public void start() {
|
||||
<b class="nc"> AnimationTimer timer = new AnimationTimer() {</b>
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
<b class="nc"> if (now - lastFrame < FRAME_INTERVAL_NS) return;</b>
|
||||
<b class="nc"> lastFrame = now;</b>
|
||||
<b class="nc"> tick(now);</b>
|
||||
}
|
||||
};
|
||||
<b class="nc"> timer.start();</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the simulation by one frame: pre-warms on first call, spawns new particles,
|
||||
* steps existing ones, removes dead ones, and updates their visual state.
|
||||
*
|
||||
* @param now the current timestamp in nanoseconds from the animation timer.
|
||||
*/
|
||||
private void tick(long now) {
|
||||
<b class="nc"> double w = scene.getWidth();</b>
|
||||
<b class="nc"> double h = scene.getHeight();</b>
|
||||
<b class="nc"> if (w == 0 || h == 0) return;</b>
|
||||
|
||||
<b class="nc"> if (!warmedUp) {</b>
|
||||
<b class="nc"> warmedUp = true;</b>
|
||||
<b class="nc"> for (int i = 0; i < MAX_PARTICLES; i++) {</b>
|
||||
<b class="nc"> Particle p = spawnParticle(w, h);</b>
|
||||
<b class="nc"> int advance = rnd.nextInt(800) + 100;</b>
|
||||
<b class="nc"> for (int f = 0; f < advance; f++) p.update();</b>
|
||||
<b class="nc"> if (!p.isDead(w, h)) {</b>
|
||||
<b class="nc"> p.addTo(pane);</b>
|
||||
<b class="nc"> p.updateVisual(w, h);</b>
|
||||
<b class="nc"> particles.add(p);</b>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<b class="nc"> if (now - lastSpawn > SPAWN_INTERVAL_NS && particles.size() < MAX_PARTICLES) {</b>
|
||||
<b class="nc"> lastSpawn = now;</b>
|
||||
<b class="nc"> int count = rnd.nextInt(2) + 1;</b>
|
||||
<b class="nc"> for (int i = 0; i < count && particles.size() < MAX_PARTICLES; i++) {</b>
|
||||
<b class="nc"> Particle p = spawnParticle(w, h);</b>
|
||||
<b class="nc"> p.addTo(pane);</b>
|
||||
<b class="nc"> particles.add(p);</b>
|
||||
}
|
||||
}
|
||||
|
||||
<b class="nc"> Iterator<Particle> it = particles.iterator();</b>
|
||||
<b class="nc"> while (it.hasNext()) {</b>
|
||||
<b class="nc"> Particle p = it.next();</b>
|
||||
<b class="nc"> for (int s = 0; s < STEPS_PER_FRAME; s++) p.update();</b>
|
||||
<b class="nc"> if (p.isDead(w, h)) {</b>
|
||||
<b class="nc"> p.removeFrom(pane);</b>
|
||||
<b class="nc"> it.remove();</b>
|
||||
} else {
|
||||
<b class="nc"> p.updateVisual(w, h);</b>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle starting near one of the four screen corners.
|
||||
*
|
||||
* @param w scene width in pixels.
|
||||
* @param h scene height in pixels.
|
||||
* @return the newly constructed {@link Particle}.
|
||||
*/
|
||||
private Particle spawnParticle(double w, double h) {
|
||||
<b class="nc"> int corner = rnd.nextInt(4);</b>
|
||||
<b class="nc"> double margin = 0.12;</b>
|
||||
double x, y;
|
||||
<b class="nc"> switch (corner) {</b>
|
||||
<b class="nc"> case 0 -> { x = rnd.nextDouble() * w * margin; y = h - rnd.nextDouble() * h * margin; }</b>
|
||||
<b class="nc"> case 1 -> { x = w - rnd.nextDouble() * w * margin; y = h - rnd.nextDouble() * h * margin; }</b>
|
||||
<b class="nc"> case 2 -> { x = rnd.nextDouble() * w * margin; y = rnd.nextDouble() * h * margin; }</b>
|
||||
<b class="nc"> default -> { x = w - rnd.nextDouble() * w * margin; y = rnd.nextDouble() * h * margin; }</b>
|
||||
}
|
||||
<b class="nc"> return new Particle(x, y, w, h, corner, rnd);</b>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A single fire particle with position, velocity, wobble, and JavaFX visual nodes.
|
||||
* Particles are typed: 0 = ember, 1 = spark, 2 = dust.
|
||||
*/
|
||||
private static class Particle {
|
||||
|
||||
/** Current X position in scene pixels. */
|
||||
double x;
|
||||
/** Current Y position in scene pixels. */
|
||||
double y;
|
||||
/** Horizontal velocity component. */
|
||||
double vx;
|
||||
/** Vertical velocity component. */
|
||||
double vy;
|
||||
/** Remaining life fraction (1.0 = full, 0.0 = dead); only decrements for sparks. */
|
||||
double life;
|
||||
/** Visual radius of the particle's core circle. */
|
||||
final double size;
|
||||
/** Current phase of the sinusoidal wobble. */
|
||||
double wobblePhase;
|
||||
/** Angular speed of the wobble oscillation. */
|
||||
final double wobbleSpeed;
|
||||
/** Amplitude factor of the wobble displacement. */
|
||||
final double wobbleAmp;
|
||||
/** Particle type: 0 = ember, 1 = spark, 2 = dust. */
|
||||
final int type;
|
||||
/** Shared random source used during physics updates. */
|
||||
final Random rnd;
|
||||
|
||||
/** Primary visible circle node. */
|
||||
final Circle core;
|
||||
/** Soft glow halo circle behind {@link #core}; non-null only for embers (type 0). */
|
||||
final Circle glow;
|
||||
|
||||
/**
|
||||
* Initializes the particle at position ({@code x}, {@code y}) with velocity
|
||||
* aimed roughly from the given {@code corner} toward the scene center.
|
||||
*
|
||||
* @param x spawn X coordinate.
|
||||
* @param y spawn Y coordinate.
|
||||
* @param w scene width (used to compute target direction).
|
||||
* @param h scene height (used to compute target direction).
|
||||
* @param corner spawn corner index: 0 = bottom-left, 1 = bottom-right, 2 = top-left, 3 = top-right.
|
||||
* @param rnd shared random source.
|
||||
*/
|
||||
<b class="nc"> Particle(double x, double y, double w, double h, int corner, Random rnd) {</b>
|
||||
<b class="nc"> this.x = x;</b>
|
||||
<b class="nc"> this.y = y;</b>
|
||||
<b class="nc"> this.life = 1.0;</b>
|
||||
<b class="nc"> this.rnd = rnd;</b>
|
||||
<b class="nc"> this.type = weightedType(rnd);</b>
|
||||
|
||||
<b class="nc"> double targetX = (corner == 0 || corner == 2) ? w * 0.75 : w * 0.25;</b>
|
||||
<b class="nc"> double targetY = (corner == 0 || corner == 1) ? h * 0.25 : h * 0.75;</b>
|
||||
<b class="nc"> double baseAngle = Math.atan2(targetY - y, targetX - x);</b>
|
||||
<b class="nc"> double angle = baseAngle + (rnd.nextDouble() - 0.5) * (Math.PI / 3.5);</b>
|
||||
|
||||
double speed;
|
||||
<b class="nc"> if (type == 0) {</b>
|
||||
<b class="nc"> speed = 0.5 + rnd.nextDouble() * 0.7;</b>
|
||||
<b class="nc"> size = rnd.nextDouble() * 3 + 2;</b>
|
||||
<b class="nc"> glow = new Circle(size * 2.2);</b>
|
||||
<b class="nc"> core = new Circle(size / 2);</b>
|
||||
<b class="nc"> } else if (type == 1) {</b>
|
||||
<b class="nc"> speed = 1.5 + rnd.nextDouble() * 2.0;</b>
|
||||
<b class="nc"> size = rnd.nextDouble() * 1.5 + 0.5;</b>
|
||||
<b class="nc"> glow = null;</b>
|
||||
<b class="nc"> core = new Circle(size / 2);</b>
|
||||
} else {
|
||||
<b class="nc"> speed = 0.2 + rnd.nextDouble() * 0.35;</b>
|
||||
<b class="nc"> size = rnd.nextDouble() * 8 + 5;</b>
|
||||
<b class="nc"> glow = null;</b>
|
||||
<b class="nc"> core = new Circle(size);</b>
|
||||
<b class="nc"> core.setFill(Color.color(0.55, 0.38, 0.22));</b>
|
||||
}
|
||||
|
||||
<b class="nc"> vx = Math.cos(angle) * speed;</b>
|
||||
<b class="nc"> vy = Math.sin(angle) * speed;</b>
|
||||
<b class="nc"> wobblePhase = rnd.nextDouble() * Math.PI * 2;</b>
|
||||
<b class="nc"> wobbleSpeed = 0.025 + rnd.nextDouble() * 0.04;</b>
|
||||
<b class="nc"> wobbleAmp = 0.1 + rnd.nextDouble() * 0.4;</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a particle type weighted toward embers (55% ember, 23% spark, 22% dust).
|
||||
*
|
||||
* @param rnd the random source.
|
||||
* @return particle type: 0, 1, or 2.
|
||||
*/
|
||||
private static int weightedType(Random rnd) {
|
||||
<b class="nc"> double r = rnd.nextDouble();</b>
|
||||
<b class="nc"> if (r < 0.55) return 0;</b>
|
||||
<b class="nc"> if (r < 0.78) return 1;</b>
|
||||
<b class="nc"> return 2;</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this particle's visual nodes to {@code pane} (glow first so core renders on top).
|
||||
*
|
||||
* @param pane the overlay pane to add nodes to.
|
||||
*/
|
||||
void addTo(Pane pane) {
|
||||
<b class="nc"> if (glow != null) pane.getChildren().add(glow);</b>
|
||||
<b class="nc"> pane.getChildren().add(core);</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this particle's visual nodes from {@code pane}.
|
||||
*
|
||||
* @param pane the overlay pane to remove nodes from.
|
||||
*/
|
||||
void removeFrom(Pane pane) {
|
||||
<b class="nc"> pane.getChildren().remove(core);</b>
|
||||
<b class="nc"> if (glow != null) pane.getChildren().remove(glow);</b>
|
||||
}
|
||||
|
||||
/** Advances this particle by one physics step: applies wobble, moves, and decrements spark life. */
|
||||
void update() {
|
||||
<b class="nc"> wobblePhase += wobbleSpeed;</b>
|
||||
<b class="nc"> vx += Math.sin(wobblePhase) * wobbleAmp * 0.05;</b>
|
||||
<b class="nc"> vy += Math.cos(wobblePhase) * wobbleAmp * 0.02;</b>
|
||||
<b class="nc"> x += vx;</b>
|
||||
<b class="nc"> y += vy;</b>
|
||||
<b class="nc"> if (type == 1) life -= 0.008 + rnd.nextDouble() * 0.006;</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} when this particle has left the scene bounds or, for sparks, its life reached zero.
|
||||
*
|
||||
* @param w scene width.
|
||||
* @param h scene height.
|
||||
* @return {@code true} if the particle should be removed.
|
||||
*/
|
||||
boolean isDead(double w, double h) {
|
||||
<b class="nc"> double pad = size * 4;</b>
|
||||
<b class="nc"> return x < -pad || x > w + pad || y < -pad || y > h + pad</b>
|
||||
|| (type == 1 && life <= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates color, opacity, and translate position of this particle's visual nodes
|
||||
* based on distance from scene edges and remaining life.
|
||||
*
|
||||
* @param w scene width.
|
||||
* @param h scene height.
|
||||
*/
|
||||
void updateVisual(double w, double h) {
|
||||
<b class="nc"> double edge = Math.min(w, h) * 0.05;</b>
|
||||
<b class="nc"> double fadeX = Math.min(x / edge, Math.min((w - x) / edge, 1.0));</b>
|
||||
<b class="nc"> double fadeY = Math.min(y / edge, Math.min((h - y) / edge, 1.0));</b>
|
||||
<b class="nc"> double posAlpha = Math.max(0, Math.min(fadeX, fadeY));</b>
|
||||
<b class="nc"> double lifeAlpha = (type == 1) ? Math.min(life * 2.0, 1.0) : 1.0;</b>
|
||||
<b class="nc"> double alpha = posAlpha * lifeAlpha;</b>
|
||||
|
||||
<b class="nc"> core.setTranslateX(x);</b>
|
||||
<b class="nc"> core.setTranslateY(y);</b>
|
||||
|
||||
<b class="nc"> if (type == 0) {</b>
|
||||
<b class="nc"> double dist = Math.sqrt((x - w/2) * (x - w/2) + (y - h/2) * (y - h/2));</b>
|
||||
<b class="nc"> double hotness = Math.max(0, 1.0 - dist / (Math.sqrt(w * w + h * h) * 0.4));</b>
|
||||
<b class="nc"> double g = 0.3 + hotness * 0.5;</b>
|
||||
<b class="nc"> core.setFill(Color.color(1.0, g, 0.0));</b>
|
||||
<b class="nc"> core.setOpacity(alpha * 0.88);</b>
|
||||
<b class="nc"> glow.setTranslateX(x);</b>
|
||||
<b class="nc"> glow.setTranslateY(y);</b>
|
||||
<b class="nc"> glow.setFill(Color.color(1.0, g * 0.35, 0.0));</b>
|
||||
<b class="nc"> glow.setOpacity(alpha * 0.10);</b>
|
||||
<b class="nc"> } else if (type == 1) {</b>
|
||||
<b class="nc"> double brightness = Math.min(life * 2.5, 1.0);</b>
|
||||
<b class="nc"> core.setFill(Color.color(1.0, brightness * 0.85 + 0.15, brightness * 0.15));</b>
|
||||
<b class="nc"> core.setOpacity(alpha);</b>
|
||||
} else {
|
||||
<b class="nc"> core.setOpacity(alpha * 0.20);</b>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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-19 22:53</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,394 @@
|
||||
|
||||
|
||||
|
||||
<!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/110)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<pre>
|
||||
<code class="sourceCode" id="sourceCode"> package it.polimi.ingsw.gc14.View.GUI;
|
||||
|
||||
import it.polimi.ingsw.gc14.Controller.ClientController;
|
||||
import it.polimi.ingsw.gc14.ErrorType;
|
||||
import it.polimi.ingsw.gc14.Model.MiniModel;
|
||||
import it.polimi.ingsw.gc14.View.IView;
|
||||
import javafx.animation.FadeTransition;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import static it.polimi.ingsw.gc14.Model.GamePackage.GameStages.ENDED;
|
||||
import static it.polimi.ingsw.gc14.Model.GamePackage.GameStages.TOTEM_CHOICE;
|
||||
|
||||
/**
|
||||
* JavaFX-based GUI implementation of {@link it.polimi.ingsw.gc14.View.IView IView}.
|
||||
*
|
||||
* <p>Manages the primary stage and switches between the login, totem choice,
|
||||
* main game, and leaderboard scenes based on the current game state.
|
||||
*/
|
||||
<b class="nc"> public class GUI extends Application implements IView {</b>
|
||||
/** The primary JavaFX stage. */
|
||||
private Stage primaryStage;
|
||||
/** Latest mini model received from the server. */
|
||||
private volatile MiniModel miniModel;
|
||||
/** FXML loader for the login scene. */
|
||||
private FXMLLoader loaderLogin;
|
||||
/** The login scene. */
|
||||
private Scene loginScene;
|
||||
/** Controller for the login FXML scene. */
|
||||
private LoginFXMLController controllerLogin;
|
||||
/** FXML loader for the totem selection scene. */
|
||||
private FXMLLoader loaderTotem;
|
||||
/** The totem selection scene. */
|
||||
private Scene totemScene;
|
||||
/** Controller for the totem FXML scene. */
|
||||
private TotemFXMLController controllerTotem;
|
||||
/** FXML loader for the main game scene. */
|
||||
private FXMLLoader loaderMain;
|
||||
/** The main game scene. */
|
||||
private Scene mainScene;
|
||||
/** Controller for the main FXML scene. */
|
||||
private MainFXMLController controllerMain;
|
||||
/** FXML loader for the leaderboard scene. */
|
||||
private FXMLLoader loaderLeaderboard;
|
||||
/** The end-of-game leaderboard scene. */
|
||||
private Scene leaderboardScene;
|
||||
/** Controller for the leaderboard FXML scene. */
|
||||
private LeaderboardFXMLController controllerLeaderboard;
|
||||
/** Client controller injected before JavaFX startup. */
|
||||
private ClientController controller;
|
||||
/** When {@code true}, the app re-enters fullscreen automatically after ESC is pressed. */
|
||||
<b class="nc"> private boolean autoReenterFullscreen = true;</b>
|
||||
/** Guards against overlapping fade transitions. */
|
||||
<b class="nc"> private boolean isFading = false;</b>
|
||||
|
||||
|
||||
/**
|
||||
* JavaFX entry point: loads all FXML scenes, wires up controllers,
|
||||
* configures fullscreen behavior, and shows the login scene.
|
||||
*
|
||||
* @param stage the primary stage provided by the JavaFX runtime.
|
||||
* @throws Exception if any FXML resource cannot be loaded.
|
||||
*/
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
<b class="nc"> this.primaryStage = stage;</b>
|
||||
<b class="nc"> loaderLogin = new FXMLLoader(getClass().getResource("/GUIScene/login.fxml"));</b>
|
||||
<b class="nc"> loginScene = new Scene(loaderLogin.load());</b>
|
||||
<b class="nc"> controllerLogin = loaderLogin.getController();</b>
|
||||
<b class="nc"> controllerLogin.setController(controller);</b>
|
||||
|
||||
|
||||
<b class="nc"> loaderTotem = new FXMLLoader(getClass().getResource("/GUIScene/totem.fxml"));</b>
|
||||
<b class="nc"> totemScene = new Scene(loaderTotem.load());</b>
|
||||
<b class="nc"> controllerTotem = loaderTotem.getController();</b>
|
||||
<b class="nc"> controllerTotem.setController(controller);</b>
|
||||
|
||||
|
||||
<b class="nc"> loaderMain = new FXMLLoader(getClass().getResource("/GUIScene/main.fxml"));</b>
|
||||
<b class="nc"> mainScene = new Scene(loaderMain.load());</b>
|
||||
<b class="nc"> controllerMain = loaderMain.getController();</b>
|
||||
<b class="nc"> controllerMain.setController(controller);</b>
|
||||
|
||||
<b class="nc"> loaderLeaderboard = new FXMLLoader(getClass().getResource("/GUIScene/standing.fxml"));</b>
|
||||
<b class="nc"> leaderboardScene = new Scene(loaderLeaderboard.load());</b>
|
||||
<b class="nc"> controllerLeaderboard = loaderLeaderboard.getController();</b>
|
||||
<b class="nc"> controllerLeaderboard.setController(controller, () -> {</b>
|
||||
<b class="nc"> controllerLogin.updateLoginButton(true);</b>
|
||||
<b class="nc"> controllerLogin.showError("");</b>
|
||||
<b class="nc"> fadeToScene(loginScene);</b>
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<b class="nc"> wrapScene(loginScene);</b>
|
||||
<b class="nc"> wrapScene(totemScene);</b>
|
||||
<b class="nc"> wrapScene(mainScene);</b>
|
||||
<b class="nc"> wrapScene(leaderboardScene);</b>
|
||||
|
||||
<b class="nc"> FireParticleSystem fireParticles = new FireParticleSystem(mainScene);</b>
|
||||
<b class="nc"> ((StackPane) mainScene.getRoot()).getChildren().add(fireParticles.getPane());</b>
|
||||
<b class="nc"> fireParticles.start();</b>
|
||||
|
||||
<b class="nc"> primaryStage.setScene(loginScene);</b>
|
||||
<b class="nc"> Rectangle2D tmp = Screen.getPrimary().getVisualBounds();</b>
|
||||
<b class="nc"> primaryStage.setWidth(tmp.getWidth());</b>
|
||||
<b class="nc"> primaryStage.setHeight(tmp.getHeight());</b>
|
||||
<b class="nc"> primaryStage.setX(tmp.getMinX());</b>
|
||||
<b class="nc"> primaryStage.setY(tmp.getMinY());</b>
|
||||
<b class="nc"> primaryStage.setResizable(false);</b>
|
||||
<b class="nc"> primaryStage.setFullScreenExitHint("");</b>
|
||||
<b class="nc"> primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);</b>
|
||||
|
||||
<b class="nc"> primaryStage.fullScreenProperty().addListener((obs, was, isNow) -> {</b>
|
||||
<b class="nc"> if (!isNow && autoReenterFullscreen) {</b>
|
||||
<b class="nc"> Platform.runLater(() -> primaryStage.setFullScreen(true));</b>
|
||||
}
|
||||
});
|
||||
|
||||
<b class="nc"> primaryStage.addEventFilter(KeyEvent.KEY_PRESSED, e -> {</b>
|
||||
<b class="nc"> if (e.getCode() == KeyCode.ESCAPE) {</b>
|
||||
<b class="nc"> autoReenterFullscreen = false;</b>
|
||||
<b class="nc"> primaryStage.setFullScreen(false);</b>
|
||||
<b class="nc"> e.consume();</b>
|
||||
<b class="nc"> } else if (e.getCode() == KeyCode.F11) {</b>
|
||||
<b class="nc"> autoReenterFullscreen = true;</b>
|
||||
<b class="nc"> primaryStage.setFullScreen(true);</b>
|
||||
<b class="nc"> e.consume();</b>
|
||||
}
|
||||
});
|
||||
|
||||
<b class="nc"> primaryStage.setTitle("Mesos");</b>
|
||||
<b class="nc"> primaryStage.setOnCloseRequest(e -> {</b>
|
||||
<b class="nc"> Platform.exit();</b>
|
||||
<b class="nc"> System.exit(0);</b>
|
||||
});
|
||||
<b class="nc"> primaryStage.show();</b>
|
||||
<b class="nc"> Platform.runLater(() -> primaryStage.setFullScreen(true));</b>
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the local mini model reference used by the GUI.
|
||||
*
|
||||
* @param miniModel the latest mini model received from the server.
|
||||
*/
|
||||
@Override
|
||||
public void setModel(MiniModel miniModel) {
|
||||
<b class="nc"> this.miniModel = miniModel;</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the client controller into this GUI.
|
||||
*
|
||||
* @param controller the client controller to use.
|
||||
*/
|
||||
public void setController(ClientController controller) {
|
||||
<b class="nc"> this.controller=controller;</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-renders the GUI on the JavaFX application thread, switching to the
|
||||
* appropriate scene based on the current game stage.
|
||||
*/
|
||||
public void render() {
|
||||
<b class="nc"> Platform.runLater(() -> {</b>
|
||||
<b class="nc"> if (miniModel == null) return;</b>
|
||||
<b class="nc"> if (controller.getMiniModel().currentState.getGameStage() == TOTEM_CHOICE) {</b>
|
||||
<b class="nc"> controllerTotem.render();</b>
|
||||
<b class="nc"> fadeToScene(totemScene);</b>
|
||||
<b class="nc"> } else if (controller.getMiniModel().currentState.getGameStage() == ENDED) {</b>
|
||||
<b class="nc"> controllerLeaderboard.render();</b>
|
||||
<b class="nc"> fadeToScene(leaderboardScene);</b>
|
||||
} else {
|
||||
<b class="nc"> controllerMain.render();</b>
|
||||
<b class="nc"> fadeToScene(mainScene);</b>
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Wraps the current root of {@code scene} in a {@link StackPane} so that
|
||||
* fade overlay rectangles can be layered over it during scene transitions.
|
||||
*
|
||||
* @param scene the scene whose root to wrap.
|
||||
*/
|
||||
private void wrapScene(Scene scene) {
|
||||
<b class="nc"> Parent root = scene.getRoot();</b>
|
||||
<b class="nc"> scene.setRoot(new StackPane(root));</b>
|
||||
}
|
||||
/**
|
||||
* Performs a black-overlay crossfade from the current scene to {@code newScene}.
|
||||
* No-op if already fading or if {@code newScene} is already displayed.
|
||||
*
|
||||
* @param newScene the scene to transition to.
|
||||
*/
|
||||
private void fadeToScene(Scene newScene) {
|
||||
<b class="nc"> Scene currentScene = primaryStage.getScene();</b>
|
||||
<b class="nc"> if (currentScene == newScene || isFading) return;</b>
|
||||
<b class="nc"> isFading = true;</b>
|
||||
|
||||
<b class="nc"> StackPane currentRoot = (StackPane) currentScene.getRoot();</b>
|
||||
<b class="nc"> Rectangle overlay = new Rectangle();</b>
|
||||
<b class="nc"> overlay.setFill(Color.BLACK);</b>
|
||||
<b class="nc"> overlay.setOpacity(0);</b>
|
||||
<b class="nc"> overlay.widthProperty().bind(currentRoot.widthProperty());</b>
|
||||
<b class="nc"> overlay.heightProperty().bind(currentRoot.heightProperty());</b>
|
||||
<b class="nc"> currentRoot.getChildren().add(overlay);</b>
|
||||
|
||||
<b class="nc"> FadeTransition fadeOut = new FadeTransition(Duration.millis(500), overlay);</b>
|
||||
<b class="nc"> fadeOut.setFromValue(0);</b>
|
||||
<b class="nc"> fadeOut.setToValue(1);</b>
|
||||
<b class="nc"> fadeOut.setOnFinished(e -> {</b>
|
||||
<b class="nc"> currentRoot.getChildren().remove(overlay);</b>
|
||||
|
||||
<b class="nc"> StackPane newRoot = (StackPane) newScene.getRoot();</b>
|
||||
<b class="nc"> Rectangle newOverlay = new Rectangle();</b>
|
||||
<b class="nc"> newOverlay.setFill(Color.BLACK);</b>
|
||||
<b class="nc"> newOverlay.setOpacity(1);</b>
|
||||
<b class="nc"> newOverlay.widthProperty().bind(newRoot.widthProperty());</b>
|
||||
<b class="nc"> newOverlay.heightProperty().bind(newRoot.heightProperty());</b>
|
||||
<b class="nc"> newRoot.getChildren().add(newOverlay);</b>
|
||||
|
||||
<b class="nc"> primaryStage.setScene(newScene);</b>
|
||||
|
||||
<b class="nc"> FadeTransition fadeIn = new FadeTransition(Duration.millis(500), newOverlay);</b>
|
||||
<b class="nc"> fadeIn.setFromValue(1);</b>
|
||||
<b class="nc"> fadeIn.setToValue(0);</b>
|
||||
<b class="nc"> fadeIn.setOnFinished(ev -> {</b>
|
||||
<b class="nc"> newRoot.getChildren().remove(newOverlay);</b>
|
||||
<b class="nc"> isFading = false;</b>
|
||||
});
|
||||
<b class="nc"> fadeIn.play();</b>
|
||||
});
|
||||
<b class="nc"> fadeOut.play();</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error on the JavaFX application thread.
|
||||
*
|
||||
* <p>If the server crashed, returns to the login scene and re-enables the login button.
|
||||
* Otherwise, delegates to the active scene's error display.
|
||||
*
|
||||
* @param error the error type.
|
||||
* @param message a human-readable description of the error.
|
||||
*/
|
||||
@Override
|
||||
public void showError(ErrorType error,String message) {
|
||||
<b class="nc"> Platform.runLater(() -> {</b>
|
||||
<b class="nc"> if (error == ErrorType.SERVER_CRASHED) {</b>
|
||||
<b class="nc"> controllerLogin.updateLoginButton(true);</b>
|
||||
<b class="nc"> controllerLogin.showError(error);</b>
|
||||
<b class="nc"> fadeToScene(loginScene);</b>
|
||||
} else {
|
||||
<b class="nc"> if (miniModel == null) return;</b>
|
||||
<b class="nc"> controllerMain.setError(true);</b>
|
||||
<b class="nc"> controllerMain.render();</b>
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
</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-19 22:53</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,493 @@
|
||||
|
||||
|
||||
|
||||
<!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/181)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<pre>
|
||||
<code class="sourceCode" id="sourceCode"> package it.polimi.ingsw.gc14.View.GUI;
|
||||
|
||||
import it.polimi.ingsw.gc14.Controller.ClientController;
|
||||
import it.polimi.ingsw.gc14.Model.Player;
|
||||
import javafx.animation.ScaleTransition;
|
||||
import it.polimi.ingsw.gc14.Model.PlayableCard;
|
||||
import javafx.event.Event;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.stage.Popup;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.LinearGradient;
|
||||
import javafx.scene.paint.Stop;
|
||||
import javafx.scene.paint.CycleMethod;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Window;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* FXML controller for the end-of-game leaderboard scene.
|
||||
*
|
||||
* <p>Displays the final player rankings and a winner/game-over banner.
|
||||
*/
|
||||
<b class="nc"> public class LeaderboardFXMLController {</b>
|
||||
/** Root pane of the leaderboard scene; used to set the background and anchor the popup. */
|
||||
@FXML private StackPane rootPane;
|
||||
/** Main vertical container holding the outcome label and ranking rows. */
|
||||
@FXML private VBox mainVBox;
|
||||
/** Vertical list populated with one {@code HBox} row per ranked player. */
|
||||
@FXML private VBox rankingList;
|
||||
|
||||
/** Client controller for fetching game state and sending actions. */
|
||||
private ClientController controller;
|
||||
/** Auto-hiding popup that shows detailed player card information. */
|
||||
private Popup popup;
|
||||
/** Content container inside {@link #popup}. */
|
||||
private VBox popupContent;
|
||||
/** Shared image cache to avoid reloading resources multiple times. */
|
||||
<b class="nc"> private static final Map<String, Image> imageCache = new HashMap<>();</b>
|
||||
/** Action run when the player returns to the login screen (disconnect + scene switch). */
|
||||
private Runnable action;
|
||||
|
||||
/** Returns a cached {@link Image} for the given classpath {@code path}, loading it on first access. */
|
||||
private Image loadImage(String path) {
|
||||
<b class="nc"> return imageCache.computeIfAbsent(path,</b>
|
||||
<b class="nc"> p -> new Image(Objects.requireNonNull(getClass().getResourceAsStream(p))));</b>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injects the client controller, a post-game action, and the login scene reference.
|
||||
*
|
||||
* @param controller the client controller.
|
||||
* @param action the action to run when the player returns to the login screen.
|
||||
*/
|
||||
public void setController(ClientController controller, Runnable action) {
|
||||
<b class="nc"> this.controller = controller;</b>
|
||||
<b class="nc"> this.action = action;</b>
|
||||
}
|
||||
|
||||
|
||||
/** Initializes the scene: loads fonts, sets up the background, and initializes the popup. */
|
||||
@FXML
|
||||
public void initialize() {
|
||||
<b class="nc"> Font.loadFont(getClass().getResourceAsStream("/Fonts/InknutAntiqua-Regular.ttf"), 14);</b>
|
||||
<b class="nc"> mainVBox.sceneProperty().addListener((obs, oldScene, newScene) -> {</b>
|
||||
<b class="nc"> if (newScene != null) newScene.getRoot().applyCss();</b>
|
||||
});
|
||||
<b class="nc"> renderBackground();</b>
|
||||
<b class="nc"> initPopup();</b>
|
||||
}
|
||||
|
||||
|
||||
/** Populates the ranking list with the final player standings and shows the outcome banner. */
|
||||
public void render() {
|
||||
<b class="nc"> rankingList.getChildren().clear();</b>
|
||||
<b class="nc"> if(!controller.getMiniModel().standingPlayers.isEmpty())</b>
|
||||
{
|
||||
<b class="nc"> List<Player> sorted = controller.getMiniModel().standingPlayers;</b>
|
||||
|
||||
<b class="nc"> boolean iWon = sorted.get(0).getUserName().equals(controller.getMyUsername());</b>
|
||||
|
||||
// Label grande Winner / Game Over
|
||||
<b class="nc"> Label outcomeLabel = new Label(iWon ? "\uD83C\uDFC6 WINNER! \uD83C\uDFC6" : "GAME OVER");</b>
|
||||
<b class="nc"> outcomeLabel.setStyle(</b>
|
||||
"-fx-font-size: 64px;" +
|
||||
"-fx-font-weight: bold;" +
|
||||
<b class="nc"> "-fx-text-fill: " + (iWon ? "#FFD700;" : "#FF4444;")</b>
|
||||
);
|
||||
<b class="nc"> DropShadow glow = new DropShadow();</b>
|
||||
<b class="nc"> glow.setColor(iWon ? Color.rgb(255, 200, 0, 0.95) : Color.rgb(220, 0, 0, 0.95));</b>
|
||||
<b class="nc"> glow.setRadius(35);</b>
|
||||
<b class="nc"> glow.setSpread(0.35);</b>
|
||||
<b class="nc"> outcomeLabel.setEffect(glow);</b>
|
||||
<b class="nc"> rankingList.getChildren().add(outcomeLabel);</b>
|
||||
|
||||
<b class="nc"> Region sep = new Region();</b>
|
||||
<b class="nc"> sep.setPrefHeight(16);</b>
|
||||
<b class="nc"> rankingList.getChildren().add(sep);</b>
|
||||
|
||||
<b class="nc"> for (int i = 0; i < sorted.size(); i++) {</b>
|
||||
<b class="nc"> rankingList.getChildren().add(createPlayerRow(i + 1, sorted.get(i)));</b>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==== EFFECTS ====
|
||||
/** Scales {@code node} to 1.02× on hover and sets a hand cursor. */
|
||||
private void addHoverZoom(Node node) {
|
||||
<b class="nc"> ScaleTransition scaleUp = new ScaleTransition(Duration.millis(150), node);</b>
|
||||
<b class="nc"> scaleUp.setToX(1.02);</b>
|
||||
<b class="nc"> scaleUp.setToY(1.02);</b>
|
||||
<b class="nc"> ScaleTransition scaleDown = new ScaleTransition(Duration.millis(150), node);</b>
|
||||
<b class="nc"> scaleDown.setToX(1.0);</b>
|
||||
<b class="nc"> scaleDown.setToY(1.0);</b>
|
||||
<b class="nc"> node.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> { scaleUp.play(); node.setCursor(Cursor.HAND); });</b>
|
||||
<b class="nc"> node.addEventHandler(MouseEvent.MOUSE_EXITED, e -> { scaleDown.play(); node.setCursor(Cursor.DEFAULT); });</b>
|
||||
}
|
||||
|
||||
/** Applies a static drop-shadow to {@code node}. */
|
||||
private void addShadow(Node node) {
|
||||
<b class="nc"> DropShadow shadow = new DropShadow();</b>
|
||||
<b class="nc"> shadow.setColor(Color.rgb(0, 0, 0, 0.6));</b>
|
||||
<b class="nc"> shadow.setRadius(12);</b>
|
||||
<b class="nc"> shadow.setOffsetX(3);</b>
|
||||
<b class="nc"> shadow.setOffsetY(3);</b>
|
||||
<b class="nc"> node.setEffect(shadow);</b>
|
||||
}
|
||||
|
||||
|
||||
// ==== ROW ====
|
||||
/** Builds a styled leaderboard row showing rank, totem, username, stats, and prestige for {@code player}. */
|
||||
private HBox createPlayerRow(int position, Player player) {
|
||||
<b class="nc"> HBox row = new HBox(20);</b>
|
||||
<b class="nc"> row.setAlignment(Pos.CENTER_LEFT);</b>
|
||||
<b class="nc"> row.setPadding(new Insets(14, 28, 14, 28));</b>
|
||||
<b class="nc"> row.setMaxWidth(Double.MAX_VALUE);</b>
|
||||
|
||||
<b class="nc"> LinearGradient gradient = new LinearGradient(</b>
|
||||
0, 0, 1, 0, true, CycleMethod.NO_CYCLE,
|
||||
<b class="nc"> new Stop(0.0, Color.rgb(0, 0, 0, 0.75)),</b>
|
||||
<b class="nc"> new Stop(0.5, Color.rgb(0, 0, 0, 0.60)),</b>
|
||||
<b class="nc"> new Stop(1.0, Color.rgb(0, 0, 0, 0.75))</b>
|
||||
);
|
||||
<b class="nc"> row.setBackground(new Background(new BackgroundFill(</b>
|
||||
gradient,
|
||||
new CornerRadii(12),
|
||||
Insets.EMPTY
|
||||
)));
|
||||
<b class="nc"> addShadow(row);</b>
|
||||
<b class="nc"> addHoverZoom(row);</b>
|
||||
|
||||
<b class="nc"> Label posLabel = new Label(position + "°");</b>
|
||||
<b class="nc"> posLabel.setMinWidth(55);</b>
|
||||
<b class="nc"> String medalColor = switch (position) {</b>
|
||||
<b class="nc"> case 1 -> "#FFD700"; // oro</b>
|
||||
<b class="nc"> case 2 -> "#C0C0C0"; // argento</b>
|
||||
<b class="nc"> case 3 -> "#CD7F32"; // bronzo</b>
|
||||
<b class="nc"> default -> "#EEEEEE";</b>
|
||||
<b class="nc"> };</b>
|
||||
<b class="nc"> posLabel.setStyle("-fx-font-size: 28px; -fx-font-weight: bold; -fx-text-fill: " + medalColor + ";");</b>
|
||||
|
||||
|
||||
<b class="nc"> ImageView totem = new ImageView(loadImage(</b>
|
||||
<b class="nc"> "/GUIImages/Totems/totem_" + player.getTotem().toString().toLowerCase(Locale.ROOT) + ".png"));</b>
|
||||
<b class="nc"> totem.setFitHeight(55);</b>
|
||||
<b class="nc"> totem.setPreserveRatio(true);</b>
|
||||
|
||||
<b class="nc"> Label nameLabel = new Label(player.getUserName());</b>
|
||||
<b class="nc"> nameLabel.setStyle(</b>
|
||||
"-fx-font-size: 22px; -fx-font-weight: bold; -fx-text-fill: " +
|
||||
<b class="nc"> (player.getUserName().equals(controller.getMyUsername()) ? "#ff6b6b;" : "#FFFFFF;")</b>
|
||||
);
|
||||
<b class="nc"> HBox.setHgrow(nameLabel, Priority.ALWAYS);</b>
|
||||
|
||||
<b class="nc"> HBox stats = createStatsBox(player);</b>
|
||||
|
||||
<b class="nc"> HBox ppBox = new HBox(6);</b>
|
||||
<b class="nc"> ppBox.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> ImageView ppIcon = new ImageView(loadImage("/GUIImages/Icons/PrestigePoint.png"));</b>
|
||||
<b class="nc"> ppIcon.setFitHeight(45);</b>
|
||||
<b class="nc"> ppIcon.setPreserveRatio(true);</b>
|
||||
<b class="nc"> Label ppLabel = new Label(String.valueOf(player.getPrestigeValue()));</b>
|
||||
<b class="nc"> ppLabel.setStyle("-fx-font-size: 28px; -fx-font-weight: bold; -fx-text-fill: #FFD700;");</b>
|
||||
<b class="nc"> ppBox.getChildren().addAll(ppIcon, ppLabel);</b>
|
||||
|
||||
<b class="nc"> row.getChildren().addAll(posLabel, totem, nameLabel, stats, ppBox);</b>
|
||||
<b class="nc"> row.setOnMouseClicked(e -> openPlayerPopup(player));</b>
|
||||
<b class="nc"> return row;</b>
|
||||
}
|
||||
|
||||
/** Creates a compact icon+count stats strip for all card types of {@code player}. */
|
||||
private HBox createStatsBox(Player player) {
|
||||
<b class="nc"> HBox stats = new HBox(14);</b>
|
||||
<b class="nc"> stats.setAlignment(Pos.CENTER);</b>
|
||||
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Food.png", String.valueOf(player.getFoodValue()), 26));</b>
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Artist.png", String.valueOf(player.getArtists().size()), 26));</b>
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Gatherer.png", String.valueOf(player.getGatherers().size()), 26));</b>
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Inventor.png", String.valueOf(player.getInventors().size()), 26));</b>
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Builder.png", String.valueOf(player.getBuilders().size()), 26));</b>
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Shaman.png", String.valueOf(player.getShamans().size()), 26));</b>
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Hunter.png", String.valueOf(player.getHunters().size()), 26));</b>
|
||||
<b class="nc"> stats.getChildren().add(createStatItem("/GUIImages/Icons/Building.png", String.valueOf(player.getBuildingCards().size()), 26));</b>
|
||||
|
||||
<b class="nc"> return stats;</b>
|
||||
}
|
||||
|
||||
/** Creates a single icon + label widget for one stat type. */
|
||||
private HBox createStatItem(String iconPath, String value, double iconHeight) {
|
||||
<b class="nc"> HBox box = new HBox(4);</b>
|
||||
<b class="nc"> box.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> ImageView icon = new ImageView(loadImage(iconPath));</b>
|
||||
<b class="nc"> icon.setFitHeight(iconHeight);</b>
|
||||
<b class="nc"> icon.setPreserveRatio(true);</b>
|
||||
<b class="nc"> Label label = new Label(value);</b>
|
||||
<b class="nc"> label.setStyle("-fx-font-size: 16px; -fx-text-fill: #FFFFFF; -fx-font-weight: bold;");</b>
|
||||
<b class="nc"> DropShadow textShadow = new DropShadow();</b>
|
||||
<b class="nc"> textShadow.setColor(Color.rgb(0, 0, 0, 0.9));</b>
|
||||
<b class="nc"> textShadow.setRadius(4);</b>
|
||||
<b class="nc"> label.setEffect(textShadow);</b>
|
||||
<b class="nc"> box.getChildren().addAll(icon, label);</b>
|
||||
<b class="nc"> return box;</b>
|
||||
}
|
||||
|
||||
|
||||
// ==== POPUP ====
|
||||
/** Creates and configures the auto-hiding player-detail popup. */
|
||||
private void initPopup() {
|
||||
<b class="nc"> popupContent = new VBox(12);</b>
|
||||
<b class="nc"> popupContent.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> popupContent.setPadding(new Insets(16));</b>
|
||||
<b class="nc"> popupContent.setStyle(</b>
|
||||
"-fx-background-color: rgba(20,10,5,0.97);" +
|
||||
"-fx-border-color: #8B4513;" +
|
||||
"-fx-border-width: 3;" +
|
||||
"-fx-border-radius: 14;" +
|
||||
"-fx-background-radius: 14;"
|
||||
);
|
||||
|
||||
<b class="nc"> popup = new Popup();</b>
|
||||
<b class="nc"> popup.getContent().add(popupContent);</b>
|
||||
<b class="nc"> popup.setAutoHide(true);</b>
|
||||
<b class="nc"> popup.addEventHandler(Event.ANY, e -> {</b>
|
||||
<b class="nc"> if (popup.getScene() != null) popup.getScene().setFill(Color.TRANSPARENT);</b>
|
||||
});
|
||||
}
|
||||
|
||||
/** Opens the player-detail popup showing all cards held by {@code player}, grouped by type. */
|
||||
private void openPlayerPopup(Player player) {
|
||||
<b class="nc"> popupContent.getChildren().clear();</b>
|
||||
|
||||
<b class="nc"> HBox header = new HBox(10);</b>
|
||||
<b class="nc"> header.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> ImageView totem = new ImageView(loadImage(</b>
|
||||
<b class="nc"> "/GUIImages/Totems/totem_" + player.getTotem().toString().toLowerCase(Locale.ROOT) + ".png"));</b>
|
||||
<b class="nc"> totem.setFitHeight(30);</b>
|
||||
<b class="nc"> totem.setPreserveRatio(true);</b>
|
||||
<b class="nc"> Label nameLabel = new Label(player.getUserName());</b>
|
||||
<b class="nc"> nameLabel.setStyle("-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: #FFD700;");</b>
|
||||
<b class="nc"> header.getChildren().addAll(totem, nameLabel);</b>
|
||||
<b class="nc"> popupContent.getChildren().add(header);</b>
|
||||
|
||||
<b class="nc"> String[][] types = {</b>
|
||||
{"Artist", "artists"},
|
||||
{"Gatherer", "gatherers"},
|
||||
{"Inventor", "inventors"},
|
||||
{"Builder", "builders"},
|
||||
{"Shaman", "shamans"},
|
||||
{"Hunter", "hunters"},
|
||||
{"Building", "buildingCards"}
|
||||
};
|
||||
|
||||
<b class="nc"> VBox grid = new VBox(6);</b>
|
||||
<b class="nc"> grid.setAlignment(Pos.CENTER_LEFT);</b>
|
||||
<b class="nc"> boolean hasAnyCard = false;</b>
|
||||
|
||||
<b class="nc"> for (String[] type : types) {</b>
|
||||
<b class="nc"> String typeName = type[0];</b>
|
||||
<b class="nc"> String field = type[1];</b>
|
||||
<b class="nc"> ArrayList<PlayableCard> cards = getPlayerCards(player.getUserName(), field);</b>
|
||||
<b class="nc"> if (cards.isEmpty()) continue;</b>
|
||||
<b class="nc"> hasAnyCard = true;</b>
|
||||
|
||||
<b class="nc"> HBox row = new HBox(6);</b>
|
||||
<b class="nc"> row.setAlignment(Pos.CENTER_LEFT);</b>
|
||||
|
||||
<b class="nc"> ImageView typeIcon = new ImageView(loadImage("/GUIImages/Icons/"+typeName+".png"));</b>
|
||||
<b class="nc"> typeIcon.setFitHeight(22);</b>
|
||||
<b class="nc"> typeIcon.setPreserveRatio(true);</b>
|
||||
<b class="nc"> row.getChildren().add(typeIcon);</b>
|
||||
|
||||
<b class="nc"> for (PlayableCard card : cards) {</b>
|
||||
<b class="nc"> ImageView img = new ImageView(loadImage("/GUIImages/Fronts/card-" + card.getIdIMG() + ".png"));</b>
|
||||
<b class="nc"> img.setFitHeight(230);</b>
|
||||
<b class="nc"> img.setPreserveRatio(true);</b>
|
||||
<b class="nc"> img.setCursor(Cursor.HAND);</b>
|
||||
<b class="nc"> row.getChildren().add(img);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> grid.getChildren().add(row);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> if (!hasAnyCard) {</b>
|
||||
<b class="nc"> Label empty = new Label("No cards");</b>
|
||||
<b class="nc"> empty.setStyle("-fx-font-size: 14px; -fx-text-fill: #888888;");</b>
|
||||
<b class="nc"> grid.getChildren().add(empty);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> ScrollPane scrollPane = new ScrollPane(grid);</b>
|
||||
<b class="nc"> scrollPane.setFitToWidth(true);</b>
|
||||
<b class="nc"> scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);</b>
|
||||
<b class="nc"> scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);</b>
|
||||
<b class="nc"> scrollPane.setStyle("-fx-background: transparent; -fx-background-color: transparent;");</b>
|
||||
|
||||
<b class="nc"> Window window = rootPane.getScene().getWindow();</b>
|
||||
<b class="nc"> scrollPane.setMaxHeight(window.getHeight() * 0.8); // cap scroll area to 80% of window height</b>
|
||||
<b class="nc"> popupContent.getChildren().add(scrollPane);</b>
|
||||
<b class="nc"> popup.show(window, 0, 0);</b>
|
||||
<b class="nc"> popup.getScene().setFill(Color.TRANSPARENT);</b>
|
||||
<b class="nc"> popup.setX(window.getX() + (window.getWidth() - popup.getWidth()) / 2);</b>
|
||||
<b class="nc"> popup.setY(window.getY() + (window.getHeight() - popup.getHeight()) / 2);</b>
|
||||
}
|
||||
|
||||
/** Returns a copy of the named card collection for {@code username}. */
|
||||
private ArrayList<PlayableCard> getPlayerCards(String username, String type) {
|
||||
<b class="nc"> Player p = controller.getMiniModel().players.get(username);</b>
|
||||
<b class="nc"> return switch (type) {</b>
|
||||
<b class="nc"> case "artists" -> new ArrayList<>(p.getArtists());</b>
|
||||
<b class="nc"> case "gatherers" -> new ArrayList<>(p.getGatherers());</b>
|
||||
<b class="nc"> case "inventors" -> new ArrayList<>(p.getInventors());</b>
|
||||
<b class="nc"> case "builders" -> new ArrayList<>(p.getBuilders());</b>
|
||||
<b class="nc"> case "shamans" -> new ArrayList<>(p.getShamans());</b>
|
||||
<b class="nc"> case "hunters" -> new ArrayList<>(p.getHunters());</b>
|
||||
<b class="nc"> case "buildingCards" -> new ArrayList<>(p.getBuildingCards());</b>
|
||||
<b class="nc"> default -> new ArrayList<>();</b>
|
||||
};
|
||||
}
|
||||
|
||||
// ==== BACKGROUND ====
|
||||
/** Sets the full-cover background image on the root pane. */
|
||||
private void renderBackground() {
|
||||
<b class="nc"> BackgroundSize size = new BackgroundSize(</b>
|
||||
BackgroundSize.AUTO, BackgroundSize.AUTO,
|
||||
false, false, true, true
|
||||
);
|
||||
<b class="nc"> rootPane.setBackground(new Background(new BackgroundImage(</b>
|
||||
<b class="nc"> loadImage("/GUIImages/Background.png"),</b>
|
||||
BackgroundRepeat.NO_REPEAT,
|
||||
BackgroundRepeat.NO_REPEAT,
|
||||
BackgroundPosition.CENTER,
|
||||
size
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
// ==== ACTIONS ====
|
||||
/** Disconnects the client and invokes the post-game action to return to the login scene. */
|
||||
@FXML
|
||||
private void onNewGame() {
|
||||
<b class="nc"> controller.disconnect();</b>
|
||||
<b class="nc"> action.run();</b>
|
||||
}
|
||||
}
|
||||
</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-19 22: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"> package it.polimi.ingsw.gc14.View.GUI;
|
||||
|
||||
import it.polimi.ingsw.gc14.Controller.ClientController;
|
||||
import it.polimi.ingsw.gc14.ErrorType;
|
||||
import it.polimi.ingsw.gc14.Network.IClient;
|
||||
import it.polimi.ingsw.gc14.Network.NetworkConfig;
|
||||
import it.polimi.ingsw.gc14.Network.InterfaceResolver;
|
||||
import it.polimi.ingsw.gc14.Network.RMI.Client.RMIClient;
|
||||
import it.polimi.ingsw.gc14.Network.TCP.Client.TCPClient;
|
||||
import javafx.animation.*;
|
||||
import javafx.application.Platform;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* FXML controller for the login scene.
|
||||
*
|
||||
* <p>Handles username/IP/player-count input, protocol selection (TCP/RMI),
|
||||
* and initiates the connection to the server.
|
||||
*/
|
||||
<b class="nc"> public class LoginFXMLController {</b>
|
||||
/** Background image displayed behind the login form. */
|
||||
@FXML private ImageView backgroundImage;
|
||||
/** Text field for the player's username. */
|
||||
@FXML private TextField campoNome;
|
||||
/** Text field for the desired number of players. */
|
||||
@FXML private TextField campoNumPlayers;
|
||||
/** Text field for the server IP address. */
|
||||
@FXML private TextField campoIP;
|
||||
/** Button that submits the login form. */
|
||||
@FXML private Button btnAccedi;
|
||||
/** Label used to show error or success messages. */
|
||||
@FXML private Label labelErrore;
|
||||
/** VBox containing the login form controls. */
|
||||
@FXML private VBox formPanel;
|
||||
/** Toggle container for switching between RMI and TCP. */
|
||||
@FXML private StackPane protocolToggle;
|
||||
/** Sliding thumb inside the protocol toggle. */
|
||||
@FXML private Region toggleThumb;
|
||||
/** Label for the RMI side of the protocol toggle. */
|
||||
@FXML private Label labelRMI;
|
||||
/** Label for the TCP side of the protocol toggle. */
|
||||
@FXML private Label labelTCP;
|
||||
/** {@code true} when RMI is selected; {@code false} for TCP. */
|
||||
<b class="nc"> private boolean isRMI = true;</b>
|
||||
/** Client controller used to initiate the connection. */
|
||||
private ClientController controller;
|
||||
/** CSS pseudo-class applied to the active protocol label. */
|
||||
<b class="nc"> private final PseudoClass activeProtocolPseudo = PseudoClass.getPseudoClass("active-protocol");</b>
|
||||
|
||||
/**
|
||||
* Injects the client controller into this FXML controller.
|
||||
*
|
||||
* @param controller the client controller to use.
|
||||
*/
|
||||
public void setController(ClientController controller) {
|
||||
<b class="nc"> this.controller = controller;</b>
|
||||
}
|
||||
|
||||
/** Initializes the scene: loads background, sets up animations, and binds input listeners. */
|
||||
@FXML
|
||||
public void initialize() {
|
||||
// Background setup
|
||||
<b class="nc"> Image img = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/GUIImages/BackgroundLogin.png")));</b>
|
||||
<b class="nc"> backgroundImage.setImage(img);</b>
|
||||
<b class="nc"> Rectangle2D screenBounds = Screen.getPrimary().getBounds();</b>
|
||||
<b class="nc"> backgroundImage.setFitWidth(screenBounds.getWidth());</b>
|
||||
<b class="nc"> backgroundImage.setFitHeight(screenBounds.getHeight());</b>
|
||||
|
||||
// Protocol toggle configuration
|
||||
<b class="nc"> protocolToggle.setOnMouseClicked(e -> switchProtocol());</b>
|
||||
<b class="nc"> protocolToggle.setStyle(protocolToggle.getStyle() + " -fx-cursor: hand;");</b>
|
||||
|
||||
<b class="nc"> labelRMI.pseudoClassStateChanged(activeProtocolPseudo, true);</b>
|
||||
<b class="nc"> labelTCP.pseudoClassStateChanged(activeProtocolPseudo, false);</b>
|
||||
|
||||
// Login Action
|
||||
<b class="nc"> btnAccedi.setOnAction(e -> onAccediClick());</b>
|
||||
}
|
||||
|
||||
/** Toggles the selected protocol between RMI and TCP, animating the toggle thumb and updating label styles. */
|
||||
private void switchProtocol() {
|
||||
<b class="nc"> isRMI = !isRMI;</b>
|
||||
|
||||
<b class="nc"> TranslateTransition tt = new TranslateTransition(Duration.millis(200), toggleThumb);</b>
|
||||
<b class="nc"> tt.setToX(isRMI ? 0 : 110);</b>
|
||||
<b class="nc"> tt.play();</b>
|
||||
|
||||
<b class="nc"> labelRMI.pseudoClassStateChanged(activeProtocolPseudo, isRMI);</b>
|
||||
<b class="nc"> labelTCP.pseudoClassStateChanged(activeProtocolPseudo, !isRMI);</b>
|
||||
}
|
||||
|
||||
/** Validates form input and starts a background thread to connect to the server. */
|
||||
@FXML
|
||||
private void onAccediClick() {
|
||||
<b class="nc"> String name = campoNome.getText().trim();</b>
|
||||
<b class="nc"> String ip = campoIP.getText().trim();</b>
|
||||
|
||||
<b class="nc"> if (name.isEmpty()) {</b>
|
||||
<b class="nc"> showError("Please select a name.");</b>
|
||||
return;
|
||||
}
|
||||
|
||||
int numPlayers;
|
||||
try {
|
||||
<b class="nc"> numPlayers = Integer.parseInt(campoNumPlayers.getText().trim());</b>
|
||||
} catch (NumberFormatException e) {
|
||||
<b class="nc"> showError("Invalid number of players.");</b>
|
||||
return;
|
||||
}
|
||||
|
||||
<b class="nc"> updateLoginButton(false);</b>
|
||||
<b class="nc"> controller.setMyUsername(name);</b>
|
||||
|
||||
<b class="nc"> new Thread(() -> {</b>
|
||||
try {
|
||||
<b class="nc"> String localInterface = InterfaceResolver.resolveLocalInterface(ip);</b>
|
||||
<b class="nc"> System.setProperty("java.rmi.server.hostname", localInterface);</b>
|
||||
<b class="nc"> connect(name, ip, numPlayers, localInterface);</b>
|
||||
} catch (Exception ex) {
|
||||
<b class="nc"> Platform.runLater(() -> {</b>
|
||||
<b class="nc"> showError("Network error: " + ex.getMessage());</b>
|
||||
<b class="nc"> updateLoginButton(true);</b>
|
||||
});
|
||||
}
|
||||
<b class="nc"> }).start();</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the login button and updates its visual style.
|
||||
*
|
||||
* @param enabled {@code true} to enable the button, {@code false} to disable it.
|
||||
*/
|
||||
public void updateLoginButton(boolean enabled) {
|
||||
<b class="nc"> btnAccedi.setDisable(!enabled);</b>
|
||||
<b class="nc"> btnAccedi.setStyle(</b>
|
||||
"-fx-font-family: 'Cinzel'; -fx-font-size: 12; -fx-font-weight: bold;" +
|
||||
"-fx-letter-spacing: 4; -fx-text-fill: #0c0601;" +
|
||||
"-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
|
||||
"-fx-padding: 13 48 13 48; -fx-background-radius: 2;" +
|
||||
<b class="nc"> "-fx-opacity: " + (enabled ? "1.0" : "0.3") + ";" +</b>
|
||||
<b class="nc"> "-fx-cursor: " + (enabled ? "hand" : "default") + ";"</b>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Connects to the server using the selected protocol and transitions to the waiting state on success. */
|
||||
private void connect(String nome, String ip, int numPlayers, String localInterface) {
|
||||
IClient client;
|
||||
<b class="nc"> if (isRMI) {</b>
|
||||
<b class="nc"> client = new RMIClient(controller, ip, NetworkConfig.RMI_PORT, localInterface);</b>
|
||||
<b class="nc"> ErrorType serverResponse = client.connect(nome, numPlayers);</b>
|
||||
<b class="nc"> if (serverResponse == null) {</b>
|
||||
<b class="nc"> controller.setClient(client);</b>
|
||||
<b class="nc"> Platform.runLater(() -> showSuccess("Connected! Waiting for other players…"));</b>
|
||||
} else {
|
||||
<b class="nc"> Platform.runLater(() -> {</b>
|
||||
<b class="nc"> showError(serverResponse);</b>
|
||||
<b class="nc"> updateLoginButton(true);</b>
|
||||
});
|
||||
}
|
||||
} else {
|
||||
<b class="nc"> client = new TCPClient(controller, ip, NetworkConfig.TCP_PORT, NetworkConfig.HEARTBEAT_PORT);</b>
|
||||
<b class="nc"> ErrorType serverResponse = client.connect(nome, numPlayers);</b>
|
||||
<b class="nc"> if (serverResponse == null) {</b>
|
||||
<b class="nc"> controller.setClient(client);</b>
|
||||
<b class="nc"> Platform.runLater(() -> showSuccess("Connected! Waiting for other players…"));</b>
|
||||
} else {
|
||||
<b class="nc"> Platform.runLater(() -> {</b>
|
||||
<b class="nc"> showError(serverResponse);</b>
|
||||
<b class="nc"> updateLoginButton(true);</b>
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Displays an error from an {@link ErrorType} constant in the login form label.
|
||||
*
|
||||
* @param errorType the error to display.
|
||||
*/
|
||||
public void showError(ErrorType errorType) {
|
||||
<b class="nc"> labelErrore.setTextFill(Color.web("#e05050"));</b>
|
||||
<b class="nc"> labelErrore.setText(errorType.toString());</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an arbitrary error message in the login form label.
|
||||
*
|
||||
* @param msg the error message to display.
|
||||
*/
|
||||
public void showError(String msg) {
|
||||
<b class="nc"> labelErrore.setTextFill(Color.web("#e05050"));</b>
|
||||
<b class="nc"> labelErrore.setText(msg);</b>
|
||||
}
|
||||
|
||||
/** Displays a success message in green in the login form label. */
|
||||
private void showSuccess(String msg) {
|
||||
<b class="nc"> labelErrore.setTextFill(Color.web("#6fcf8a"));</b>
|
||||
<b class="nc"> labelErrore.setText(msg);</b>
|
||||
}
|
||||
}
|
||||
</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-19 22:53</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,930 @@
|
||||
|
||||
|
||||
|
||||
<!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/53)
|
||||
</span>
|
||||
</td>
|
||||
<td class="coverageStat">
|
||||
<span class="percent">
|
||||
0%
|
||||
</span>
|
||||
<span class="absValue">
|
||||
(0/137)
|
||||
</span>
|
||||
</td>
|
||||
<td class="coverageStat">
|
||||
<span class="percent">
|
||||
0%
|
||||
</span>
|
||||
<span class="absValue">
|
||||
(0/448)
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<pre>
|
||||
<code class="sourceCode" id="sourceCode"> package it.polimi.ingsw.gc14.View.GUI;
|
||||
|
||||
import it.polimi.ingsw.gc14.Controller.ClientController;
|
||||
import it.polimi.ingsw.gc14.Model.*;
|
||||
import it.polimi.ingsw.gc14.Model.Cards.BuildingCard;
|
||||
import it.polimi.ingsw.gc14.Model.Cards.TribeCard;
|
||||
import it.polimi.ingsw.gc14.Model.Orders.OrderPlayer;
|
||||
import it.polimi.ingsw.gc14.Network.EventType;
|
||||
import javafx.application.Platform;
|
||||
import javafx.animation.PauseTransition;
|
||||
import javafx.animation.ScaleTransition;
|
||||
import javafx.animation.TranslateTransition;
|
||||
import javafx.event.Event;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.effect.ColorAdjust;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.*;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Popup;
|
||||
import javafx.stage.Window;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* FXML controller for the main game scene.
|
||||
*
|
||||
* <p>Renders the board (upper/lower tribe and building card rows), player stats,
|
||||
* the player's hand, and action buttons (skip, details).
|
||||
*/
|
||||
<b class="nc"> public class MainFXMLController {</b>
|
||||
/** Scrollable container holding all player side-panel cards. */
|
||||
@FXML private ScrollPane playerSide;
|
||||
/** Root HBox containing all main scene elements. */
|
||||
@FXML private HBox mainHBox;
|
||||
/** HBox holding the board slot and order cards. */
|
||||
@FXML private HBox board;
|
||||
/** HBox holding the upper tribe card row (and upper building stack). */
|
||||
@FXML private HBox upperList;
|
||||
/** HBox holding the lower tribe card row (and lower building stack). */
|
||||
@FXML private HBox lowerList;
|
||||
/** HBox displaying the local player's card piles. */
|
||||
@FXML private HBox myHand;
|
||||
/** Button that sends a skip-turn command. */
|
||||
@FXML private Button skipBtn;
|
||||
/** Button that opens the game details popup. */
|
||||
@FXML private Button detailsBtn;
|
||||
/** Button that disconnects and closes the client. */
|
||||
@FXML private Button quitBtn;
|
||||
/** Label showing the current round number and game stage. */
|
||||
@FXML private Label infoText;
|
||||
/** Maps each player's username to their current food-value label in the side panel. */
|
||||
<b class="nc"> private final Map<String, Label> foodLabels = new HashMap<>();</b>
|
||||
/** Maps each player's username to their current prestige-value label in the side panel. */
|
||||
<b class="nc"> private final Map<String, Label> prestigeLabels = new HashMap<>();</b>
|
||||
/** Maps each player's username to their player-card VBox in the side panel. */
|
||||
<b class="nc"> private final Map<String, VBox> playerCards = new HashMap<>();</b>
|
||||
/** Maps each player's username to their ordered list of card-type icon ImageViews. */
|
||||
<b class="nc"> private final Map<String, List<ImageView>> iconViews = new HashMap<>();</b>
|
||||
/** Shared popup used for card previews and building selection. */
|
||||
private Popup popup;
|
||||
/** HBox inside {@link #popup} that holds the displayed card images. */
|
||||
private HBox popupCards;
|
||||
/** Client controller for fetching game state and sending player actions. */
|
||||
private ClientController controller;
|
||||
|
||||
/** {@code true} when the last received event was an error response. */
|
||||
private boolean isError;
|
||||
|
||||
/**
|
||||
* Sets the error flag; called by {@link GUI} before delegating to {@link #render()}.
|
||||
*
|
||||
* @param error {@code true} if the last received event was an error response.
|
||||
*/
|
||||
<b class="nc"> public void setError(boolean error) { this.isError = error; }</b>
|
||||
|
||||
// ==== IMAGE CACHE ====
|
||||
<b class="nc"> private static final Map<String, Image> imageCache = new HashMap<>();</b>
|
||||
|
||||
/** Returns a cached {@link Image} for the given classpath {@code path}, loading it on first access. */
|
||||
private Image loadImage(String path) {
|
||||
<b class="nc"> return imageCache.computeIfAbsent(path,</b>
|
||||
<b class="nc"> p -> new Image(Objects.requireNonNull(getClass().getResourceAsStream(p))));</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the client controller and wires up the skip-turn button action.
|
||||
*
|
||||
* @param controller the client controller to use.
|
||||
*/
|
||||
public void setController(ClientController controller) {
|
||||
<b class="nc"> this.controller = controller;</b>
|
||||
<b class="nc"> skipBtn.setOnAction(e -> controller.skipTurn());</b>
|
||||
|
||||
<b class="nc"> PauseTransition quitTimer = new PauseTransition(javafx.util.Duration.seconds(3));</b>
|
||||
<b class="nc"> quitTimer.setOnFinished(e -> {</b>
|
||||
<b class="nc"> quitBtn.setText("Quit");</b>
|
||||
<b class="nc"> quitBtn.setStyle("");</b>
|
||||
});
|
||||
<b class="nc"> final boolean[] quitPending = {false};</b>
|
||||
<b class="nc"> quitBtn.setOnAction(e -> {</b>
|
||||
<b class="nc"> if (quitPending[0]) {</b>
|
||||
<b class="nc"> controller.disconnect();</b>
|
||||
<b class="nc"> Platform.exit();</b>
|
||||
<b class="nc"> System.exit(0);</b>
|
||||
} else {
|
||||
<b class="nc"> quitPending[0] = true;</b>
|
||||
<b class="nc"> quitBtn.setText("Sure?");</b>
|
||||
<b class="nc"> quitBtn.setStyle("-fx-background-color: #c0392b; -fx-text-fill: white;");</b>
|
||||
<b class="nc"> quitTimer.playFromStart();</b>
|
||||
<b class="nc"> quitTimer.setOnFinished(ev -> {</b>
|
||||
<b class="nc"> quitPending[0] = false;</b>
|
||||
<b class="nc"> quitBtn.setText("Quit");</b>
|
||||
<b class="nc"> quitBtn.setStyle("");</b>
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Initializes the scene: loads fonts, sets up the popup, and registers input listeners. */
|
||||
@FXML
|
||||
public void initialize() {
|
||||
<b class="nc"> Font.loadFont(getClass().getResourceAsStream("/Fonts/InknutAntiqua-Regular.ttf"), 14);</b>
|
||||
|
||||
<b class="nc"> addHoverZoom(skipBtn);</b>
|
||||
<b class="nc"> addHoverZoom(detailsBtn);</b>
|
||||
<b class="nc"> addHoverZoom(quitBtn);</b>
|
||||
<b class="nc"> popup = new Popup();</b>
|
||||
<b class="nc"> mainHBox.sceneProperty().addListener((obs, oldScene, newScene) -> {</b>
|
||||
<b class="nc"> if (newScene != null) {</b>
|
||||
<b class="nc"> newScene.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {</b>
|
||||
<b class="nc"> if (popup.isShowing()) popup.hide();</b>
|
||||
});
|
||||
<b class="nc"> newScene.getRoot().applyCss();</b>
|
||||
<b class="nc"> newScene.windowProperty().addListener((o2, ow, nw) -> {</b>
|
||||
<b class="nc"> if (nw != null) {</b>
|
||||
<b class="nc"> nw.focusedProperty().addListener((o3, wf, nf) -> {</b>
|
||||
<b class="nc"> if (!nf) popup.hide();</b>
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
<b class="nc"> popup.hide();</b>
|
||||
}
|
||||
});
|
||||
<b class="nc"> initPopup();</b>
|
||||
<b class="nc"> detailsBtn.setOnAction(e -> openDetailsPopup());</b>
|
||||
<b class="nc"> renderBackground();</b>
|
||||
<b class="nc"> isError=false;</b>
|
||||
}
|
||||
|
||||
|
||||
// ==== RENDER ====
|
||||
/** Incrementally re-renders only the parts of the scene affected by the last network event. */
|
||||
public void render() {
|
||||
<b class="nc"> if(foodLabels.isEmpty() || controller.getMiniModel().lastEvent == null</b>
|
||||
<b class="nc"> || controller.getMiniModel().lastEvent.getEventType().equals(EventType.TOTEM_CHOICE)</b>
|
||||
<b class="nc"> || controller.getMiniModel().lastEvent.getEventType().equals(EventType.RECONNECT_PLAYER)</b>
|
||||
<b class="nc"> || controller.getMiniModel().lastEvent.getEventType().equals(EventType.DISCONNECTED_PLAYER))</b>
|
||||
<b class="nc"> buildSidePanel();</b>
|
||||
<b class="nc"> if(isError)</b>
|
||||
{
|
||||
<b class="nc"> updateSidePanel();</b>
|
||||
<b class="nc"> isError=false;</b>
|
||||
return;
|
||||
}
|
||||
<b class="nc"> if(controller.getMiniModel().lastEvent==null ||controller.getMiniModel().lastEvent.getEventType().equals(EventType.TOTEM_CHOICE) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.NEXT_ROUND) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.RECONNECT_PLAYER) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.DISCONNECTED_PLAYER)) {</b>
|
||||
<b class="nc"> renderMyHand();</b>
|
||||
<b class="nc"> renderUpper();</b>
|
||||
<b class="nc"> renderBoard();</b>
|
||||
<b class="nc"> renderLower();</b>
|
||||
<b class="nc"> updateSidePanel();</b>
|
||||
return;
|
||||
}
|
||||
<b class="nc"> if(controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_UPPER_TRIBE) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_UPPER_BUILD))</b>
|
||||
{
|
||||
<b class="nc"> renderUpper();</b>
|
||||
}
|
||||
<b class="nc"> if(controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_LOWER_TRIBE) || controller.getMiniModel().lastEvent.getEventType().equals(EventType.DRAW_LOWER_BUILD))</b>
|
||||
{
|
||||
<b class="nc"> renderLower();</b>
|
||||
}
|
||||
<b class="nc"> if(controller.getMiniModel().lastEvent.getUsername().equals(controller.getMyUsername()))</b>
|
||||
{
|
||||
<b class="nc"> renderMyHand();</b>
|
||||
}
|
||||
<b class="nc"> renderBoard();</b>
|
||||
<b class="nc"> updateSidePanel();</b>
|
||||
}
|
||||
|
||||
|
||||
// ==== EFFECTS ====
|
||||
/** Binds a rounded-rectangle clip to {@code img} so its corners are cropped. */
|
||||
private void addClip(ImageView img) {
|
||||
<b class="nc"> Rectangle clip = new Rectangle();</b>
|
||||
<b class="nc"> clip.setArcWidth(20);</b>
|
||||
<b class="nc"> clip.setArcHeight(20);</b>
|
||||
<b class="nc"> img.setClip(clip);</b>
|
||||
<b class="nc"> clip.heightProperty().bind(img.layoutBoundsProperty().map(b -> b.getHeight()));</b>
|
||||
<b class="nc"> clip.widthProperty().bind(img.layoutBoundsProperty().map(b -> b.getWidth()));</b>
|
||||
}
|
||||
|
||||
/** Scales {@code node} to 1.1× on hover and restores on exit; sets a hand cursor. */
|
||||
private void addHoverZoom(Node node) {
|
||||
<b class="nc"> ScaleTransition scaleUp = new ScaleTransition(Duration.millis(150), node);</b>
|
||||
<b class="nc"> scaleUp.setToX(1.1);</b>
|
||||
<b class="nc"> scaleUp.setToY(1.1);</b>
|
||||
<b class="nc"> ScaleTransition scaleDown = new ScaleTransition(Duration.millis(150), node);</b>
|
||||
<b class="nc"> scaleDown.setToX(1.0);</b>
|
||||
<b class="nc"> scaleDown.setToY(1.0);</b>
|
||||
<b class="nc"> node.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> { scaleUp.play(); node.setCursor(Cursor.HAND); });</b>
|
||||
<b class="nc"> node.addEventHandler(MouseEvent.MOUSE_EXITED, e -> { scaleDown.play(); node.setCursor(Cursor.DEFAULT); });</b>
|
||||
}
|
||||
|
||||
/** Applies a drop-shadow to {@code node} that intensifies on hover. */
|
||||
private void addShadow(Node node) {
|
||||
<b class="nc"> DropShadow shadow = new DropShadow();</b>
|
||||
<b class="nc"> shadow.setColor(Color.rgb(0, 0, 0, 0.3));</b>
|
||||
<b class="nc"> shadow.setRadius(6);</b>
|
||||
<b class="nc"> shadow.setOffsetX(2);</b>
|
||||
<b class="nc"> shadow.setOffsetY(2);</b>
|
||||
<b class="nc"> node.setEffect(shadow);</b>
|
||||
<b class="nc"> node.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> {</b>
|
||||
<b class="nc"> shadow.setRadius(16);</b>
|
||||
<b class="nc"> shadow.setOffsetX(4);</b>
|
||||
<b class="nc"> shadow.setOffsetY(4);</b>
|
||||
<b class="nc"> shadow.setColor(Color.rgb(0, 0, 0, 0.5));</b>
|
||||
});
|
||||
<b class="nc"> node.addEventHandler(MouseEvent.MOUSE_EXITED, e -> {</b>
|
||||
<b class="nc"> shadow.setRadius(6);</b>
|
||||
<b class="nc"> shadow.setOffsetX(2);</b>
|
||||
<b class="nc"> shadow.setOffsetY(2);</b>
|
||||
<b class="nc"> shadow.setColor(Color.rgb(0, 0, 0, 0.3));</b>
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ==== ELEMENTS ====
|
||||
|
||||
/** Builds the side-panel card widget for {@code player} with totem, stats, and card-type icons. */
|
||||
private VBox buildPlayerCard(Player player) {
|
||||
<b class="nc"> VBox card = new VBox(3);</b>
|
||||
<b class="nc"> VBox.setMargin(card, new Insets(15, 15, 0, 15));</b>
|
||||
<b class="nc"> card.getStyleClass().add("player-card");</b>
|
||||
<b class="nc"> card.setPadding(new Insets(4));</b>
|
||||
|
||||
<b class="nc"> HBox headerRow = new HBox(8);</b>
|
||||
<b class="nc"> headerRow.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> ImageView totem = new ImageView(loadImage("/GUIImages/Totems/totem_"</b>
|
||||
<b class="nc"> + player.getTotem().toString().toLowerCase(Locale.ROOT) + ".png"));</b>
|
||||
<b class="nc"> totem.setFitHeight(20);</b>
|
||||
<b class="nc"> totem.setPreserveRatio(true);</b>
|
||||
<b class="nc"> Label usernameLabel = new Label(player.getUserName());</b>
|
||||
<b class="nc"> usernameLabel.getStyleClass().add("label-medium");</b>
|
||||
<b class="nc"> usernameLabel.setStyle("-fx-text-fill: #711423;");</b>
|
||||
<b class="nc"> if (player.getUserName().equals(controller.getMyUsername())) {</b>
|
||||
<b class="nc"> usernameLabel.setText(usernameLabel.getText() + " (you)");</b>
|
||||
}
|
||||
<b class="nc"> if (controller.getMiniModel().disconnectedPlayers.contains(player.getUserName())) {</b>
|
||||
<b class="nc"> card.getStyleClass().add("player-card-crashed");</b>
|
||||
}
|
||||
<b class="nc"> headerRow.getChildren().addAll(totem, usernameLabel);</b>
|
||||
|
||||
<b class="nc"> HBox statsRow = new HBox(10);</b>
|
||||
<b class="nc"> statsRow.setAlignment(Pos.CENTER);</b>
|
||||
|
||||
<b class="nc"> HBox foodBox = new HBox(4);</b>
|
||||
<b class="nc"> foodBox.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> ImageView foodIcon = new ImageView(loadImage("/GUIImages/Icons/Food.png"));</b>
|
||||
<b class="nc"> foodIcon.setFitHeight(20);</b>
|
||||
<b class="nc"> foodIcon.setPreserveRatio(true);</b>
|
||||
<b class="nc"> Label foodLabel = new Label(String.valueOf(player.getFoodValue()));</b>
|
||||
<b class="nc"> foodLabel.getStyleClass().add("label-small");</b>
|
||||
<b class="nc"> foodLabels.put(player.getUserName(), foodLabel);</b>
|
||||
<b class="nc"> foodBox.getChildren().addAll(foodIcon, foodLabel);</b>
|
||||
|
||||
<b class="nc"> HBox prestigeBox = new HBox(1);</b>
|
||||
<b class="nc"> prestigeBox.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> ImageView ppIcon = new ImageView(loadImage("/GUIImages/Icons/PrestigePoint.png"));</b>
|
||||
<b class="nc"> ppIcon.setFitHeight(35);</b>
|
||||
<b class="nc"> ppIcon.setPreserveRatio(true);</b>
|
||||
<b class="nc"> Label prestigeLabel = new Label(String.valueOf(player.getPrestigeValue()));</b>
|
||||
<b class="nc"> prestigeLabel.getStyleClass().add("label-small");</b>
|
||||
<b class="nc"> prestigeLabels.put(player.getUserName(), prestigeLabel);</b>
|
||||
<b class="nc"> prestigeBox.getChildren().addAll(ppIcon, prestigeLabel);</b>
|
||||
|
||||
<b class="nc"> ImageView buildingIcon = new ImageView(loadImage("/GUIImages/Icons/Building.png"));</b>
|
||||
<b class="nc"> buildingIcon.setFitWidth(28);</b>
|
||||
<b class="nc"> buildingIcon.setFitHeight(28);</b>
|
||||
<b class="nc"> buildingIcon.setPreserveRatio(true);</b>
|
||||
<b class="nc"> buildingIcon.setOnMouseClicked(e -> {</b>
|
||||
<b class="nc"> ArrayList<PlayableCard> cards = getPlayerCards(player.getUserName(), "building");</b>
|
||||
<b class="nc"> if (!cards.isEmpty()) openPopup(cards);</b>
|
||||
});
|
||||
<b class="nc"> addHoverZoom(buildingIcon);</b>
|
||||
|
||||
<b class="nc"> statsRow.getChildren().addAll(foodBox, prestigeBox, buildingIcon);</b>
|
||||
|
||||
<b class="nc"> HBox iconsRow1 = new HBox(6);</b>
|
||||
<b class="nc"> iconsRow1.setSpacing(20);</b>
|
||||
<b class="nc"> iconsRow1.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> HBox iconsRow2 = new HBox(6);</b>
|
||||
<b class="nc"> iconsRow2.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> iconsRow2.setSpacing(20);</b>
|
||||
|
||||
<b class="nc"> String[][] iconTypes = {</b>
|
||||
{"Artist", "artists"},
|
||||
{"Gatherer", "gatherers"},
|
||||
{"Inventor", "inventors"},
|
||||
{"Builder", "builders"},
|
||||
{"Shaman", "shamans"},
|
||||
{"Hunter", "hunters"}
|
||||
};
|
||||
|
||||
<b class="nc"> List<ImageView> icons = new ArrayList<>();</b>
|
||||
// building must be index 0; updateSidePanel iterates icons in ["building","artists",...] order
|
||||
<b class="nc"> icons.add(buildingIcon);</b>
|
||||
|
||||
<b class="nc"> for (int i = 0; i < iconTypes.length; i++) {</b>
|
||||
<b class="nc"> String iconName = iconTypes[i][0];</b>
|
||||
<b class="nc"> String fieldName = iconTypes[i][1];</b>
|
||||
|
||||
<b class="nc"> ImageView icon = new ImageView(loadImage("/GUIImages/Icons/" + iconName + ".png"));</b>
|
||||
<b class="nc"> icon.setFitWidth(28);</b>
|
||||
<b class="nc"> icon.setFitHeight(28);</b>
|
||||
<b class="nc"> icon.setPreserveRatio(true);</b>
|
||||
<b class="nc"> icon.setOnMouseClicked(e -> {</b>
|
||||
<b class="nc"> ArrayList<PlayableCard> cards = getPlayerCards(player.getUserName(), fieldName);</b>
|
||||
<b class="nc"> if (!cards.isEmpty()) openPopup(cards);</b>
|
||||
});
|
||||
<b class="nc"> addHoverZoom(icon);</b>
|
||||
<b class="nc"> icons.add(icon);</b>
|
||||
|
||||
<b class="nc"> if (i < 3) iconsRow1.getChildren().add(icon);</b>
|
||||
<b class="nc"> else iconsRow2.getChildren().add(icon);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> iconViews.put(player.getUserName(), icons);</b>
|
||||
|
||||
<b class="nc"> card.getChildren().addAll(headerRow, statsRow, iconsRow1, iconsRow2);</b>
|
||||
<b class="nc"> return card;</b>
|
||||
}
|
||||
|
||||
/** Creates a {@link StackPane} containing the order card image for a game of {@code num} players. */
|
||||
private StackPane createOrder(String num) {
|
||||
<b class="nc"> ImageView img = new ImageView(loadImage("/GUIImages/Orders/order-" + num + ".png"));</b>
|
||||
<b class="nc"> img.fitHeightProperty().bind(board.sceneProperty().get().heightProperty().subtract(56).divide(4).multiply(0.94));</b>
|
||||
<b class="nc"> img.setPreserveRatio(true);</b>
|
||||
<b class="nc"> addClip(img);</b>
|
||||
<b class="nc"> return new StackPane(img);</b>
|
||||
}
|
||||
|
||||
/** Creates a {@link StackPane} for a board slot, optionally overlaying the occupying player's totem. */
|
||||
private StackPane createSlot(Slot slot, boolean withZoom, boolean withShadow) {
|
||||
<b class="nc"> ImageView img = new ImageView(loadImage("/GUIImages/Fronts/card-" + slot.getSlotId() + ".png"));</b>
|
||||
<b class="nc"> img.setPreserveRatio(true);</b>
|
||||
<b class="nc"> img.fitHeightProperty().bind(board.sceneProperty().get().heightProperty().subtract(56).divide(4).multiply(0.94));</b>
|
||||
<b class="nc"> addClip(img);</b>
|
||||
<b class="nc"> StackPane wrapper = new StackPane(img);</b>
|
||||
|
||||
<b class="nc"> Player player = controller.getMiniModel().slotPlayerMap.get(slot);</b>
|
||||
<b class="nc"> if (player != null && controller.getMiniModel().getPositionByUsername(player.getUserName()) != -1) {</b>
|
||||
<b class="nc"> ImageView totem = new ImageView(loadImage("/GUIImages/Totems/totem_" + player.getTotem().toString().toLowerCase(Locale.ROOT) + ".png"));</b>
|
||||
<b class="nc"> totem.fitHeightProperty().bind(img.fitHeightProperty().multiply(0.332));</b>
|
||||
<b class="nc"> totem.setPreserveRatio(true);</b>
|
||||
<b class="nc"> StackPane.setAlignment(totem, Pos.TOP_LEFT);</b>
|
||||
<b class="nc"> img.fitHeightProperty().addListener((obs, ov, nv) -> {</b>
|
||||
<b class="nc"> StackPane.setMargin(totem, new Insets(0, 0, 0, 0.224 * nv.doubleValue()));</b>
|
||||
});
|
||||
<b class="nc"> if (img.getFitHeight() > 0) {</b>
|
||||
<b class="nc"> StackPane.setMargin(totem, new Insets(0, 0, 0, 0.224 * img.getFitHeight()));</b>
|
||||
}
|
||||
<b class="nc"> wrapper.getChildren().add(totem);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> if (withShadow) addShadow(wrapper);</b>
|
||||
<b class="nc"> if (withZoom) addHoverZoom(wrapper);</b>
|
||||
<b class="nc"> return wrapper;</b>
|
||||
}
|
||||
|
||||
/** Creates a {@link StackPane} for a playable card, scaling its height to a fraction of {@code parent}'s scene. */
|
||||
private StackPane createCard(PlayableCard card, boolean withZoom, boolean withShadow, Region parent) {
|
||||
<b class="nc"> ImageView img = new ImageView(loadImage("/GUIImages/Fronts/card-" + card.getIdIMG() + ".png"));</b>
|
||||
<b class="nc"> img.setPreserveRatio(true);</b>
|
||||
<b class="nc"> if(controller.getMiniModel().players.size()==5)</b>
|
||||
{
|
||||
<b class="nc"> img.fitHeightProperty().bind(parent.getScene().heightProperty().subtract(56).divide(4).multiply(0.85));</b>
|
||||
}
|
||||
else
|
||||
<b class="nc"> img.fitHeightProperty().bind(parent.getScene().heightProperty().subtract(56).divide(4).multiply(0.90));</b>
|
||||
<b class="nc"> addClip(img);</b>
|
||||
<b class="nc"> StackPane wrapper = new StackPane(img);</b>
|
||||
<b class="nc"> if (withShadow) addShadow(wrapper);</b>
|
||||
<b class="nc"> if (withZoom) addHoverZoom(wrapper);</b>
|
||||
<b class="nc"> return wrapper;</b>
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==== GROUPS ====
|
||||
/** Builds the full side panel from scratch, creating a player card for each connected player. */
|
||||
private void buildSidePanel() {
|
||||
<b class="nc"> infoText.setText("Round: "+ controller.getMiniModel().currentState.getRound() + " • " + controller.getMiniModel().currentState.getGameStage().toString());</b>
|
||||
<b class="nc"> VBox playerList = new VBox();</b>
|
||||
<b class="nc"> playerList.setFillWidth(true);</b>
|
||||
<b class="nc"> for (Player p : controller.getMiniModel().players.values()) {</b>
|
||||
<b class="nc"> VBox card = buildPlayerCard(p);</b>
|
||||
<b class="nc"> playerCards.put(p.getUserName(), card);</b>
|
||||
<b class="nc"> playerList.getChildren().add(card);</b>
|
||||
}
|
||||
<b class="nc"> playerSide.setContent(playerList);</b>
|
||||
}
|
||||
|
||||
/** Updates food/prestige labels, current-player highlight, card-icon opacity, and plays error shake if needed. */
|
||||
private void updateSidePanel() {
|
||||
<b class="nc"> infoText.setText("Round: "+ controller.getMiniModel().currentState.getRound() + " • " + controller.getMiniModel().currentState.getGameStage().toString());</b>
|
||||
<b class="nc"> if (controller.getMiniModel().currentState.getCurrentPlayer() == null) return;</b>
|
||||
<b class="nc"> String current = controller.getMiniModel().currentState.getCurrentPlayer().getUserName();</b>
|
||||
<b class="nc"> String[] fields = {"building", "artists", "gatherers", "inventors", "builders", "shamans", "hunters"};</b>
|
||||
|
||||
<b class="nc"> for (Player p : controller.getMiniModel().players.values()) {</b>
|
||||
<b class="nc"> String u = p.getUserName();</b>
|
||||
|
||||
<b class="nc"> foodLabels.get(u).setText(String.valueOf(p.getFoodValue()));</b>
|
||||
<b class="nc"> prestigeLabels.get(u).setText(String.valueOf(p.getPrestigeValue()));</b>
|
||||
|
||||
<b class="nc"> List<ImageView> icons = iconViews.get(u);</b>
|
||||
<b class="nc"> for (int i = 0; i < icons.size(); i++) {</b>
|
||||
<b class="nc"> boolean empty = getPlayerCards(u, fields[i]).isEmpty();</b>
|
||||
<b class="nc"> icons.get(i).setOpacity(empty ? 0.3 : 1.0);</b>
|
||||
<b class="nc"> icons.get(i).setEffect(empty ? new ColorAdjust() : null);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> VBox card = playerCards.get(u);</b>
|
||||
<b class="nc"> if(controller.getMiniModel().disconnectedPlayers.contains(u))</b>
|
||||
{
|
||||
<b class="nc"> card.getStyleClass().clear();</b>
|
||||
<b class="nc"> card.getStyleClass().add("player-card-crashed");</b>
|
||||
}
|
||||
else {
|
||||
<b class="nc"> card.getStyleClass().clear();</b>
|
||||
<b class="nc"> card.getStyleClass().add("player-card");</b>
|
||||
}
|
||||
|
||||
<b class="nc"> if (u.equals(current)) {</b>
|
||||
<b class="nc"> card.setStyle("-fx-effect: dropshadow(gaussian, #fff8dc, 15, 0.10, 0, 0)");</b>
|
||||
<b class="nc"> ScaleTransition st = new ScaleTransition(Duration.millis(150), card);</b>
|
||||
<b class="nc"> st.setToX(1.1);</b>
|
||||
<b class="nc"> st.setToY(1.1);</b>
|
||||
<b class="nc"> st.play();</b>
|
||||
} else {
|
||||
<b class="nc"> ScaleTransition st = new ScaleTransition(Duration.millis(150), card);</b>
|
||||
<b class="nc"> st.setToX(1.0);</b>
|
||||
<b class="nc"> st.setToY(1.0);</b>
|
||||
<b class="nc"> st.play();</b>
|
||||
<b class="nc"> card.setStyle("");</b>
|
||||
}
|
||||
|
||||
<b class="nc"> if (isError && u.equals(controller.getMyUsername())) {</b>
|
||||
<b class="nc"> TranslateTransition tt = new TranslateTransition(Duration.millis(56), card);</b>
|
||||
<b class="nc"> tt.setFromX(0);</b>
|
||||
<b class="nc"> tt.setByX(10);</b>
|
||||
<b class="nc"> tt.setCycleCount(6);</b>
|
||||
<b class="nc"> tt.setAutoReverse(true);</b>
|
||||
<b class="nc"> tt.play();</b>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Renders the top card of the upper building stack with a count badge; clicking opens the selection popup. */
|
||||
private void drawUpperBuilding() {
|
||||
<b class="nc"> ArrayList<BuildingCard> cardList = new ArrayList<>(controller.getMiniModel().upperListBuildingCards);</b>
|
||||
<b class="nc"> if (!cardList.isEmpty()) {</b>
|
||||
<b class="nc"> StackPane img = createCard(cardList.getLast(), true, true, upperList);</b>
|
||||
<b class="nc"> Label label = new Label(String.valueOf(cardList.size()));</b>
|
||||
<b class="nc"> label.setTranslateY(-56);</b>
|
||||
<b class="nc"> label.setTranslateX(10);</b>
|
||||
<b class="nc"> label.getStyleClass().add("label-large");</b>
|
||||
<b class="nc"> StackPane.setAlignment(label, Pos.TOP_RIGHT);</b>
|
||||
<b class="nc"> img.getChildren().add(label);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> openPopupUpperBuilding(cardList));</b>
|
||||
<b class="nc"> upperList.getChildren().add(img);</b>
|
||||
}
|
||||
}
|
||||
|
||||
/** Renders the top card of the lower building stack with a count badge; clicking opens the selection popup. */
|
||||
private void drawLowerBuilding() {
|
||||
<b class="nc"> ArrayList<BuildingCard> cardList = new ArrayList<>(controller.getMiniModel().lowerListBuildingCards);</b>
|
||||
<b class="nc"> if (!cardList.isEmpty()) {</b>
|
||||
<b class="nc"> StackPane img = createCard(cardList.getLast(), true, true, lowerList);</b>
|
||||
<b class="nc"> Label label = new Label(String.valueOf(cardList.size()));</b>
|
||||
<b class="nc"> label.setTranslateY(-56);</b>
|
||||
<b class="nc"> label.setTranslateX(10);</b>
|
||||
<b class="nc"> label.getStyleClass().add("label-large");</b>
|
||||
<b class="nc"> StackPane.setAlignment(label, Pos.TOP_RIGHT);</b>
|
||||
<b class="nc"> img.getChildren().add(label);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> openPopupLowerBuilding(cardList));</b>
|
||||
<b class="nc"> lowerList.getChildren().add(img);</b>
|
||||
}
|
||||
}
|
||||
|
||||
/** Renders a single card-type pile in the hand area, or an invisible placeholder if the list is empty. */
|
||||
private void drawMyHandList(ArrayList<? extends PlayableCard> cardList) {
|
||||
<b class="nc"> if (!cardList.isEmpty()) {</b>
|
||||
<b class="nc"> StackPane img = createCard(cardList.getLast(), true, true, myHand);</b>
|
||||
<b class="nc"> Label label = new Label(String.valueOf(cardList.size()));</b>
|
||||
<b class="nc"> label.setTranslateY(-56);</b>
|
||||
<b class="nc"> label.setTranslateX(10);</b>
|
||||
<b class="nc"> label.getStyleClass().add("label-large");</b>
|
||||
<b class="nc"> StackPane.setAlignment(label, Pos.TOP_RIGHT);</b>
|
||||
<b class="nc"> img.getChildren().add(label);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> openPopup(new ArrayList<>(cardList)));</b>
|
||||
<b class="nc"> myHand.getChildren().add(img);</b>
|
||||
} else {
|
||||
<b class="nc"> Region placeholder = new Region();</b>
|
||||
<b class="nc"> placeholder.prefHeightProperty().bind(myHand.getScene().heightProperty().subtract(56).divide(4).multiply(0.90));</b>
|
||||
<b class="nc"> placeholder.prefWidthProperty().bind(placeholder.prefHeightProperty().multiply(0.675));</b>
|
||||
<b class="nc"> placeholder.setStyle("-fx-background-color: transparent;");</b>
|
||||
<b class="nc"> myHand.getChildren().add(placeholder);</b>
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a copy of the named card collection for {@code username}. */
|
||||
private ArrayList<PlayableCard> getPlayerCards(String username, String type) {
|
||||
<b class="nc"> Player p = controller.getMiniModel().players.get(username);</b>
|
||||
<b class="nc"> return switch (type) {</b>
|
||||
<b class="nc"> case "artists" -> new ArrayList<>(p.getArtists());</b>
|
||||
<b class="nc"> case "gatherers" -> new ArrayList<>(p.getGatherers());</b>
|
||||
<b class="nc"> case "inventors" -> new ArrayList<>(p.getInventors());</b>
|
||||
<b class="nc"> case "builders" -> new ArrayList<>(p.getBuilders());</b>
|
||||
<b class="nc"> case "shamans" -> new ArrayList<>(p.getShamans());</b>
|
||||
<b class="nc"> case "hunters" -> new ArrayList<>(p.getHunters());</b>
|
||||
<b class="nc"> case "building" -> new ArrayList<>(p.getBuildingCards());</b>
|
||||
<b class="nc"> default -> new ArrayList<>();</b>
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// ==== ABSOLUTES ====
|
||||
/** Clears and rebuilds the upper tribe row with click handlers for drawing, then appends the upper building stack. */
|
||||
private void renderUpper() {
|
||||
<b class="nc"> upperList.setSpacing(14);</b>
|
||||
<b class="nc"> upperList.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> upperList.getChildren().clear();</b>
|
||||
|
||||
<b class="nc"> int i = 0;</b>
|
||||
<b class="nc"> for (TribeCard card : controller.getMiniModel().upperListTribeCards) {</b>
|
||||
<b class="nc"> final int index = i;</b>
|
||||
StackPane img;
|
||||
<b class="nc"> if(!card.isEventCard()) {</b>
|
||||
<b class="nc"> img = createCard(card, true, true, upperList);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> controller.drawUpperTribeCard(index));</b>
|
||||
} else {
|
||||
<b class="nc"> img = createCard(card, false, false, upperList);</b>
|
||||
}
|
||||
<b class="nc"> upperList.getChildren().add(img);</b>
|
||||
<b class="nc"> i++;</b>
|
||||
}
|
||||
<b class="nc"> drawUpperBuilding();</b>
|
||||
}
|
||||
|
||||
/** Clears and rebuilds the lower tribe row with click handlers for drawing, then appends the lower building stack. */
|
||||
private void renderLower() {
|
||||
<b class="nc"> lowerList.setSpacing(14);</b>
|
||||
<b class="nc"> lowerList.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> lowerList.getChildren().clear();</b>
|
||||
|
||||
<b class="nc"> int i = 0;</b>
|
||||
<b class="nc"> for (TribeCard card : controller.getMiniModel().lowerListTribeCards) {</b>
|
||||
<b class="nc"> final int index = i;</b>
|
||||
StackPane img;
|
||||
<b class="nc"> if(!card.isEventCard()) {</b>
|
||||
<b class="nc"> img = createCard(card, true, true, lowerList);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> controller.drawLowerTribeCard(index));</b>
|
||||
} else {
|
||||
<b class="nc"> img = createCard(card, false, false, lowerList);</b>
|
||||
}
|
||||
<b class="nc"> lowerList.getChildren().add(img);</b>
|
||||
<b class="nc"> i++;</b>
|
||||
}
|
||||
<b class="nc"> drawLowerBuilding();</b>
|
||||
}
|
||||
|
||||
/** Sets the full-cover background image on the main HBox container. */
|
||||
private void renderBackground() {
|
||||
<b class="nc"> BackgroundSize size = new BackgroundSize(</b>
|
||||
BackgroundSize.AUTO, BackgroundSize.AUTO,
|
||||
false, false, true, true
|
||||
);
|
||||
<b class="nc"> mainHBox.setBackground(new Background(new BackgroundImage(</b>
|
||||
<b class="nc"> loadImage("/GUIImages/Background.png"),</b>
|
||||
BackgroundRepeat.NO_REPEAT,
|
||||
BackgroundRepeat.NO_REPEAT,
|
||||
BackgroundPosition.CENTER,
|
||||
size
|
||||
)));
|
||||
}
|
||||
|
||||
/** Clears the board area and orchestrates deck, order card, and slot-map rendering. */
|
||||
private void renderBoard() {
|
||||
<b class="nc"> board.setSpacing(14);</b>
|
||||
<b class="nc"> board.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> board.getChildren().clear();</b>
|
||||
<b class="nc"> renderDeck();</b>
|
||||
<b class="nc"> renderOrder();</b>
|
||||
<b class="nc"> renderSlotMap();</b>
|
||||
}
|
||||
|
||||
/** Adds the era-specific deck back image to the board. */
|
||||
private void renderDeck() {
|
||||
<b class="nc"> String path = switch (controller.getMiniModel().currentState.getEra()) {</b>
|
||||
<b class="nc"> case 2 -> "/GUIImages/Backs/back-030.png";</b>
|
||||
<b class="nc"> case 3 -> "/GUIImages/Backs/back-058.png";</b>
|
||||
<b class="nc"> default -> "/GUIImages/Backs/back-001.png";</b>
|
||||
<b class="nc"> };</b>
|
||||
<b class="nc"> ImageView back = new ImageView(loadImage(path));</b>
|
||||
<b class="nc"> back.fitHeightProperty().bind(board.sceneProperty().get().heightProperty().subtract(56).divide(4).multiply(0.94));</b>
|
||||
<b class="nc"> back.setPreserveRatio(true);</b>
|
||||
<b class="nc"> addClip(back);</b>
|
||||
<b class="nc"> board.getChildren().add(back);</b>
|
||||
}
|
||||
|
||||
/** Renders the order card and overlays each player's totem at their proportional position. */
|
||||
private void renderOrder() {
|
||||
<b class="nc"> int numPlayers = controller.getMiniModel().players.size();</b>
|
||||
<b class="nc"> StackPane card = createOrder(Integer.toString(numPlayers));</b>
|
||||
|
||||
<b class="nc"> Map<Integer, double[]> slotPositions = Map.of(</b>
|
||||
<b class="nc"> 2, new double[]{0.229, 0.413},</b>
|
||||
<b class="nc"> 3, new double[]{0.183, 0.367, 0.548},</b>
|
||||
<b class="nc"> 4, new double[]{0.142, 0.316, 0.503, 0.690},</b>
|
||||
<b class="nc"> 5, new double[]{0.066, 0.251, 0.433, 0.617, 0.802}</b>
|
||||
);
|
||||
<b class="nc"> double[] positions = slotPositions.get(numPlayers);</b>
|
||||
<b class="nc"> if (positions == null) return;</b>
|
||||
|
||||
<b class="nc"> Pane overlay = new Pane();</b>
|
||||
<b class="nc"> overlay.setPickOnBounds(false);</b>
|
||||
<b class="nc"> overlay.setMouseTransparent(true);</b>
|
||||
|
||||
<b class="nc"> for (int i = 0; i < controller.getMiniModel().orderLogicCard.getPlayerList().size(); i++) {</b>
|
||||
<b class="nc"> OrderPlayer op = controller.getMiniModel().orderLogicCard.getPlayerList().get(i);</b>
|
||||
|
||||
<b class="nc"> ImageView totem = new ImageView(loadImage(</b>
|
||||
<b class="nc"> "/GUIImages/Totems/totem_" + op.getPlayer().getTotem().toString().toLowerCase(Locale.ROOT) + ".png"</b>
|
||||
));
|
||||
<b class="nc"> totem.setPreserveRatio(true);</b>
|
||||
|
||||
<b class="nc"> if (op.isPlayed()) {</b>
|
||||
<b class="nc"> totem.setVisible(false);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> final double yRatio = positions[i];</b>
|
||||
|
||||
<b class="nc"> overlay.prefHeightProperty().bind(card.heightProperty());</b>
|
||||
<b class="nc"> overlay.prefWidthProperty().bind(card.widthProperty());</b>
|
||||
|
||||
<b class="nc"> totem.fitHeightProperty().bind(card.heightProperty().multiply(0.323));</b>
|
||||
|
||||
<b class="nc"> card.heightProperty().addListener((obs, ov, nv) -> {</b>
|
||||
<b class="nc"> totem.setLayoutY(nv.doubleValue() * (yRatio-0.196));</b>
|
||||
});
|
||||
<b class="nc"> card.widthProperty().addListener((obs, ov, nv) -> {</b>
|
||||
<b class="nc"> totem.setLayoutX(nv.doubleValue() * 0.364);</b>
|
||||
});
|
||||
|
||||
<b class="nc"> if (card.getHeight() > 0) totem.setLayoutY(card.getHeight() *(yRatio-0.196));</b>
|
||||
<b class="nc"> if (card.getWidth() > 0) totem.setLayoutX(card.getWidth() * 0.364);</b>
|
||||
|
||||
<b class="nc"> overlay.getChildren().add(totem);</b>
|
||||
}
|
||||
|
||||
<b class="nc"> card.getChildren().add(overlay);</b>
|
||||
<b class="nc"> board.getChildren().add(card);</b>
|
||||
}
|
||||
|
||||
/** Adds a clickable slot widget for each entry in the slot-player map. */
|
||||
private void renderSlotMap() {
|
||||
<b class="nc"> int i = 0;</b>
|
||||
<b class="nc"> for (Map.Entry<Slot, Player> entry : controller.getMiniModel().slotPlayerMap.entrySet()) {</b>
|
||||
<b class="nc"> Slot slot = entry.getKey();</b>
|
||||
<b class="nc"> final int index = i;</b>
|
||||
<b class="nc"> StackPane img = createSlot(slot, true, true);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> controller.slotChoice(index));</b>
|
||||
<b class="nc"> board.getChildren().add(img);</b>
|
||||
<b class="nc"> i++;</b>
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears and re-renders all seven card-type piles in the local player's hand area. */
|
||||
private void renderMyHand() {
|
||||
<b class="nc"> myHand.getChildren().clear();</b>
|
||||
<b class="nc"> myHand.setSpacing(14);</b>
|
||||
<b class="nc"> myHand.setAlignment(Pos.CENTER);</b>
|
||||
|
||||
<b class="nc"> Player me = controller.getMiniModel().players.get(controller.getMyUsername());</b>
|
||||
<b class="nc"> drawMyHandList(me.getArtists());</b>
|
||||
<b class="nc"> drawMyHandList(me.getGatherers());</b>
|
||||
<b class="nc"> drawMyHandList(me.getInventors());</b>
|
||||
<b class="nc"> drawMyHandList(me.getBuilders());</b>
|
||||
<b class="nc"> drawMyHandList(me.getShamans());</b>
|
||||
<b class="nc"> drawMyHandList(me.getHunters());</b>
|
||||
<b class="nc"> drawMyHandList(me.getBuildingCards());</b>
|
||||
}
|
||||
|
||||
|
||||
// ==== POPUP ====
|
||||
/** Creates and configures the shared card-preview popup container. */
|
||||
private void initPopup() {
|
||||
<b class="nc"> popupCards = new HBox(10);</b>
|
||||
<b class="nc"> popupCards.setAlignment(Pos.CENTER);</b>
|
||||
<b class="nc"> popupCards.setPadding(new Insets(10));</b>
|
||||
<b class="nc"> popupCards.setStyle(</b>
|
||||
"-fx-background-color: #b42224;" +
|
||||
"-fx-border-color: black;" +
|
||||
"-fx-border-width: 2;" +
|
||||
"-fx-border-radius: 10;" +
|
||||
"-fx-background-radius: 10;"
|
||||
);
|
||||
|
||||
<b class="nc"> popup.getContent().add(popupCards);</b>
|
||||
<b class="nc"> popup.setAutoHide(false);</b>
|
||||
<b class="nc"> popup.addEventHandler(Event.ANY, e -> {</b>
|
||||
<b class="nc"> if (popup.getScene() != null) {</b>
|
||||
<b class="nc"> popup.getScene().setFill(Color.TRANSPARENT);</b>
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Centers the popup over the current window and makes it visible. */
|
||||
private void showPopup() {
|
||||
<b class="nc"> Window window = myHand.getScene().getWindow();</b>
|
||||
<b class="nc"> popup.show(window, 0, 0);</b>
|
||||
<b class="nc"> popup.getScene().setFill(Color.TRANSPARENT);</b>
|
||||
<b class="nc"> popup.setX(window.getX() + (window.getWidth() - popup.getWidth()) / 2);</b>
|
||||
<b class="nc"> popup.setY(window.getY() + (window.getHeight() - popup.getHeight()) / 2);</b>
|
||||
}
|
||||
|
||||
/** Populates the popup with non-clickable card images and shows it. */
|
||||
private void openPopup(ArrayList<? extends PlayableCard> cardList) {
|
||||
<b class="nc"> popupCards.getChildren().clear();</b>
|
||||
<b class="nc"> for (PlayableCard card : cardList) {</b>
|
||||
<b class="nc"> popupCards.getChildren().add(createCardPopup(card, false, false));</b>
|
||||
}
|
||||
<b class="nc"> showPopup();</b>
|
||||
}
|
||||
|
||||
/** Populates the popup with clickable upper-building cards; clicking one draws it via the controller. */
|
||||
private void openPopupUpperBuilding(ArrayList<BuildingCard> cardList) {
|
||||
<b class="nc"> popupCards.getChildren().clear();</b>
|
||||
<b class="nc"> for (int i = 0; i < cardList.size(); i++) {</b>
|
||||
<b class="nc"> final int index = i;</b>
|
||||
<b class="nc"> StackPane img = createCardPopup(cardList.get(i), true, false);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> {</b>
|
||||
<b class="nc"> popup.hide();</b>
|
||||
<b class="nc"> controller.drawUpperBuildingCard(index);</b>
|
||||
});
|
||||
<b class="nc"> popupCards.getChildren().add(img);</b>
|
||||
}
|
||||
<b class="nc"> showPopup();</b>
|
||||
}
|
||||
|
||||
/** Populates the popup with clickable lower-building cards; clicking one draws it via the controller. */
|
||||
private void openPopupLowerBuilding(ArrayList<BuildingCard> cardList) {
|
||||
<b class="nc"> popupCards.getChildren().clear();</b>
|
||||
<b class="nc"> for (int i = 0; i < cardList.size(); i++) {</b>
|
||||
<b class="nc"> final int index = i;</b>
|
||||
<b class="nc"> StackPane img = createCardPopup(cardList.get(i), true, false);</b>
|
||||
<b class="nc"> img.setOnMouseClicked(e -> {</b>
|
||||
<b class="nc"> popup.hide();</b>
|
||||
<b class="nc"> controller.drawLowerBuildingCard(index);</b>
|
||||
});
|
||||
<b class="nc"> popupCards.getChildren().add(img);</b>
|
||||
}
|
||||
<b class="nc"> showPopup();</b>
|
||||
}
|
||||
|
||||
/** Shows the deck details/rules card in the popup. */
|
||||
private void openDetailsPopup() {
|
||||
<b class="nc"> popupCards.getChildren().clear();</b>
|
||||
<b class="nc"> ImageView img = new ImageView(loadImage("/GUIImages/Backs/back-118.png"));</b>
|
||||
<b class="nc"> img.setFitHeight(290);</b>
|
||||
<b class="nc"> img.setPreserveRatio(true);</b>
|
||||
<b class="nc"> addClip(img);</b>
|
||||
<b class="nc"> StackPane wrapper = new StackPane(img);</b>
|
||||
<b class="nc"> addShadow(wrapper);</b>
|
||||
<b class="nc"> popupCards.getChildren().add(wrapper);</b>
|
||||
<b class="nc"> showPopup();</b>
|
||||
}
|
||||
|
||||
/** Creates a fixed-height {@link StackPane} card widget suitable for use inside the popup. */
|
||||
private StackPane createCardPopup(PlayableCard card, boolean withZoom, boolean withShadow) {
|
||||
<b class="nc"> ImageView img = new ImageView(loadImage("/GUIImages/Fronts/card-" + card.getIdIMG() + ".png"));</b>
|
||||
<b class="nc"> img.setFitHeight(200);</b>
|
||||
<b class="nc"> img.setPreserveRatio(true);</b>
|
||||
<b class="nc"> addClip(img);</b>
|
||||
<b class="nc"> StackPane wrapper = new StackPane(img);</b>
|
||||
<b class="nc"> if (withShadow) addShadow(wrapper);</b>
|
||||
<b class="nc"> if (withZoom) addHoverZoom(wrapper);</b>
|
||||
<b class="nc"> return wrapper;</b>
|
||||
}
|
||||
}
|
||||
</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-19 22:53</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,346 @@
|
||||
|
||||
|
||||
|
||||
<!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"> package it.polimi.ingsw.gc14.View.GUI;
|
||||
|
||||
import it.polimi.ingsw.gc14.Controller.ClientController;
|
||||
import it.polimi.ingsw.gc14.Model.Totems;
|
||||
import javafx.animation.*;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.geometry.Rectangle2D;
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.Screen;
|
||||
import javafx.util.Duration;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* FXML controller for the totem selection scene.
|
||||
*
|
||||
* <p>Displays the available totems and lets the current player choose one;
|
||||
* other players see a waiting banner.
|
||||
*/
|
||||
<b class="nc"> public class TotemFXMLController {</b>
|
||||
/** Background image displayed behind the totem selection grid. */
|
||||
@FXML private ImageView backgroundImage;
|
||||
/** HBox containing the totem card widgets. */
|
||||
@FXML private HBox mainHBox;
|
||||
/** Banner label showing whose turn it is to choose a totem. */
|
||||
@FXML private Label turnBanner;
|
||||
/** Button that submits the selected totem to the server. */
|
||||
@FXML private Button confirmButton;
|
||||
|
||||
/** Client controller for sending the totem choice. */
|
||||
private ClientController controller;
|
||||
/** Index of the currently selected totem, or {@code -1} if none selected. */
|
||||
<b class="nc"> private int selectedIndex = -1;</b>
|
||||
/** The {@link StackPane} frame of the currently selected totem card, or {@code null}. */
|
||||
<b class="nc"> private StackPane selectedFrame = null;</b>
|
||||
|
||||
/**
|
||||
* Injects the client controller into this FXML controller.
|
||||
*
|
||||
* @param controller the client controller to use.
|
||||
*/
|
||||
public void setController(ClientController controller) {
|
||||
<b class="nc"> this.controller = controller;</b>
|
||||
}
|
||||
|
||||
/** Initializes the scene: sets the background image to fill the screen. */
|
||||
@FXML
|
||||
public void initialize() {
|
||||
<b class="nc"> Image img = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/GUIImages/Background.png")));</b>
|
||||
<b class="nc"> backgroundImage.setImage(img);</b>
|
||||
<b class="nc"> Rectangle2D screenBounds = Screen.getPrimary().getBounds();</b>
|
||||
<b class="nc"> backgroundImage.setFitWidth(screenBounds.getWidth());</b>
|
||||
<b class="nc"> backgroundImage.setFitHeight(screenBounds.getHeight());</b>
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the totem selection cards with entry animations.
|
||||
* Disables interaction for players who are not the current chooser.
|
||||
*/
|
||||
public void render() {
|
||||
<b class="nc"> mainHBox.getChildren().clear();</b>
|
||||
<b class="nc"> selectedIndex = -1;</b>
|
||||
<b class="nc"> selectedFrame = null;</b>
|
||||
|
||||
<b class="nc"> if (controller.getMiniModel().currentState.getCurrentPlayer() == null) return;</b>
|
||||
<b class="nc"> String chooser = controller.getMiniModel().currentState.getCurrentPlayer().getUserName();</b>
|
||||
<b class="nc"> boolean myTurn = chooser.equals(controller.getMyUsername());</b>
|
||||
|
||||
<b class="nc"> updateBanner(chooser, myTurn);</b>
|
||||
<b class="nc"> updateConfirmButton(false);</b>
|
||||
|
||||
<b class="nc"> for (int i = 0; i < controller.getMiniModel().availableTotems.size(); i++) {</b>
|
||||
<b class="nc"> Totems totem = controller.getMiniModel().availableTotems.get(i);</b>
|
||||
<b class="nc"> VBox card = buildCard(totem, i, myTurn);</b>
|
||||
<b class="nc"> mainHBox.getChildren().add(card);</b>
|
||||
|
||||
<b class="nc"> card.setOpacity(0);</b>
|
||||
<b class="nc"> card.setTranslateY(14);</b>
|
||||
<b class="nc"> PauseTransition delay = new PauseTransition(Duration.millis(80 + i * 65));</b>
|
||||
<b class="nc"> delay.setOnFinished(e -> {</b>
|
||||
<b class="nc"> FadeTransition ft = new FadeTransition(Duration.millis(320), card);</b>
|
||||
<b class="nc"> ft.setToValue(1);</b>
|
||||
<b class="nc"> TranslateTransition tt = new TranslateTransition(Duration.millis(320), card);</b>
|
||||
<b class="nc"> tt.setToY(0);</b>
|
||||
<b class="nc"> new ParallelTransition(ft, tt).play();</b>
|
||||
});
|
||||
<b class="nc"> delay.play();</b>
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the turn banner text and style based on whether it is the local player's turn. */
|
||||
private void updateBanner(String chooser, boolean myTurn) {
|
||||
<b class="nc"> if (myTurn) {</b>
|
||||
<b class="nc"> turnBanner.setText("✦ It's your turn to choose ✦");</b>
|
||||
<b class="nc"> turnBanner.setStyle(</b>
|
||||
"-fx-font-family: 'Cinzel'; -fx-font-size: 13; -fx-font-weight: 'bold'; -fx-letter-spacing: 3;" +
|
||||
"-fx-text-fill: #000000;" +
|
||||
"-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
|
||||
"-fx-border-color: #000000; -fx-border-width: 1;" +
|
||||
"-fx-border-radius: 3; -fx-background-radius: 3;" +
|
||||
"-fx-padding: 10 28 10 28;" +
|
||||
"-fx-effect: dropshadow(gaussian, rgba(244,192,90,0.3), 18, 0, 0, 0);"
|
||||
);
|
||||
} else {
|
||||
<b class="nc"> turnBanner.setText("Waiting for " + chooser + " to choose his totem…");</b>
|
||||
<b class="nc"> turnBanner.setStyle(</b>
|
||||
"-fx-font-family: 'Cinzel'; -fx-font-size: 11; -fx-letter-spacing: 2;" +
|
||||
"-fx-text-fill: #000000;" +
|
||||
"-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
|
||||
"-fx-border-color: #000000; -fx-border-width: 1;" +
|
||||
"-fx-border-radius: 3; -fx-background-radius: 3;" +
|
||||
"-fx-padding: 10 28 10 28;"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Builds a totem card widget; if {@code interactive}, wires click/hover handlers and selection logic. */
|
||||
private VBox buildCard(Totems totem, int idx, boolean interactive) {
|
||||
<b class="nc"> ImageView img = new ImageView(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/GUIImages/Totems/totem_" + String.valueOf(totem).toLowerCase(Locale.ROOT) + ".png"))));</b>
|
||||
<b class="nc"> img.setFitHeight(150);</b>
|
||||
<b class="nc"> img.setPreserveRatio(true);</b>
|
||||
|
||||
<b class="nc"> StackPane frame = new StackPane(img);</b>
|
||||
<b class="nc"> frame.setPrefSize(100, 145);</b>
|
||||
<b class="nc"> frame.setBackground(Background.EMPTY);</b>
|
||||
<b class="nc"> img.setStyle(frameStyle(false));</b>
|
||||
|
||||
<b class="nc"> Region base = new Region();</b>
|
||||
<b class="nc"> base.setPrefSize(100, 10);</b>
|
||||
|
||||
<b class="nc"> Label name = new Label(capitalize(totem));</b>
|
||||
<b class="nc"> name.setStyle(nameStyle(false));</b>
|
||||
|
||||
<b class="nc"> VBox card = new VBox(4, frame, base, name);</b>
|
||||
<b class="nc"> card.setAlignment(Pos.CENTER);</b>
|
||||
|
||||
<b class="nc"> if (!interactive) {</b>
|
||||
<b class="nc"> card.setOpacity(0.35);</b>
|
||||
<b class="nc"> card.setCursor(Cursor.DEFAULT);</b>
|
||||
<b class="nc"> return card;</b>
|
||||
}
|
||||
|
||||
<b class="nc"> card.setCursor(Cursor.HAND);</b>
|
||||
|
||||
<b class="nc"> card.setOnMouseEntered(e -> {</b>
|
||||
<b class="nc"> if (frame != selectedFrame) {</b>
|
||||
<b class="nc"> ScaleTransition st = new ScaleTransition(Duration.millis(140), card);</b>
|
||||
<b class="nc"> st.setToX(1.06); st.setToY(1.06); st.play();</b>
|
||||
}
|
||||
});
|
||||
<b class="nc"> card.setOnMouseExited(e -> {</b>
|
||||
<b class="nc"> if (frame != selectedFrame) {</b>
|
||||
<b class="nc"> ScaleTransition st = new ScaleTransition(Duration.millis(140), card);</b>
|
||||
<b class="nc"> st.setToX(1.0); st.setToY(1.0); st.play();</b>
|
||||
}
|
||||
});
|
||||
|
||||
<b class="nc"> card.setOnMouseClicked(e -> {</b>
|
||||
<b class="nc"> mainHBox.getChildren().forEach(n -> {</b>
|
||||
<b class="nc"> if (n instanceof VBox v) {</b>
|
||||
<b class="nc"> StackPane f = (StackPane) v.getChildren().get(0);</b>
|
||||
<b class="nc"> Label l = (Label) v.getChildren().get(2);</b>
|
||||
<b class="nc"> f.setStyle(frameStyle(false));</b>
|
||||
<b class="nc"> l.setStyle(nameStyle(false));</b>
|
||||
<b class="nc"> ScaleTransition st = new ScaleTransition(Duration.millis(130), v);</b>
|
||||
<b class="nc"> st.setToX(1.0); st.setToY(1.0); st.play();</b>
|
||||
}
|
||||
});
|
||||
|
||||
<b class="nc"> frame.setStyle(frameStyle(true));</b>
|
||||
<b class="nc"> name.setStyle(nameStyle(true));</b>
|
||||
<b class="nc"> selectedFrame = frame;</b>
|
||||
<b class="nc"> selectedIndex = idx;</b>
|
||||
|
||||
<b class="nc"> ScaleTransition pop = new ScaleTransition(Duration.millis(160), card);</b>
|
||||
<b class="nc"> pop.setToX(1.08); pop.setToY(1.08);</b>
|
||||
<b class="nc"> pop.setAutoReverse(true); pop.setCycleCount(2); pop.play();</b>
|
||||
|
||||
<b class="nc"> updateConfirmButton(true);</b>
|
||||
});
|
||||
|
||||
<b class="nc"> return card;</b>
|
||||
}
|
||||
|
||||
/** Enables or disables the confirm button and updates its visual opacity to reflect the state. */
|
||||
private void updateConfirmButton(boolean enabled) {
|
||||
<b class="nc"> confirmButton.setDisable(!enabled);</b>
|
||||
<b class="nc"> confirmButton.setStyle(</b>
|
||||
"-fx-font-family: 'Cinzel'; -fx-font-size: 12; -fx-font-weight: bold;" +
|
||||
"-fx-letter-spacing: 4; -fx-text-fill: #0c0601;" +
|
||||
"-fx-background-color: linear-gradient(to right, #f4c05a, #c8791a, #f4c05a);" +
|
||||
"-fx-padding: 12 48 12 48; -fx-background-radius: 2;" +
|
||||
<b class="nc"> "-fx-opacity: " + (enabled ? "1.0" : "0.3") + ";" +</b>
|
||||
<b class="nc"> "-fx-cursor: " + (enabled ? "hand" : "default") + ";"</b>
|
||||
);
|
||||
}
|
||||
|
||||
/** Returns the CSS style string for the totem card frame, highlighting it when {@code selected}. */
|
||||
private String frameStyle(boolean selected) {
|
||||
<b class="nc"> return selected ? "-fx-effect: dropshadow(gaussian, #ffffff, 20, 0.33, 0, 0);" : "";</b>
|
||||
}
|
||||
|
||||
/** Returns the CSS style string for the totem name label, brightening it when {@code selected}. */
|
||||
private String nameStyle(boolean selected) {
|
||||
return "-fx-font-family: 'Cinzel'; -fx-font-size: 10; -fx-letter-spacing: 2;" +
|
||||
<b class="nc"> "-fx-text-fill: " + (selected ? "#FFD700" : "#ffffff") + ";";</b>
|
||||
}
|
||||
|
||||
/** Returns the totem name with only the first letter capitalized. */
|
||||
private String capitalize(Totems t) {
|
||||
<b class="nc"> String s = t.name().toLowerCase(Locale.ROOT);</b>
|
||||
<b class="nc"> return Character.toUpperCase(s.charAt(0)) + s.substring(1);</b>
|
||||
}
|
||||
|
||||
|
||||
/** Submits the selected totem index to the controller when the confirm button is clicked. */
|
||||
@FXML
|
||||
private void onConfirm() {
|
||||
<b class="nc"> if (selectedIndex >= 0) {</b>
|
||||
<b class="nc"> controller.totemChoice(selectedIndex);</b>
|
||||
}
|
||||
}
|
||||
}
|
||||
</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-19 22:53</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user