Code Sketch
Bixuu02 diwali animtion game
Category: Programming
import java.awt.{BasicStroke, Color, Dimension, Font, GradientPaint, Graphics, Graphics2D, KeyboardFocusManager, KeyEventDispatcher, Polygon, RenderingHints}
import java.awt.event.{ActionEvent, ActionListener, KeyEvent, MouseEvent, MouseListener, WindowAdapter, WindowEvent}
import java.awt.geom.RoundRectangle2D
import java.awt.image.BufferedImage
import javax.swing.{JComponent, JFrame, JOptionPane, JPanel, SwingUtilities, Timer, WindowConstants}
import scala.util.Random
/**
* BIXUU DIWALI TRAIN ADVENTURE
* Complete standalone Scala + Java Swing/AWT game for Kojo.
* Controls: WASD / Arrow keys = move; E / ENTER / SPACE = interact;
* 1 = Gokuldham Family; 2 = Normal Family; P = pause; ESC = menu.
* No external libraries or image files required.
*/
object BixuuDiwaliGame {
def main(args: Array[String]): Unit = {
SwingUtilities.invokeLater(new Runnable {
override def run(): Unit = createWindow()
})
}
private def createWindow(): Unit = {
val frame = new JFrame("BIXUU DIWALI TRAIN ADVENTURE | Scala + Swing")
val game = new GamePanel()
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setContentPane(game)
frame.setSize(1100, 720)
frame.setMinimumSize(new Dimension(900, 620))
frame.setLocationRelativeTo(null)
frame.setIconImage(makeAppIcon())
frame.addWindowFocusListener(new WindowAdapter {
override def windowLostFocus(e: WindowEvent): Unit = game.clearHeldKeys()
})
frame.setVisible(true)
game.startLoop()
game.requestFocusInWindow()
}
private def makeAppIcon(): BufferedImage = {
val img = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB)
val g = img.createGraphics()
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g.setPaint(new GradientPaint(0, 0, new Color(35, 15, 65), 64, 64, new Color(230, 88, 18)))
g.fillRoundRect(0, 0, 64, 64, 16, 16)
g.setColor(new Color(255, 205, 77)); g.fillOval(17, 33, 30, 13)
g.setColor(new Color(255, 242, 170)); g.fillOval(27, 24, 10, 15)
g.setColor(new Color(255, 255, 225)); g.fillOval(30, 27, 4, 10)
g.dispose()
img
}
private class DecoTarget(val x: Double, val y: Double, val label: String, var done: Boolean = false)
private class GamePanel extends JPanel with MouseListener {
private val rng = new Random(17)
private var phase = "menu" // menu, playing, complete
private var family = "Gokuldham Family"
private var familyName = "My Family"
private var playerName = "Bixuu"
private var score = 0
private var tick = 0
private var walkFrame = 0
private var pause = false
private var playerX = 160.0
private var playerY = 470.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 scene = "letter" // letter, ticket, missed, wait, coach, goaStation, goaHome, diwali
private var ticketChecked = false
private var currentCoach = 1
private var coachCoins = Array(false, false, false)
private var trainArrivalTicks = 0
private var travelTicks = 0
private var ticketBooked = false
private var stage = 0
private var supplies = false
private var pujaDone = false
private var targets = Array.empty[DecoTarget]
private var stairAnimating = false
private var stairTick = 0
private var stairStartX = 0.0
private var stairStartY = 0.0
private var toast = "Welcome! Choose your family to begin."
private var toastTicks = 180
private var menuGokul = new java.awt.Rectangle(0, 0, 0, 0)
private var menuNormal = new java.awt.Rectangle(0, 0, 0, 0)
setFocusable(true)
setBackground(new Color(16, 12, 35))
addMouseListener(this)
private val movementKeys: 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 supportedKeys: Set[Int] = movementKeys ++ Set(
KeyEvent.VK_E, KeyEvent.VK_ENTER, KeyEvent.VK_SPACE,
KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_NUMPAD1, KeyEvent.VK_NUMPAD2,
KeyEvent.VK_P, KeyEvent.VK_ESCAPE
)
// Global dispatcher fixes focus-related keyboard failures in embedded editors such as Kojo.
// It responds only when this game's window is active.
private val keyDispatcher: KeyEventDispatcher = new KeyEventDispatcher {
override def dispatchKeyEvent(e: KeyEvent): Boolean = {
val win = SwingUtilities.getWindowAncestor(GamePanel.this)
if (win == null || !win.isActive || !supportedKeys.contains(e.getKeyCode)) {
false
} else if (e.getID == KeyEvent.KEY_PRESSED) {
val code = e.getKeyCode
if (!heldKeys.contains(code)) {
heldKeys += code
updateMovementFlags()
if (!movementKeys.contains(code)) handleKeyPressed(code)
}
true
} else if (e.getID == KeyEvent.KEY_RELEASED) {
heldKeys -= e.getKeyCode
updateMovementFlags()
true
} else false
}
}
private def installGlobalKeyboardControls(): Unit = {
KeyboardFocusManager.getCurrentKeyboardFocusManager.addKeyEventDispatcher(keyDispatcher)
}
// Register after keyDispatcher has been initialized.
installGlobalKeyboardControls()
def clearHeldKeys(): Unit = {
heldKeys.clear()
updateMovementFlags()
}
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 handleKeyPressed(code: Int): Unit = {
if (code == KeyEvent.VK_E || code == KeyEvent.VK_ENTER || code == KeyEvent.VK_SPACE) {
if (phase == "menu") startGame(family)
else interact()
} else if (code == KeyEvent.VK_1 || code == KeyEvent.VK_NUMPAD1) {
if (phase == "menu") startGame("Gokuldham Family")
} else if (code == KeyEvent.VK_2 || code == KeyEvent.VK_NUMPAD2) {
if (phase == "menu") startGame("Normal Family")
} else if (code == KeyEvent.VK_P) {
if (phase == "playing") {
pause = !pause
if (!pause) showToast("Game resumed.", 80)
}
} else if (code == KeyEvent.VK_ESCAPE) {
phase = "menu"
pause = false
stairAnimating = false
clearHeldKeys()
showToast("Choose a family and start a new Diwali adventure!", 150)
}
repaint()
}
def startLoop(): Unit = {
val timer = new Timer(30, new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
updateGame()
repaint()
}
})
timer.setCoalesce(true)
timer.start()
}
private def askName(prompt: String, defaultValue: String): String = {
val answer = JOptionPane.showInputDialog(this, prompt, defaultValue)
if (answer == null || answer.trim.isEmpty) defaultValue else answer.trim.take(24)
}
private def startGame(which: String): Unit = {
clearHeldKeys()
family = which
playerName = askName("Choose your character name:", "Bixuu")
familyName = if (family == "Normal Family") askName("Enter your family / house name:", "My Family") else "Gokuldham Society"
phase = "playing"
pause = false
score = 0
stage = 0
supplies = false
pujaDone = false
scene = "letter"
ticketChecked = false
ticketBooked = false
currentCoach = 1
coachCoins = Array(false, false, false)
trainArrivalTicks = 0
travelTicks = 0
stairAnimating = false
targets = Array.empty[DecoTarget]
playerX = math.max(70.0, getWidth * 0.16)
playerY = math.max(365.0, getHeight * 0.66)
showToast("A letter has arrived from Goa! Find it and read your family message.", 220)
requestFocusInWindow()
}
private def beginDiwaliMissions(): Unit = {
scene = "diwali"
stage = 0
supplies = false
pujaDone = false
stairAnimating = false
if (family == "Gokuldham Family") {
playerX = 410.0; playerY = 325.0
targets = Array(
new DecoTarget(505, 480, "Diyas"),
new DecoTarget(570, 500, "Flower Toran"),
new DecoTarget(640, 475, "Fairy Lights"),
new DecoTarget(685, 515, "Rangoli")
)
showToast("Welcome! Walk to the staircase and press E to go down.", 200)
} else {
playerX = 160.0; playerY = 480.0
targets = Array(
new DecoTarget(120, 475, "Diyas"),
new DecoTarget(185, 510, "Flower Toran"),
new DecoTarget(250, 470, "Fairy Lights"),
new DecoTarget(310, 510, "Rangoli")
)
showToast("You reached Goa! Buy decoration supplies, decorate your home, and perform puja.", 220)
}
}
private def showToast(message: String, ticks: Int): Unit = {
toast = message
toastTicks = ticks
}
private def distance(x1: Double, y1: Double, x2: Double, y2: Double): Double = {
val dx = x1 - x2; val dy = y1 - y2
math.sqrt(dx * dx + dy * dy)
}
private def updateGame(): Unit = {
tick += 1
if (toastTicks > 0) toastTicks -= 1
if (phase != "playing" || pause) return
if (scene == "wait" && trainArrivalTicks < 100) trainArrivalTicks += 1
if (scene == "coach" && travelTicks < 360) travelTicks += 1
if (stairAnimating) {
stairTick += 1
val progress = math.min(1.0, stairTick / 34.0)
playerX = stairStartX + (420.0 - stairStartX) * progress
playerY = stairStartY + (405.0 - stairStartY) * progress
walkFrame += 1
if (progress >= 1.0) {
stairAnimating = false
stage = 1
score += 20
showToast("You came down the stairs! Ask the society secretary for permission.", 190)
}
return
}
var dx = 0.0; var dy = 0.0
if (moveLeft) dx -= 1.0
if (moveRight) dx += 1.0
if (moveUp) dy -= 1.0
if (moveDown) dy += 1.0
if (dx != 0.0 || dy != 0.0) {
val len = math.sqrt(dx * dx + dy * dy)
val speed = if (scene == "coach") 3.6 else 3.35
playerX += dx / len * speed
playerY += dy / len * speed
playerX = math.max(30.0, math.min(getWidth - 30.0, playerX))
playerY = math.max(290.0, math.min(getHeight - 95.0, playerY))
walkFrame += 1
}
}
private def interact(): Unit = {
if (phase == "menu") { startGame(family); return }
if (phase == "complete") {
phase = "menu"
showToast("Choose a family and start a new adventure!", 150)
return
}
if (pause) { pause = false; showToast("Game resumed.", 80); return }
if (stairAnimating) return
if (scene != "diwali") interactJourney() else interactDiwali()
}
private def interactJourney(): Unit = {
val w = getWidth.toDouble; val h = getHeight.toDouble
scene match {
case "letter" =>
if (distance(playerX, playerY, w * 0.70, h * 0.63) < 112) {
scene = "ticket"; playerX = w * 0.12; playerY = h * 0.66
score += 10
showToast("Letter: Come to Goa for Diwali! Your grandparents are waiting. Go to the station ticket counter.", 230)
} else showToast("Move to the glowing letter on the table and press E / ENTER.", 120)
case "ticket" =>
if (distance(playerX, playerY, w * 0.78, h * 0.60) < 125) {
ticketBooked = true; scene = "missed"; playerX = w * 0.12; playerY = h * 0.66
score += 20
showToast("Ticket booked! Oh no, the train has left. Find the TTE on the platform.", 210)
} else showToast("Walk to the ticket counter and press E / ENTER to book your ticket.", 120)
case "missed" =>
if (distance(playerX, playerY, w * 0.70, h * 0.62) < 120) {
scene = "wait"; trainArrivalTicks = 0; playerX = w * 0.14; playerY = h * 0.66
score += 15
showToast("TTE: The next train is coming soon. Wait near the marked platform door.", 200)
} else showToast("Talk to the TTE (Train Ticket Examiner) to ask about the next train.", 125)
case "wait" =>
if (trainArrivalTicks < 100) showToast("The next train is on its way. Please wait a little.", 100)
else if (distance(playerX, playerY, w * 0.53, h * 0.62) < 150) {
scene = "coach"; currentCoach = 1; ticketChecked = false; travelTicks = 0
playerX = w * 0.12; playerY = h * 0.64
showToast("You boarded the next train! Show your ticket to the TTE in Coach 1.", 200)
} else showToast("The train has arrived! Walk to the glowing open door and press E.", 130)
case "coach" =>
val coinX = w * 0.44; val coinY = h * 0.57
if (!coachCoins(currentCoach - 1) && distance(playerX, playerY, coinX, coinY) < 70) {
coachCoins(currentCoach - 1) = true; score += 25
showToast("Coin collected! " + coachCoins.count(identity) + "/3 collected.", 130)
} else if (currentCoach == 1 && !ticketChecked && distance(playerX, playerY, w * 0.70, h * 0.59) < 110) {
ticketChecked = true; score += 35
showToast("TTE: Ticket verified! Collect your coins and move through all three coaches.", 190)
} else if (distance(playerX, playerY, w * 0.88, h * 0.64) < 105) {
if (!ticketChecked) showToast("Show your ticket to the TTE in Coach 1 first.", 130)
else if (currentCoach < 3) {
currentCoach += 1; playerX = w * 0.12; playerY = h * 0.64
showToast("You entered Coach " + currentCoach + ". Find the coin and move to the right-side door.", 170)
} else if (travelTicks < 220) {
showToast("The train is waiting at a signal. Journey progress: " + math.min(100, travelTicks * 100 / 220) + "%.", 115)
} else {
scene = "goaStation"; playerX = w * 0.12; playerY = h * 0.66; score += 50
showToast("The train has arrived in Goa! Walk to the station exit.", 200)
}
} else {
val hint = if (currentCoach == 1 && !ticketChecked) "Find the TTE in Coach 1 and show your ticket." else "Collect the glowing coin or walk to the door on the right."
showToast(hint, 110)
}
case "goaStation" =>
if (distance(playerX, playerY, w * 0.84, h * 0.64) < 130) {
scene = "goaHome"; playerX = w * 0.13; playerY = h * 0.66; score += 20
showToast("You are outside Goa station. Find your grandparents' house.", 200)
} else showToast("Walk to the glowing station exit on the right and press E.", 120)
case "goaHome" =>
if (distance(playerX, playerY, w * 0.74, h * 0.60) < 130) {
score += 50
showToast("Aaji-Ajoba: Welcome home! Happy Diwali! Let us prepare for the celebration.", 170)
beginDiwaliMissions()
} else showToast("Walk to Aaji-Ajoba near the front door and press E.", 120)
case _ => showToast("Follow the glowing marker, then press E / ENTER.", 100)
}
}
private def interactDiwali(): Unit = {
if (family == "Gokuldham Family") {
stage match {
case 0 =>
if (distance(playerX, playerY, 420, 355) < 105) {
stairAnimating = true; stairTick = 0; stairStartX = playerX; stairStartY = playerY
showToast("Walking down the staircase...", 70)
} else showToast("Walk close to the staircase in the middle building and press E.", 110)
case 1 =>
if (distance(playerX, playerY, 535, 430) < 100) {
stage = 2; score += 30
showToast("Secretary: Permission granted! Visit Abdul's shop for supplies.", 170)
} else showToast("Go near the society secretary and press E.", 110)
case 2 =>
if (distance(playerX, playerY, getWidth - 175, 420) < 115) buySupplies()
else showToast("Walk to Abdul's shop on the right and press E.", 110)
case 3 => decorateAtNearestTarget()
case 4 =>
if (distance(playerX, playerY, 535, 535) < 105) finishPuja()
else showToast("Go to the community puja area and press E.", 110)
case _ => phase = "complete"
}
} else {
stage match {
case 0 =>
if (distance(playerX, playerY, getWidth - 175, 420) < 115) buySupplies()
else showToast("Go to Abdul's shop on the right to buy supplies.", 110)
case 1 => decorateAtNearestTarget()
case 2 =>
if (distance(playerX, playerY, 225, 535) < 105) finishPuja()
else showToast("Return to your home puja area and press E.", 110)
case _ => phase = "complete"
}
}
}
private def buySupplies(): Unit = {
if (!supplies) {
supplies = true; score += 50
stage = if (family == "Gokuldham Family") 3 else 1
showToast("Abdul: Decoration supplies collected! Go decorate the glowing markers.", 180)
} else showToast("You already have all the decoration supplies. Happy Diwali!", 120)
}
private def decorateAtNearestTarget(): Unit = {
if (!supplies) { showToast("First collect decoration supplies from Abdul's shop!", 120); return }
var nearest: DecoTarget = null; var nearestD = 99999.0
targets.foreach { t =>
if (!t.done) {
val d = distance(playerX, playerY, t.x, t.y)
if (d < nearestD) { nearestD = d; nearest = t }
}
}
if (nearest != null && nearestD < 88) {
nearest.done = true; score += 25
val remaining = targets.count(t => !t.done)
if (remaining == 0) {
stage = if (family == "Gokuldham Family") 4 else 2
showToast("Fantastic! All four decorations are done. Go to the puja area.", 180)
} else showToast(nearest.label + " decorated! " + (4 - remaining) + "/4 complete.", 135)
} else {
val next = targets.find(t => !t.done)
if (next.isDefined) showToast("Move near the glowing " + next.get.label + " marker and press E.", 110)
else showToast("All decorations complete. Go to the puja area.", 100)
}
}
private def finishPuja(): Unit = {
if (!supplies) showToast("Collect the supplies before starting puja.", 110)
else if (targets.exists(t => !t.done)) showToast("Finish all four decoration tasks first.", 110)
else {
pujaDone = true; score += 100; phase = "complete"
showToast("Shubh Deepawali! Mission completed!", 250)
}
}
private def missionText: String = {
if (scene != "diwali") scene match {
case "letter" => "MISSION 1/7: Find the letter from Goa."
case "ticket" => "MISSION 2/7: Book your railway ticket."
case "missed" => "MISSION 3/7: Talk to the TTE."
case "wait" => "MISSION 4/7: Wait for and board the next train."
case "coach" => "MISSION 5/7: Verify ticket, collect coins, cross coaches."
case "goaStation" => "MISSION 6/7: Exit Goa station."
case "goaHome" => "MISSION 7/7: Greet Aaji-Ajoba."
case _ => "Continue your adventure."
} else if (family == "Gokuldham Family") stage match {
case 0 => "DIWALI 1/5: Go down the society staircase."
case 1 => "DIWALI 2/5: Ask the society secretary."
case 2 => "DIWALI 3/5: Collect supplies from Abdul."
case 3 => "DIWALI 4/5: Decorate all markers (" + targets.count(_.done) + "/4)."
case 4 => "DIWALI 5/5: Go to community puja."
case _ => "All missions completed!"
} else stage match {
case 0 => "DIWALI 1/3: Buy supplies from Abdul."
case 1 => "DIWALI 2/3: Decorate your home (" + targets.count(_.done) + "/4)."
case 2 => "DIWALI 3/3: Go to home puja."
case _ => "All missions completed!"
}
}
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 (phase == "menu") drawMenu(g)
else {
if (scene == "diwali") drawDiwaliWorld(g) else drawJourneyScene(g)
drawHUD(g)
if (phase == "complete") drawComplete(g)
if (pause) drawPause(g)
}
}
private def font(size: Int, style: Int = Font.PLAIN): Font = new Font("SansSerif", style, size)
private def centered(g: Graphics2D, text: String, x: Int, y: Int): Unit = g.drawString(text, x - g.getFontMetrics.stringWidth(text) / 2, y)
private def drawMenu(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight
g.setPaint(new GradientPaint(0, 0, new Color(17, 10, 45), 0, h, new Color(84, 27, 62))); g.fillRect(0, 0, w, h)
drawStars(g, 0, 0, w, h, 90)
g.setColor(new Color(255, 224, 146, 35)); g.fillOval(w - 180, 35, 120, 120)
g.setColor(new Color(255, 235, 176)); g.fillOval(w - 153, 62, 66, 66)
g.setFont(font(40, Font.BOLD)); g.setColor(new Color(255, 200, 67)); centered(g, "BIXUU DIWALI TRAIN ADVENTURE", w / 2, 103)
g.setFont(font(17, Font.BOLD)); g.setColor(new Color(255, 239, 206)); centered(g, "LETTER > TICKET > TRAIN > GOA > DIWALI", w / 2, 142)
g.setFont(font(14, Font.PLAIN)); g.setColor(new Color(227, 218, 246)); centered(g, "Explore three train coaches, visit your grandparents, and celebrate Diwali", w / 2, 177)
val cardW = math.min(360, (w - 90) / 2); val cardH = 235; val gap = 28
val left = (w - (cardW * 2 + gap)) / 2; val top = 215
menuGokul = new java.awt.Rectangle(left, top, cardW, cardH)
menuNormal = new java.awt.Rectangle(left + cardW + gap, top, cardW, cardH)
drawFamilyCard(g, menuGokul, "GOKULDHAM FAMILY", "Train + Society missions", "Goa trip > stairs > permission", "Shop > decorate > community puja", true)
drawFamilyCard(g, menuNormal, "NORMAL FAMILY", "Train + Home missions", "Goa trip > grandparents' home", "Buy supplies > decorate > puja", false)
g.setFont(font(14, Font.BOLD)); g.setColor(new Color(255, 220, 124)); centered(g, "CLICK A CARD OR PRESS 1 / 2", w / 2, top + cardH + 35)
g.setFont(font(13, Font.PLAIN)); g.setColor(new Color(221, 211, 238)); centered(g, "WASD / ARROWS = MOVE E / ENTER / SPACE = INTERACT", w / 2, h - 27)
}
private def drawFamilyCard(g: Graphics2D, r: java.awt.Rectangle, heading: String, sub: String, line1: String, line2: String, society: Boolean): Unit = {
g.setPaint(new GradientPaint(r.x, r.y, new Color(57, 39, 84), r.x + r.width, r.y + r.height, new Color(29, 23, 59)))
g.fill(new RoundRectangle2D.Double(r.x, r.y, r.width, r.height, 28, 28))
g.setStroke(new BasicStroke(2f)); g.setColor(if (society) new Color(255, 185, 64) else new Color(71, 219, 193))
g.draw(new RoundRectangle2D.Double(r.x, r.y, r.width, r.height, 28, 28))
if (society) drawMiniBuilding(g, r.x + r.width / 2 - 48, r.y + 20, 96, 72) else drawMiniHouse(g, r.x + r.width / 2 - 46, r.y + 26, 92, 66)
g.setFont(font(20, Font.BOLD)); g.setColor(new Color(255, 219, 120)); centered(g, heading, r.x + r.width / 2, r.y + 121)
g.setFont(font(15, Font.BOLD)); g.setColor(Color.WHITE); centered(g, sub, r.x + r.width / 2, r.y + 150)
g.setFont(font(12, Font.PLAIN)); g.setColor(new Color(226, 219, 242)); centered(g, line1, r.x + r.width / 2, r.y + 176); centered(g, line2, r.x + r.width / 2, r.y + 195)
g.setFont(font(12, Font.BOLD)); g.setColor(if (society) new Color(255, 198, 86) else new Color(106, 237, 210))
centered(g, if (society) "CLICK TO PLAY (1)" else "CLICK TO PLAY (2)", r.x + r.width / 2, r.y + 219)
}
private def drawJourneyScene(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight
g.setPaint(new GradientPaint(0, 0, new Color(16, 20, 59), 0, h, new Color(82, 37, 74))); g.fillRect(0, 0, w, h)
drawStars(g, 0, 0, w, 255, 55)
g.setColor(new Color(255, 221, 155, 35)); g.fillOval(w - 175, 28, 105, 105)
g.setColor(new Color(255, 231, 171)); g.fillOval(w - 149, 54, 55, 55)
g.setPaint(new GradientPaint(0, 260, new Color(100, 92, 125), 0, h, new Color(35, 38, 61))); g.fillRect(0, 260, w, h - 260)
g.setColor(new Color(255, 255, 255, 15))
var gx = -40; while (gx < w + 100) { g.drawLine(gx, 330, gx + 125, h); gx += 100 }
var gy = 350; while (gy < h) { g.drawLine(0, gy, w, gy); gy += 46 }
scene match {
case "letter" =>
drawMiniHouse(g, 70, 300, 280, 190)
g.setColor(new Color(86, 48, 52)); g.fillRoundRect((w * 0.58).toInt, 400, 180, 95, 12, 12)
g.setColor(new Color(188, 111, 72)); g.fillRect((w * 0.57).toInt, 390, 190, 16)
g.setColor(new Color(255, 232, 185)); g.fillRoundRect((w * 0.66).toInt, 370, 66, 52, 6, 6)
g.setColor(new Color(174, 48, 52)); g.fillRoundRect((w * 0.67).toInt, 379, 48, 32, 4, 4)
drawMarker(g, (w * 0.70).toInt, (h * 0.63).toInt - 28, new Color(255, 214, 86))
drawPerson(g, w * 0.43, h * 0.62, new Color(77, 159, 184), new Color(242, 190, 143), tick)
g.setFont(font(16, Font.BOLD)); g.setColor(new Color(255, 231, 166)); centered(g, "LETTER FROM GOA", (w * 0.70).toInt, 355)
g.setFont(font(13)); g.setColor(Color.WHITE); centered(g, "Your grandparents invited you for Diwali", (w * 0.52).toInt, 545)
case "ticket" =>
drawStation(g, w, h, "CITY RAILWAY STATION"); drawRailTracks(g, w, h)
val cx = (w * 0.68).toInt; val cy = 345
g.setColor(new Color(47, 42, 72)); g.fillRoundRect(cx - 15, cy, 265, 126, 14, 14)
g.setColor(new Color(243, 167, 77)); g.fillRoundRect(cx - 15, cy - 18, 265, 35, 10, 10)
g.setFont(font(18, Font.BOLD)); g.setColor(new Color(62, 36, 55)); centered(g, "TICKET COUNTER", cx + 117, cy + 5)
drawPerson(g, w * 0.78, h * 0.60, new Color(64, 137, 190), new Color(245, 190, 140), tick)
case "missed" =>
drawStation(g, w, h, "PLATFORM 1 - DEPARTED"); drawRailTracks(g, w, h)
val trainX = w - ((tick * 7) % (w + 600)) - 480; drawPassengerTrain(g, trainX.toInt, 365, 146, 88)
drawPerson(g, w * 0.70, h * 0.62, new Color(43, 111, 170), new Color(244, 194, 145), tick)
g.setFont(font(16, Font.BOLD)); g.setColor(new Color(255, 206, 108)); centered(g, "OH NO! THE TRAIN HAS LEFT", w / 2, 305)
case "wait" =>
drawStation(g, w, h, "PLATFORM 2 - NEXT TRAIN"); drawRailTracks(g, w, h)
val p = math.min(1.0, trainArrivalTicks / 100.0); val tx = ((w + 80) + (w * 0.30 - (w + 80)) * p).toInt
drawPassengerTrain(g, tx, 365, 146, 88)
g.setFont(font(14, Font.BOLD)); g.setColor(new Color(255, 220, 130)); centered(g, if (trainArrivalTicks < 100) "NEXT TRAIN ARRIVING... " + trainArrivalTicks + "%" else "TRAIN ARRIVED - BOARD THE OPEN DOOR", w / 2, 300)
if (trainArrivalTicks >= 100) drawMarker(g, (w * 0.53).toInt, (h * 0.62).toInt - 35, new Color(100, 248, 193))
case "coach" =>
g.setPaint(new GradientPaint(0, 260, new Color(36, 47, 82), 0, h, new Color(23, 29, 50))); g.fillRect(0, 260, w, h - 260)
var i = 0
while (i < 5) {
val wx = 28 + i * ((w - 55) / 5)
g.setColor(new Color(15, 30, 58)); g.fillRoundRect(wx, 300, 138, 82, 10, 10)
g.setPaint(new GradientPaint(wx, 302, new Color(113, 197, 212), wx, 380, new Color(77, 145, 109))); g.fillRoundRect(wx + 5, 305, 128, 72, 8, 8)
val slide = (tick * 2 + i * 41) % 150; g.setColor(new Color(41, 119, 75)); g.fillOval(wx + slide - 15, 335, 35, 45); g.fillRect(wx + slide, 325, 7, 54); i += 1
}
g.setColor(new Color(255, 212, 124)); g.fillRect(0, 393, w, 7)
g.setPaint(new GradientPaint(0, 400, new Color(67, 77, 104), 0, h, new Color(36, 40, 62))); g.fillRect(0, 400, w, h - 400)
var sx = 25
while (sx < w - 80) { g.setColor(new Color(21, 29, 57)); g.fillRoundRect(sx, 420, 75, 92, 12, 12); g.setColor(new Color(178, 63, 79)); g.fillRoundRect(sx + 5, 425, 65, 42, 9, 9); sx += 145 }
g.setColor(new Color(18, 24, 42)); g.fillRoundRect(w - 105, 390, 72, 180, 9, 9); g.setColor(new Color(255, 194, 87)); g.drawRoundRect(w - 105, 390, 72, 180, 9, 9)
g.setFont(font(15, Font.BOLD)); g.setColor(new Color(255, 223, 143)); centered(g, "COACH " + currentCoach, w / 2, 285)
if (currentCoach == 1 && !ticketChecked) { drawPerson(g, w * 0.70, h * 0.59, new Color(43, 111, 170), new Color(244, 194, 145), tick); drawMarker(g, (w * 0.70).toInt, (h * 0.59).toInt - 35, new Color(98, 242, 192)) }
if (!coachCoins(currentCoach - 1)) { val cx = (w * 0.44).toInt; val cy = (h * 0.57).toInt; g.setColor(new Color(255, 205, 55, 60)); g.fillOval(cx - 20, cy - 20, 40, 40); g.setColor(new Color(255, 198, 42)); g.fillOval(cx - 10, cy - 10, 20, 20); g.setColor(new Color(255, 244, 163)); g.drawOval(cx - 6, cy - 6, 12, 12) }
g.setColor(new Color(255, 218, 115)); g.setFont(font(12, Font.BOLD)); g.drawString("COACH " + currentCoach + " / 3", 25, h - 98); g.drawString("Journey: " + math.min(100, travelTicks * 100 / 220) + "%", 25, h - 78)
case "goaStation" =>
drawStation(g, w, h, "GOA - MADGAON STATION"); drawRailTracks(g, w, h); drawPassengerTrain(g, 40, 365, 146, 88)
drawPalm(g, w - 210, 285); drawPalm(g, w - 105, 305); drawMarker(g, (w * 0.84).toInt, (h * 0.64).toInt - 30, new Color(100, 248, 193))
g.setFont(font(14, Font.BOLD)); g.setColor(new Color(255, 225, 144)); centered(g, "EXIT TO GOA", (w * 0.84).toInt, 340)
case "goaHome" =>
g.setColor(new Color(30, 80, 71)); g.fillRect(0, 260, w, h - 260); g.setColor(new Color(63, 117, 82)); g.fillOval(35, 280, 200, 125); g.fillOval(w - 260, 275, 225, 140)
drawPalm(g, 70, 310); drawPalm(g, w - 95, 315); drawMiniHouse(g, (w * 0.32).toInt, 318, 300, 210)
drawDiyas(g, (w * 0.32).toInt + 60, 505, 0.65); drawRangoli(g, (w * 0.32).toInt + 250, 545)
drawPerson(g, w * 0.70, h * 0.60, new Color(155, 88, 160), new Color(232, 179, 133), tick); drawPerson(g, w * 0.78, h * 0.60, new Color(65, 143, 104), new Color(236, 188, 142), tick + 4)
g.setFont(font(12, Font.BOLD)); g.setColor(new Color(255, 241, 199)); centered(g, "AAJI", (w * 0.70).toInt, (h * 0.60).toInt + 35); centered(g, "AJOBA", (w * 0.78).toInt, (h * 0.60).toInt + 35)
drawMarker(g, (w * 0.74).toInt, (h * 0.60).toInt - 38, new Color(255, 215, 84)); g.setFont(font(15, Font.BOLD)); centered(g, "AAJI-AJOBA'S HOUSE - GOA", w / 2, 285)
case _ =>
}
drawPlayer(g)
drawAmbient(g)
}
private def drawDiwaliWorld(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight
g.setPaint(new GradientPaint(0, 0, new Color(20, 17, 57), 0, 245, new Color(91, 40, 81))); g.fillRect(0, 0, w, h)
drawStars(g, 0, 0, w, 210, 55)
g.setColor(new Color(255, 223, 143, 32)); g.fillOval(w - 170, 25, 108, 108); g.setColor(new Color(255, 229, 164)); g.fillOval(w - 146, 49, 60, 60)
drawSkyline(g, w)
g.setPaint(new GradientPaint(0, 205, new Color(88, 76, 111), 0, h, new Color(39, 42, 64))); g.fillRect(0, 205, w, h - 205)
g.setColor(new Color(255, 194, 68, 120)); g.fillRoundRect(0, h - 70, w, 70, 14, 14); g.setColor(new Color(58, 42, 66)); g.fillRect(0, h - 65, w, 65)
drawRoadDashes(g, w, h)
drawNormalHouse(g); drawSociety(g); drawShop(g); drawSecretary(g); drawPujaArea(g); drawDecorationTargets(g); drawPlayer(g); drawAmbient(g)
}
private def drawHUD(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight
g.setColor(new Color(12, 12, 31, 225)); g.fillRoundRect(14, 12, w - 28, 94, 18, 18)
g.setColor(new Color(255, 199, 79)); g.setStroke(new BasicStroke(1.5f)); g.drawRoundRect(14, 12, w - 28, 94, 18, 18)
g.setFont(font(18, Font.BOLD)); g.setColor(new Color(255, 212, 108)); g.drawString(if (scene == "diwali") "BIXUU DIWALI ADVENTURE" else "BIXUU TRAIN ADVENTURE", 30, 38)
g.setFont(font(11, Font.BOLD)); g.setColor(Color.WHITE); g.drawString(playerName + " | " + (if (family == "Normal Family") familyName else family), 31, 57)
g.setFont(font(13, Font.BOLD)); g.setColor(new Color(131, 245, 205)); g.drawString("SCORE " + score + " COINS " + coachCoins.count(identity) + "/3", 31, 81)
g.setFont(font(13, Font.BOLD)); g.setColor(new Color(255, 243, 219)); g.drawString(missionText, 285, 39)
g.setFont(font(11, Font.PLAIN)); g.setColor(new Color(222, 218, 240)); g.drawString("WASD / ARROWS: MOVE E / ENTER / SPACE: INTERACT P: PAUSE ESC: MENU", 285, 61)
if (scene == "coach") { g.setColor(new Color(255, 211, 107)); g.drawString("Coach " + currentCoach + "/3 | Ticket " + (if (ticketChecked) "verified" else "not checked") + " | Journey " + math.min(100, travelTicks * 100 / 220) + "%", 285, 82) }
else if (scene == "wait") { g.setColor(new Color(255, 211, 107)); g.drawString("Train arrival: " + trainArrivalTicks + "%", 285, 82) }
else { g.setColor(new Color(255, 211, 107)); g.drawString(if (scene == "diwali") "Supplies: " + (if (supplies) "COLLECTED" else "NEEDED") + " Decorations: " + targets.count(_.done) + "/4" else "Goa family trip - Diwali celebration", 285, 82) }
if (toastTicks > 0 && toast != null && toast.nonEmpty) {
g.setFont(font(13, Font.BOLD)); val tw = math.min(w - 38, g.getFontMetrics.stringWidth(toast) + 34)
g.setColor(new Color(15, 12, 34, 232)); g.fillRoundRect((w - tw) / 2, h - 68, tw, 32, 12, 12)
g.setColor(new Color(255, 207, 93)); g.drawRoundRect((w - tw) / 2, h - 68, tw, 32, 12, 12)
g.setColor(new Color(255, 245, 222)); centered(g, toast, w / 2, h - 47)
}
g.setFont(font(11, Font.BOLD)); g.setColor(new Color(255, 230, 156)); centered(g, "Move close to the glowing marker, then press E / ENTER", w / 2, h - 25)
}
private def drawPlayer(g: Graphics2D): Unit = {
val moving = moveLeft || moveRight || moveUp || moveDown
val bob = if (moving) math.sin(walkFrame * 0.65) * 3.0 else math.sin(tick * 0.06) * 1.2
val x = playerX.toInt; val y = (playerY + bob).toInt
g.setColor(new Color(0, 0, 0, 75)); g.fillOval(x - 18, y + 27, 37, 13)
g.setStroke(new BasicStroke(6f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND))
val swing = if (moving) (math.sin(walkFrame * 0.65) * 4).toInt else 0
g.setColor(new Color(34, 44, 86)); g.drawLine(x - 5, y + 12, x - 7 + swing, y + 28); g.drawLine(x + 5, y + 12, x + 7 - swing, y + 28)
g.setColor(new Color(255, 198, 93)); g.fillRoundRect(x - 14, y - 2, 28, 24, 9, 9)
g.setColor(new Color(51, 115, 208)); g.fillRoundRect(x - 13, y - 2, 26, 18, 8, 8)
g.setColor(new Color(249, 191, 143)); g.fillOval(x - 10, y - 23, 20, 22)
g.setColor(new Color(56, 34, 30)); g.fillArc(x - 11, y - 26, 22, 16, 0, 180)
g.setColor(new Color(33, 25, 40)); g.fillOval(x - 5, y - 14, 2, 3); g.fillOval(x + 4, y - 14, 2, 3)
g.setColor(new Color(126, 55, 43)); g.drawArc(x - 4, y - 10, 8, 5, 200, 140)
g.setColor(new Color(247, 207, 105)); g.setFont(font(10, Font.BOLD)); centered(g, playerName.take(12).toUpperCase, x, y - 33)
}
private def drawDecorationTargets(g: Graphics2D): Unit = {
targets.foreach { t =>
if (t.done) {
t.label match {
case "Diyas" => drawDiyas(g, t.x.toInt - 21, t.y.toInt - 5, 0.52)
case "Flower Toran" => drawToran(g, t.x.toInt - 40, t.y.toInt - 14, 80, 20, true)
case "Fairy Lights" =>
var i = 0; while (i < 6) { val lx = t.x.toInt - 28 + i * 11; g.setColor(new Color(255, 228, 111)); g.fillOval(lx, t.y.toInt - 9 + (i % 2) * 4, 7, 7); i += 1 }
case "Rangoli" => drawRangoli(g, t.x.toInt, t.y.toInt)
case _ =>
}
g.setColor(new Color(96, 245, 174)); g.setFont(font(10, Font.BOLD)); centered(g, "DONE", t.x.toInt, t.y.toInt + 24)
} else if (phase == "playing" && ((family == "Gokuldham Family" && stage == 3) || (family == "Normal Family" && stage == 1))) {
drawMarker(g, t.x.toInt, t.y.toInt - 20 + (math.sin(tick * 0.12) * 4).toInt, new Color(82, 237, 201))
g.setColor(new Color(236, 255, 248)); g.setFont(font(10, Font.BOLD)); centered(g, t.label.toUpperCase, t.x.toInt, t.y.toInt + 20)
}
}
}
private def drawSecretary(g: Graphics2D): Unit = {
drawPerson(g, 535, 430, new Color(68, 137, 196), new Color(247, 194, 147), tick)
g.setFont(font(11, Font.BOLD)); g.setColor(new Color(250, 245, 220)); centered(g, "SECRETARY", 535, 459)
if (family == "Gokuldham Family" && stage == 1) drawMarker(g, 535, 383, new Color(93, 233, 180))
}
private def drawPujaArea(g: Graphics2D): Unit = {
val px = if (family == "Gokuldham Family") 535 else 225; val py = 535
g.setColor(new Color(20, 14, 30, 80)); g.fillOval(px - 48, py + 1, 96, 18)
g.setColor(new Color(145, 67, 83)); g.fillRoundRect(px - 38, py - 15, 76, 27, 5, 5)
g.setColor(new Color(255, 188, 70)); g.fillRect(px - 41, py - 20, 82, 8)
g.setColor(new Color(255, 223, 139)); g.fillOval(px - 22, py - 45, 44, 32)
g.setColor(new Color(185, 72, 57)); g.fillOval(px - 13, py - 38, 26, 24); g.setColor(new Color(255, 221, 117)); g.fillOval(px - 5, py - 31, 10, 10)
drawMiniDiyas(g, px - 27, py - 4); drawMiniDiyas(g, px + 7, py - 4)
g.setFont(font(11, Font.BOLD)); g.setColor(new Color(255, 242, 201)); centered(g, if (family == "Gokuldham Family") "COMMUNITY PUJA" else "HOME PUJA", px, py + 31)
if ((family == "Gokuldham Family" && stage == 4) || (family == "Normal Family" && stage == 2)) drawMarker(g, px, py - 65, new Color(255, 210, 77))
if (pujaDone) centered(g, "SHUBH DEEPAWALI!", px, py - 76)
}
private def drawShop(g: Graphics2D): Unit = {
val x = getWidth - 270; val y = 280; val w = 225; val h = 159
g.setColor(new Color(10, 7, 23, 90)); g.fillOval(x - 8, y + h - 1, w + 22, 23)
g.setPaint(new GradientPaint(x, y, new Color(94, 47, 85), x + w, y + h, new Color(49, 31, 65))); g.fillRect(x, y, w, h)
val roof = new Polygon(Array(x - 10, x + w / 2, x + w + 10), Array(y + 8, y - 34, y + 8), 3)
g.setColor(new Color(240, 137, 56)); g.fillPolygon(roof); g.setColor(new Color(255, 206, 112)); g.fillRect(x + 12, y + 13, w - 24, 38)
g.setFont(font(20, Font.BOLD)); g.setColor(new Color(75, 33, 58)); centered(g, "ABDUL'S SHOP", x + w / 2, y + 39)
g.setColor(new Color(41, 28, 53)); g.fillRoundRect(x + 18, y + 68, 91, 79, 6, 6); drawMiniDiyas(g, x + 29, y + 93)
g.setColor(new Color(244, 80, 115)); g.fillOval(x + 64, y + 105, 13, 13); g.setColor(new Color(89, 224, 175)); g.fillOval(x + 79, y + 105, 13, 13)
g.setColor(new Color(255, 226, 147)); g.fillRoundRect(x + 131, y + 91, 68, 52, 6, 6); g.setColor(new Color(64, 47, 76)); g.fillRect(x + 137, y + 97, 56, 39)
g.setColor(new Color(255, 219, 121)); g.setFont(font(12, Font.BOLD)); centered(g, "Lights", x + 165, y + 116)
if (supplies) { g.setColor(new Color(46, 220, 152)); g.fillOval(x + w - 31, y + 8, 20, 20); g.setColor(new Color(13, 65, 48)); centered(g, "?", x + w - 21, y + 23) }
}
private def drawSociety(g: Graphics2D): Unit = {
val x = 347; val y = 205; val w = 365; val h = 249
g.setColor(new Color(10, 7, 22, 100)); g.fillOval(x - 10, y + h - 2, w + 35, 32)
g.setPaint(new GradientPaint(x, y, new Color(89, 95, 151), x + w, y + h, new Color(53, 48, 94))); g.fillRect(x, y, w, h)
g.setColor(new Color(255, 198, 109)); g.fillRect(x, y, w, 10)
var row = 0
while (row < 3) { var col = 0; while (col < 4) { val wx = x + 22 + col * 83; val wy = y + 25 + row * 62
g.setColor(new Color(28, 27, 61)); g.fillRoundRect(wx, wy, 58, 43, 5, 5); g.setColor(new Color(255, 221, 135, 235)); g.fillRect(wx + 7, wy + 7, 44, 26)
g.setColor(new Color(48, 54, 91)); g.fillRect(wx + 7, wy + 7, 44, 5); g.setColor(new Color(30, 25, 53)); g.drawLine(wx + 29, wy + 7, wx + 29, wy + 33); g.drawLine(wx + 4, wy + 35, wx + 54, wy + 35); col += 1 }; row += 1 }
g.setColor(new Color(35, 31, 60)); g.fillRoundRect(x + 150, y + 171, 66, 78, 8, 8); g.setColor(new Color(255, 197, 90)); g.fillOval(x + 202, y + 211, 7, 7)
g.setColor(new Color(191, 190, 218)); var i = 0; while (i < 7) { g.fillRect(x + 43 + i * 7, y + 127 + i * 13, 66 - i * 7, 11); i += 1 }
g.setColor(new Color(255, 232, 178)); g.setFont(font(15, Font.BOLD)); centered(g, "GOKULDHAM SOCIETY", x + w / 2, y - 8)
drawToran(g, x + 14, y + 8, w - 28, 16, targets.exists(_.done))
}
private def drawNormalHouse(g: Graphics2D): Unit = {
val x = 24; val y = 255; val w = 300; val h = 190
g.setColor(new Color(15, 12, 27, 90)); g.fillOval(x - 2, y + h - 3, w + 35, 29)
g.setPaint(new GradientPaint(x, y, new Color(255, 180, 109), x + w, y + h, new Color(142, 69, 89))); g.fillRect(x, y + 40, w, h - 40)
g.setColor(new Color(95, 44, 73)); g.fillPolygon(new Polygon(Array(x - 18, x + w / 2, x + w + 18), Array(y + 48, y - 28, y + 48), 3))
g.setColor(new Color(255, 218, 149)); g.fillRect(x + 15, y + 56, w - 30, 8)
g.setColor(new Color(89, 45, 51)); g.fillRoundRect(x + 128, y + 95, 53, 95, 7, 7); g.setColor(new Color(255, 202, 95)); g.fillOval(x + 167, y + 142, 6, 6)
drawWindow(g, x + 35, y + 87, 52, 47); drawWindow(g, x + 215, y + 87, 52, 47)
g.setFont(font(14, Font.BOLD)); g.setColor(new Color(255, 246, 208)); centered(g, if (family == "Normal Family") familyName.take(16).toUpperCase else "NEIGHBOUR'S HOME", x + w / 2, y + 22)
drawToran(g, x + 106, y + 84, 106, 20, targets.exists(_.done))
if (family == "Normal Family" && targets.exists(t => t.label == "Diyas" && t.done)) { drawDiyas(g, x + 35, y + h - 6, 0.75); drawDiyas(g, x + 228, y + h - 6, 0.75) }
}
private def drawSkyline(g: Graphics2D, w: Int): Unit = {
val buildings = Array((0, 145, 78, 120), (80, 125, 90, 140), (180, 150, 65, 112), (250, 130, 90, 132), (700, 135, 86, 128), (800, 155, 66, 108), (875, 120, 100, 143), (970, 148, 80, 116))
buildings.foreach { b => val (x, y, bw, bh) = b; g.setColor(new Color(30, 25, 56)); g.fillRect(x, y, bw, bh); g.setColor(new Color(255, 195, 93, 90)); var wx = x + 10
while (wx < x + bw - 7) { var wy = y + 12; while (wy < y + bh - 7) { if ((wx + wy) % 3 != 0) g.fillRect(wx, wy, 5, 7); wy += 19 }; wx += 17 } }
}
private def drawStation(g: Graphics2D, w: Int, h: Int, title: String): Unit = {
g.setColor(new Color(38, 43, 76)); g.fillRect(0, 250, w, 130); g.setColor(new Color(255, 192, 91)); g.fillRect(0, 250, w, 10)
var bx = 18; while (bx < w) { g.setColor(new Color(62, 66, 98)); g.fillRoundRect(bx, 272, 125, 79, 12, 12); g.setColor(new Color(255, 220, 143, 160)); g.fillRect(bx + 11, 285, 103, 8); g.setColor(new Color(28, 39, 66)); g.fillRect(bx + 20, 307, 84, 29); bx += 145 }
g.setColor(new Color(24, 24, 46)); g.fillRoundRect(w / 2 - 230, 205, 460, 56, 15, 15); g.setColor(new Color(255, 206, 100)); g.setStroke(new BasicStroke(2f)); g.drawRoundRect(w / 2 - 230, 205, 460, 56, 15, 15)
g.setFont(font(21, Font.BOLD)); g.setColor(new Color(255, 228, 158)); centered(g, title, w / 2, 240)
}
private def drawRailTracks(g: Graphics2D, w: Int, h: Int): Unit = {
val y = h - 122; g.setColor(new Color(43, 45, 58)); g.fillRect(0, y - 7, w, 42)
g.setColor(new Color(199, 202, 208)); g.setStroke(new BasicStroke(4f)); g.drawLine(0, y, w, y); g.drawLine(0, y + 24, w, y + 24)
g.setColor(new Color(113, 94, 91)); var x = 0; while (x < w) { g.fillRect(x, y - 8, 11, 40); x += 27 }
}
private def drawPassengerTrain(g: Graphics2D, x: Int, y: Int, coachW: Int, coachH: Int): Unit = {
val totalW = coachW * 3 + 84; g.setColor(new Color(7, 9, 20, 95)); g.fillOval(x - 8, y + coachH + 28, totalW + 25, 23)
var i = 0
while (i < 3) { val cx = x + i * coachW; g.setPaint(new GradientPaint(cx, y, new Color(211, 225, 230), cx, y + coachH, new Color(70, 119, 164))); g.fillRoundRect(cx, y, coachW - 8, coachH, 13, 13)
g.setColor(new Color(207, 56, 67)); g.fillRect(cx + 3, y + coachH - 18, coachW - 14, 8); var wx = cx + 12
while (wx < cx + coachW - 25) { g.setColor(new Color(27, 49, 76)); g.fillRoundRect(wx, y + 13, 22, 27, 4, 4); g.setColor(new Color(154, 217, 233)); g.fillRect(wx + 3, y + 16, 16, 5); wx += 31 }
g.setColor(new Color(36, 46, 66)); g.fillOval(cx + 20, y + coachH - 4, 25, 25); g.fillOval(cx + coachW - 45, y + coachH - 4, 25, 25); g.setColor(new Color(176, 183, 196)); g.fillOval(cx + 26, y + coachH + 2, 13, 13); g.fillOval(cx + coachW - 39, y + coachH + 2, 13, 13); i += 1 }
val ex = x + coachW * 3; g.setPaint(new GradientPaint(ex, y + 10, new Color(225, 222, 209), ex + 84, y + coachH, new Color(54, 101, 149))); g.fillRoundRect(ex, y + 9, 84, coachH - 1, 18, 18)
g.setColor(new Color(255, 201, 74)); g.fillRoundRect(ex + 8, y + coachH - 30, 67, 12, 5, 5); g.setColor(new Color(28, 45, 70)); g.fillRoundRect(ex + 15, y + 18, 47, 25, 5, 5)
}
private def drawPalm(g: Graphics2D, x: Int, y: Int): Unit = {
g.setColor(new Color(103, 71, 48)); g.fillPolygon(new Polygon(Array(x - 4, x + 5, x + 10, x + 3), Array(y + 72, y + 72, y, y), 4))
g.setColor(new Color(37, 110, 69)); g.fillArc(x - 47, y - 20, 60, 38, 25, 135); g.fillArc(x - 20, y - 32, 62, 40, 15, 140); g.fillArc(x - 49, y - 4, 64, 37, 190, 130); g.fillArc(x - 22, y - 8, 62, 37, 210, 120)
}
private def drawPerson(g: Graphics2D, xx: Double, yy: Double, shirt: Color, skin: Color, frame: Int): Unit = {
val x = xx.toInt; val y = yy.toInt; g.setColor(new Color(0, 0, 0, 65)); g.fillOval(x - 15, y + 19, 31, 10)
g.setColor(new Color(35, 36, 66)); g.setStroke(new BasicStroke(5f)); g.drawLine(x - 4, y + 8, x - 7, y + 21); g.drawLine(x + 4, y + 8, x + 7, y + 21)
g.setColor(shirt); g.fillRoundRect(x - 13, y - 7, 26, 19, 7, 7); g.setColor(skin); g.fillOval(x - 9, y - 25, 18, 19); g.setColor(new Color(49, 30, 28)); g.fillArc(x - 10, y - 28, 20, 14, 0, 180)
}
private def drawAmbient(g: Graphics2D): Unit = {
var i = 0
while (i < 13) { val xx = ((i * 97 + tick * (i % 3 + 1)) % math.max(1, getWidth)).toInt; val yy = 225 + ((i * 41 + tick / 3) % math.max(1, getHeight - 270)).toInt; val alpha = 70 + ((math.sin(tick * 0.08 + i) + 1) * 60).toInt; g.setColor(new Color(255, 208, 93, alpha)); g.fillOval(xx, yy, 3, 3); i += 1 }
}
private def drawMarker(g: Graphics2D, x: Int, y: Int, c: Color): Unit = {
val pulse = (math.sin(tick * 0.13) * 3).toInt; g.setColor(new Color(c.getRed, c.getGreen, c.getBlue, 45)); g.fillOval(x - 15 - pulse, y - 15 - pulse, 30 + pulse * 2, 30 + pulse * 2); g.setColor(c); g.setStroke(new BasicStroke(2f)); g.drawOval(x - 8, y - 8, 16, 16); g.fillOval(x - 3, y - 3, 6, 6)
}
private def drawToran(g: Graphics2D, x: Int, y: Int, width: Int, height: Int, bright: Boolean): Unit = {
g.setColor(new Color(40, 26, 44)); g.setStroke(new BasicStroke(2f)); g.drawArc(x, y - 2, width, height * 2, 180, 180); var i = 0
while (i <= 9) { val xx = x + i * width / 9; val yy = y + height / 2 + (math.sin(i * math.Pi / 9) * height * 0.55).toInt; g.setColor(if (i % 2 == 0) new Color(255, 183, 65) else new Color(64, 205, 142)); g.fillOval(xx - 4, yy - 3, 8, 10); if (bright) { g.setColor(new Color(255, 222, 103, 70)); g.fillOval(xx - 9, yy - 8, 18, 18) }; i += 1 }
if (bright) { g.setColor(new Color(255, 224, 143)); g.setStroke(new BasicStroke(3f)); g.drawArc(x, y - 2, width, height * 2, 180, 180) }
}
private def drawDiyas(g: Graphics2D, x: Int, y: Int, scale: Double): Unit = {
var i = 0
while (i < 3) { val xx = x + (i * 25 * scale).toInt; val yy = y + (if (i == 1) -4 else 0); g.setColor(new Color(255, 190, 58, 45)); g.fillOval(xx - 8, yy - 12, math.max(1, (32 * scale).toInt), math.max(1, (28 * scale).toInt)); g.setColor(new Color(204, 91, 40)); g.fillOval(xx, yy, math.max(1, (21 * scale).toInt), math.max(1, (10 * scale).toInt)); g.setColor(new Color(255, 207, 82)); g.fillOval(xx + (5 * scale).toInt, yy - (5 * scale).toInt, math.max(1, (11 * scale).toInt), math.max(1, (7 * scale).toInt)); g.setColor(new Color(255, 249, 191)); g.fillOval(xx + (9 * scale).toInt, yy - (11 * scale).toInt, math.max(1, (4 * scale).toInt), math.max(1, (8 * scale).toInt)); i += 1 }
}
private def drawMiniDiyas(g: Graphics2D, x: Int, y: Int): Unit = {
var i = 0; while (i < 3) { val xx = x + i * 18; g.setColor(new Color(241, 128, 49)); g.fillOval(xx, y, 13, 7); g.setColor(new Color(255, 230, 125)); g.fillOval(xx + 4, y - 5, 5, 6); i += 1 }
}
private def drawRangoli(g: Graphics2D, x: Int, y: Int): Unit = {
val colors = Array(new Color(244, 86, 115), new Color(255, 205, 72), new Color(77, 212, 172), new Color(127, 115, 255))
g.setColor(new Color(255, 231, 151, 40)); g.fillOval(x - 27, y - 13, 54, 28); var i = 0
while (i < 8) { val angle = i * math.Pi / 4.0; val cx = x + (math.cos(angle) * 17).toInt; val cy = y + (math.sin(angle) * 8).toInt; g.setColor(colors(i % colors.length)); g.fillOval(cx - 5, cy - 4, 10, 8); i += 1 }
g.setColor(new Color(255, 244, 193)); g.fillOval(x - 4, y - 3, 8, 6)
}
private def drawMiniBuilding(g: Graphics2D, x: Int, y: Int, w: Int, h: Int): Unit = {
g.setColor(new Color(76, 69, 132)); g.fillRect(x, y, w, h); g.setColor(new Color(255, 205, 103)); g.fillRect(x, y, w, 6); var row = 0
while (row < 2) { var col = 0; while (col < 3) { g.setColor(new Color(255, 226, 153)); g.fillRect(x + 10 + col * 27, y + 13 + row * 23, 16, 14); col += 1 }; row += 1 }
}
private def drawMiniHouse(g: Graphics2D, x: Int, y: Int, w: Int, h: Int): Unit = {
val roof = new Polygon(Array(x - 5, x + w / 2, x + w + 5), Array(y + 22, y - 5, y + 22), 3); g.setColor(new Color(220, 117, 72)); g.fillPolygon(roof); g.setColor(new Color(249, 191, 128)); g.fillRect(x + 4, y + 20, w - 8, h - 20); g.setColor(new Color(88, 48, 62)); g.fillRect(x + w / 2 - 10, y + 38, 20, h - 38); g.setColor(new Color(255, 225, 153)); g.fillRect(x + 13, y + 31, 17, 16); g.fillRect(x + w - 30, y + 31, 17, 16)
}
private def drawWindow(g: Graphics2D, x: Int, y: Int, w: Int, h: Int): Unit = {
g.setColor(new Color(59, 46, 78)); g.fillRoundRect(x, y, w, h, 5, 5); g.setColor(new Color(255, 226, 153)); g.fillRect(x + 5, y + 5, w - 10, h - 10); g.setColor(new Color(72, 57, 85)); g.setStroke(new BasicStroke(3f)); g.drawLine(x + w / 2, y + 4, x + w / 2, y + h - 4); g.drawLine(x + 4, y + h / 2, x + w - 4, y + h / 2)
}
private def drawRoadDashes(g: Graphics2D, w: Int, h: Int): Unit = {
g.setColor(new Color(255, 224, 133, 100)); var x = 15; while (x < w) { g.fillRoundRect(x, h - 42, 34, 3, 4, 4); x += 65 }
}
private def drawStars(g: Graphics2D, x: Int, y: Int, w: Int, h: Int, count: Int): Unit = {
var i = 0; while (i < count) { val sx = x + ((i * 137 + 31) % math.max(1, w)); val sy = y + ((i * 67 + 13) % math.max(1, h)); val a = 70 + ((i * 29) % 160); g.setColor(new Color(255, 239, 199, a)); val s = if (i % 7 == 0) 3 else 2; g.fillOval(sx, sy, s, s); i += 1 }
}
private def drawComplete(g: Graphics2D): Unit = {
val w = getWidth; val h = getHeight; g.setColor(new Color(11, 8, 28, 210)); g.fillRect(0, 0, w, h); drawStars(g, 0, 0, w, h, 55)
g.setColor(new Color(255, 202, 72, 50)); g.fillOval(w / 2 - 200, h / 2 - 180, 400, 320)
g.setPaint(new GradientPaint(w / 2 - 220, h / 2 - 130, new Color(75, 43, 91), w / 2 + 220, h / 2 + 150, new Color(33, 27, 61))); g.fillRoundRect(w / 2 - 265, h / 2 - 160, 530, 315, 34, 34)
g.setStroke(new BasicStroke(3f)); g.setColor(new Color(255, 205, 82)); g.drawRoundRect(w / 2 - 265, h / 2 - 160, 530, 315, 34, 34)
drawDiyas(g, w / 2 - 180, h / 2 + 36, 1.0); drawDiyas(g, w / 2 + 125, h / 2 + 36, 1.0)
g.setFont(font(36, Font.BOLD)); g.setColor(new Color(255, 208, 80)); centered(g, "SHUBH DEEPAWALI!", w / 2, h / 2 - 89)
g.setFont(font(21, Font.BOLD)); g.setColor(Color.WHITE); centered(g, "MISSION COMPLETED", w / 2, h / 2 - 48)
g.setFont(font(18)); g.setColor(new Color(226, 222, 243)); centered(g, family + " - All tasks finished", w / 2, h / 2 - 12)
g.setFont(font(24, Font.BOLD)); g.setColor(new Color(121, 248, 201)); centered(g, "FINAL SCORE: " + score, w / 2, h / 2 + 29)
g.setFont(font(15, Font.BOLD)); g.setColor(new Color(255, 222, 139)); centered(g, "Press ENTER to play again | ESC for family selection", w / 2, h / 2 + 99)
}
private def drawPause(g: Graphics2D): Unit = {
g.setColor(new Color(9, 7, 22, 195)); g.fillRect(0, 0, getWidth, getHeight); g.setFont(font(34, Font.BOLD)); g.setColor(new Color(255, 211, 99)); centered(g, "PAUSED", getWidth / 2, getHeight / 2 - 10); g.setFont(font(16, Font.BOLD)); g.setColor(Color.WHITE); centered(g, "Press P or E to continue", getWidth / 2, getHeight / 2 + 28)
}
override def mouseClicked(e: MouseEvent): Unit = {
if (phase == "menu") {
if (menuGokul.contains(e.getPoint)) startGame("Gokuldham Family")
else if (menuNormal.contains(e.getPoint)) startGame("Normal Family")
}
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: press Run in Kojo and the game window opens.
BixuuDiwaliGame.main(Array.empty[String])