Code Sketch
yoifgggh
Category: Programming
import java.awt._
import java.awt.event._
import javax.swing._
import scala.collection.mutable.ArrayBuffer
import scala.util.Random
// ============================================================
// STOP THE SECONDS - ULTRA ARENA X
// FULL SCALA + JAVA SWING VERSION
// COPY-PASTE READY
// NO EXTERNAL ASSETS
// ============================================================
// ============================================================
// WINDOW
// ============================================================
val SCREEN_W = 1100
val SCREEN_H = 700
val frame = new JFrame("STOP THE SECONDS - ULTRA ARENA X")
frame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
frame.setSize(
SCREEN_W,
SCREEN_H
)
frame.setResizable(false)
// ============================================================
// GAME SCREENS
// ============================================================
val MENU = 0
val NAME_SCREEN = 1
val TARGET_SCREEN = 2
val GAME = 3
val RESULT = 4
val LEADERBOARD = 5
val ACHIEVEMENTS = 6
val STATS = 7
val HELP = 8
val SETTINGS = 9
val PAUSE = 10
var screen = MENU
// ============================================================
// PLAYER
// ============================================================
var playerName = ""
var score = 0
var coins = 0
var xp = 0
var level = 1
var combo = 0
var bestCombo = 0
var round = 1
var maxRounds = 10
// ============================================================
// TARGETS
// ============================================================
val targetOptions =
Array(
3.0,
5.0,
7.0,
10.0,
15.0
)
var targetIndex = 2
var targetTime = targetOptions(targetIndex)
// ============================================================
// GAME MODES
// ============================================================
var difficulty = 1
// 0 EASY
// 1 NORMAL
// 2 HARD
// 3 EXTREME
var mode = 0
// 0 CLASSIC
// 1 CHALLENGE
// 2 PRACTICE
// ============================================================
// TIMER
// ============================================================
var timerRunning = false
var countdownActive = false
var startNano = 0L
var elapsed = 0.0
var countdown = 0.0
// ============================================================
// CURRENT ROUND RESULT
// ============================================================
var lastDifference = 0.0
var lastJudgement = ""
var judgementTimer = 0.0
// ============================================================
// ROUND STATS
// ============================================================
var perfects = 0
var greats = 0
var goods = 0
var misses = 0
var totalAttempts = 0
// ============================================================
// LIFETIME STATS
// ============================================================
var totalGames = 0
var totalRoundsPlayed = 0
var lifetimeScore = 0
var lifetimePerfects = 0
var lifetimeMisses = 0
var lifetimeBestCombo = 0
var fastestReaction = 999999.0
var closestHit = 999999.0
// ============================================================
// EXTRA ANIMATION STATE
// ============================================================
var animationTime = 0.0
var pulse = 0.0
var menuPulse = 0.0
var screenFlash = 0.0
var shake = 0.0
// FIXED: levelFlash IS DECLARED HERE
var levelFlash = 0.0
// ============================================================
// SETTINGS
// ============================================================
var soundEnabled = true
var particlesEnabled = true
var screenShakeEnabled = true
var animatedBackground = true
// ============================================================
// INPUT
// ============================================================
val keys =
scala.collection.mutable.Set[Int]()
// ============================================================
// RANDOM
// ============================================================
val random =
new Random()
// ============================================================
// LEADERBOARD DATA
// ============================================================
case class PlayerRecord(
name: String,
score: Int,
level: Int,
combo: Int,
accuracy: Double,
difficulty: String
)
val leaderboard =
ArrayBuffer.empty[PlayerRecord]
// ============================================================
// ACHIEVEMENTS
// ============================================================
case class Achievement(
name: String,
description: String,
var unlocked: Boolean
)
val achievements =
ArrayBuffer(
Achievement(
"FIRST STEP",
"Complete your first round",
false
),
Achievement(
"PERFECT",
"Hit a target perfectly",
false
),
Achievement(
"COMBO MACHINE",
"Reach a 5x combo",
false
),
Achievement(
"MASTER",
"Reach level 5",
false
),
Achievement(
"TEN ROUNDS",
"Complete 10 rounds",
false
),
Achievement(
"COIN HUNTER",
"Reach 500 coins",
false
),
Achievement(
"PRECISION",
"Reach 90% accuracy",
false
),
Achievement(
"ULTRA",
"Reach 5000 score",
false
)
)
// ============================================================
// PARTICLES
// ============================================================
case class Particle(
var x: Double,
var y: Double,
var vx: Double,
var vy: Double,
var life: Double,
var maxLife: Double,
var size: Double
)
val particles =
ArrayBuffer.empty[Particle]
// ============================================================
// FLOATING TEXT
// ============================================================
case class FloatingText(
var message: String,
var x: Double,
var y: Double,
var life: Double,
var color: Color
)
val floatingTexts =
ArrayBuffer.empty[FloatingText]
// ============================================================
// STARS
// ============================================================
case class Star(
var x: Double,
var y: Double,
var size: Double,
var speed: Double,
var alpha: Int
)
val stars =
ArrayBuffer.empty[Star]
var starIndex = 0
while (starIndex < 180) {
stars +=
Star(
random.nextDouble() * SCREEN_W,
random.nextDouble() * SCREEN_H,
1.0 + random.nextDouble() * 3.0,
0.2 + random.nextDouble() * 0.8,
80 + random.nextInt(170)
)
starIndex += 1
}
// ============================================================
// HELPER FUNCTIONS
// ============================================================
def clamp(
value: Double,
minimum: Double,
maximum: Double
): Double = {
math.max(
minimum,
math.min(
maximum,
value
)
)
}
def difficultyName(): String = {
difficulty match {
case 0 => "EASY"
case 1 => "NORMAL"
case 2 => "HARD"
case 3 => "EXTREME"
case _ => "NORMAL"
}
}
def modeNameString(): String = {
mode match {
case 0 => "CLASSIC"
case 1 => "CHALLENGE"
case 2 => "PRACTICE"
case _ => "CLASSIC"
}
}
def accuracy(): Double = {
if (totalAttempts <= 0) {
0.0
} else {
(
perfects * 100.0 +
greats * 80.0 +
goods * 55.0
) / totalAttempts.toDouble
}
}
def beep(
frequency: Int
): Unit = {
if (soundEnabled) {
try {
Toolkit
.getDefaultToolkit
.beep()
} catch {
case _: Exception =>
}
}
}
// ============================================================
// PARTICLE SYSTEM
// ============================================================
def spawnBurst(
px: Double,
py: Double,
requestedCount: Int
): Unit = {
if (!particlesEnabled)
return
val count =
math.min(
requestedCount,
100
)
var i = 0
while (
i < count &&
particles.length < 250
) {
val angle =
random.nextDouble() *
math.Pi * 2.0
val speed =
0.8 +
random.nextDouble() * 3.8
particles +=
Particle(
px,
py,
math.cos(angle) * speed,
math.sin(angle) * speed,
0.5 + random.nextDouble() * 0.8,
0.5 + random.nextDouble() * 0.8,
2.0 + random.nextDouble() * 6.0
)
i += 1
}
}
def updateParticles(
dt: Double
): Unit = {
if (!particlesEnabled) {
particles.clear()
return
}
var i = particles.length - 1
while (i >= 0) {
val p =
particles(i)
p.x +=
p.vx * 60.0 * dt
p.y +=
p.vy * 60.0 * dt
p.vy +=
0.025
p.life -= dt
if (p.life <= 0) {
particles.remove(i)
}
i -= 1
}
}
// ============================================================
// FLOATING TEXT
// ============================================================
def addFloatingText(
message: String,
px: Double,
py: Double,
color: Color
): Unit = {
floatingTexts +=
FloatingText(
message,
px,
py,
1.5,
color
)
while (floatingTexts.length > 35) {
floatingTexts.remove(0)
}
}
def updateFloatingTexts(
dt: Double
): Unit = {
var i = floatingTexts.length - 1
while (i >= 0) {
val f =
floatingTexts(i)
f.y -=
28.0 * dt
f.life -= dt
if (f.life <= 0) {
floatingTexts.remove(i)
}
i -= 1
}
}
// ============================================================
// GAME START
// ============================================================
def startNewGame(): Unit = {
score = 0
coins = 0
xp = 0
level = 1
combo = 0
bestCombo = 0
perfects = 0
greats = 0
goods = 0
misses = 0
totalAttempts = 0
round = 1
elapsed = 0.0
countdown = 0.0
timerRunning = false
countdownActive = false
lastDifference = 0.0
lastJudgement = ""
judgementTimer = 0.0
levelFlash = 0.0
particles.clear()
floatingTexts.clear()
totalGames += 1
screen = GAME
spawnBurst(
SCREEN_W / 2.0,
350.0,
70
)
beep(80)
}
// ============================================================
// RESET ROUND
// ============================================================
def resetRound(): Unit = {
timerRunning = false
countdownActive = false
elapsed = 0.0
countdown = 0.0
lastDifference = 0.0
lastJudgement = ""
judgementTimer = 0.0
particles.clear()
floatingTexts.clear()
spawnBurst(
SCREEN_W / 2.0,
350.0,
25
)
}
// ============================================================
// START COUNTDOWN
// ============================================================
def startCountdown(): Unit = {
if (timerRunning)
return
countdown = 3.0
countdownActive = true
beep(60)
}
// ============================================================
// STOP TIMER
// ============================================================
def stopTimer(): Unit = {
if (!timerRunning)
return
timerRunning = false
elapsed =
(
System.nanoTime() -
startNano
) / 1000000000.0
evaluateRound()
}
// ============================================================
// EVALUATE ROUND
// ============================================================
def evaluateRound(): Unit = {
totalAttempts += 1
totalRoundsPlayed += 1
lastDifference =
math.abs(
elapsed - targetTime
)
if (lastDifference < closestHit)
closestHit =
lastDifference
if (elapsed < fastestReaction)
fastestReaction =
elapsed
// Accuracy windows
val perfectWindow =
difficulty match {
case 0 => 0.05
case 1 => 0.08
case 2 => 0.11
case 3 => 0.14
case _ => 0.08
}
val greatWindow =
difficulty match {
case 0 => 0.13
case 1 => 0.20
case 2 => 0.28
case 3 => 0.36
case _ => 0.20
}
val goodWindow =
difficulty match {
case 0 => 0.25
case 1 => 0.38
case 2 => 0.50
case 3 => 0.65
case _ => 0.38
}
// ----------------------------------------------------------
// PERFECT
// ----------------------------------------------------------
if (lastDifference <= perfectWindow) {
lastJudgement = "PERFECT"
perfects += 1
lifetimePerfects += 1
combo += 1
if (combo > bestCombo)
bestCombo = combo
if (bestCombo > lifetimeBestCombo)
lifetimeBestCombo =
bestCombo
val multiplier =
math.min(
10,
1 + combo / 2
)
val base =
500 * (difficulty + 1)
val points =
base * multiplier
score += points
val coinReward =
25 * (difficulty + 1)
coins += coinReward
addXP(
40 + difficulty * 15
)
spawnBurst(
SCREEN_W / 2.0,
350.0,
90
)
addFloatingText(
"PERFECT +" + points,
SCREEN_W / 2.0 - 80,
270.0,
new Color(90, 255, 170)
)
if (screenShakeEnabled)
shake = 8.0
screenFlash = 0.30
beep(90)
}
// ----------------------------------------------------------
// GREAT
// ----------------------------------------------------------
else if (
lastDifference <= greatWindow
) {
lastJudgement = "GREAT"
greats += 1
combo += 1
if (combo > bestCombo)
bestCombo = combo
if (bestCombo > lifetimeBestCombo)
lifetimeBestCombo =
bestCombo
val multiplier =
math.min(
8,
1 + combo / 3
)
val points =
300 *
(difficulty + 1) *
multiplier
score += points
coins +=
15 * (difficulty + 1)
addXP(28)
spawnBurst(
SCREEN_W / 2.0,
350.0,
55
)
addFloatingText(
"GREAT +" + points,
SCREEN_W / 2.0 - 65,
270.0,
new Color(100, 210, 255)
)
beep(80)
}
// ----------------------------------------------------------
// GOOD
// ----------------------------------------------------------
else if (
lastDifference <= goodWindow
) {
lastJudgement = "GOOD"
goods += 1
combo += 1
if (combo > bestCombo)
bestCombo = combo
val points =
150 *
(difficulty + 1)
score += points
coins +=
8 * (difficulty + 1)
addXP(18)
spawnBurst(
SCREEN_W / 2.0,
350.0,
30
)
addFloatingText(
"GOOD +" + points,
SCREEN_W / 2.0 - 55,
270.0,
new Color(255, 220, 100)
)
beep(70)
}
// ----------------------------------------------------------
// MISS
// ----------------------------------------------------------
else {
lastJudgement = "MISS"
misses += 1
lifetimeMisses += 1
combo = 0
addFloatingText(
"MISS",
SCREEN_W / 2.0 - 30,
270.0,
new Color(255, 80, 100)
)
if (screenShakeEnabled)
shake = 12.0
screenFlash = 0.18
beep(40)
}
// ----------------------------------------------------------
// UPDATE LIFETIME
// ----------------------------------------------------------
lifetimeScore += score
judgementTimer = 2.2
checkAchievements()
screen = RESULT
}
// ============================================================
// XP SYSTEM
// ============================================================
def addXP(
amount: Int
): Unit = {
xp += amount
while (xp >= level * 100) {
xp -=
level * 100
level += 1
levelFlash = 1.0
coins += 50
spawnBurst(
SCREEN_W / 2.0,
350.0,
85
)
addFloatingText(
"LEVEL UP!",
SCREEN_W / 2.0 - 50,
210.0,
new Color(255, 220, 80)
)
beep(100)
}
}
// ============================================================
// NEXT ROUND
// ============================================================
def nextRound(): Unit = {
if (round >= maxRounds) {
finishSession()
} else {
round += 1
targetTime =
targetOptions(targetIndex)
elapsed = 0.0
countdown = 0.0
timerRunning = false
countdownActive = false
judgementTimer = 0.0
lastJudgement = ""
particles.clear()
floatingTexts.clear()
screen = GAME
spawnBurst(
SCREEN_W / 2.0,
350.0,
35
)
}
}
// ============================================================
// FINISH SESSION
// ============================================================
def finishSession(): Unit = {
val record =
PlayerRecord(
playerName,
score,
level,
bestCombo,
accuracy(),
difficultyName()
)
leaderboard += record
// FIXED:
// No takeInPlace with two arguments.
// We calculate sorted top 10 and rebuild the buffer.
val sorted =
leaderboard
.sortBy(
r => -r.score
)
.take(10)
leaderboard.clear()
leaderboard ++= sorted
checkAchievements()
screen = MENU
}
// ============================================================
// ACHIEVEMENTS
// ============================================================
def unlockAchievement(
achievementName: String
): Unit = {
var i = 0
while (i < achievements.length) {
val a =
achievements(i)
if (a.name == achievementName) {
if (!a.unlocked) {
a.unlocked = true
coins += 100
addFloatingText(
"ACHIEVEMENT: " + achievementName,
350.0,
180.0,
new Color(255, 220, 80)
)
beep(95)
}
}
i += 1
}
}
def checkAchievements(): Unit = {
if (totalRoundsPlayed >= 1)
unlockAchievement(
"FIRST STEP"
)
if (perfects >= 1)
unlockAchievement(
"PERFECT"
)
if (bestCombo >= 5)
unlockAchievement(
"COMBO MACHINE"
)
if (level >= 5)
unlockAchievement(
"MASTER"
)
if (totalRoundsPlayed >= 10)
unlockAchievement(
"TEN ROUNDS"
)
if (coins >= 500)
unlockAchievement(
"COIN HUNTER"
)
if (accuracy() >= 90.0)
unlockAchievement(
"PRECISION"
)
if (score >= 5000)
unlockAchievement(
"ULTRA"
)
}
// ============================================================
// UPDATE STARS
// ============================================================
def updateStars(
dt: Double
): Unit = {
if (!animatedBackground)
return
var i = 0
while (i < stars.length) {
val s =
stars(i)
s.y +=
s.speed * 12.0 * dt
if (s.y > SCREEN_H) {
s.y = 0.0
s.x =
random.nextDouble() *
SCREEN_W
}
i += 1
}
}
// ============================================================
// GAME UPDATE LOOP
// ============================================================
def updateGame(
dt: Double
): Unit = {
animationTime += dt
menuPulse += dt * 2.0
pulse += dt * 3.0
if (screenFlash > 0.0) {
screenFlash =
math.max(
0.0,
screenFlash - dt * 3.0
)
}
if (shake > 0.0) {
shake =
math.max(
0.0,
shake - dt * 16.0
)
}
if (levelFlash > 0.0) {
levelFlash =
math.max(
0.0,
levelFlash - dt
)
}
updateStars(dt)
updateParticles(dt)
updateFloatingTexts(dt)
if (judgementTimer > 0.0) {
judgementTimer =
math.max(
0.0,
judgementTimer - dt
)
}
// ----------------------------------------------------------
// GAME TIMER
// ----------------------------------------------------------
if (
screen == GAME &&
countdownActive
) {
countdown -= dt
if (countdown <= 0.0) {
countdownActive = false
timerRunning = true
startNano =
System.nanoTime()
elapsed = 0.0
beep(65)
}
}
if (
screen == GAME &&
timerRunning
) {
elapsed =
(
System.nanoTime() -
startNano
) / 1000000000.0
}
}
// ============================================================
// UI PANEL
// ============================================================
val gamePanel =
new JPanel {
setPreferredSize(
new Dimension(
SCREEN_W,
SCREEN_H
)
)
setBackground(
new Color(
5,
8,
18
)
)
setFocusable(true)
// ========================================================
// PAINT
// ========================================================
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(graphics)
val g =
graphics.asInstanceOf[Graphics2D]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
g.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON
)
var sx = 0
var sy = 0
if (
screenShakeEnabled &&
shake > 0.0
) {
sx =
((random.nextDouble() - 0.5) * shake)
.toInt
sy =
((random.nextDouble() - 0.5) * shake)
.toInt
}
g.translate(
sx,
sy
)
drawBackground(g)
screen match {
case MENU =>
drawMenu(g)
case NAME_SCREEN =>
drawNameScreen(g)
case TARGET_SCREEN =>
drawTargetScreen(g)
case GAME =>
drawGame(g)
case RESULT =>
drawResult(g)
case LEADERBOARD =>
drawLeaderboard(g)
case ACHIEVEMENTS =>
drawAchievements(g)
case STATS =>
drawStatistics(g)
case HELP =>
drawHelp(g)
case SETTINGS =>
drawSettings(g)
case PAUSE =>
drawGame(g)
drawPause(g)
case _ =>
drawMenu(g)
}
if (screenFlash > 0.0) {
val alpha =
math.min(
130,
(screenFlash * 230.0).toInt
)
g.setColor(
new Color(
255,
255,
255,
alpha
)
)
g.fillRect(
-10,
-10,
SCREEN_W + 20,
SCREEN_H + 20
)
}
g.translate(
-sx,
-sy
)
}
// ========================================================
// BACKGROUND
// ========================================================
def drawBackground(
g: Graphics2D
): Unit = {
val gradient =
new GradientPaint(
0,
0,
new Color(
4,
7,
20
),
0,
SCREEN_H,
new Color(
17,
5,
35
)
)
g.setPaint(gradient)
g.fillRect(
0,
0,
SCREEN_W,
SCREEN_H
)
// ------------------------------------------------------
// STARS
// ------------------------------------------------------
if (animatedBackground) {
var i = 0
while (i < stars.length) {
val s =
stars(i)
val alpha =
clamp(
s.alpha +
math.sin(
animationTime * 2.0 +
s.x
) * 50.0,
20.0,
255.0
).toInt
g.setColor(
new Color(
160,
220,
255,
alpha
)
)
g.fillOval(
s.x.toInt,
s.y.toInt,
math.max(
1,
s.size.toInt
),
math.max(
1,
s.size.toInt
)
)
i += 1
}
}
// ------------------------------------------------------
// GRID
// ------------------------------------------------------
g.setColor(
new Color(
50,
120,
160,
42
)
)
val offset =
if (animatedBackground)
((animationTime * 24.0) % 50.0).toInt
else
0
var gx = -50 + offset
while (
gx < SCREEN_W + 50
) {
g.drawLine(
gx,
0,
gx,
SCREEN_H
)
gx += 50
}
var gy = -50 + offset
while (
gy < SCREEN_H + 50
) {
g.drawLine(
0,
gy,
SCREEN_W,
gy
)
gy += 50
}
// ------------------------------------------------------
// CENTRAL GLOW
// ------------------------------------------------------
val glowRadius =
(
280.0 +
math.sin(
animationTime * 2.0
) * 30.0
).toFloat
val radial =
new RadialGradientPaint(
new Point(
SCREEN_W / 2,
SCREEN_H / 2
),
glowRadius,
Array(
0.0f,
0.45f,
1.0f
),
Array(
new Color(
50,
120,
255,
35
),
new Color(
120,
20,
220,
12
),
new Color(
0,
0,
0,
0
)
)
)
g.setPaint(radial)
g.fillRect(
0,
0,
SCREEN_W,
SCREEN_H
)
}
// ========================================================
// TEXT
// ========================================================
def centered(
g: Graphics2D,
text: String,
y: Int,
size: Int,
color: Color
): Unit = {
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
size
)
)
g.setColor(color)
val width =
g.getFontMetrics.stringWidth(text)
g.drawString(
text,
(SCREEN_W - width) / 2,
y
)
}
// ========================================================
// PANEL
// ========================================================
def panelBox(
g: Graphics2D,
x: Int,
y: Int,
w: Int,
h: Int
): Unit = {
g.setColor(
new Color(
10,
15,
35,
235
)
)
g.fillRoundRect(
x,
y,
w,
h,
24,
24
)
g.setColor(
new Color(
80,
180,
255,
150
)
)
g.setStroke(
new BasicStroke(
2.0f
)
)
g.drawRoundRect(
x,
y,
w,
h,
24,
24
)
}
// ========================================================
// BUTTON
// ========================================================
def drawButton(
g: Graphics2D,
text: String,
x: Int,
y: Int,
w: Int,
h: Int,
active: Boolean
): Unit = {
g.setColor(
if (active)
new Color(
30,
145,
235,
210
)
else
new Color(
18,
30,
65,
225
)
)
g.fillRoundRect(
x,
y,
w,
h,
16,
16
)
g.setColor(
if (active)
new Color(
120,
235,
255
)
else
new Color(
75,
120,
180
)
)
g.drawRoundRect(
x,
y,
w,
h,
16,
16
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
16
)
)
val width =
g.getFontMetrics
.stringWidth(text)
g.setColor(
Color.WHITE
)
g.drawString(
text,
x + (w - width) / 2,
y + h / 2 + 6
)
}
// ========================================================
// MENU
// ========================================================
def drawMenu(
g: Graphics2D
): Unit = {
val titleY =
110 +
(
math.sin(
menuPulse
) * 5.0
).toInt
centered(
g,
"STOP THE SECONDS",
titleY,
52,
new Color(
100,
225,
255
)
)
centered(
g,
"ULTRA ARENA X",
titleY + 55,
28,
new Color(
220,
100,
255
)
)
centered(
g,
"PRECISION ? COMBO ? LEVEL ? ACHIEVEMENTS",
titleY + 88,
14,
new Color(
160,
190,
220
)
)
drawButton(
g,
"PLAY",
390,
250,
320,
55,
true
)
drawButton(
g,
"LEADERBOARD",
390,
320,
320,
50,
false
)
drawButton(
g,
"ACHIEVEMENTS",
390,
385,
320,
50,
false
)
drawButton(
g,
"STATISTICS",
390,
450,
320,
50,
false
)
drawButton(
g,
"SETTINGS",
390,
515,
155,
45,
false
)
drawButton(
g,
"HELP",
555,
515,
155,
45,
false
)
centered(
g,
"ENTER = PLAY L = LEADERBOARD H = HELP",
615,
13,
new Color(
120,
150,
180
)
)
}
// ========================================================
// NAME SCREEN
// ========================================================
def drawNameScreen(
g: Graphics2D
): Unit = {
centered(
g,
"CREATE YOUR PLAYER",
120,
34,
new Color(
100,
220,
255
)
)
panelBox(
g,
270,
185,
560,
195
)
centered(
g,
if (playerName.isEmpty)
"TYPE YOUR NAME"
else
playerName,
270,
30,
Color.WHITE
)
centered(
g,
"ENTER = CONTINUE",
330,
15,
new Color(
100,
220,
255
)
)
centered(
g,
"BACKSPACE = DELETE",
360,
13,
new Color(
150,
165,
190
)
)
centered(
g,
"ESC = MENU",
610,
13,
new Color(
120,
135,
160
)
)
}
// ========================================================
// TARGET SCREEN
// ========================================================
def drawTargetScreen(
g: Graphics2D
): Unit = {
centered(
g,
"TARGET CONFIGURATION",
80,
34,
new Color(
255,
215,
95
)
)
centered(
g,
"PLAYER: " + playerName,
115,
16,
Color.WHITE
)
var y = 155
var i = 0
while (
i < targetOptions.length
) {
drawButton(
g,
f"${targetOptions(i)}%.2f SECONDS",
380,
y,
340,
48,
i == targetIndex
)
y += 58
i += 1
}
centered(
g,
"UP / DOWN OR W / S = TARGET",
465,
14,
new Color(
150,
200,
230
)
)
centered(
g,
"D = DIFFICULTY M = MODE",
495,
13,
new Color(
160,
175,
195
)
)
centered(
g,
"ENTER = START",
530,
16,
new Color(
100,
255,
180
)
)
centered(
g,
"DIFFICULTY: " +
difficultyName() +
" MODE: " +
modeNameString(),
580,
15,
new Color(
255,
170,
100
)
)
}
// ========================================================
// GAME SCREEN
// ========================================================
def drawGame(
g: Graphics2D
): Unit = {
// ------------------------------------------------------
// TOP BAR
// ------------------------------------------------------
g.setColor(
new Color(
7,
11,
28,
245
)
)
g.fillRect(
0,
0,
SCREEN_W,
82
)
g.setColor(
new Color(
70,
180,
255,
120
)
)
g.drawLine(
0,
82,
SCREEN_W,
82
)
// Player
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
18
)
)
g.setColor(
Color.WHITE
)
g.drawString(
playerName,
28,
30
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
12
)
)
g.setColor(
new Color(
150,
190,
220
)
)
g.drawString(
"LEVEL " + level,
28,
51
)
// Score
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
19
)
)
g.setColor(
new Color(
255,
220,
90
)
)
g.drawString(
"SCORE " + score,
180,
30
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
12
)
)
g.setColor(
new Color(
120,
230,
170
)
)
g.drawString(
"COINS " + coins,
180,
51
)
// Combo
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
19
)
)
g.setColor(
new Color(
255,
110,
220
)
)
g.drawString(
"COMBO x" + combo,
315,
30
)
// Round
g.setColor(
new Color(
120,
200,
255
)
)
g.drawString(
"ROUND " +
round +
"/" +
maxRounds,
500,
30
)
// Target
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
12
)
)
g.setColor(Color.WHITE)
g.drawString(
f"TARGET $targetTime%.2f s",
500,
51
)
// XP BAR
g.setColor(
new Color(
25,
35,
65
)
)
g.fillRoundRect(
650,
25,
190,
11,
8,
8
)
val needed =
math.max(
1,
level * 100
)
val ratio =
clamp(
xp.toDouble /
needed.toDouble,
0.0,
1.0
)
g.setColor(
new Color(
90,
180,
255
)
)
g.fillRoundRect(
650,
25,
(190.0 * ratio).toInt,
11,
8,
8
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
11
)
)
g.setColor(
new Color(
165,
185,
215
)
)
g.drawString(
"XP " +
xp +
"/" +
needed,
650,
54
)
g.drawString(
difficultyName(),
910,
30
)
g.drawString(
modeNameString(),
910,
52
)
// ------------------------------------------------------
// MAIN ARENA
// ------------------------------------------------------
val centerX =
SCREEN_W / 2
val centerY =
350
val arenaRadius =
(
180.0 +
math.sin(pulse) * 10.0
).toInt
// Outer rings
var ring =
arenaRadius + 55
while (ring > arenaRadius - 5) {
val alpha =
clamp(
90.0 -
(ring - arenaRadius) *
2.0,
0.0,
90.0
).toInt
g.setColor(
new Color(
60,
180,
255,
alpha
)
)
g.drawOval(
centerX - ring,
centerY - ring,
ring * 2,
ring * 2
)
ring -= 8
}
// Arena body
g.setColor(
new Color(
6,
20,
42,
230
)
)
g.fillOval(
centerX - arenaRadius,
centerY - arenaRadius,
arenaRadius * 2,
arenaRadius * 2
)
g.setColor(
new Color(
80,
200,
255
)
)
g.setStroke(
new BasicStroke(
3.0f
)
)
g.drawOval(
centerX - arenaRadius,
centerY - arenaRadius,
arenaRadius * 2,
arenaRadius * 2
)
// Target ring
val targetRadius =
92 +
(
math.sin(
animationTime * 3.0
) * 5.0
).toInt
g.setColor(
new Color(
190,
90,
255,
150
)
)
g.setStroke(
new BasicStroke(
4.0f
)
)
g.drawOval(
centerX - targetRadius,
centerY - targetRadius,
targetRadius * 2,
targetRadius * 2
)
// Additional rotating target marks
var mark = 0
while (mark < 8) {
val ang =
animationTime +
mark *
math.Pi /
4.0
val mx =
(
centerX +
math.cos(ang) *
targetRadius
).toInt
val my =
(
centerY +
math.sin(ang) *
targetRadius
).toInt
g.setColor(
new Color(
120,
240,
255
)
)
g.fillOval(
mx - 4,
my - 4,
8,
8
)
mark += 1
}
// Core
val coreRadius =
22 +
(
math.sin(
animationTime * 5.0
) * 4.0
).toInt
g.setColor(
new Color(
110,
235,
255,
220
)
)
g.fillOval(
centerX - coreRadius,
centerY - coreRadius,
coreRadius * 2,
coreRadius * 2
)
g.setColor(Color.WHITE)
g.drawOval(
centerX - coreRadius - 4,
centerY - coreRadius - 4,
(coreRadius + 4) * 2,
(coreRadius + 4) * 2
)
// ------------------------------------------------------
// COUNTDOWN / TIMER
// ------------------------------------------------------
if (countdownActive) {
centered(
g,
math.ceil(
countdown
).toInt.toString,
355,
68,
new Color(
255,
220,
90
)
)
} else {
centered(
g,
f"$elapsed%.2f",
355,
60,
Color.WHITE
)
centered(
g,
if (timerRunning)
"STOP THE CLOCK"
else
"READY",
390,
15,
new Color(
120,
220,
255
)
)
}
// ------------------------------------------------------
// JUDGEMENT
// ------------------------------------------------------
if (judgementTimer > 0.0) {
val judgementColor =
lastJudgement match {
case "PERFECT" =>
new Color(
100,
255,
170
)
case "GREAT" =>
new Color(
100,
210,
255
)
case "GOOD" =>
new Color(
255,
220,
100
)
case _ =>
new Color(
255,
90,
100
)
}
centered(
g,
lastJudgement,
470,
32,
judgementColor
)
centered(
g,
f"ERROR $lastDifference%.3f s",
500,
14,
new Color(
180,
190,
210
)
)
}
// ------------------------------------------------------
// LEVEL FLASH
// ------------------------------------------------------
if (levelFlash > 0.0) {
val alpha =
clamp(
levelFlash * 180.0,
0.0,
180.0
).toInt
g.setColor(
new Color(
255,
220,
80,
alpha
)
)
g.setStroke(
new BasicStroke(
4.0f
)
)
g.drawOval(
centerX - 150,
centerY - 150,
300,
300
)
}
// ------------------------------------------------------
// PARTICLES
// ------------------------------------------------------
if (particlesEnabled) {
var i = 0
while (
i < particles.length
) {
val p =
particles(i)
val alpha =
clamp(
(
p.life /
p.maxLife
) * 255.0,
0.0,
255.0
).toInt
g.setColor(
new Color(
110,
220,
255,
alpha
)
)
val s =
math.max(
1,
p.size.toInt
)
g.fillOval(
p.x.toInt,
p.y.toInt,
s,
s
)
i += 1
}
}
// ------------------------------------------------------
// FLOATING TEXT
// ------------------------------------------------------
var fi = 0
while (
fi < floatingTexts.length
) {
val f =
floatingTexts(fi)
val alpha =
clamp(
f.life * 255.0,
0.0,
255.0
).toInt
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
18
)
)
g.setColor(
new Color(
f.color.getRed,
f.color.getGreen,
f.color.getBlue,
alpha
)
)
g.drawString(
f.message,
f.x.toInt,
f.y.toInt
)
fi += 1
}
// ------------------------------------------------------
// HELP TEXT
// ------------------------------------------------------
centered(
g,
if (countdownActive)
"GET READY..."
else if (timerRunning)
"SPACE = STOP"
else
"SPACE = START",
555,
17,
new Color(
100,
230,
255
)
)
centered(
g,
"P = PAUSE R = RESET L = LEADERBOARD H = HELP",
590,
13,
new Color(
135,
155,
180
)
)
centered(
g,
"ESC = MENU",
620,
13,
new Color(
120,
135,
160
)
)
}
// ========================================================
// RESULT
// ========================================================
def drawResult(
g: Graphics2D
): Unit = {
centered(
g,
"ROUND COMPLETE",
90,
38,
new Color(
100,
225,
255
)
)
panelBox(
g,
250,
135,
600,
390
)
centered(
g,
lastJudgement,
225,
40,
lastJudgement match {
case "PERFECT" =>
new Color(
100,
255,
170
)
case "GREAT" =>
new Color(
100,
210,
255
)
case "GOOD" =>
new Color(
255,
220,
100
)
case _ =>
new Color(
255,
90,
100
)
}
)
centered(
g,
f"ERROR $lastDifference%.3f seconds",
270,
17,
Color.WHITE
)
centered(
g,
"SCORE: " + score,
315,
22,
new Color(
255,
220,
90
)
)
centered(
g,
"COMBO: x" + combo,
350,
18,
new Color(
255,
110,
220
)
)
centered(
g,
"LEVEL: " + level,
385,
18,
new Color(
100,
210,
255
)
)
centered(
g,
f"ACCURACY: ${accuracy()}%.1f%%",
420,
18,
new Color(
120,
230,
170
)
)
centered(
g,
"ENTER = NEXT ROUND",
470,
16,
Color.WHITE
)
centered(
g,
"L = LEADERBOARD ESC = MENU",
510,
13,
new Color(
140,
160,
190
)
)
}
// ========================================================
// LEADERBOARD
// ========================================================
def drawLeaderboard(
g: Graphics2D
): Unit = {
centered(
g,
"SESSION LEADERBOARD",
75,
34,
new Color(
255,
220,
100
)
)
val sorted =
leaderboard
.sortBy(
p => -p.score
)
.take(10)
if (sorted.isEmpty) {
centered(
g,
"NO SCORES YET",
300,
24,
new Color(
150,
170,
200
)
)
} else {
var y = 125
var rank = 1
sorted.foreach { p =>
panelBox(
g,
160,
y - 25,
780,
52
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
16
)
)
g.setColor(
if (rank == 1)
new Color(
255,
215,
80
)
else if (rank == 2)
new Color(
210,
220,
235
)
else if (rank == 3)
new Color(
220,
160,
100
)
else
Color.WHITE
)
g.drawString(
"#" + rank,
185,
y + 5
)
g.setColor(Color.WHITE)
g.drawString(
p.name,
250,
y + 5
)
g.setColor(
new Color(
255,
220,
90
)
)
g.drawString(
"SCORE " + p.score,
500,
y + 5
)
g.setColor(
new Color(
100,
220,
180
)
)
g.drawString(
f"${p.accuracy}%.1f%%",
680,
y + 5
)
g.setColor(
new Color(
150,
180,
220
)
)
g.drawString(
"LVL " + p.level,
790,
y + 5
)
rank += 1
y += 58
}
}
centered(
g,
"ESC = MENU",
645,
14,
new Color(
130,
150,
180
)
)
}
// ========================================================
// ACHIEVEMENTS
// ========================================================
def drawAchievements(
g: Graphics2D
): Unit = {
centered(
g,
"ACHIEVEMENTS",
75,
34,
new Color(
255,
215,
100
)
)
var y = 115
achievements.foreach { a =>
panelBox(
g,
220,
y - 25,
660,
55
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
18
)
)
g.setColor(
if (a.unlocked)
new Color(
100,
255,
180
)
else
new Color(
100,
110,
130
)
)
g.drawString(
if (a.unlocked)
"?"
else
"?",
245,
y + 4
)
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
15
)
)
g.setColor(Color.WHITE)
g.drawString(
a.name,
285,
y
)
g.setFont(
new Font(
"SansSerif",
Font.PLAIN,
12
)
)
g.setColor(
new Color(
160,
180,
205
)
)
g.drawString(
a.description,
285,
y + 20
)
y += 65
}
centered(
g,
"ESC = MENU",
650,
14,
new Color(
130,
150,
180
)
)
}
// ========================================================
// STATISTICS
// ========================================================
def drawStatistics(
g: Graphics2D
): Unit = {
centered(
g,
"PLAYER STATISTICS",
80,
34,
new Color(
100,
220,
255
)
)
panelBox(
g,
260,
120,
580,
450
)
val fastest =
if (fastestReaction > 100000)
"--"
else
f"$fastestReaction%.3f s"
val closest =
if (closestHit > 100000)
"--"
else
f"$closestHit%.3f s"
val stats =
Array(
"PLAYER: " + playerName,
"LEVEL: " + level,
"CURRENT SCORE: " + score,
"LIFETIME SCORE: " + lifetimeScore,
"TOTAL GAMES: " + totalGames,
"ROUNDS PLAYED: " + totalRoundsPlayed,
"PERFECTS: " + lifetimePerfects,
"MISSES: " + lifetimeMisses,
"BEST COMBO: x" + lifetimeBestCombo,
"COINS: " + coins,
"FASTEST: " + fastest,
"CLOSEST HIT: " + closest,
f"CURRENT ACCURACY: ${accuracy()}%.1f%%"
)
var y = 160
stats.foreach { line =>
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
15
)
)
g.setColor(Color.WHITE)
g.drawString(
line,
325,
y
)
y += 30
}
centered(
g,
"ESC = MENU",
640,
14,
new Color(
130,
150,
180
)
)
}
// ========================================================
// HELP
// ========================================================
def drawHelp(
g: Graphics2D
): Unit = {
centered(
g,
"HOW TO PLAY",
72,
36,
new Color(
100,
220,
255
)
)
panelBox(
g,
160,
105,
780,
500
)
val help =
Array(
"SPACE Start / Stop the timer",
"UP/DOWN Select target",
"W/S Select target",
"D Change difficulty",
"M Change mode",
"P Pause",
"R Reset round",
"L Leaderboard",
"H Help",
"ESC Return to menu",
"",
"PERFECT = closest timing",
"GREAT = very close",
"GOOD = acceptable timing",
"MISS = outside timing window",
"",
"Higher difficulty = smaller timing window",
"Combo increases your score multiplier",
"Successful rounds give XP and coins",
"Level-ups grant bonus coins",
"Achievements grant bonus coins"
)
var y = 145
help.foreach { line =>
g.setFont(
new Font(
"SansSerif",
if (line.contains("="))
Font.BOLD
else
Font.PLAIN,
15
)
)
g.setColor(
if (line.startsWith("PERFECT"))
new Color(
100,
255,
170
)
else if (
line.startsWith("GREAT")
)
new Color(
100,
210,
255
)
else if (
line.startsWith("GOOD")
)
new Color(
255,
220,
100
)
else if (
line.startsWith("MISS")
)
new Color(
255,
100,
110
)
else
Color.WHITE
)
g.drawString(
line,
220,
y
)
y += 21
}
centered(
g,
"ESC = MENU",
645,
14,
new Color(
130,
150,
180
)
)
}
// ========================================================
// SETTINGS
// ========================================================
def drawSettings(
g: Graphics2D
): Unit = {
centered(
g,
"SETTINGS",
80,
36,
new Color(
190,
120,
255
)
)
panelBox(
g,
300,
140,
500,
390
)
setting(
g,
"1. SOUND",
soundEnabled,
220
)
setting(
g,
"2. PARTICLES",
particlesEnabled,
285
)
setting(
g,
"3. SCREEN SHAKE",
screenShakeEnabled,
350
)
setting(
g,
"4. ANIMATED BACKGROUND",
animatedBackground,
415
)
centered(
g,
"Press 1 / 2 / 3 / 4 to toggle",
560,
14,
new Color(
155,
175,
205
)
)
centered(
g,
"ESC = MENU",
610,
14,
new Color(
130,
150,
180
)
)
}
def setting(
g: Graphics2D,
label: String,
enabled: Boolean,
y: Int
): Unit = {
g.setFont(
new Font(
"SansSerif",
Font.BOLD,
17
)
)
g.setColor(
Color.WHITE
)
g.drawString(
label,
350,
y
)
g.setColor(
if (enabled)
new Color(
100,
255,
180
)
else
new Color(
255,
100,
110
)
)
g.drawString(
if (enabled)
"ON"
else
"OFF",
700,
y
)
}
// ========================================================
// PAUSE
// ========================================================
def drawPause(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
0,
0,
0,
175
)
)
g.fillRect(
0,
0,
SCREEN_W,
SCREEN_H
)
centered(
g,
"PAUSED",
300,
48,
new Color(
100,
220,
255
)
)
centered(
g,
"P OR SPACE = RESUME",
355,
18,
Color.WHITE
)
centered(
g,
"ESC = MENU",
390,
14,
new Color(
150,
165,
190
)
)
}
}
// ============================================================
// ATTACH PANEL
// ============================================================
frame.setContentPane(
gamePanel
)
// ============================================================
// KEYBOARD INPUT
// ============================================================
gamePanel.addKeyListener(
new KeyAdapter {
override def keyPressed(
e: KeyEvent
): Unit = {
val key =
e.getKeyCode
keys += key
handleKeyPressed(
key
)
}
override def keyReleased(
e: KeyEvent
): Unit = {
keys -= e.getKeyCode
}
override def keyTyped(
e: KeyEvent
): Unit = {
handleKeyTyped(
e.getKeyChar
)
}
}
)
// ============================================================
// KEY PRESSED HANDLER
// ============================================================
def handleKeyPressed(
key: Int
): Unit = {
// ----------------------------------------------------------
// ESC
// ----------------------------------------------------------
if (
key == KeyEvent.VK_ESCAPE
) {
if (
screen == GAME ||
screen == PAUSE
) {
timerRunning = false
countdownActive = false
screen = MENU
} else {
screen = MENU
}
gamePanel.repaint()
return
}
// ----------------------------------------------------------
// MENU
// ----------------------------------------------------------
if (screen == MENU) {
key match {
case KeyEvent.VK_ENTER =>
playerName = ""
screen = NAME_SCREEN
case KeyEvent.VK_L =>
screen = LEADERBOARD
case KeyEvent.VK_A =>
screen = ACHIEVEMENTS
case KeyEvent.VK_S =>
screen = STATS
case KeyEvent.VK_H =>
screen = HELP
case KeyEvent.VK_O =>
screen = SETTINGS
case _ =>
}
return
}
// ----------------------------------------------------------
// NAME SCREEN
// ----------------------------------------------------------
if (
screen == NAME_SCREEN
) {
if (
key == KeyEvent.VK_ENTER
) {
if (
playerName
.trim
.nonEmpty
) {
playerName =
playerName
.trim
.take(18)
screen =
TARGET_SCREEN
}
} else if (
key == KeyEvent.VK_BACK_SPACE
) {
if (
playerName.nonEmpty
) {
playerName =
playerName.dropRight(1)
}
}
gamePanel.repaint()
return
}
// ----------------------------------------------------------
// TARGET SCREEN
// ----------------------------------------------------------
if (
screen == TARGET_SCREEN
) {
key match {
case KeyEvent.VK_UP |
KeyEvent.VK_W =>
targetIndex =
math.max(
0,
targetIndex - 1
)
targetTime =
targetOptions(targetIndex)
case KeyEvent.VK_DOWN |
KeyEvent.VK_S =>
targetIndex =
math.min(
targetOptions.length - 1,
targetIndex + 1
)
targetTime =
targetOptions(targetIndex)
case KeyEvent.VK_D =>
difficulty =
(difficulty + 1) % 4
case KeyEvent.VK_M =>
mode =
(mode + 1) % 3
case KeyEvent.VK_ENTER =>
startNewGame()
case _ =>
}
gamePanel.repaint()
return
}
// ----------------------------------------------------------
// GAME
// ----------------------------------------------------------
if (
screen == GAME
) {
key match {
case KeyEvent.VK_SPACE =>
if (!countdownActive) {
if (!timerRunning)
startCountdown()
else
stopTimer()
}
case KeyEvent.VK_P =>
timerRunning = false
countdownActive = false
screen = PAUSE
case KeyEvent.VK_R =>
resetRound()
case KeyEvent.VK_L =>
timerRunning = false
countdownActive = false
screen = LEADERBOARD
case KeyEvent.VK_H =>
timerRunning = false
countdownActive = false
screen = HELP
case _ =>
}
gamePanel.repaint()
return
}
// ----------------------------------------------------------
// PAUSE
// ----------------------------------------------------------
if (
screen == PAUSE
) {
if (
key == KeyEvent.VK_P ||
key == KeyEvent.VK_SPACE
) {
screen = GAME
}
gamePanel.repaint()
return
}
// ----------------------------------------------------------
// RESULT
// ----------------------------------------------------------
if (
screen == RESULT
) {
key match {
case KeyEvent.VK_ENTER =>
nextRound()
case KeyEvent.VK_L =>
screen = LEADERBOARD
case _ =>
}
gamePanel.repaint()
return
}
// ----------------------------------------------------------
// SETTINGS
// ----------------------------------------------------------
if (
screen == SETTINGS
) {
key match {
case KeyEvent.VK_1 =>
soundEnabled = !soundEnabled
case KeyEvent.VK_2 =>
particlesEnabled = !particlesEnabled
if (!particlesEnabled)
particles.clear()
case KeyEvent.VK_3 =>
screenShakeEnabled =
!screenShakeEnabled
case KeyEvent.VK_4 =>
animatedBackground =
!animatedBackground
case _ =>
}
gamePanel.repaint()
return
}
gamePanel.repaint()
}
// ============================================================
// TEXT INPUT HANDLER
// ============================================================
def handleKeyTyped(
ch: Char
): Unit = {
if (
screen == NAME_SCREEN
) {
if (
ch >= ' ' &&
ch <= '~'
) {
if (
playerName.length < 18
) {
playerName += ch
}
}
gamePanel.repaint()
}
}
// ============================================================
// MOUSE INPUT
// ============================================================
gamePanel.addMouseListener(
new MouseAdapter {
override def mousePressed(
e: MouseEvent
): Unit = {
handleMouse(
e.getX,
e.getY
)
}
}
)
// ============================================================
// MOUSE HANDLER
// ============================================================
def handleMouse(
x: Int,
y: Int
): Unit = {
// ----------------------------------------------------------
// MENU BUTTONS
// ----------------------------------------------------------
if (screen == MENU) {
if (
x >= 390 &&
x <= 710 &&
y >= 250 &&
y <= 305
) {
playerName = ""
screen = NAME_SCREEN
} else if (
x >= 390 &&
x <= 710 &&
y >= 320 &&
y <= 370
) {
screen = LEADERBOARD
} else if (
x >= 390 &&
x <= 710 &&
y >= 385 &&
y <= 435
) {
screen = ACHIEVEMENTS
} else if (
x >= 390 &&
x <= 710 &&
y >= 450 &&
y <= 500
) {
screen = STATS
} else if (
x >= 390 &&
x <= 545 &&
y >= 515 &&
y <= 560
) {
screen = SETTINGS
} else if (
x >= 555 &&
x <= 710 &&
y >= 515 &&
y <= 560
) {
screen = HELP
}
}
// ----------------------------------------------------------
// GAME CLICK
// ----------------------------------------------------------
else if (
screen == GAME
) {
val dx =
x -
SCREEN_W / 2.0
val dy =
y -
350.0
val distance =
math.hypot(
dx,
dy
)
if (
distance < 220
) {
if (
!countdownActive
) {
if (
!timerRunning
) {
startCountdown()
} else {
stopTimer()
}
}
}
}
// ----------------------------------------------------------
// OTHER SCREENS
// ----------------------------------------------------------
else if (
screen == LEADERBOARD ||
screen == ACHIEVEMENTS ||
screen == STATS ||
screen == HELP ||
screen == SETTINGS
) {
if (y > 580) {
screen = MENU
}
}
gamePanel.requestFocusInWindow()
gamePanel.repaint()
}
// ============================================================
// MAIN SWING TIMER
// ============================================================
val swingTimer =
new javax.swing.Timer(
16,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
updateGame(
0.016
)
gamePanel.repaint()
}
}
)
swingTimer.start()
// ============================================================
// INITIAL WINDOW
// ============================================================
frame.setLocationRelativeTo(null)
frame.setVisible(true)
gamePanel.requestFocusInWindow()
println(
"=================================================="
)
println(
" STOP THE SECONDS - ULTRA ARENA X"
)
println(
" FULL SCALA SWING VERSION LOADED"
)
println(
"=================================================="
)