Code Sketch
Bixuu02 navratri animation
Category: Programming
import java.awt.{BasicStroke, Color, Dimension, Font, GradientPaint, Graphics, Graphics2D, Polygon, RenderingHints, KeyboardFocusManager, KeyEventDispatcher}
import java.awt.event.{ActionEvent, ActionListener, KeyEvent, MouseEvent, MouseListener, FocusAdapter, FocusEvent}
import java.awt.geom.{Ellipse2D, RoundRectangle2D}
import java.awt.image.BufferedImage
import javax.swing.{JComponent, JFrame, JPanel, SwingUtilities, Timer, WindowConstants}
import scala.util.Random
/**
* BIXUU NAVRATRI 3D ADVENTURE
* Kojo-friendly Scala + Java Swing/AWT game. No external libraries or image files.
* A small custom perspective/isometric renderer gives the world a 3D look.
* Controls: WASD / Arrow Keys = move, E / ENTER / SPACE = interact,
* 1 / ENTER = start, P = pause, ESC = menu.
*/
object Navratri3DAdventure {
def main(args: Array[String]): Unit = {
SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = createWindow()
})
}
private def createWindow(): Unit = {
val frame = new JFrame("BIXUU NAVRATRI 3D ADVENTURE | Scala + Swing")
val game = new GamePanel()
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setContentPane(game)
frame.setSize(1120, 740)
frame.setMinimumSize(new Dimension(920, 640))
frame.setLocationRelativeTo(null)
frame.setIconImage(makeIcon())
frame.setVisible(true)
game.startLoop()
SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = game.requestFocusInWindow()
})
}
private def makeIcon(): BufferedImage = {
val image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB)
val g = image.createGraphics()
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g.setPaint(new GradientPaint(0, 0, new Color(37, 21, 77), 64, 64, new Color(225, 79, 51)))
g.fillRoundRect(0, 0, 64, 64, 15, 15)
g.setColor(new Color(255, 199, 58))
g.fillOval(13, 34, 38, 16)
g.setColor(new Color(255, 237, 156))
g.fillOval(29, 20, 8, 18)
g.drawLine(22, 29, 42, 29)
g.dispose()
image
}
private class GamePanel extends JPanel with MouseListener {
private val random = new Random(41)
// Named palettes avoid Kojo/Scala parser ambiguity with Array(...)(index).
private val giftColors: Array[Color] = Array(
new Color(225, 57, 102),
new Color(57, 183, 148),
new Color(255, 190, 64),
new Color(113, 113, 230),
new Color(245, 119, 71)
)
private val dancerColors: Array[Color] = Array(
new Color(224, 63, 128),
new Color(69, 191, 153),
new Color(244, 177, 52),
new Color(111, 111, 222)
)
private val baggageColors: Array[Color] = Array(
new Color(232, 77, 104),
new Color(74, 198, 154),
new Color(252, 193, 70),
new Color(106, 127, 235),
new Color(235, 135, 67)
)
private var scene = "menu"
private var playerName = "Bixuu"
private var businessName = "Bixuu Industries"
private var playerX = 0.0
private var playerZ = 0.0
private var moveUp = false
private var moveDown = false
private var moveLeft = false
private var moveRight = false
private val heldKeys = scala.collection.mutable.Set.empty[Int]
private var tick = 0
private var walkFrame = 0
private var score = 0
private var toast = "Welcome! Your special Navratri invitation is waiting at the office."
private var toastTicks = 230
private var paused = false
private var carFixed = false
private var giftCollected = false
private var checkedIn = false
private var securityCleared = false
private var flightTicks = 0
private var baggageCleared = false
private var dressedForNavratri = false
private var danceStarted = false
private var danceStartTick = 0
private var danceTaps = 0
private var prize = ""
private val movementCodes: Set[Int] = Set(
KeyEvent.VK_W, KeyEvent.VK_A, KeyEvent.VK_S, KeyEvent.VK_D,
KeyEvent.VK_UP, KeyEvent.VK_DOWN, KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT
)
private val handledCodes: Set[Int] = movementCodes ++ Set(
KeyEvent.VK_E, KeyEvent.VK_ENTER, KeyEvent.VK_SPACE,
KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_P, KeyEvent.VK_ESCAPE
)
setFocusable(true)
setFocusTraversalKeysEnabled(false)
setBackground(new Color(14, 15, 40))
addMouseListener(this)
addFocusListener(new FocusAdapter {
override def focusLost(e: FocusEvent): Unit = resetKeys()
})
// IMPORTANT: construct the dispatcher before registering it. Registering it
// earlier used the still-null field and made every keyboard control appear dead.
private val keyDispatcher: KeyEventDispatcher = new KeyEventDispatcher {
override def dispatchKeyEvent(e: KeyEvent): Boolean = {
val window = SwingUtilities.getWindowAncestor(GamePanel.this)
if (window == null || !window.isActive || !handledCodes.contains(e.getKeyCode)) {
false
} else if (e.getID == KeyEvent.KEY_PRESSED) {
val key = e.getKeyCode
if (!heldKeys.contains(key)) {
heldKeys += key
updateMovementFlags()
if (!movementCodes.contains(key)) handleKeyPress(key)
}
true
} else if (e.getID == KeyEvent.KEY_RELEASED) {
heldKeys -= e.getKeyCode
updateMovementFlags()
true
} else false
}
}
// This statement now runs only after keyDispatcher has been initialized.
installKeyboardControls()
private def installKeyboardControls(): Unit = {
KeyboardFocusManager.getCurrentKeyboardFocusManager.addKeyEventDispatcher(keyDispatcher)
}
private def updateMovementFlags(): Unit = {
moveUp = heldKeys.contains(KeyEvent.VK_W) || heldKeys.contains(KeyEvent.VK_UP)
moveDown = heldKeys.contains(KeyEvent.VK_S) || heldKeys.contains(KeyEvent.VK_DOWN)
moveLeft = heldKeys.contains(KeyEvent.VK_A) || heldKeys.contains(KeyEvent.VK_LEFT)
moveRight = heldKeys.contains(KeyEvent.VK_D) || heldKeys.contains(KeyEvent.VK_RIGHT)
}
private def resetKeys(): Unit = {
heldKeys.clear()
updateMovementFlags()
}
def startLoop(): Unit = {
val timer = new Timer(30, new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
updateGame()
repaint()
}
})
timer.start()
}
private def showToast(message: String, duration: Int = 160): Unit = {
toast = message
toastTicks = duration
}
private def resetPosition(): Unit = {
playerX = 0.0
playerZ = 0.0
walkFrame = 0
moveUp = false
moveDown = false
moveLeft = false
moveRight = false
}
private def startGame(): Unit = {
scene = "office"
playerX = 0.0
playerZ = 0.0
score = 0
carFixed = false
giftCollected = false
checkedIn = false
securityCleared = false
flightTicks = 0
baggageCleared = false
dressedForNavratri = false
danceStarted = false
danceTaps = 0
prize = ""
paused = false
resetKeys()
showToast("A royal Navratri invitation arrived! Go to the glowing invitation on your office desk.", 250)
}
private def handleKeyPress(key: Int): Unit = {
if (key == KeyEvent.VK_ESCAPE) {
scene = "menu"
paused = false
resetPosition()
showToast("Choose START to begin your Navratri journey.", 130)
return
}
if (key == KeyEvent.VK_P && scene != "menu" && scene != "complete") {
paused = !paused
showToast(if (paused) "Game paused. Press P to continue." else "Game resumed.", 90)
return
}
if (scene == "menu") {
if (key == KeyEvent.VK_1 || key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE) startGame()
return
}
if (scene == "complete") {
if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE || key == KeyEvent.VK_E) {
scene = "menu"
showToast("Ready for another Navratri trip? Press ENTER to play.", 130)
}
return
}
if (paused) return
if (scene == "stadium" && danceStarted &&
(key == KeyEvent.VK_E || key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE)) {
danceTaps += 1
score += 5
showToast("Dandiya beat " + danceTaps + "! Keep tapping SPACE / ENTER / E to dance.", 45)
} else if (key == KeyEvent.VK_E || key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE) {
interact()
}
}
private def updateGame(): Unit = {
tick += 1
if (toastTicks > 0) toastTicks -= 1
if (scene == "menu" || scene == "complete" || paused) return
var dx = 0.0
var dz = 0.0
if (moveLeft) dx -= 1.0
if (moveRight) dx += 1.0
if (moveUp) dz += 1.0
if (moveDown) dz -= 1.0
if (dx != 0.0 || dz != 0.0) {
val length = math.sqrt(dx * dx + dz * dz)
playerX += (dx / length) * 0.095
playerZ += (dz / length) * 0.095
playerX = math.max(-6.0, math.min(6.0, playerX))
playerZ = math.max(-6.0, math.min(6.0, playerZ))
walkFrame += 1
}
if (scene == "plane" && flightTicks < 280) flightTicks += 1
if (scene == "stadium" && danceStarted && tick - danceStartTick >= 320) finishDance()
}
private def targetPoint(): (Double, Double) = scene match {
case "office" => (0.0, 2.35)
case "carTrouble" => (1.55, 0.72)
case "giftShop" => (0.0, 2.05)
case "mumbaiAirport" => (0.0, 2.15)
case "mumbaiSecurity" => (0.0, 1.8)
case "plane" => (0.0, 1.5)
case "gujaratAirport" => (0.0, 2.05)
case "home" => (0.0, 2.15)
case "dressChange" => (0.0, 2.05)
case "stadium" => (0.0, 1.75)
case _ => (0.0, 0.0)
}
private def nearTarget(radius: Double = 1.45): Boolean = {
val target = targetPoint()
val dx = playerX - target._1
val dz = playerZ - target._2
math.sqrt(dx * dx + dz * dz) <= radius
}
private def nextScene(next: String, message: String): Unit = {
scene = next
resetPosition()
showToast(message, 220)
}
private def interact(): Unit = {
if (!nearTarget()) {
showToast("Walk toward the glowing marker, then press E / ENTER.", 95)
return
}
scene match {
case "office" =>
score += 10
nextScene("carTrouble", "Invitation accepted! Your car has broken down on the road. Walk to the car and inspect it.")
case "carTrouble" =>
if (!carFixed) {
carFixed = true
score += 25
showToast("Mechanic fixed the car! Press E again near the car to drive to the gift shop.", 180)
} else {
nextScene("giftShop", "Car repaired! You drove to the gift shop. Collect a festive gift for the host.")
}
case "giftShop" =>
if (!giftCollected) {
giftCollected = true
score += 50
showToast("Gift collected! Press E again at the counter to continue to Mumbai Airport.", 180)
} else {
nextScene("mumbaiAirport", "You reached Mumbai Airport with the gift. Go to airline check-in.")
}
case "mumbaiAirport" =>
checkedIn = true
score += 25
nextScene("mumbaiSecurity", "Check-in complete! Show your boarding pass and go through airport security.")
case "mumbaiSecurity" =>
securityCleared = true
score += 25
flightTicks = 0
nextScene("plane", "Security check complete. Board the flight to Gujarat and enjoy the journey!")
case "plane" =>
if (flightTicks < 180) {
showToast("The plane is flying toward Gujarat. Please wait for landing: " + (flightTicks * 100 / 180) + "%.", 100)
} else {
score += 40
nextScene("gujaratAirport", "Welcome to Gujarat! Leave the plane and collect your luggage after arrival checks.")
}
case "gujaratAirport" =>
baggageCleared = true
score += 25
nextScene("home", "Arrival and baggage checks complete! Your car is ready. Drive to your home and get ready for Navratri.")
case "home" =>
nextScene("dressChange", "Welcome home! Go to your wardrobe and change into your colourful Navratri outfit.")
case "dressChange" =>
if (!dressedForNavratri) {
dressedForNavratri = true
score += 20
showToast("Navratri outfit ready! Press E again to travel to the Dandiya Stadium.", 180)
} else {
nextScene("stadium", "You reached the grand Gujarat Navratri stadium! Walk to the dance floor and press E to begin.")
}
case "stadium" =>
if (!danceStarted) {
danceStarted = true
danceStartTick = tick
danceTaps = 0
showToast("DANDIYA TIME! Tap SPACE / ENTER / E to the beat for 10 seconds. Aim for 12 beats for FIRST PRIZE!", 230)
}
case _ =>
}
}
private def finishDance(): Unit = {
prize = if (danceTaps >= 12) "FIRST PRIZE" else "SECOND PRIZE"
score += (if (prize == "FIRST PRIZE") 500 else 200)
scene = "complete"
paused = false
showToast("Amazing Navratri! You won " + prize + "!", 280)
}
// ---------- 3D projection helpers ----------
// Fixed isometric camera: the character visibly moves across the world
// instead of staying pinned to screen centre while the floor follows it.
private def project(x: Double, y: Double, z: Double): java.awt.Point = {
val sx = getWidth * 0.5 + (x - z) * 35.0
val sy = getHeight * 0.535 + (x + z) * 17.5 - y * 48.0
new java.awt.Point(sx.toInt, sy.toInt)
}
private def polygon(points: Seq[java.awt.Point]): Polygon = {
new Polygon(points.map(_.x).toArray, points.map(_.y).toArray, points.length)
}
private def drawBox(g: Graphics2D, x: Double, z: Double, width: Double, depth: Double,
height: Double, top: Color, left: Color, right: Color,
baseY: Double = 0.0): Unit = {
val a = project(x - width / 2, baseY + height, z - depth / 2)
val b = project(x + width / 2, baseY + height, z - depth / 2)
val c = project(x + width / 2, baseY + height, z + depth / 2)
val d = project(x - width / 2, baseY + height, z + depth / 2)
val A = project(x - width / 2, baseY, z - depth / 2)
val B = project(x + width / 2, baseY, z - depth / 2)
val C = project(x + width / 2, baseY, z + depth / 2)
val D = project(x - width / 2, baseY, z + depth / 2)
g.setColor(left)
g.fillPolygon(polygon(Seq(a, d, D, A)))
g.setColor(right)
g.fillPolygon(polygon(Seq(b, c, C, B)))
g.setColor(left.darker())
g.fillPolygon(polygon(Seq(d, c, C, D)))
g.setColor(top)
g.fillPolygon(polygon(Seq(a, b, c, d)))
g.setColor(new Color(15, 14, 36, 150))
g.setStroke(new BasicStroke(1f))
g.drawPolygon(polygon(Seq(a, b, c, d)))
}
private def drawGround(g: Graphics2D): Unit = {
var j = -9
while (j <= 9) {
var i = -9
while (i <= 9) {
// The ground grid is in fixed world coordinates, like the scenery.
val cx = i * 0.82
val cz = j * 0.82
val tile = polygon(Seq(
project(cx - 0.41, 0, cz - 0.41),
project(cx + 0.41, 0, cz - 0.41),
project(cx + 0.41, 0, cz + 0.41),
project(cx - 0.41, 0, cz + 0.41)
))
val tone = if ((i + j) % 2 == 0) new Color(72, 75, 105) else new Color(62, 65, 94)
g.setColor(tone)
g.fillPolygon(tile)
g.setColor(new Color(177, 175, 201, 22))
g.drawPolygon(tile)
i += 1
}
j += 1
}
}
private def drawMarker(g: Graphics2D, x: Double, z: Double, color: Color): Unit = {
val p = project(x, 0.05, z)
val pulse = (math.sin(tick * 0.16) * 5).toInt
g.setColor(new Color(color.getRed, color.getGreen, color.getBlue, 38))
g.fillOval(p.x - 21 - pulse, p.y - 8 - pulse / 3, 42 + pulse * 2, 19 + pulse)
g.setColor(color)
g.setStroke(new BasicStroke(2.2f))
g.drawOval(p.x - 15 - pulse, p.y - 6 - pulse / 3, 30 + pulse * 2, 13 + pulse)
g.fillOval(p.x - 4, p.y - 18, 8, 8)
g.drawLine(p.x, p.y - 10, p.x, p.y - 2)
g.setStroke(new BasicStroke(1f))
}
private def drawPerson(g: Graphics2D, x: Double, z: Double, shirt: Color, pants: Color, scale: Double = 1.0): Unit = {
val p = project(x, 0, z)
val bob = math.sin(tick * 0.12 + x * 2.0) * 1.4
g.setColor(new Color(0, 0, 0, 70))
g.fillOval(p.x - (13 * scale).toInt, p.y - 2, (26 * scale).toInt, (8 * scale).toInt)
g.setStroke(new BasicStroke((4.5 * scale).toFloat, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND))
g.setColor(pants)
g.drawLine(p.x - 4, p.y - 18, p.x - 6, p.y - 3)
g.drawLine(p.x + 4, p.y - 18, p.x + 6, p.y - 3)
g.setColor(shirt)
g.fillRoundRect(p.x - (9 * scale).toInt, p.y - (39 * scale).toInt + bob.toInt,
(18 * scale).toInt, (23 * scale).toInt, 7, 7)
g.setColor(new Color(241, 190, 147))
g.fillOval(p.x - (7 * scale).toInt, p.y - (54 * scale).toInt + bob.toInt,
(14 * scale).toInt, (15 * scale).toInt)
g.setColor(new Color(52, 31, 28))
g.fillArc(p.x - (8 * scale).toInt, p.y - (57 * scale).toInt + bob.toInt,
(16 * scale).toInt, (11 * scale).toInt, 0, 180)
g.setStroke(new BasicStroke(1f))
}
private def drawPlayer(g: Graphics2D): Unit = {
val p = project(playerX, 0, playerZ)
val bob = if (moveUp || moveDown || moveLeft || moveRight) math.sin(walkFrame * 0.7) * 3.0 else math.sin(tick * 0.06) * 1.0
g.setColor(new Color(0, 0, 0, 95))
g.fillOval(p.x - 21, p.y - 3, 42, 13)
val legSwing = if (moveUp || moveDown || moveLeft || moveRight) (math.sin(walkFrame * 0.7) * 5).toInt else 0
g.setStroke(new BasicStroke(7f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND))
g.setColor(new Color(42, 31, 76))
g.drawLine(p.x - 5, p.y - 20, p.x - 7 + legSwing, p.y - 3)
g.drawLine(p.x + 5, p.y - 20, p.x + 7 - legSwing, p.y - 3)
g.setColor(new Color(249, 166, 48))
g.fillRoundRect(p.x - 14, p.y - 45 + bob.toInt, 28, 27, 9, 9)
g.setColor(if (dressedForNavratri || scene == "stadium") new Color(212, 52, 131) else new Color(45, 142, 214))
g.fillRoundRect(p.x - 12, p.y - 44 + bob.toInt, 24, 21, 8, 8)
g.setColor(new Color(244, 192, 151))
g.fillOval(p.x - 10, p.y - 66 + bob.toInt, 20, 21)
g.setColor(new Color(57, 31, 30))
g.fillArc(p.x - 11, p.y - 70 + bob.toInt, 22, 15, 0, 180)
g.setColor(Color.BLACK)
g.fillOval(p.x - 5, p.y - 56 + bob.toInt, 2, 3)
g.fillOval(p.x + 4, p.y - 56 + bob.toInt, 2, 3)
if (scene == "stadium" && danceStarted) {
g.setStroke(new BasicStroke(4f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND))
val swing = (math.sin(tick * 0.45) * 12).toInt
g.setColor(new Color(249, 187, 67))
g.drawLine(p.x - 10, p.y - 39, p.x - 23, p.y - 49 - swing)
g.drawLine(p.x + 10, p.y - 39, p.x + 23, p.y - 49 + swing)
g.setColor(new Color(255, 238, 186))
g.drawLine(p.x - 27, p.y - 53 - swing, p.x - 17, p.y - 45 - swing)
g.drawLine(p.x + 17, p.y - 45 + swing, p.x + 27, p.y - 53 + swing)
}
g.setStroke(new BasicStroke(1f))
g.setFont(new Font("SansSerif", Font.BOLD, 10))
g.setColor(new Color(255, 238, 179))
centered(g, playerName.toUpperCase, p.x, p.y - 76 + bob.toInt)
}
private def drawCar(g: Graphics2D, x: Double, z: Double, broken: Boolean): Unit = {
drawBox(g, x, z, 2.0, 1.0, 0.48, new Color(228, 65, 70), new Color(142, 38, 58), new Color(179, 43, 53))
drawBox(g, x - 0.08, z, 1.12, 0.78, 0.36, new Color(105, 198, 225), new Color(43, 81, 119), new Color(62, 107, 145), 0.46)
val p1 = project(x - 0.62, 0.22, z - 0.46)
val p2 = project(x + 0.64, 0.22, z - 0.46)
val p3 = project(x - 0.62, 0.22, z + 0.46)
val p4 = project(x + 0.64, 0.22, z + 0.46)
Seq(p1, p2, p3, p4).foreach { p =>
g.setColor(new Color(23, 25, 42)); g.fillOval(p.x - 8, p.y - 4, 16, 13)
g.setColor(new Color(166, 183, 202)); g.fillOval(p.x - 4, p.y - 1, 8, 7)
}
val head = project(x + 0.7, 0.3, z - 0.45)
g.setColor(new Color(255, 243, 174)); g.fillOval(head.x - 4, head.y - 4, 8, 7)
if (broken) {
val smoke = project(x + 0.48, 1.0 + math.sin(tick * 0.1) * 0.15, z + 0.12)
g.setColor(new Color(124, 129, 150, 125)); g.fillOval(smoke.x - 13, smoke.y - 13, 26, 23)
g.setColor(new Color(184, 188, 202, 100)); g.fillOval(smoke.x + 2, smoke.y - 25, 20, 19)
}
if (carFixed) {
val light = project(x, 1.2, z)
g.setColor(new Color(125, 255, 192)); g.fillOval(light.x - 5, light.y - 5, 10, 10)
}
}
private def drawTree(g: Graphics2D, x: Double, z: Double): Unit = {
drawBox(g, x, z, 0.26, 0.26, 1.1, new Color(129, 78, 47), new Color(92, 53, 35), new Color(106, 61, 38))
val p = project(x, 1.65, z)
g.setColor(new Color(33, 110, 71)); g.fillOval(p.x - 23, p.y - 17, 46, 39)
g.setColor(new Color(45, 139, 79)); g.fillOval(p.x - 16, p.y - 31, 36, 35)
g.setColor(new Color(67, 164, 92)); g.fillOval(p.x - 11, p.y - 24, 20, 19)
}
private def drawHouse(g: Graphics2D, x: Double, z: Double, width: Double, name: String): Unit = {
drawBox(g, x, z, width, 2.1, 2.0, new Color(245, 184, 122), new Color(155, 87, 87), new Color(187, 103, 86))
val door = project(x, 0.8, z - 1.06)
g.setColor(new Color(92, 47, 57)); g.fillRoundRect(door.x - 16, door.y - 35, 32, 52, 5, 5)
val win1 = project(x - width * 0.27, 1.25, z - 1.08)
val win2 = project(x + width * 0.27, 1.25, z - 1.08)
Seq(win1, win2).foreach { p =>
g.setColor(new Color(33, 63, 97)); g.fillRoundRect(p.x - 13, p.y - 13, 26, 25, 4, 4)
g.setColor(new Color(132, 222, 238)); g.fillRect(p.x - 9, p.y - 9, 18, 17)
g.setColor(new Color(47, 63, 87)); g.drawLine(p.x, p.y - 9, p.x, p.y + 8)
}
val roofLeft = project(x - width / 2 - 0.2, 2.05, z - 1.05)
val roofTop = project(x, 3.0, z - 1.0)
val roofRight = project(x + width / 2 + 0.2, 2.05, z - 1.05)
g.setColor(new Color(137, 43, 75))
g.fillPolygon(polygon(Seq(roofLeft, roofTop, roofRight)))
g.setColor(new Color(255, 218, 151))
g.setFont(new Font("SansSerif", Font.BOLD, 11))
centered(g, name, project(x, 2.25, z - 1.15).x, project(x, 2.25, z - 1.15).y)
}
private def drawShop(g: Graphics2D): Unit = {
drawBox(g, 0.0, 2.65, 3.0, 1.3, 1.65, new Color(240, 153, 68), new Color(122, 53, 72), new Color(170, 66, 69))
drawBox(g, 0.0, 1.9, 2.0, 0.5, 0.65, new Color(255, 204, 107), new Color(111, 58, 64), new Color(145, 60, 61), 0.05)
val sign = project(0, 2.35, 1.95)
g.setColor(new Color(255, 238, 177))
g.setFont(new Font("SansSerif", Font.BOLD, 15))
centered(g, "GIFT SHOP", sign.x, sign.y)
var i = 0
while (i < 5) {
val x = -0.7 + i * 0.35
val p = project(x, 0.9 + (i % 2) * 0.08, 1.7)
g.setColor(giftColors(i))
g.fillRoundRect(p.x - 8, p.y - 8, 16, 15, 3, 3)
g.setColor(new Color(255, 232, 165)); g.drawLine(p.x, p.y - 8, p.x, p.y - 13)
i += 1
}
}
private def drawAirportBuilding(g: Graphics2D, gujarat: Boolean): Unit = {
drawBox(g, 0.0, 5.0, 8.6, 1.8, 2.2, new Color(159, 203, 229), new Color(63, 101, 147), new Color(83, 125, 162))
drawBox(g, 0.0, 4.0, 7.6, 0.5, 0.35, new Color(255, 188, 76), new Color(111, 76, 90), new Color(145, 85, 94), 2.05)
var i = -3
while (i <= 3) {
val p = project(i.toDouble, 1.25, 4.02)
g.setColor(new Color(29, 58, 93)); g.fillRoundRect(p.x - 20, p.y - 29, 40, 55, 5, 5)
g.setColor(new Color(147, 226, 239)); g.fillRect(p.x - 15, p.y - 24, 30, 43)
i += 1
}
val title = if (gujarat) "GUJARAT AIRPORT" else "MUMBAI INTERNATIONAL AIRPORT"
val p = project(0, 2.6, 4.05)
g.setColor(new Color(255, 244, 197)); g.setFont(new Font("SansSerif", Font.BOLD, 17)); centered(g, title, p.x, p.y)
}
private def drawDiyasAndGarlands(g: Graphics2D): Unit = {
var i = -4
while (i <= 4) {
val p = project(i * 0.72, 2.7, 1.0)
val pulse = (math.sin(tick * 0.12 + i) * 3).toInt
g.setColor(new Color(255, 199, 72, 50)); g.fillOval(p.x - 11 - pulse, p.y - 15 - pulse, 22 + pulse * 2, 22 + pulse * 2)
g.setColor(if (i % 2 == 0) new Color(255, 207, 77) else new Color(70, 234, 188))
g.fillOval(p.x - 4, p.y - 7, 8, 9)
i += 1
}
}
private def drawPlane(g: Graphics2D): Unit = {
// Plane body, wings and tail are projected boxes rather than a flat picture.
drawBox(g, 0.0, 2.0, 1.4, 4.0, 0.62, new Color(226, 234, 241), new Color(70, 115, 162), new Color(110, 159, 195), 0.45)
drawBox(g, 0.0, 2.0, 4.5, 0.72, 0.19, new Color(189, 218, 237), new Color(76, 124, 166), new Color(119, 164, 195), 0.5)
drawBox(g, 0.0, -0.1, 1.75, 0.8, 0.28, new Color(255, 196, 77), new Color(168, 99, 69), new Color(202, 126, 66), 0.58)
var i = -2
while (i <= 2) {
val p = project(0.72, 0.78, 0.5 + i * 0.67)
g.setColor(new Color(34, 61, 94)); g.fillRoundRect(p.x - 7, p.y - 5, 13, 10, 3, 3)
g.setColor(new Color(137, 218, 241)); g.fillRect(p.x - 5, p.y - 3, 9, 5)
i += 1
}
// Moving clouds create a sense of flight.
var c = 0
while (c < 9) {
val xx = ((c * 157 + tick * (c % 3 + 1)) % math.max(1, getWidth)).toInt
val yy = 120 + ((c * 47) % 190)
g.setColor(new Color(255, 255, 255, 95))
g.fillOval(xx, yy, 72, 24); g.fillOval(xx + 17, yy - 11, 38, 30); g.fillOval(xx + 38, yy - 4, 35, 25)
c += 1
}
g.setColor(new Color(255, 232, 163)); g.setFont(new Font("SansSerif", Font.BOLD, 18))
centered(g, "FLIGHT TO GUJARAT", getWidth / 2, 165)
g.setFont(new Font("SansSerif", Font.BOLD, 14))
centered(g, "IN-FLIGHT JOURNEY: " + math.min(100, flightTicks * 100 / 180) + "%", getWidth / 2, 193)
}
private def drawStadium(g: Graphics2D): Unit = {
// Large stepped arena, central dance floor, lights, and dandiya crowd.
drawBox(g, 0, 4.0, 9.0, 1.4, 1.1, new Color(107, 52, 111), new Color(55, 35, 89), new Color(75, 39, 103))
drawBox(g, 0, 2.4, 5.5, 1.3, 0.4, new Color(245, 175, 67), new Color(143, 60, 95), new Color(183, 65, 101))
drawBox(g, 0, 1.7, 3.9, 0.72, 0.16, new Color(64, 204, 181), new Color(28, 93, 108), new Color(35, 135, 132), 0.42)
val stageLabel = project(0, 1.3, 1.7)
g.setColor(new Color(255, 241, 192)); g.setFont(new Font("SansSerif", Font.BOLD, 14)); centered(g, "NAVRATRI DANDIYA", stageLabel.x, stageLabel.y)
var i = 0
while (i < 16) {
val angle = i * math.Pi * 2 / 16.0
val x = math.cos(angle) * 4.0
val z = 2.3 + math.sin(angle) * 2.1
drawPerson(g, x, z, dancerColors(i % dancerColors.length), new Color(53, 42, 85), 0.78)
}
// Decorative arches and strings of lights.
var n = -4
while (n <= 4) {
val poleX = n.toDouble
drawBox(g, poleX, 3.2, 0.12, 0.12, 2.6, new Color(255, 186, 70), new Color(143, 66, 94), new Color(178, 76, 108))
val light = project(poleX, 2.8, 3.2)
g.setColor(new Color(255, 222, 104, 70)); g.fillOval(light.x - 9, light.y - 9, 18, 18)
g.setColor(if (n % 2 == 0) new Color(255, 219, 95) else new Color(80, 246, 203))
g.fillOval(light.x - 4, light.y - 4, 8, 8)
n += 1
}
if (danceStarted) {
val p = project(0, 2.2, 1.5)
var k = 0
while (k < 8) {
val xx = p.x + ((math.sin(tick * 0.15 + k) * (22 + k * 3))).toInt
val yy = p.y - 65 - ((math.cos(tick * 0.18 + k) * 18).toInt)
g.setColor(if (k % 2 == 0) new Color(255, 206, 73, 150) else new Color(79, 236, 200, 140))
g.fillOval(xx, yy, 8, 8)
k += 1
}
}
}
private def drawSceneWorld(g: Graphics2D): Unit = {
val w = getWidth
val h = getHeight
val skyTop = if (scene == "stadium") new Color(25, 10, 59) else new Color(32, 40, 91)
val skyBottom = if (scene == "stadium") new Color(124, 36, 99) else new Color(207, 105, 110)
g.setPaint(new GradientPaint(0, 0, skyTop, 0, h, skyBottom))
g.fillRect(0, 0, w, h)
// Horizon glow and stars.
g.setColor(new Color(255, 213, 139, 35)); g.fillOval(w - 210, 35, 130, 130)
g.setColor(new Color(255, 231, 172, 225)); g.fillOval(w - 179, 66, 68, 68)
var s = 0
while (s < 45) {
val sx = (s * 139 + 23) % math.max(1, w)
val sy = (s * 59 + 17) % 230
g.setColor(new Color(255, 243, 204, 90 + (s % 7) * 20))
g.fillOval(sx, sy, if (s % 9 == 0) 3 else 2, if (s % 9 == 0) 3 else 2)
s += 1
}
drawGround(g)
// Environment props.
scene match {
case "office" =>
drawBox(g, -3.4, 3.0, 2.2, 1.0, 2.35, new Color(111, 142, 190), new Color(47, 64, 117), new Color(68, 88, 145))
drawBox(g, 0, 2.45, 2.2, 0.92, 0.7, new Color(154, 92, 67), new Color(86, 47, 51), new Color(115, 61, 55))
drawBox(g, 0, 2.35, 0.48, 0.34, 0.08, new Color(255, 230, 164), new Color(181, 129, 71), new Color(207, 151, 77), 0.73)
drawBox(g, 0, 2.35, 0.1, 0.08, 0.02, new Color(202, 49, 70), new Color(137, 43, 59), new Color(165, 52, 69), 0.82)
drawTree(g, -4.2, 0.4); drawTree(g, 4.2, 2.3)
drawMarker(g, 0, 2.35, new Color(255, 216, 78))
val title = project(0, 1.18, 2.0); g.setColor(new Color(255, 240, 190)); g.setFont(new Font("SansSerif", Font.BOLD, 12)); centered(g, "INVITATION", title.x, title.y)
case "carTrouble" =>
drawTree(g, -4.0, 2.0); drawTree(g, 4.5, 2.6)
drawBox(g, -1.2, 2.0, 8.5, 1.25, 0.14, new Color(65, 71, 88), new Color(40, 44, 62), new Color(46, 51, 67))
drawCar(g, 1.55, 0.72, !carFixed)
drawPerson(g, 3.0, 0.0, new Color(40, 135, 203), new Color(34, 41, 76), 0.95)
val p = project(3.0, 1.45, 0.0); g.setColor(new Color(255, 240, 198)); g.setFont(new Font("SansSerif", Font.BOLD, 11)); centered(g, "MECHANIC", p.x, p.y)
drawMarker(g, 1.55, 0.72, if (carFixed) new Color(102, 255, 174) else new Color(255, 110, 92))
case "giftShop" =>
drawShop(g)
drawTree(g, -3.4, 0.1); drawTree(g, 3.5, 0.2)
drawBox(g, 0.0, 1.95, 0.45, 0.42, 0.38, new Color(255, 198, 62), new Color(165, 72, 89), new Color(206, 85, 105), 0.68)
val gift = project(0, 1.4, 1.95); g.setColor(new Color(255, 242, 189)); g.setFont(new Font("SansSerif", Font.BOLD, 11)); centered(g, if (giftCollected) "GIFT COLLECTED" else "YOUR GIFT", gift.x, gift.y)
drawMarker(g, 0, 2.05, if (giftCollected) new Color(99, 255, 176) else new Color(255, 216, 79))
case "mumbaiAirport" =>
drawAirportBuilding(g, false)
drawBox(g, 0, 2.15, 2.5, 0.7, 0.75, new Color(84, 186, 202), new Color(42, 83, 118), new Color(55, 111, 139))
val p = project(0, 1.05, 1.7); g.setColor(new Color(255, 245, 199)); g.setFont(new Font("SansSerif", Font.BOLD, 13)); centered(g, "AIRLINE CHECK-IN", p.x, p.y)
drawBox(g, -2.3, 0.4, 0.42, 0.5, 0.62, new Color(199, 59, 105), new Color(111, 45, 82), new Color(143, 52, 94))
drawMarker(g, 0, 2.15, new Color(255, 220, 79))
case "mumbaiSecurity" =>
drawAirportBuilding(g, false)
drawBox(g, 0, 2.0, 1.45, 0.58, 1.62, new Color(68, 192, 199), new Color(30, 92, 125), new Color(43, 124, 153))
drawBox(g, 0, 2.0, 0.45, 0.22, 1.35, new Color(32, 54, 83), new Color(20, 39, 66), new Color(24, 46, 72), 0.12)
drawBox(g, -0.5, 1.3, 0.7, 0.45, 0.22, new Color(236, 166, 73), new Color(111, 71, 58), new Color(151, 87, 61))
drawPerson(g, 1.3, 1.4, new Color(45, 91, 176), new Color(36, 40, 76), 1.05)
val p = project(0, 2.0, 1.6); g.setColor(new Color(255, 241, 187)); g.setFont(new Font("SansSerif", Font.BOLD, 12)); centered(g, "SECURITY CHECK", p.x, p.y)
drawMarker(g, 0, 1.8, new Color(100, 246, 196))
case "plane" =>
drawPlane(g)
drawBox(g, -1.7, 0.3, 0.9, 0.72, 0.82, new Color(74, 92, 139), new Color(37, 49, 89), new Color(53, 66, 109))
drawBox(g, 1.7, 0.3, 0.9, 0.72, 0.82, new Color(74, 92, 139), new Color(37, 49, 89), new Color(53, 66, 109))
drawMarker(g, 0, 1.5, new Color(100, 246, 196))
case "gujaratAirport" =>
drawAirportBuilding(g, true)
drawBox(g, 0, 2.05, 3.5, 0.7, 0.62, new Color(87, 172, 186), new Color(43, 79, 115), new Color(57, 102, 132))
var b = -2
while (b <= 2) {
drawBox(g, b * 0.48, 2.0, 0.35, 0.35, 0.34, baggageColors(b + 2), new Color(68, 51, 92), new Color(88, 62, 113), 0.65)
b += 1
}
val p = project(0, 1.4, 1.65); g.setColor(new Color(255, 241, 193)); g.setFont(new Font("SansSerif", Font.BOLD, 12)); centered(g, "BAGGAGE CLAIM", p.x, p.y)
drawMarker(g, 0, 2.05, new Color(100, 246, 196))
case "home" =>
drawTree(g, -3.5, 1.6); drawTree(g, 3.7, 1.3)
drawHouse(g, 0, 3.0, 4.2, "BIXUU HOME")
drawCar(g, -2.4, 0.5, false)
drawDiyasAndGarlands(g)
drawMarker(g, 0, 2.15, new Color(255, 216, 79))
case "dressChange" =>
drawBox(g, 0, 4.2, 6.8, 1.7, 2.5, new Color(167, 112, 144), new Color(78, 48, 91), new Color(102, 56, 103))
drawBox(g, 0, 2.15, 1.2, 0.6, 2.0, new Color(229, 170, 92), new Color(117, 65, 65), new Color(157, 83, 73))
drawBox(g, -0.22, 2.1, 0.06, 0.5, 1.7, new Color(70, 38, 69), new Color(51, 33, 58), new Color(61, 38, 62), 0.12)
val p = project(0, 2.35, 1.7); g.setColor(new Color(255, 237, 185)); g.setFont(new Font("SansSerif", Font.BOLD, 13)); centered(g, "NAVRATRI OUTFIT", p.x, p.y)
drawMarker(g, 0, 2.05, if (dressedForNavratri) new Color(100, 255, 177) else new Color(255, 216, 79))
case "stadium" =>
drawStadium(g)
drawMarker(g, 0, 1.75, new Color(100, 246, 196))
case _ =>
}
drawPlayer(g)
// Goal hint floats above the glowing objective.
if (scene != "stadium" || !danceStarted) {
val t = targetPoint()
val p = project(t._1, 1.25, t._2)
g.setFont(new Font("SansSerif", Font.BOLD, 11))
g.setColor(new Color(255, 248, 218))
centered(g, "OBJECTIVE", p.x, p.y)
}
}
private def sceneTitle: String = scene match {
case "office" => "1 / 9 - THE INVITATION"
case "carTrouble" => if (carFixed) "2 / 9 - CAR REPAIRED" else "2 / 9 - CAR BREAKDOWN"
case "giftShop" => "3 / 9 - FESTIVE GIFT SHOP"
case "mumbaiAirport" => "4 / 9 - MUMBAI AIRPORT CHECK-IN"
case "mumbaiSecurity" => "5 / 9 - SECURITY CHECK"
case "plane" => "6 / 9 - FLIGHT TO GUJARAT"
case "gujaratAirport" => "7 / 9 - GUJARAT ARRIVAL"
case "home" => "8 / 9 - HOME IN GUJARAT"
case "dressChange" => "9 / 9 - GET READY FOR DANDIYA"
case "stadium" => "GRAND GUJARAT NAVRATRI STADIUM"
case _ => "NAVRATRI ADVENTURE"
}
private def objectiveText: String = scene match {
case "office" => "Find the glowing invitation on your office desk and press E."
case "carTrouble" => if (carFixed) "Car fixed! Press E near the car again to drive to the gift shop." else "Your car broke down. Go to the car and press E to call the mechanic."
case "giftShop" => if (giftCollected) "Gift collected! Press E again to continue to Mumbai Airport." else "Go to the gift shop counter and collect the host's gift."
case "mumbaiAirport" => "Reach airline check-in and collect your boarding pass."
case "mumbaiSecurity" => "Pass your carry-on bag through security and complete the check."
case "plane" => if (flightTicks < 180) "Flight in progress. Wait for Gujarat landing, then press E." else "We have landed! Walk to the marker and press E to leave the plane."
case "gujaratAirport" => "Collect your luggage and finish the arrival checks."
case "home" => "Drive to your home in Gujarat and go to the front door."
case "dressChange" => if (dressedForNavratri) "Outfit ready! Press E again to go to the Dandiya Stadium." else "Choose your colourful Navratri outfit at the wardrobe."
case "stadium" => if (danceStarted) "DANCE! Tap SPACE / ENTER / E to the beat. 12 beats = FIRST PRIZE!" else "Go to the dance floor and press E to start Garba / Dandiya."
case _ => "Enjoy your Navratri adventure!"
}
override def paintComponent(raw: Graphics): Unit = {
super.paintComponent(raw)
val g = raw.asInstanceOf[Graphics2D]
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)
if (scene == "menu") drawMenu(g)
else if (scene == "complete") drawComplete(g)
else {
drawSceneWorld(g)
drawHUD(g)
if (paused) drawPause(g)
}
}
private def drawMenu(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight
g.setPaint(new GradientPaint(0, 0, new Color(20, 9, 57), 0, h, new Color(129, 31, 94)))
g.fillRect(0, 0, w, h)
var i = 0
while (i < 110) {
val x = (i * 137 + 19) % math.max(1, w)
val y = (i * 71 + 7) % math.max(1, h)
g.setColor(new Color(255, 240, 200, 80 + (i % 6) * 25))
g.fillOval(x, y, if (i % 8 == 0) 3 else 2, if (i % 8 == 0) 3 else 2)
i += 1
}
// Isometric floor and festive 3D scene preview.
val oldX = playerX; val oldZ = playerZ
playerX = 0; playerZ = 0
drawBox(g, 0, 3.3, 4.6, 1.5, 1.65, new Color(229, 139, 78), new Color(106, 46, 89), new Color(144, 57, 95))
drawBox(g, 0, 2.55, 2.7, 0.7, 0.35, new Color(255, 202, 83), new Color(121, 49, 79), new Color(173, 60, 96))
var n = -4
while (n <= 4) {
val p = project(n.toDouble, 2.1, 3.0)
g.setColor(if (n % 2 == 0) new Color(255, 215, 87) else new Color(69, 238, 199))
g.fillOval(p.x - 5, p.y - 5, 10, 10)
n += 1
}
playerX = oldX; playerZ = oldZ
g.setFont(new Font("SansSerif", Font.BOLD, 40))
g.setColor(new Color(255, 207, 75))
centered(g, "BIXUU NAVRATRI 3D ADVENTURE", w / 2, 83)
g.setFont(new Font("SansSerif", Font.BOLD, 18))
g.setColor(new Color(255, 238, 207))
centered(g, "INVITATION > CAR TROUBLE > GIFT > FLIGHT TO GUJARAT", w / 2, 125)
centered(g, "HOME > NAVRATRI OUTFIT > GARBA / DANDIYA > PRIZES", w / 2, 153)
g.setFont(new Font("SansSerif", Font.BOLD, 20))
g.setColor(new Color(119, 255, 214))
centered(g, "PRESS ENTER OR 1 TO START", w / 2, h - 95)
g.setFont(new Font("SansSerif", Font.PLAIN, 15))
g.setColor(new Color(244, 231, 255))
centered(g, "CLICK GAME WINDOW FIRST | WASD / ARROWS: MOVE | E / ENTER: INTERACT | P: PAUSE | ESC: MENU", w / 2, h - 59)
centered(g, "Dance challenge: tap SPACE / ENTER / E - 12 beats wins FIRST PRIZE!", w / 2, h - 31)
}
private def drawHUD(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight
g.setColor(new Color(12, 12, 35, 225)); g.fillRoundRect(15, 12, w - 30, 105, 18, 18)
g.setColor(new Color(255, 196, 75)); g.setStroke(new BasicStroke(1.5f)); g.drawRoundRect(15, 12, w - 30, 105, 18, 18)
g.setFont(new Font("SansSerif", Font.BOLD, 19)); g.setColor(new Color(255, 211, 103)); g.drawString("BIXUU NAVRATRI 3D", 31, 40)
g.setFont(new Font("SansSerif", Font.BOLD, 12)); g.setColor(Color.WHITE); g.drawString(playerName + " | " + businessName, 32, 61)
g.setFont(new Font("SansSerif", Font.BOLD, 14)); g.setColor(new Color(127, 249, 208)); g.drawString("SCORE: " + score, 32, 85)
g.setFont(new Font("SansSerif", Font.BOLD, 13)); g.setColor(new Color(255, 243, 215)); g.drawString(sceneTitle, 335, 39)
g.setFont(new Font("SansSerif", Font.PLAIN, 12)); g.setColor(new Color(224, 218, 245)); g.drawString(objectiveText, 335, 61)
g.setFont(new Font("SansSerif", Font.BOLD, 11)); g.setColor(new Color(255, 206, 105))
g.drawString("WASD / ARROWS MOVE | E / ENTER INTERACT | P PAUSE | ESC MENU", 335, 86)
if (scene == "stadium" && danceStarted) {
g.setColor(new Color(118, 255, 207)); g.drawString("DANDIYA BEATS: " + danceTaps + " / 12 FOR FIRST PRIZE", 335, 104)
} else if (scene == "plane") {
g.setColor(new Color(118, 255, 207)); g.drawString("FLIGHT: " + math.min(100, flightTicks * 100 / 180) + "%", 335, 104)
} else {
val bagStatus = if (giftCollected) "GIFT: READY" else "GIFT: NOT COLLECTED"
g.setColor(new Color(118, 255, 207)); g.drawString(bagStatus, 335, 104)
}
if (toastTicks > 0 && toast != null && toast.nonEmpty) {
val fm = g.getFontMetrics(new Font("SansSerif", Font.BOLD, 13))
g.setFont(new Font("SansSerif", Font.BOLD, 13))
val tw = math.min(w - 40, fm.stringWidth(toast) + 36)
val tx = (w - tw) / 2
g.setColor(new Color(13, 11, 36, 229)); g.fillRoundRect(tx, h - 66, tw, 32, 12, 12)
g.setColor(new Color(255, 205, 83)); g.drawRoundRect(tx, h - 66, tw, 32, 12, 12)
g.setColor(new Color(255, 246, 225)); centered(g, toast, w / 2, h - 45)
}
g.setFont(new Font("SansSerif", Font.BOLD, 11)); g.setColor(new Color(255, 230, 157))
centered(g, "Move toward the glowing ring. Get close, then press E / ENTER.", w / 2, h - 20)
}
private def drawPause(g: Graphics2D): Unit = {
g.setColor(new Color(8, 7, 26, 195)); g.fillRect(0, 0, getWidth, getHeight)
g.setFont(new Font("SansSerif", Font.BOLD, 42)); g.setColor(new Color(255, 214, 102))
centered(g, "PAUSED", getWidth / 2, getHeight / 2 - 8)
g.setFont(new Font("SansSerif", Font.BOLD, 17)); g.setColor(Color.WHITE)
centered(g, "Press P to continue", getWidth / 2, getHeight / 2 + 30)
}
private def drawComplete(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight
g.setPaint(new GradientPaint(0, 0, new Color(22, 8, 57), 0, h, new Color(119, 25, 93)))
g.fillRect(0, 0, w, h)
var i = 0
while (i < 70) {
val x = (i * 103 + tick * (i % 3 + 1)) % math.max(1, w)
val y = (i * 59 + tick) % math.max(1, h)
g.setColor(if (i % 2 == 0) new Color(255, 211, 76, 190) else new Color(73, 241, 204, 190))
g.fillOval(x, y, 4, 4)
i += 1
}
g.setColor(new Color(13, 9, 36, 220)); g.fillRoundRect(w / 2 - 300, h / 2 - 205, 600, 410, 35, 35)
g.setColor(new Color(255, 203, 77)); g.setStroke(new BasicStroke(3f)); g.drawRoundRect(w / 2 - 300, h / 2 - 205, 600, 410, 35, 35)
g.setFont(new Font("SansSerif", Font.BOLD, 34)); g.setColor(new Color(255, 209, 77))
centered(g, "SHUBH NAVRATRI!", w / 2, h / 2 - 130)
g.setFont(new Font("SansSerif", Font.BOLD, 23)); g.setColor(Color.WHITE)
centered(g, "DANDIYA CELEBRATION COMPLETE", w / 2, h / 2 - 83)
g.setFont(new Font("SansSerif", Font.BOLD, 38)); g.setColor(new Color(119, 255, 208))
centered(g, prize, w / 2, h / 2 - 18)
g.setFont(new Font("SansSerif", Font.BOLD, 22)); g.setColor(new Color(255, 228, 142))
centered(g, "FINAL SCORE: " + score, w / 2, h / 2 + 29)
g.setFont(new Font("SansSerif", Font.PLAIN, 16)); g.setColor(new Color(239, 229, 253))
centered(g, "You travelled to Gujarat, enjoyed Garba and Dandiya, and won your prize!", w / 2, h / 2 + 67)
g.setFont(new Font("SansSerif", Font.BOLD, 16)); g.setColor(new Color(255, 220, 119))
centered(g, "Press ENTER / SPACE to return to menu", w / 2, h / 2 + 125)
}
private def centered(g: Graphics2D, text: String, x: Int, y: Int): Unit = {
val fm = g.getFontMetrics
g.drawString(text, x - fm.stringWidth(text) / 2, y)
}
override def mouseClicked(e: MouseEvent): Unit = {
if (scene == "menu") startGame()
requestFocusInWindow()
}
override def mousePressed(e: MouseEvent): Unit = {}
override def mouseReleased(e: MouseEvent): Unit = {}
override def mouseEntered(e: MouseEvent): Unit = {}
override def mouseExited(e: MouseEvent): Unit = {}
}
}
// KOJO AUTO-START: paste the entire file into Kojo and press Run.
Navratri3DAdventure.main(Array.empty[String])