Code Sketch
yooooiiiiiii
Category: Programming
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.JButton
import javax.swing.JLabel
import javax.swing.JTextField
import javax.swing.JTextArea
import javax.swing.JScrollPane
import javax.swing.JOptionPane
import javax.swing.WindowConstants
import javax.swing.SwingUtilities
import javax.swing.BoxLayout
import javax.swing.BorderFactory
import javax.swing.Timer
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.Color
import java.awt.Font
import java.awt.BasicStroke
import java.awt.GradientPaint
import java.awt.Dimension
import java.awt.RenderingHints
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.awt.event.ActionListener
import java.net.ServerSocket
import java.net.Socket
import java.net.InetAddress
import java.io.PrintWriter
import java.io.BufferedReader
import java.io.InputStreamReader
import javax.sound.sampled.AudioFormat
import javax.sound.sampled.AudioSystem
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Set
import scala.util.Try
/* =========================================================
REAL DRIVE X
FULL SINGLE FILE
========================================================= */
/* =========================================================
MAIN WINDOW
========================================================= */
var frame: JFrame = null
var root: JPanel = null
var cards: java.awt.CardLayout = null
var menuPanel: JPanel = null
var lobbyPanel: JPanel = null
var gamePanel: DrivePanel = null
/* =========================================================
LOBBY UI
========================================================= */
var lobbyNameField: JTextField = null
var lobbyHostField: JTextField = null
var lobbyPortField: JTextField = null
var lobbyChatField: JTextField = null
var lobbyStatusLabel: JLabel = null
var lobbyPlayersLabel: JLabel = null
var lobbyModeLabel: JLabel = null
var lobbyChatArea: JTextArea = null
/* =========================================================
SCREEN STATE
========================================================= */
var currentScreen = "menu"
var online = false
var multiplayer = false
var soloOnline = false
var ready = false
var raceStarted = false
var matchmaking = false
var playerName = "Driver"
var lastHost = "127.0.0.1"
var lastPort = 5555
/* =========================================================
SERVER
========================================================= */
var serverSocket: ServerSocket = null
var serverRunning = false
var actualServerPort = 5555
case class ServerClient(
id: String,
name: String,
socket: Socket,
out: PrintWriter,
var x: Double,
var distance: Double,
var speed: Double,
var gear: String,
var mode: String,
var ready: Boolean
)
val serverClients =
ArrayBuffer[ServerClient]()
/* =========================================================
CLIENT
========================================================= */
var clientSocket: Socket = null
var clientOut: PrintWriter = null
var clientIn: BufferedReader = null
var clientConnected = false
var clientId = "P0"
case class RemotePlayer(
id: String,
name: String,
var x: Double,
var distance: Double,
var speed: Double,
var gear: String,
var mode: String,
var ready: Boolean
)
val remotePlayers =
ArrayBuffer[RemotePlayer]()
/* =========================================================
TRAFFIC
========================================================= */
case class TrafficCar(
var lane: Int,
var z: Double,
var speed: Double,
var kind: String,
var police: Boolean,
var phase: Double
)
val traffic =
ArrayBuffer[TrafficCar]()
/* =========================================================
KEY INPUT
========================================================= */
val keys =
Set[Int]()
/* =========================================================
GAME VARIABLES
========================================================= */
var gameDistance = 0.0
var gameSpeed = 0.0
var fuel = 100.0
var damage = 0.0
var score = 0
var missionNumber = 1
var missionProgress = 0
var missionTarget = 1000.0
var carX = 0.0
var steering = 0.0
var throttle = false
var braking = false
var nitro = false
var horn = false
var lights = false
var highBeam = false
var leftIndicator = false
var rightIndicator = false
var hazards = false
var rain = false
var wipers = true
var autoGear = true
var gear = "N"
var cruiseControl = false
var cruiseSpeed = 80.0
var nitroHeat = 0.0
var paused = false
var gameRunning = false
var timePhase = 0
var flashMessage = ""
var flashTime = 0L
var lastTick =
System.currentTimeMillis()
var lastNetworkSend = 0L
var lastTrafficSpawn = 0L
var lastSound = 0L
var repairCooldown = 0L
/* =========================================================
ROAD HELPERS
========================================================= */
def laneToX(
lane: Int
): Double = {
if (lane == 0) {
-0.66
} else if (lane == 1) {
0.0
} else {
0.66
}
}
def roadHalfWidth(
z: Double
): Double = {
82.0 +
245.0 * z
}
def trafficScreenX(
lane: Int,
z: Double,
width: Int
): Int = {
val half =
roadHalfWidth(z)
(
width / 2.0 +
laneToX(lane) *
half
).toInt
}
def interpolateRoadX(
tl: Int,
tr: Int,
bl: Int,
br: Int,
topY: Int,
bottomY: Int,
y: Int,
fraction: Double
): Int = {
val t =
(
y -
topY
).toDouble /
Math.max(
1,
bottomY -
topY
).toDouble
val left =
tl +
(
(
bl -
tl
) *
t
).toInt
val right =
tr +
(
(
br -
tr
) *
t
).toInt
left +
(
(
right -
left
) *
fraction
).toInt
}
/* =========================================================
GENERAL HELPERS
========================================================= */
def showCard(
name: String
): Unit = {
currentScreen =
name
cards.show(
root,
name
)
}
def cleanText(
text: String
): String = {
if (text == null) {
""
} else {
text
.replace("|", "/")
.replace("~", "-")
.replace("\n", " ")
.replace("\r", " ")
}
}
def modeText(): String = {
if (!online) {
"OFFLINE SOLO"
} else if (multiplayer) {
"MULTIPLAYER"
} else {
"ONLINE SOLO"
}
}
def onOff(
value: Boolean
): String = {
if (value) {
"ON"
} else {
"OFF"
}
}
def gearNumber(
g: String
): Int = {
if (g == "R") {
-1
} else if (g == "N") {
0
} else {
Try(
g.toInt
).getOrElse(
0
)
}
}
def localIP(): String = {
try {
InetAddress
.getLocalHost
.getHostAddress
} catch {
case _: Throwable =>
"127.0.0.1"
}
}
/* =========================================================
SOUND
========================================================= */
def playTone(
frequency: Int,
duration: Int
): Unit = {
val now =
System.currentTimeMillis()
if (
now -
lastSound <
30L
) {
return
}
lastSound =
now
val soundThread =
new Thread(
new Runnable {
def run(): Unit = {
try {
val sampleRate =
44100.0f
val format =
new AudioFormat(
sampleRate,
8,
1,
true,
false
)
val line =
AudioSystem
.getSourceDataLine(
format
)
line.open(
format
)
line.start()
val samples =
Math.max(
1,
(
sampleRate *
duration.toDouble /
1000.0
).toInt
)
val buffer =
new Array[Byte](
samples
)
var i = 0
while (
i <
samples
) {
val angle =
2.0 *
Math.PI *
frequency.toDouble *
i.toDouble /
sampleRate.toDouble
buffer(i) =
(
Math.sin(
angle
) *
105.0
).toByte
i += 1
}
line.write(
buffer,
0,
buffer.length
)
line.drain()
line.stop()
line.close()
} catch {
case _: Throwable =>
try {
java.awt.Toolkit
.getDefaultToolkit
.beep()
} catch {
case _: Throwable =>
}
}
}
}
)
soundThread.setDaemon(
true
)
soundThread.start()
}
def flash(
text: String
): Unit = {
flashMessage =
text
flashTime =
System.currentTimeMillis() +
2200L
playTone(
720,
60
)
}
/* =========================================================
LIGHTS / WEATHER
========================================================= */
def toggleLights(): Unit = {
lights =
!lights
if (lights) {
flash("HEADLIGHTS ON")
} else {
flash("HEADLIGHTS OFF")
}
}
def toggleHighBeam(): Unit = {
highBeam =
!highBeam
if (highBeam) {
flash("HIGH BEAM ON")
} else {
flash("HIGH BEAM OFF")
}
}
def toggleRain(): Unit = {
rain =
!rain
if (rain) {
flash(
"RAIN STARTED"
)
playTone(
240,
100
)
} else {
flash(
"RAIN STOPPED"
)
}
}
def toggleAutoGear(): Unit = {
autoGear =
!autoGear
if (autoGear) {
flash("AUTO GEAR")
} else {
flash("MANUAL GEAR")
}
}
/* =========================================================
INDICATORS
========================================================= */
def toggleLeftIndicator(): Unit = {
leftIndicator =
!leftIndicator
rightIndicator =
false
}
def toggleRightIndicator(): Unit = {
rightIndicator =
!rightIndicator
leftIndicator =
false
}
/* =========================================================
MANUAL GEAR
========================================================= */
def setManualGear(
value: String
): Unit = {
if (!autoGear) {
gear =
value
playTone(
430,
55
)
}
}
def autoShift(): Unit = {
if (
gameSpeed <
4.0
) {
gear =
"N"
} else if (
gameSpeed <
28.0
) {
gear =
"1"
} else if (
gameSpeed <
55.0
) {
gear =
"2"
} else if (
gameSpeed <
82.0
) {
gear =
"3"
} else if (
gameSpeed <
118.0
) {
gear =
"4"
} else {
gear =
"5"
}
}
/* =========================================================
CRUISE CONTROL
========================================================= */
def toggleCruise(): Unit = {
cruiseControl =
!cruiseControl
if (cruiseControl) {
cruiseSpeed =
Math.max(
35.0,
Math.min(
150.0,
Math.abs(
gameSpeed
)
)
)
flash(
"CRUISE " +
cruiseSpeed.toInt +
" KM/H"
)
} else {
flash(
"CRUISE OFF"
)
}
}
/* =========================================================
SERVER
========================================================= */
def startServer(
wantedPort: Int
): Boolean = {
if (
serverRunning
) {
return true
}
var port =
wantedPort
var opened:
ServerSocket =
null
while (
port <
wantedPort +
25 &&
opened ==
null
) {
try {
val s =
new ServerSocket()
s.setReuseAddress(
true
)
s.bind(
new java.net.InetSocketAddress(
"0.0.0.0",
port
)
)
opened =
s
} catch {
case _: Throwable =>
port += 1
}
}
if (
opened ==
null
) {
return false
}
serverSocket =
opened
actualServerPort =
port
serverRunning =
true
addLobbyChat(
"[SERVER] LISTENING " +
localIP() +
":" +
actualServerPort
)
val acceptThread =
new Thread(
new Runnable {
def run(): Unit = {
while (
serverRunning
) {
try {
val socket =
serverSocket
.accept()
val t =
new Thread(
new Runnable {
def run(): Unit = {
handleServerClient(
socket
)
}
}
)
t.setDaemon(
true
)
t.start()
} catch {
case _: Throwable =>
}
}
}
}
)
acceptThread.setDaemon(
true
)
acceptThread.start()
true
}
/* =========================================================
SERVER BROADCAST
========================================================= */
def broadcastServer(
message: String
): Unit = {
serverClients.synchronized {
var i = 0
while (
i <
serverClients.length
) {
try {
serverClients(i)
.out
.println(
message
)
serverClients(i)
.out
.flush()
} catch {
case _: Throwable =>
}
i += 1
}
}
}
/* =========================================================
PLAYER LIST
========================================================= */
def broadcastPlayers(): Unit = {
val builder =
new StringBuilder(
"PLAYERS"
)
serverClients.synchronized {
var i = 0
while (
i <
serverClients.length
) {
val c =
serverClients(i)
builder.append(
"|"
)
builder.append(
cleanText(
c.id
)
)
builder.append(
"~"
)
builder.append(
cleanText(
c.name
)
)
builder.append(
"~"
)
builder.append(
c.x
)
builder.append(
"~"
)
builder.append(
c.distance
)
builder.append(
"~"
)
builder.append(
c.speed
)
builder.append(
"~"
)
builder.append(
cleanText(
c.gear
)
)
builder.append(
"~"
)
builder.append(
cleanText(
c.mode
)
)
builder.append(
"~"
)
builder.append(
c.ready
)
i += 1
}
}
broadcastServer(
builder.toString()
)
updateLobbyPlayers()
}
/* =========================================================
SERVER CLIENT HANDLER
========================================================= */
def handleServerClient(
socket: Socket
): Unit = {
var input:
BufferedReader =
null
var output:
PrintWriter =
null
var client:
ServerClient =
null
try {
input =
new BufferedReader(
new InputStreamReader(
socket.getInputStream
)
)
output =
new PrintWriter(
socket.getOutputStream,
true
)
val first =
input.readLine()
if (
first ==
null
) {
socket.close()
return
}
val hp =
first.split(
"\\|",
-1
)
var name =
"Driver"
if (
hp.length >=
2
) {
name =
cleanText(
hp(1)
)
}
val id =
"P" +
Math.abs(
System.nanoTime()
)
.toString
.takeRight(
7
)
client =
ServerClient(
id,
name,
socket,
output,
0.0,
0.0,
0.0,
"N",
"ONLINE SOLO",
false
)
serverClients.synchronized {
serverClients +=
client
}
output.println(
"WELCOME|" +
id +
"|" +
actualServerPort
)
output.flush()
broadcastServer(
"SYSTEM|" +
cleanText(
name
) +
" joined."
)
broadcastPlayers()
var listening =
true
while (
listening
) {
val line =
input.readLine()
if (
line ==
null
) {
listening =
false
} else {
val p =
line.split(
"\\|",
-1
)
if (
p.length >
0
) {
if (
p(0) ==
"STATE"
) {
if (
p.length >=
9
) {
client.x =
Try(
p(3).toDouble
)
.getOrElse(
0.0
)
client.distance =
Try(
p(4).toDouble
)
.getOrElse(
0.0
)
client.speed =
Try(
p(5).toDouble
)
.getOrElse(
0.0
)
client.gear =
p(6)
client.mode =
p(7)
client.ready =
p(8) ==
"true"
}
broadcastPlayers()
}
if (
p(0) ==
"CHAT"
) {
if (
p.length >=
3
) {
broadcastServer(
"CHAT|" +
cleanText(
client.name
) +
"|" +
cleanText(
p(2)
)
)
}
}
if (
p(0) ==
"READY"
) {
client.ready =
true
broadcastServer(
"SYSTEM|" +
cleanText(
client.name
) +
" is READY."
)
broadcastPlayers()
}
if (
p(0) ==
"NOTREADY"
) {
client.ready =
false
broadcastPlayers()
}
if (
p(0) ==
"MODE"
) {
if (
p.length >=
2
) {
client.mode =
p(1)
}
broadcastPlayers()
}
if (
p(0) ==
"START"
) {
broadcastServer(
"RACE_START"
)
}
if (
p(0) ==
"LOBBY"
) {
client.mode =
"LOBBY"
client.ready =
false
broadcastPlayers()
}
}
}
}
} catch {
case _: Throwable =>
}
if (
client !=
null
) {
broadcastServer(
"SYSTEM|" +
cleanText(
client.name
) +
" left."
)
removeServerClient(
client.id
)
} else {
try {
socket.close()
} catch {
case _: Throwable =>
}
}
}
/* =========================================================
REMOVE CLIENT
========================================================= */
def removeServerClient(
id: String
): Unit = {
serverClients.synchronized {
var i =
serverClients.length -
1
while (
i >=
0
) {
if (
serverClients(i).id ==
id
) {
try {
serverClients(i)
.socket
.close()
} catch {
case _: Throwable =>
}
serverClients.remove(
i
)
}
i -= 1
}
}
broadcastPlayers()
}
/* =========================================================
STOP SERVER
========================================================= */
def stopServer(): Unit = {
serverRunning =
false
try {
if (
serverSocket !=
null
) {
serverSocket.close()
}
} catch {
case _: Throwable =>
}
serverClients.synchronized {
var i = 0
while (
i <
serverClients.length
) {
try {
serverClients(i)
.socket
.close()
} catch {
case _: Throwable =>
}
i += 1
}
serverClients.clear()
}
}
/* =========================================================
CLIENT CONNECT
========================================================= */
def connectToServer(
host: String,
port: Int
): Boolean = {
disconnectClient()
try {
val socket =
new Socket()
socket.connect(
new java.net.InetSocketAddress(
host,
port
),
4000
)
socket.setTcpNoDelay(
true
)
clientSocket =
socket
clientOut =
new PrintWriter(
socket.getOutputStream,
true
)
clientIn =
new BufferedReader(
new InputStreamReader(
socket.getInputStream
)
)
clientOut.println(
"HELLO|" +
cleanText(
playerName
)
)
clientOut.flush()
clientConnected =
true
online =
true
lastHost =
host
lastPort =
port
val listener =
new Thread(
new Runnable {
def run(): Unit = {
listenClient()
}
}
)
listener.setDaemon(
true
)
listener.start()
true
} catch {
case _: Throwable =>
clientConnected =
false
online =
false
false
}
}
/* =========================================================
CLIENT DISCONNECT
========================================================= */
def disconnectClient(): Unit = {
clientConnected =
false
online =
false
matchmaking =
false
try {
if (
clientSocket !=
null
) {
clientSocket.close()
}
} catch {
case _: Throwable =>
}
clientSocket =
null
clientOut =
null
clientIn =
null
remotePlayers.synchronized {
remotePlayers.clear()
}
}
/* =========================================================
CLIENT SEND
========================================================= */
def sendClient(
message: String
): Unit = {
if (
!clientConnected
) {
return
}
try {
clientOut.println(
message
)
clientOut.flush()
} catch {
case _: Throwable =>
clientConnected =
false
online =
false
}
}
/* =========================================================
CLIENT LISTENER
========================================================= */
def listenClient(): Unit = {
try {
var running =
true
while (
running &&
clientConnected
) {
val line =
clientIn.readLine()
if (
line ==
null
) {
running =
false
} else {
receiveMessage(
line
)
}
}
} catch {
case _: Throwable =>
}
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
if (
clientConnected
) {
clientConnected =
false
online =
false
if (
lobbyStatusLabel !=
null
) {
lobbyStatusLabel.setText(
"CONNECTION CLOSED"
)
}
}
}
}
)
}
/* =========================================================
RECEIVE MESSAGE
========================================================= */
def receiveMessage(
message: String
): Unit = {
val p =
message.split(
"\\|",
-1
)
if (
p.length ==
0
) {
return
}
if (
p(0) ==
"WELCOME"
) {
if (
p.length >=
2
) {
clientId =
p(1)
}
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
if (
lobbyStatusLabel !=
null
) {
lobbyStatusLabel.setText(
"CONNECTED ? " +
clientId
)
}
}
}
)
}
if (
p(0) ==
"SYSTEM"
) {
if (
p.length >=
2
) {
addLobbyChat(
"[SYSTEM] " +
p(1)
)
}
}
if (
p(0) ==
"CHAT"
) {
if (
p.length >=
3
) {
addLobbyChat(
p(1) +
": " +
p(2)
)
}
}
if (
p(0) ==
"PLAYERS"
) {
parsePlayers(
p
)
}
if (
p(0) ==
"RACE_START"
) {
raceStarted =
true
matchmaking =
false
startGame()
}
}
/* =========================================================
PARSE PLAYERS
========================================================= */
def parsePlayers(
p: Array[String]
): Unit = {
remotePlayers.synchronized {
remotePlayers.clear()
var i =
1
while (
i <
p.length
) {
val q =
p(i).split(
"~",
-1
)
if (
q.length >=
8
) {
if (
q(0) !=
clientId
) {
remotePlayers +=
RemotePlayer(
q(0),
q(1),
Try(
q(2).toDouble
)
.getOrElse(
0.0
),
Try(
q(3).toDouble
)
.getOrElse(
0.0
),
Try(
q(4).toDouble
)
.getOrElse(
0.0
),
q(5),
q(6),
q(7) ==
"true"
)
}
}
i += 1
}
}
updateLobbyPlayers()
}
/* =========================================================
SEND GAME STATE
========================================================= */
def sendGameState(): Unit = {
if (
!clientConnected ||
!online
) {
return
}
val now =
System.currentTimeMillis()
if (
now -
lastNetworkSend <
65L
) {
return
}
lastNetworkSend =
now
sendClient(
"STATE|" +
clientId +
"|" +
cleanText(
playerName
) +
"|" +
carX +
"|" +
gameDistance +
"|" +
gameSpeed +
"|" +
gear +
"|" +
modeText() +
"|" +
ready
)
}
/* =========================================================
CHAT
========================================================= */
def addLobbyChat(
text: String
): Unit = {
if (
lobbyChatArea ==
null
) {
return
}
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
lobbyChatArea.append(
text +
"\n"
)
lobbyChatArea.setCaretPosition(
lobbyChatArea
.getDocument
.getLength
)
}
}
)
}
def sendChat(
text: String
): Unit = {
if (
text.trim.isEmpty
) {
return
}
if (
clientConnected
) {
sendClient(
"CHAT|" +
clientId +
"|" +
cleanText(
text
)
)
} else {
addLobbyChat(
"LOCAL: " +
text
)
}
}
/* =========================================================
LOBBY COUNT
========================================================= */
def updateLobbyPlayers(): Unit = {
if (
lobbyPlayersLabel ==
null
) {
return
}
val remoteCount =
remotePlayers.synchronized {
remotePlayers.length
}
val hostCount =
serverClients.synchronized {
serverClients.length
}
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
if (
serverRunning
) {
lobbyPlayersLabel.setText(
"HOST " +
localIP() +
":" +
actualServerPort +
" ? PLAYERS " +
hostCount
)
} else {
lobbyPlayersLabel.setText(
"ONLINE PLAYERS ? " +
remoteCount
)
}
}
}
)
}
/* =========================================================
GAME RESET
========================================================= */
def resetGame(): Unit = {
gameDistance =
0.0
gameSpeed =
0.0
fuel =
100.0
damage =
0.0
score =
0
missionNumber =
1
missionProgress =
0
missionTarget =
1000.0
carX =
0.0
steering =
0.0
throttle =
false
braking =
false
nitro =
false
horn =
false
lights =
false
highBeam =
false
leftIndicator =
false
rightIndicator =
false
hazards =
false
rain =
false
cruiseControl =
false
cruiseSpeed =
80.0
nitroHeat =
0.0
gear =
"N"
paused =
false
gameRunning =
true
raceStarted =
false
repairCooldown =
0L
traffic.clear()
var i =
0
while (
i <
16
) {
val kind =
trafficKind(
i
)
traffic +=
TrafficCar(
i % 3,
0.10 +
i.toDouble *
0.072,
38.0 +
(
i % 6
) *
9.0,
kind,
i % 10 ==
0,
i.toDouble
)
i += 1
}
playTone(
820,
120
)
}
/* =========================================================
TRAFFIC TYPE
========================================================= */
def trafficKind(
index: Int
): String = {
if (
index %
7 ==
0
) {
"TRUCK"
} else if (
index %
4 ==
0
) {
"BUS"
} else {
"CAR"
}
}
/* =========================================================
START GAME
========================================================= */
def startGame(): Unit = {
resetGame()
showCard(
"game"
)
if (
gamePanel !=
null
) {
gamePanel.requestFocusInWindow()
}
}
/* =========================================================
ONLINE SOLO
========================================================= */
def startOnlineSolo(): Unit = {
if (
!clientConnected
) {
flash(
"NOT CONNECTED"
)
return
}
multiplayer =
false
soloOnline =
true
matchmaking =
false
ready =
true
sendClient(
"MODE|ONLINE SOLO"
)
sendClient(
"READY"
)
startGame()
}
/* =========================================================
MULTIPLAYER
========================================================= */
def startMultiplayer(): Unit = {
if (
!clientConnected
) {
flash(
"NOT CONNECTED"
)
return
}
multiplayer =
true
soloOnline =
false
ready =
true
sendClient(
"MODE|MULTIPLAYER"
)
sendClient(
"READY"
)
sendClient(
"START"
)
}
/* =========================================================
AUTO MATCH
========================================================= */
def startAutoMatch(): Unit = {
if (
lobbyNameField !=
null
) {
playerName =
cleanText(
lobbyNameField
.getText
)
}
if (
playerName.isEmpty
) {
playerName =
"Driver"
}
matchmaking =
true
if (
lobbyStatusLabel !=
null
) {
lobbyStatusLabel.setText(
"AUTO MATCH SEARCHING..."
)
}
/*
Automatically starts a local/LAN
matchmaking host.
*/
if (
!serverRunning
) {
val started =
startServer(
5555
)
if (!started) {
matchmaking =
false
lobbyStatusLabel.setText(
"AUTO MATCH SERVER FAILED"
)
return
}
}
if (
!clientConnected
) {
val connected =
connectToServer(
"127.0.0.1",
actualServerPort
)
if (!connected) {
matchmaking =
false
lobbyStatusLabel.setText(
"AUTO MATCH CONNECTION FAILED"
)
return
}
}
multiplayer =
true
soloOnline =
false
ready =
true
sendClient(
"MODE|MULTIPLAYER"
)
sendClient(
"READY"
)
lobbyStatusLabel.setText(
"MATCH FOUND ? STARTING"
)
playTone(
900,
100
)
startGame()
}
/* =========================================================
LEAVE GAME
========================================================= */
def leaveToLobby(): Unit = {
gameRunning =
false
throttle =
false
braking =
false
nitro =
false
horn =
false
cruiseControl =
false
ready =
false
sendClient(
"LOBBY"
)
showCard(
"lobby"
)
updateLobbyPlayers()
}
/* =========================================================
MENU
========================================================= */
def returnToMenu(): Unit = {
gameRunning =
false
sendClient(
"LOBBY"
)
disconnectClient()
stopServer()
multiplayer =
false
soloOnline =
false
ready =
false
matchmaking =
false
showCard(
"menu"
)
}
/* =========================================================
EXIT
========================================================= */
def exitApplication(): Unit = {
try {
disconnectClient()
} catch {
case _: Throwable =>
}
try {
stopServer()
} catch {
case _: Throwable =>
}
try {
frame.dispose()
} catch {
case _: Throwable =>
}
}
/* =========================================================
GAME UPDATE
========================================================= */
def updateGame(): Unit = {
if (
!gameRunning ||
paused
) {
return
}
val now =
System.currentTimeMillis()
val raw =
(
now -
lastTick
).toDouble /
1000.0
val dt =
Math.min(
0.06,
Math.max(
0.001,
raw
)
)
lastTick =
now
updateControls(
dt
)
updateTraffic(
dt
)
detectCollisions()
updateMission()
updateTime()
updateNitroHeat()
updateRepair()
sendGameState()
}
/* =========================================================
CONTROLS
========================================================= */
def updateControls(
dt: Double
): Unit = {
val leftPressed =
keys.contains(
KeyEvent.VK_LEFT
) ||
keys.contains(
KeyEvent.VK_A
)
val rightPressed =
keys.contains(
KeyEvent.VK_RIGHT
) ||
keys.contains(
KeyEvent.VK_D
)
val upPressed =
keys.contains(
KeyEvent.VK_UP
) ||
keys.contains(
KeyEvent.VK_W
)
val downPressed =
keys.contains(
KeyEvent.VK_DOWN
) ||
keys.contains(
KeyEvent.VK_S
)
var steeringInput =
0.0
if (
leftPressed
) {
steeringInput =
-1.0
}
if (
rightPressed
) {
steeringInput =
1.0
}
/*
SMOOTH FULL STEERING
*/
if (
steeringInput !=
0.0
) {
steering +=
(
steeringInput -
steering
) *
Math.min(
1.0,
11.0 *
dt
)
} else {
steering *=
Math.max(
0.0,
1.0 -
7.0 *
dt
)
if (
Math.abs(
steering
) <
0.012
) {
steering =
0.0
}
}
val gas =
throttle ||
upPressed
val brake =
braking ||
downPressed
val currentGear =
gearNumber(
gear
)
var acceleration =
0.0
if (
cruiseControl &&
!gas &&
!brake
) {
if (
gameSpeed <
cruiseSpeed
) {
acceleration +=
14.0
}
}
if (
gas
) {
cruiseControl =
false
if (
currentGear >
0
) {
acceleration =
31.0 +
currentGear.toDouble *
7.5
}
if (
gear ==
"R"
) {
acceleration =
-26.0
}
}
if (
brake
) {
cruiseControl =
false
acceleration -=
75.0
}
if (
nitro &&
fuel >
2.0 &&
gameSpeed >
8.0 &&
nitroHeat <
100.0
) {
acceleration +=
86.0
fuel -=
6.5 *
dt
nitroHeat +=
36.0 *
dt
} else {
nitroHeat -=
18.0 *
dt
}
if (
nitroHeat <
0.0
) {
nitroHeat =
0.0
}
if (
nitroHeat >
100.0
) {
nitroHeat =
100.0
nitro =
false
flash(
"NITRO OVERHEAT"
)
}
gameSpeed +=
acceleration *
dt
if (
!gas &&
!brake &&
!cruiseControl &&
acceleration ==
0.0
) {
if (
gameSpeed >
0.0
) {
gameSpeed -=
9.0 *
dt
}
if (
gameSpeed <
0.0
) {
gameSpeed +=
9.0 *
dt
}
}
if (
gameSpeed >
188.0
) {
gameSpeed =
188.0
}
if (
gameSpeed <
-42.0
) {
gameSpeed =
-42.0
}
if (
autoGear
) {
autoShift()
}
/*
FULL LATERAL MOVEMENT
*/
val speedFactor =
0.62 +
Math.min(
1.65,
Math.abs(
gameSpeed
) /
75.0
)
val lateral =
steering *
speedFactor *
dt *
1.28
carX +=
lateral
/*
Road edges
*/
if (
carX <
-1.08
) {
carX =
-1.08
steering =
0.0
damage +=
0.18
}
if (
carX >
1.08
) {
carX =
1.08
steering =
0.0
damage +=
0.18
}
/*
Road distance
*/
val forwardSpeed =
Math.max(
0.0,
gameSpeed
)
gameDistance +=
forwardSpeed *
dt *
0.12
/*
Fuel
*/
fuel -=
(
0.45 +
forwardSpeed *
0.0035
) *
dt
if (
fuel <
0.0
) {
fuel =
0.0
}
if (
fuel <=
0.0
) {
gameSpeed -=
17.0 *
dt
}
/*
Score
*/
score +=
(
forwardSpeed *
dt *
0.13
).toInt
}
/* =========================================================
TRAFFIC UPDATE
========================================================= */
def updateTraffic(
dt: Double
): Unit = {
var i =
0
while (
i <
traffic.length
) {
val car =
traffic(i)
car.z +=
(
car.speed -
gameSpeed
) *
dt *
0.00165
car.phase +=
dt *
2.0
if (
car.z >
1.28
) {
car.z =
-0.18
car.lane =
Math.abs(
(
System.nanoTime() /
1000L +
i
).toInt
) % 3
car.speed =
40.0 +
(
Math.abs(
System.nanoTime()
) %
75L
).toDouble
}
if (
car.z <
-0.45
) {
car.z =
1.05
}
i += 1
}
val now =
System.currentTimeMillis()
if (
now -
lastTrafficSpawn >
2100L
) {
lastTrafficSpawn =
now
val lane =
Math.abs(
(
System.nanoTime() /
1000L
).toInt
) % 3
val policeCar =
(
Math.abs(
System.nanoTime()
) %
13L
) ==
0
var newKind =
"CAR"
val selector =
Math.abs(
(
System.nanoTime() /
1000L
).toInt
) % 10
if (
selector <
2
) {
newKind =
"BUS"
}
if (
selector ==
8
) {
newKind =
"TRUCK"
}
traffic +=
TrafficCar(
lane,
-0.16,
42.0 +
(
Math.abs(
System.nanoTime()
) %
75L
).toDouble,
newKind,
policeCar,
0.0
)
if (
traffic.length >
23
) {
traffic.remove(
0
)
}
}
}
/* =========================================================
COLLISION
========================================================= */
def detectCollisions(): Unit = {
var i =
0
while (
i <
traffic.length
) {
val car =
traffic(i)
if (
car.z >
0.82 &&
car.z <
1.04
) {
val trafficX =
laneToX(
car.lane
)
val difference =
Math.abs(
trafficX -
carX
)
if (
difference <
0.23
) {
val impact =
Math.abs(
gameSpeed -
car.speed
)
damage +=
4.0 +
impact *
0.075
score -=
30
if (
score <
0
) {
score =
0
}
gameSpeed *=
0.70
car.z =
-0.25
if (
car.police
) {
score -=
80
if (
score <
0
) {
score =
0
}
flash(
"POLICE FINE!"
)
} else {
flash(
"COLLISION!"
)
}
playTone(
110,
180
)
}
}
i += 1
}
if (
damage >
100.0
) {
damage =
100.0
gameSpeed *=
0.50
flash(
"VEHICLE HEAVILY DAMAGED"
)
}
}
/* =========================================================
REPAIR / FUEL CHECKPOINT
========================================================= */
def updateRepair(): Unit = {
if (
gameDistance >
0.0 &&
missionNumber %
4 ==
0
) {
val now =
System.currentTimeMillis()
if (
now -
repairCooldown >
12000L
) {
if (
gameDistance.toInt %
3500 <
2
) {
damage -=
8.0
fuel +=
8.0
if (
damage <
0.0
) {
damage =
0.0
}
if (
fuel >
100.0
) {
fuel =
100.0
}
repairCooldown =
now
flash(
"SERVICE CHECKPOINT"
)
playTone(
950,
130
)
}
}
}
}
/* =========================================================
NITRO HEAT
========================================================= */
def updateNitroHeat(): Unit = {
if (
!nitro
) {
nitroHeat -=
12.0 /
30.0
}
if (
nitroHeat <
0.0
) {
nitroHeat =
0.0
}
}
/* =========================================================
MISSION
========================================================= */
def updateMission(): Unit = {
missionProgress =
Math.min(
missionTarget.toInt,
gameDistance.toInt
)
if (
gameDistance >=
missionTarget
) {
missionNumber +=
1
missionTarget +=
1000.0 +
missionNumber.toDouble *
300.0
score +=
1000
playTone(
1000,
170
)
flash(
"CHECKPOINT " +
missionNumber
)
}
}
/* =========================================================
DAY / NIGHT
========================================================= */
def updateTime(): Unit = {
timePhase =
(
gameDistance /
2500.0
).toInt %
4
}
/* =========================================================
BUTTON FACTORY
========================================================= */
def makeButton(
text: String,
width: Int,
height: Int
): JButton = {
val button =
new JButton(
text
)
button.setPreferredSize(
new Dimension(
width,
height
)
)
button.setFocusPainted(
false
)
button.setFont(
new Font(
"Arial",
Font.BOLD,
15
)
)
button
}
/* =========================================================
MENU
========================================================= */
def makeMenu(): JPanel = {
val panel =
new JPanel()
panel.setLayout(
new BoxLayout(
panel,
BoxLayout.Y_AXIS
)
)
panel.setBorder(
BorderFactory
.createEmptyBorder(
30,
70,
30,
70
)
)
val title =
new JLabel(
"REAL DRIVE X"
)
title.setFont(
new Font(
"Arial",
Font.BOLD,
44
)
)
title.setAlignmentX(
0.5f
)
panel.add(
title
)
val subtitle =
new JLabel(
"3D STYLE ? ONLINE ? MULTIPLAYER ? TRAFFIC"
)
subtitle.setAlignmentX(
0.5f
)
panel.add(
subtitle
)
panel.add(
javax.swing.Box
.createVerticalStrut(
28
)
)
val hostButton =
makeButton(
"HOST ONLINE",
350,
54
)
hostButton.setAlignmentX(
0.5f
)
hostButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
val nameInput =
JOptionPane.showInputDialog(
frame,
"Driver name:",
"Driver"
)
if (
nameInput !=
null &&
nameInput.trim.nonEmpty
) {
playerName =
cleanText(
nameInput.trim
)
}
val portInput =
JOptionPane.showInputDialog(
frame,
"Server port:",
"5555"
)
val port =
Try(
Option(
portInput
)
.getOrElse(
"5555"
)
.toInt
)
.getOrElse(
5555
)
val started =
startServer(
port
)
if (
started
) {
val connected =
connectToServer(
"127.0.0.1",
actualServerPort
)
if (
connected
) {
lobbyStatusLabel.setText(
"HOST CONNECTED ? " +
localIP() +
":" +
actualServerPort
)
showCard(
"lobby"
)
} else {
JOptionPane.showMessageDialog(
frame,
"Local host connection failed."
)
}
} else {
JOptionPane.showMessageDialog(
frame,
"Could not open server port."
)
}
}
}
)
panel.add(
hostButton
)
panel.add(
javax.swing.Box
.createVerticalStrut(
10
)
)
val joinButton =
makeButton(
"JOIN ONLINE",
350,
54
)
joinButton.setAlignmentX(
0.5f
)
joinButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
val nameInput =
JOptionPane.showInputDialog(
frame,
"Driver name:",
"Driver"
)
if (
nameInput !=
null &&
nameInput.trim.nonEmpty
) {
playerName =
cleanText(
nameInput.trim
)
}
val hostInput =
JOptionPane.showInputDialog(
frame,
"Host IP:",
lastHost
)
if (
hostInput ==
null ||
hostInput.trim.isEmpty
) {
return
}
val portInput =
JOptionPane.showInputDialog(
frame,
"Port:",
lastPort.toString
)
val port =
Try(
Option(
portInput
)
.getOrElse(
lastPort.toString
)
.toInt
)
.getOrElse(
lastPort
)
lobbyStatusLabel.setText(
"CONNECTING..."
)
val connected =
connectToServer(
hostInput.trim,
port
)
if (
connected
) {
lobbyHostField.setText(
hostInput.trim
)
lobbyPortField.setText(
port.toString
)
lobbyStatusLabel.setText(
"CONNECTED"
)
showCard(
"lobby"
)
} else {
lobbyStatusLabel.setText(
"CONNECTION FAILED"
)
JOptionPane.showMessageDialog(
frame,
"Connection failed.\nCheck IP, port and firewall."
)
}
}
}
)
panel.add(
joinButton
)
panel.add(
javax.swing.Box
.createVerticalStrut(
10
)
)
val autoMatchButton =
makeButton(
"AUTO MATCH",
350,
54
)
autoMatchButton.setAlignmentX(
0.5f
)
autoMatchButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
val nameInput =
JOptionPane.showInputDialog(
frame,
"Driver name:",
"Driver"
)
if (
nameInput !=
null &&
nameInput.trim.nonEmpty
) {
playerName =
cleanText(
nameInput.trim
)
}
showCard(
"lobby"
)
startAutoMatch()
}
}
)
panel.add(
autoMatchButton
)
panel.add(
javax.swing.Box
.createVerticalStrut(
10
)
)
val offlineButton =
makeButton(
"OFFLINE SOLO",
350,
54
)
offlineButton.setAlignmentX(
0.5f
)
offlineButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
online =
false
multiplayer =
false
soloOnline =
false
startGame()
}
}
)
panel.add(
offlineButton
)
panel.add(
javax.swing.Box
.createVerticalStrut(
10
)
)
val exitButton =
makeButton(
"EXIT",
350,
50
)
exitButton.setAlignmentX(
0.5f
)
exitButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
exitApplication()
}
}
)
panel.add(
exitButton
)
panel
}
/* =========================================================
LOBBY
========================================================= */
def makeLobby(): JPanel = {
val panel =
new JPanel()
panel.setLayout(
new BoxLayout(
panel,
BoxLayout.Y_AXIS
)
)
panel.setBorder(
BorderFactory
.createEmptyBorder(
18,
30,
18,
30
)
)
val title =
new JLabel(
"ONLINE LOBBY"
)
title.setFont(
new Font(
"Arial",
Font.BOLD,
34
)
)
title.setAlignmentX(
0.5f
)
panel.add(
title
)
lobbyStatusLabel =
new JLabel(
"NOT CONNECTED"
)
lobbyStatusLabel.setAlignmentX(
0.5f
)
panel.add(
lobbyStatusLabel
)
lobbyPlayersLabel =
new JLabel(
"PLAYERS: 0"
)
lobbyPlayersLabel.setAlignmentX(
0.5f
)
panel.add(
lobbyPlayersLabel
)
lobbyModeLabel =
new JLabel(
"ONLINE SOLO ? MULTIPLAYER ? AUTO MATCH"
)
lobbyModeLabel.setAlignmentX(
0.5f
)
panel.add(
lobbyModeLabel
)
panel.add(
javax.swing.Box
.createVerticalStrut(
8
)
)
val form =
new JPanel()
form.add(
new JLabel(
"NAME"
)
)
lobbyNameField =
new JTextField(
"Driver",
11
)
form.add(
lobbyNameField
)
form.add(
new JLabel(
"HOST"
)
)
lobbyHostField =
new JTextField(
"127.0.0.1",
11
)
form.add(
lobbyHostField
)
form.add(
new JLabel(
"PORT"
)
)
lobbyPortField =
new JTextField(
"5555",
7
)
form.add(
lobbyPortField
)
panel.add(
form
)
val modes =
new JPanel()
val soloButton =
makeButton(
"ONLINE SOLO",
165,
43
)
soloButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
playerName =
cleanText(
lobbyNameField.getText
)
if (
playerName.isEmpty
) {
playerName =
"Driver"
}
startOnlineSolo()
}
}
)
modes.add(
soloButton
)
val multiplayerButton =
makeButton(
"MULTIPLAYER",
165,
43
)
multiplayerButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
playerName =
cleanText(
lobbyNameField.getText
)
if (
playerName.isEmpty
) {
playerName =
"Driver"
}
startMultiplayer()
}
}
)
modes.add(
multiplayerButton
)
val autoLobbyButton =
makeButton(
"AUTO MATCH",
145,
43
)
autoLobbyButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
startAutoMatch()
}
}
)
modes.add(
autoLobbyButton
)
panel.add(
modes
)
/*
IMPORTANT FIX:
local button is named readyButton,
NOT ready.
*/
val readyButton =
makeButton(
"READY",
105,
42
)
readyButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
ready =
true
sendClient(
"READY"
)
addLobbyChat(
"[LOCAL] READY"
)
}
}
)
val notReadyButton =
makeButton(
"NOT READY",
115,
42
)
notReadyButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
ready =
false
sendClient(
"NOTREADY"
)
}
}
)
val readyRow =
new JPanel()
readyRow.add(
readyButton
)
readyRow.add(
notReadyButton
)
panel.add(
readyRow
)
val chatTitle =
new JLabel(
"CHAT"
)
chatTitle.setAlignmentX(
0.5f
)
panel.add(
chatTitle
)
lobbyChatArea =
new JTextArea(
8,
60
)
lobbyChatArea.setEditable(
false
)
panel.add(
new JScrollPane(
lobbyChatArea
)
)
val chatRow =
new JPanel()
lobbyChatField =
new JTextField(
36
)
val sendButton =
makeButton(
"SEND",
95,
40
)
def sendCurrentChat(): Unit = {
val text =
lobbyChatField
.getText
.trim
if (
text.nonEmpty
) {
sendChat(
text
)
lobbyChatField
.setText(
""
)
}
}
sendButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
sendCurrentChat()
}
}
)
lobbyChatField.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
sendCurrentChat()
}
}
)
chatRow.add(
lobbyChatField
)
chatRow.add(
sendButton
)
panel.add(
chatRow
)
val navigation =
new JPanel()
val retryButton =
makeButton(
"RETRY",
105,
42
)
retryButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
val host =
lobbyHostField
.getText
.trim
val port =
Try(
lobbyPortField
.getText
.trim
.toInt
)
.getOrElse(
5555
)
lobbyStatusLabel.setText(
"RECONNECTING..."
)
val connected =
connectToServer(
host,
port
)
if (
connected
) {
lobbyStatusLabel.setText(
"CONNECTED"
)
} else {
lobbyStatusLabel.setText(
"RECONNECT FAILED"
)
}
}
}
)
navigation.add(
retryButton
)
val backButton =
makeButton(
"BACK TO MENU",
155,
42
)
backButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
returnToMenu()
}
}
)
navigation.add(
backButton
)
panel.add(
navigation
)
panel
}
/* =========================================================
GAME PANEL
========================================================= */
class DrivePanel
extends JPanel {
setFocusable(
true
)
/* =======================================================
KEYBOARD
======================================================= */
addKeyListener(
new KeyAdapter {
override def keyPressed(
e: KeyEvent
): Unit = {
val k =
e.getKeyCode
keys +=
k
if (
k ==
KeyEvent.VK_ESCAPE
) {
leaveToLobby()
return
}
if (
k ==
KeyEvent.VK_SPACE
) {
nitro =
true
}
if (
k ==
KeyEvent.VK_H
) {
horn =
true
playTone(
560,
120
)
}
if (
k ==
KeyEvent.VK_L
) {
toggleLights()
}
if (
k ==
KeyEvent.VK_B
) {
toggleHighBeam()
}
if (
k ==
KeyEvent.VK_G
) {
toggleAutoGear()
}
if (
k ==
KeyEvent.VK_P
) {
paused =
!paused
}
if (
k ==
KeyEvent.VK_T
) {
toggleRain()
}
if (
k ==
KeyEvent.VK_Q
) {
toggleLeftIndicator()
}
if (
k ==
KeyEvent.VK_E
) {
toggleRightIndicator()
}
if (
k ==
KeyEvent.VK_C
) {
toggleCruise()
}
if (
k ==
KeyEvent.VK_1
) {
setManualGear(
"1"
)
}
if (
k ==
KeyEvent.VK_2
) {
setManualGear(
"2"
)
}
if (
k ==
KeyEvent.VK_3
) {
setManualGear(
"3"
)
}
if (
k ==
KeyEvent.VK_4
) {
setManualGear(
"4"
)
}
if (
k ==
KeyEvent.VK_5
) {
setManualGear(
"5"
)
}
if (
k ==
KeyEvent.VK_R
) {
setManualGear(
"R"
)
}
if (
k ==
KeyEvent.VK_N
) {
setManualGear(
"N"
)
}
if (
k ==
KeyEvent.VK_F
) {
flash(
"WIPERS " +
onOff(wipers)
)
}
}
override def keyReleased(
e: KeyEvent
): Unit = {
val k =
e.getKeyCode
keys -=
k
if (
k ==
KeyEvent.VK_SPACE
) {
nitro =
false
}
if (
k ==
KeyEvent.VK_H
) {
horn =
false
}
}
}
)
/* =======================================================
MOUSE / TOUCH
======================================================= */
addMouseListener(
new MouseAdapter {
override def mousePressed(
e: MouseEvent
): Unit = {
val x =
e.getX
val y =
e.getY
val w =
getWidth
val h =
getHeight
/*
bottom controls
*/
if (
y >
h -
100
) {
if (
x <
120
) {
steering =
-1.0
return
}
if (
x >=
120 &&
x <
245
) {
braking =
true
return
}
if (
x >
w -
245 &&
x <=
w -
120
) {
throttle =
true
return
}
if (
x >
w -
120
) {
steering =
1.0
return
}
}
/*
top controls
*/
if (
y <
78
) {
if (
x <
105
) {
toggleLights()
return
}
if (
x >=
105 &&
x <
210
) {
toggleHighBeam()
return
}
if (
x >=
210 &&
x <
315
) {
nitro =
true
return
}
if (
x >=
315 &&
x <
420
) {
horn =
true
playTone(
560,
120
)
return
}
}
}
override def mouseReleased(
e: MouseEvent
): Unit = {
throttle =
false
braking =
false
nitro =
false
horn =
false
steering =
0.0
}
}
)
/* =======================================================
PAINT
======================================================= */
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setRenderingHint(
RenderingHints
.KEY_ANTIALIASING,
RenderingHints
.VALUE_ANTIALIAS_ON
)
drawSky(g)
drawRoad(g)
drawTraffic(g)
drawRemotePlayers(g)
drawRain(g)
drawPlayerCar(g)
drawCockpit(g)
drawHUD(g)
drawTouchControls(g)
if (
paused
) {
drawPause(g)
}
if (
flashTime >
System.currentTimeMillis()
) {
drawFlash(g)
}
}
/* =======================================================
SKY
======================================================= */
def drawSky(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
var top =
new Color(
70,
155,
235
)
var bottom =
new Color(
200,
232,
250
)
if (
timePhase ==
1
) {
top =
new Color(
238,
132,
75
)
bottom =
new Color(
255,
215,
150
)
}
if (
timePhase ==
2
) {
top =
new Color(
8,
15,
40
)
bottom =
new Color(
48,
58,
100
)
}
if (
timePhase ==
3
) {
top =
new Color(
110,
65,
130
)
bottom =
new Color(
210,
125,
135
)
}
g.setPaint(
new GradientPaint(
0,
0,
top,
0,
h,
bottom
)
)
g.fillRect(
0,
0,
w,
h
)
if (
timePhase ==
2
) {
drawStars(
g
)
}
drawSunMoon(
g
)
}
def drawStars(
g: Graphics2D
): Unit = {
var i =
0
while (
i <
80
) {
val x =
(
i *
89
) %
Math.max(
1,
getWidth
)
val y =
15 +
(
i *
43
) %
Math.max(
1,
getHeight /
2
)
g.setColor(
new Color(
255,
255,
255,
190
)
)
val s =
2 +
i %
3
g.fillOval(
x,
y,
s,
s
)
i += 1
}
}
def drawSunMoon(
g: Graphics2D
): Unit = {
if (
timePhase ==
2
) {
g.setColor(
new Color(
235,
242,
255
)
)
g.fillOval(
getWidth -
145,
50,
75,
75
)
} else {
g.setColor(
new Color(
255,
220,
90
)
)
g.fillOval(
getWidth -
160,
45,
95,
95
)
}
}
/* =======================================================
ROAD
======================================================= */
def drawRoad(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
val horizon =
h /
3
g.setColor(
new Color(
54,
108,
55
)
)
g.fillRect(
0,
horizon,
w,
h -
horizon
)
val topY =
horizon -
20
val bottomY =
h
val tl =
w /
2 -
85
val tr =
w /
2 +
85
val bl =
w /
2 -
275
val br =
w /
2 +
275
val road =
new java.awt.Polygon()
road.addPoint(
tl,
topY
)
road.addPoint(
tr,
topY
)
road.addPoint(
br,
bottomY
)
road.addPoint(
bl,
bottomY
)
g.setColor(
new Color(
44,
45,
48
)
)
g.fillPolygon(
road
)
/*
subtle road strips
*/
g.setColor(
new Color(
58,
59,
63
)
)
g.drawLine(
w / 2 -
50,
topY,
w / 2 -
170,
bottomY
)
g.drawLine(
w / 2 +
50,
topY,
w / 2 +
170,
bottomY
)
/*
edges
*/
g.setColor(
new Color(
235,
230,
210
)
)
g.setStroke(
new BasicStroke(
7.0f
)
)
g.drawLine(
tl,
topY,
bl,
bottomY
)
g.drawLine(
tr,
topY,
br,
bottomY
)
drawLaneLine(
g,
tl,
tr,
bl,
br,
topY,
bottomY,
1.0 /
3.0
)
drawLaneLine(
g,
tl,
tr,
bl,
br,
topY,
bottomY,
2.0 /
3.0
)
drawRoadside(
g,
horizon
)
}
def drawLaneLine(
g: Graphics2D,
tl: Int,
tr: Int,
bl: Int,
br: Int,
topY: Int,
bottomY: Int,
fraction: Double
): Unit = {
g.setColor(
new Color(
245,
245,
245,
215
)
)
var i =
0
while (
i <
11
) {
if (
i %
2 ==
0
) {
val y1 =
topY +
(
(
bottomY -
topY
) *
i.toDouble /
10.0
).toInt
val y2 =
topY +
(
(
bottomY -
topY
) *
(
i +
0.5
).toDouble /
10.0
).toInt
val x1 =
interpolateRoadX(
tl,
tr,
bl,
br,
topY,
bottomY,
y1,
fraction
)
val x2 =
interpolateRoadX(
tl,
tr,
bl,
br,
topY,
bottomY,
y2,
fraction
)
g.setStroke(
new BasicStroke(
(
2.0 +
i *
0.78
).toFloat
)
)
g.drawLine(
x1,
y1,
x2,
y2
)
}
i += 1
}
}
def drawRoadside(
g: Graphics2D,
horizon: Int
): Unit = {
var i =
0
while (
i <
10
) {
val y =
horizon +
24 +
i *
58
val left =
60 +
i *
13
val right =
getWidth -
75 -
i *
13
g.setColor(
new Color(
30,
90,
38
)
)
g.fillRect(
left,
y,
8,
45
)
g.fillOval(
left -
18,
y -
25,
46,
38
)
g.fillRect(
right,
y,
8,
45
)
g.fillOval(
right -
18,
y -
25,
46,
38
)
i += 1
}
}
/* =======================================================
TRAFFIC DRAW
======================================================= */
def drawTraffic(
g: Graphics2D
): Unit = {
var i =
0
while (
i <
traffic.length
) {
drawTrafficVehicle(
g,
traffic(i)
)
i += 1
}
}
def drawTrafficVehicle(
g: Graphics2D,
car: TrafficCar
): Unit = {
val w =
getWidth
val h =
getHeight
val horizon =
h /
3
val z =
Math.max(
0.02,
Math.min(
1.0,
car.z
)
)
val y =
horizon +
(
h -
horizon
) *
z
val cx =
trafficScreenX(
car.lane,
z,
w
)
val cw =
(
19.0 +
64.0 *
z
).toInt
val ch =
(
16.0 +
82.0 *
z
).toInt
val left =
cx -
cw /
2
val top =
y.toInt -
ch /
2
/*
shadow
*/
g.setColor(
new Color(
0,
0,
0,
110
)
)
g.fillOval(
left -
cw /
5,
(
y +
ch *
0.34
).toInt,
cw +
cw /
2,
Math.max(
5,
ch /
6
)
)
/*
body color
*/
var body =
new Color(
215,
42,
45
)
if (
car.kind ==
"BUS"
) {
body =
new Color(
45,
105,
215
)
}
if (
car.kind ==
"TRUCK"
) {
body =
new Color(
194,
135,
60
)
}
if (
car.police
) {
body =
new Color(
232,
235,
240
)
}
/*
body
*/
g.setColor(
body
)
g.fillRoundRect(
left,
top,
cw,
ch,
Math.max(
6,
cw /
4
),
Math.max(
6,
cw /
4
)
)
/*
roof / glass
*/
g.setColor(
new Color(
22,
40,
60
)
)
g.fillRoundRect(
left +
cw /
7,
top +
ch /
6,
(
cw *
5 /
7
),
(
ch /
3
),
6,
6
)
/*
windshield reflection
*/
g.setColor(
new Color(
150,
210,
230,
100
)
)
g.fillRect(
left +
cw /
5,
top +
ch /
5,
(
cw *
3 /
5
),
Math.max(
2,
ch /
10
)
)
/*
front lights
*/
g.setColor(
new Color(
255,
245,
180
)
)
val lightW =
Math.max(
2,
cw /
7
)
val lightH =
Math.max(
2,
ch /
9
)
g.fillOval(
left +
cw /
9,
top +
ch -
ch /
4,
lightW,
lightH
)
g.fillOval(
left +
cw -
cw /
9 -
lightW,
top +
ch -
ch /
4,
lightW,
lightH
)
/*
wheels
*/
g.setColor(
Color.BLACK
)
g.fillOval(
left -
1,
top +
ch -
ch /
5,
Math.max(
3,
cw /
5
),
Math.max(
3,
ch /
4
)
)
g.fillOval(
left +
cw -
Math.max(
3,
cw /
5
) +
1,
top +
ch -
ch /
5,
Math.max(
3,
cw /
5
),
Math.max(
3,
ch /
4
)
)
/*
POLICE LIGHTBAR
*/
if (
car.police
) {
g.setColor(
Color.RED
)
g.fillRect(
left +
cw /
4,
top +
ch /
18,
cw /
5,
Math.max(
2,
ch /
11
)
)
g.setColor(
Color.BLUE
)
g.fillRect(
left +
cw /
2,
top +
ch /
18,
cw /
5,
Math.max(
2,
ch /
11
)
)
}
}
/* =======================================================
REMOTE PLAYER CARS
======================================================= */
def drawRemotePlayers(
g: Graphics2D
): Unit = {
if (
!multiplayer
) {
return
}
remotePlayers.synchronized {
var i =
0
while (
i <
remotePlayers.length
) {
val p =
remotePlayers(i)
val relative =
p.distance -
gameDistance
if (
Math.abs(
relative
) <
850.0
) {
val z =
Math.max(
0.18,
Math.min(
1.0,
0.58 +
relative /
1500.0
)
)
val half =
roadHalfWidth(
z
)
val cx =
getWidth /
2.0 +
p.x *
half
val y =
getHeight /
3 +
(
getHeight -
getHeight /
3
) *
z
val cw =
28.0 +
40.0 *
z
val ch =
36.0 +
52.0 *
z
g.setColor(
new Color(
150,
80,
255
)
)
g.fillRoundRect(
(
cx -
cw /
2.0
).toInt,
(
y -
ch /
2.0
).toInt,
cw.toInt,
ch.toInt,
12,
12
)
g.setColor(
new Color(
25,
42,
60
)
)
g.fillRoundRect(
(
cx -
cw *
0.3
).toInt,
(
y -
ch *
0.28
).toInt,
(
cw *
0.6
).toInt,
(
ch *
0.23
).toInt,
5,
5
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
11
)
)
g.drawString(
p.name,
(
cx -
30
).toInt,
(
y -
ch /
2.0 -
7
).toInt
)
}
i += 1
}
}
}
/* =======================================================
PLAYER CAR
======================================================= */
def drawPlayerCar(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
val cx =
w /
2.0 +
carX *
235.0
val y =
h -
210
val cw =
150.0
val ch =
185.0
/*
big shadow
*/
g.setColor(
new Color(
0,
0,
0,
120
)
)
g.fillOval(
(
cx -
92
).toInt,
(
y +
66
).toInt,
184,
50
)
/*
body
*/
g.setColor(
new Color(
205,
25,
36
)
)
g.fillRoundRect(
(
cx -
cw /
2.0
).toInt,
(
y -
ch /
2.0
).toInt,
cw.toInt,
ch.toInt,
35,
35
)
/*
side highlight
*/
g.setColor(
new Color(
240,
75,
80,
150
)
)
g.fillRoundRect(
(
cx -
cw /
2.0 +
12
).toInt,
(
y -
60
).toInt,
10,
112,
8,
8
)
/*
roof
*/
val roof =
new java.awt.Polygon()
roof.addPoint(
(
cx -
55
).toInt,
(
y -
70
).toInt
)
roof.addPoint(
(
cx -
35
).toInt,
(
y -
105
).toInt
)
roof.addPoint(
(
cx +
35
).toInt,
(
y -
105
).toInt
)
roof.addPoint(
(
cx +
55
).toInt,
(
y -
70
).toInt
)
g.setColor(
new Color(
145,
20,
30
)
)
g.fillPolygon(
roof
)
/*
glass
*/
g.setColor(
new Color(
20,
42,
65
)
)
g.fillRoundRect(
(
cx -
42
).toInt,
(
y -
82
).toInt,
84,
45,
12,
12
)
/*
glass reflection
*/
g.setColor(
new Color(
170,
220,
240,
110
)
)
g.fillRect(
(
cx -
31
).toInt,
(
y -
74
).toInt,
24,
5
)
/*
headlights
*/
g.setColor(
new Color(
255,
250,
205
)
)
g.fillRoundRect(
(
cx -
59
).toInt,
(
y +
55
).toInt,
35,
18,
10,
10
)
g.fillRoundRect(
(
cx +
24
).toInt,
(
y +
55
).toInt,
35,
18,
10,
10
)
/*
headlights beam
*/
if (
lights ||
highBeam
) {
val alpha =
if (
highBeam
) {
58
} else {
30
}
g.setColor(
new Color(
255,
250,
210,
alpha
)
)
val beamLeft =
new java.awt.Polygon()
beamLeft.addPoint(
(
cx -
48
).toInt,
(
y +
67
).toInt
)
beamLeft.addPoint(
(
cx -
12
).toInt,
(
y +
67
).toInt
)
beamLeft.addPoint(
(
cx -
110
).toInt,
h
)
beamLeft.addPoint(
(
cx -
135
).toInt,
h
)
g.fillPolygon(
beamLeft
)
val beamRight =
new java.awt.Polygon()
beamRight.addPoint(
(
cx +
12
).toInt,
(
y +
67
).toInt
)
beamRight.addPoint(
(
cx +
48
).toInt,
(
y +
67
).toInt
)
beamRight.addPoint(
(
cx +
135
).toInt,
h
)
beamRight.addPoint(
(
cx +
110
).toInt,
h
)
g.fillPolygon(
beamRight
)
}
/*
wheels
*/
g.setColor(
Color.BLACK
)
g.fillOval(
(
cx -
77
).toInt,
(
y -
28
).toInt,
29,
66
)
g.fillOval(
(
cx +
48
).toInt,
(
y -
28
).toInt,
29,
66
)
/*
wheel hubs
*/
g.setColor(
new Color(
165,
170,
180
)
)
g.fillOval(
(
cx -
70
).toInt,
(
y -
12
).toInt,
14,
28
)
g.fillOval(
(
cx +
55
).toInt,
(
y -
12
).toInt,
14,
28
)
/*
front grille
*/
g.setColor(
new Color(
38,
40,
43
)
)
g.fillRoundRect(
(
cx -
37
).toInt,
(
y +
55
).toInt,
74,
28,
8,
8
)
/*
grille bars
*/
g.setColor(
new Color(
120,
120,
125
)
)
var bar =
0
while (
bar <
5
) {
g.drawLine(
(
cx -
25 +
bar *
12
).toInt,
(
y +
59
).toInt,
(
cx -
25 +
bar *
12
).toInt,
(
y +
79
).toInt
)
bar += 1
}
/*
bumper
*/
g.setColor(
new Color(
55,
55,
60
)
)
g.fillRoundRect(
(
cx -
58
).toInt,
(
y +
79
).toInt,
116,
18,
10,
10
)
/*
indicators
*/
val blink =
(
System.currentTimeMillis() /
300L
) % 2L
if (
(
leftIndicator ||
hazards
) &&
blink ==
0L
) {
g.setColor(
new Color(
255,
170,
30
)
)
g.fillOval(
(
cx -
67
).toInt,
(
y +
39
).toInt,
18,
12
)
}
if (
(
rightIndicator ||
hazards
) &&
blink ==
0L
) {
g.setColor(
new Color(
255,
170,
30
)
)
g.fillOval(
(
cx +
49
).toInt,
(
y +
39
).toInt,
18,
12
)
}
/*
steering angle visual
*/
g.setColor(
new Color(
210,
210,
215
)
)
g.setStroke(
new BasicStroke(
6.0f
)
)
val wheelAngle =
steering *
0.55
val steeringRadius =
48.0
val sx =
w /
2.0
val sy =
h -
78.0
val ex =
sx +
Math.sin(
wheelAngle
) *
steeringRadius
val ey =
sy -
Math.cos(
wheelAngle
) *
steeringRadius
g.drawLine(
sx.toInt,
sy.toInt,
ex.toInt,
ey.toInt
)
}
/* =======================================================
RAIN
======================================================= */
def drawRain(
g: Graphics2D
): Unit = {
if (
!rain
) {
return
}
g.setColor(
new Color(
175,
205,
250,
125
)
)
var i =
0
while (
i <
150
) {
val x =
(
i *
97 +
System.currentTimeMillis() /
7L
).toInt %
Math.max(
1,
getWidth
)
val y =
(
i *
43 +
System.currentTimeMillis() /
5L
).toInt %
Math.max(
1,
getHeight
)
g.drawLine(
x,
y,
x -
5,
y +
18
)
i += 1
}
/*
windshield wiper
*/
if (
wipers
) {
g.setColor(
new Color(
30,
30,
35,
130
)
)
g.setStroke(
new BasicStroke(
5.0f
)
)
val angle =
Math.sin(
System.currentTimeMillis() /
700.0
)
val cx =
getWidth /
2.0
val cy =
getHeight -
220.0
val len =
180.0
val ex =
cx +
angle *
len
val ey =
cy -
60.0
g.drawLine(
cx.toInt,
cy.toInt,
ex.toInt,
ey.toInt
)
}
}
/* =======================================================
COCKPIT
======================================================= */
def drawCockpit(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
/*
dashboard
*/
g.setColor(
new Color(
12,
15,
20,
235
)
)
g.fillRoundRect(
w /
2 -
265,
h -
180,
530,
165,
32,
32
)
/*
speedometer
*/
g.setColor(
new Color(
3,
6,
9
)
)
g.fillOval(
w /
2 -
145,
h -
155,
125,
125
)
g.setColor(
new Color(
235,
235,
240
)
)
g.setStroke(
new BasicStroke(
7.0f
)
)
g.drawOval(
w /
2 -
145,
h -
155,
125,
125
)
/*
gear dial
*/
g.setColor(
new Color(
3,
6,
9
)
)
g.fillOval(
w /
2 +
20,
h -
155,
125,
125
)
g.setColor(
new Color(
235,
235,
240
)
)
g.drawOval(
w /
2 +
20,
h -
155,
125,
125
)
/*
speed text
*/
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
15
)
)
g.drawString(
"KM/H",
w /
2 -
114,
h -
48
)
g.drawString(
"GEAR",
w /
2 +
58,
h -
118
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
29
)
)
g.drawString(
Math.abs(
gameSpeed
).toInt.toString,
w /
2 -
115,
h -
73
)
g.drawString(
gear,
w /
2 +
71,
h -
73
)
/*
speed needle
*/
val speedAngle =
-Math.PI *
0.75 +
Math.min(
180.0,
Math.abs(
gameSpeed
)
) /
180.0 *
Math.PI *
1.5
val sx =
w /
2.0 -
82.0
val sy =
h -
92.0
val needleLen =
45.0
val nx =
sx +
Math.cos(
speedAngle
) *
needleLen
val ny =
sy +
Math.sin(
speedAngle
) *
needleLen
g.setColor(
new Color(
255,
70,
70
)
)
g.setStroke(
new BasicStroke(
4.0f
)
)
g.drawLine(
sx.toInt,
sy.toInt,
nx.toInt,
ny.toInt
)
/*
nitro heat bar
*/
val barX =
w /
2 -
8
val barY =
h -
55
g.setColor(
new Color(
35,
35,
40
)
)
g.fillRoundRect(
barX,
barY,
16,
38,
7,
7
)
g.setColor(
new Color(
60,
160,
255
)
)
val heatHeight =
(
34.0 *
nitroHeat /
100.0
).toInt
g.fillRoundRect(
barX +
2,
barY +
36 -
heatHeight,
12,
heatHeight,
5,
5
)
}
/* =======================================================
HUD
======================================================= */
def drawHUD(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
7,
12,
20,
225
)
)
g.fillRoundRect(
15,
95,
305,
230,
18,
18
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
16
)
)
g.drawString(
"REAL DRIVE X",
30,
120
)
g.setFont(
new Font(
"Arial",
Font.PLAIN,
13
)
)
g.drawString(
"MODE: " +
modeText(),
30,
145
)
g.drawString(
"SPEED: " +
Math.abs(
gameSpeed
).toInt +
" km/h",
30,
167
)
g.drawString(
"DISTANCE: " +
gameDistance.toInt +
" m",
30,
189
)
g.drawString(
"SCORE: " +
score,
30,
211
)
g.drawString(
"FUEL: " +
fuel.toInt +
"%",
30,
233
)
g.drawString(
"DAMAGE: " +
damage.toInt +
"%",
30,
255
)
g.drawString(
"MISSION " +
missionNumber +
" ? " +
missionProgress +
"/" +
missionTarget.toInt,
30,
277
)
g.drawString(
"NITRO HEAT: " +
nitroHeat.toInt +
"%",
30,
299
)
g.drawString(
"CRUISE: " +
onOff(cruiseControl),
30,
319
)
drawGPS(
g
)
}
/* =======================================================
GPS
======================================================= */
def drawGPS(
g: Graphics2D
): Unit = {
val x =
getWidth -
190
val y =
95
g.setColor(
new Color(
8,
18,
22,
235
)
)
g.fillRoundRect(
x,
y,
175,
205,
18,
18
)
g.setColor(
new Color(
60,
155,
80
)
)
g.fillRoundRect(
x +
18,
y +
18,
138,
125,
20,
20
)
g.setColor(
new Color(
35,
35,
35
)
)
g.fillRect(
x +
78,
y +
18,
28,
124
)
/*
route
*/
g.setColor(
new Color(
245,
230,
80
)
)
g.setStroke(
new BasicStroke(
3.0f
)
)
g.drawLine(
x +
92,
y +
28,
x +
78,
y +
76
)
g.drawLine(
x +
78,
y +
76,
x +
102,
y +
112
)
g.drawLine(
x +
102,
y +
112,
x +
90,
y +
138
)
/*
player
*/
g.setColor(
Color.WHITE
)
g.fillOval(
x +
86,
y +
99,
10,
10
)
/*
destination
*/
g.setColor(
Color.RED
)
g.fillOval(
x +
101,
y +
43,
12,
12
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
13
)
)
g.drawString(
"GPS / MAP",
x +
48,
y +
164
)
g.setFont(
new Font(
"Arial",
Font.PLAIN,
11
)
)
g.drawString(
"DESTINATION",
x +
42,
y +
182
)
g.drawString(
"CHECKPOINT",
x +
47,
y +
198
)
}
/* =======================================================
TOUCH BUTTONS
======================================================= */
def drawTouchControls(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
drawControl(
g,
18,
h -
90,
96,
58,
"LEFT"
)
drawControl(
g,
123,
h -
90,
96,
58,
"BRAKE"
)
drawControl(
g,
w -
219,
h -
90,
96,
58,
"GAS"
)
drawControl(
g,
w -
114,
h -
90,
96,
58,
"RIGHT"
)
drawControl(
g,
18,
18,
90,
48,
"LIGHT"
)
drawControl(
g,
118,
18,
90,
48,
"BEAM"
)
drawControl(
g,
218,
18,
90,
48,
"NITRO"
)
drawControl(
g,
318,
18,
90,
48,
"HORN"
)
}
def drawControl(
g: Graphics2D,
x: Int,
y: Int,
w: Int,
h: Int,
text: String
): Unit = {
g.setColor(
new Color(
15,
20,
30,
205
)
)
g.fillRoundRect(
x,
y,
w,
h,
15,
15
)
g.setColor(
new Color(
255,
255,
255,
175
)
)
g.setStroke(
new BasicStroke(
2.0f
)
)
g.drawRoundRect(
x,
y,
w,
h,
15,
15
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
12
)
)
val tw =
g.getFontMetrics
.stringWidth(
text
)
g.drawString(
text,
x +
(
w -
tw
) /
2,
y +
h /
2 +
5
)
}
/* =======================================================
PAUSE
======================================================= */
def drawPause(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
0,
0,
0,
180
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
55
)
)
g.drawString(
"PAUSED",
getWidth /
2 -
110,
getHeight /
2
)
}
/* =======================================================
FLASH
======================================================= */
def drawFlash(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
0,
0,
0,
180
)
)
g.fillRoundRect(
getWidth /
2 -
180,
70,
360,
56,
18,
18
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
20
)
)
val tw =
g.getFontMetrics
.stringWidth(
flashMessage
)
g.drawString(
flashMessage,
getWidth /
2 -
tw /
2,
106
)
}
}
/* =========================================================
BUILD WINDOW
========================================================= */
frame =
new JFrame(
"REAL DRIVE X"
)
frame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
frame.setSize(
1280,
820
)
frame.setLocationRelativeTo(
null
)
cards =
new java.awt.CardLayout()
root =
new JPanel(
cards
)
menuPanel =
makeMenu()
lobbyPanel =
makeLobby()
gamePanel =
new DrivePanel()
root.add(
menuPanel,
"menu"
)
root.add(
lobbyPanel,
"lobby"
)
root.add(
gamePanel,
"game"
)
frame.setContentPane(
root
)
frame.setVisible(
true
)
showCard(
"menu"
)
/* =========================================================
MAIN TIMER
========================================================= */
val gameTimer =
new Timer(
33,
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
updateGame()
if (
gamePanel !=
null
) {
gamePanel.repaint()
}
if (
flashTime >
0L &&
flashTime <
System.currentTimeMillis()
) {
flashTime =
0L
}
if (
currentScreen ==
"game" &&
gamePanel !=
null
) {
gamePanel.requestFocusInWindow()
}
updateLobbyPlayers()
}
}
)
gameTimer.start()
/* =========================================================
WINDOW FOCUS
========================================================= */
frame.addWindowFocusListener(
new java.awt.event.WindowAdapter {
override def windowGainedFocus(
e: java.awt.event.WindowEvent
): Unit = {
if (
gamePanel !=
null &&
currentScreen ==
"game"
) {
gamePanel
.requestFocusInWindow()
}
}
override def windowClosing(
e: java.awt.event.WindowEvent
): Unit = {
exitApplication()
}
}
)