Code Sketch
yoiiii
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 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
========================================================= */
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
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 lobbyChatArea: JTextArea = null
var lobbyModeLabel: JLabel = null
var currentScreen = "menu"
var online = false
var multiplayer = false
var soloOnline = false
var ready = false
var raceStarted = false
var playerName = "Driver"
var lastHost = "127.0.0.1"
var lastPort = 5555
var serverSocket: ServerSocket = null
var serverRunning = false
var actualServerPort = 5555
var clientSocket: Socket = null
var clientOut: PrintWriter = null
var clientIn: BufferedReader = null
var clientId = "P0"
var clientConnected = false
/* =========================================================
DATA
========================================================= */
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
)
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
)
case class TrafficCar(
var lane: Int,
var z: Double,
var speed: Double,
var kind: String,
var police: Boolean
)
val serverClients =
ArrayBuffer[ServerClient]()
val remotePlayers =
ArrayBuffer[RemotePlayer]()
val traffic =
ArrayBuffer[TrafficCar]()
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 gear = "N"
var autoGear = true
var lights = false
var highBeam = false
var leftIndicator = false
var rightIndicator = false
var hazards = false
var rain = false
var wipers = true
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
/* =========================================================
GLOBAL ROAD HELPERS
IMPORTANT: laneToX is GLOBAL
========================================================= */
def laneToX(lane: Int): Double = {
if (lane == 0) {
-0.66
} else if (lane == 1) {
0.0
} else {
0.66
}
}
def laneCenterX(
width: Int,
lane: Int,
roadHalf: Double
): Double = {
width / 2.0 +
laneToX(lane) * roadHalf
}
/* =========================================================
GENERAL HELPERS
========================================================= */
def showCard(name: String): Unit = {
currentScreen = name
cards.show(
root,
name
)
}
def cleanText(s: String): String = {
if (s == null) {
""
} else {
s.replace("|", "/")
.replace("~", "-")
}
}
def boolText(v: Boolean): String = {
if (v) {
"ON"
} else {
"OFF"
}
}
def modeText(): String = {
if (!online) {
"OFFLINE SOLO"
} else if (multiplayer) {
"MULTIPLAYER"
} else {
"ONLINE SOLO"
}
}
def gearNumber(g: String): Int = {
if (g == "R") {
-1
} else if (g == "N") {
0
} else {
Try(g.toInt).getOrElse(0)
}
}
def addFlash(text: String): Unit = {
flashMessage = text
flashTime =
System.currentTimeMillis() + 2200L
playTone(
700,
70
)
}
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 < 35L) {
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 count =
Math.max(
1,
(
sampleRate *
duration.toDouble /
1000.0
).toInt
)
val buffer =
new Array[Byte](count)
var i = 0
while (i < count) {
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()
}
/* =========================================================
GEAR / CAR CONTROLS
========================================================= */
def toggleLights(): Unit = {
lights = !lights
if (lights) {
addFlash("HEADLIGHTS ON")
} else {
addFlash("HEADLIGHTS OFF")
}
}
def toggleHighBeam(): Unit = {
highBeam = !highBeam
if (highBeam) {
addFlash("HIGH BEAM ON")
} else {
addFlash("HIGH BEAM OFF")
}
}
def toggleAutoGear(): Unit = {
autoGear = !autoGear
if (autoGear) {
addFlash("AUTO GEAR")
} else {
addFlash("MANUAL GEAR")
}
}
def toggleRain(): Unit = {
rain = !rain
if (rain) {
addFlash("RAIN STARTED")
playTone(240, 120)
} else {
addFlash("RAIN STOPPED")
}
}
def shiftUp(): Unit = {
if (autoGear) {
return
}
if (gear == "N") {
gear = "1"
} else if (gear == "1") {
gear = "2"
} else if (gear == "2") {
gear = "3"
} else if (gear == "3") {
gear = "4"
} else if (gear == "4") {
gear = "5"
}
playTone(430, 55)
}
def manualReverse(): Unit = {
if (!autoGear) {
gear = "R"
playTone(300, 70)
}
}
/* =========================================================
SERVER
========================================================= */
def startServer(
requestedPort: Int
): Boolean = {
if (serverRunning) {
return true
}
var port = requestedPort
var opened: ServerSocket = null
while (
port < requestedPort + 25 &&
opened == null
) {
try {
val temp =
new ServerSocket()
temp.setReuseAddress(true)
temp.bind(
new java.net.InetSocketAddress(
"0.0.0.0",
port
)
)
opened = temp
} catch {
case _: Throwable =>
port += 1
}
}
if (opened == null) {
return false
}
serverSocket = opened
actualServerPort = port
serverRunning = true
addLobbyChat(
"[SERVER] Listening on " +
localIP() +
":" +
actualServerPort
)
val acceptThread =
new Thread(
new Runnable {
def run(): Unit = {
while (serverRunning) {
try {
val socket =
serverSocket.accept()
val clientThread =
new Thread(
new Runnable {
def run(): Unit = {
handleServerClient(
socket
)
}
}
)
clientThread.setDaemon(true)
clientThread.start()
} catch {
case _: Throwable =>
}
}
}
}
)
acceptThread.setDaemon(true)
acceptThread.start()
true
}
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
}
}
}
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()
}
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()
}
def handleServerClient(
socket: Socket
): Unit = {
var input: BufferedReader = null
var output: PrintWriter = null
var player: 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 h =
first.split("\\|", -1)
var name = "Driver"
if (h.length >= 2) {
name =
cleanText(h(1))
}
val id =
"P" +
Math.abs(
System.nanoTime()
).toString
.takeRight(6)
player =
ServerClient(
id,
name,
socket,
output,
0.0,
0.0,
0.0,
"N",
"ONLINE SOLO",
false
)
serverClients.synchronized {
serverClients += player
}
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) {
player.x =
Try(p(3).toDouble)
.getOrElse(0.0)
player.distance =
Try(p(4).toDouble)
.getOrElse(0.0)
player.speed =
Try(p(5).toDouble)
.getOrElse(0.0)
player.gear = p(6)
player.mode = p(7)
player.ready =
p(8) == "true"
}
broadcastPlayers()
}
if (p(0) == "CHAT") {
if (p.length >= 3) {
broadcastServer(
"CHAT|" +
cleanText(player.name) +
"|" +
cleanText(p(2))
)
}
}
if (p(0) == "READY") {
player.ready = true
broadcastServer(
"SYSTEM|" +
cleanText(player.name) +
" is READY."
)
broadcastPlayers()
}
if (p(0) == "NOTREADY") {
player.ready = false
broadcastPlayers()
}
if (p(0) == "MODE") {
if (p.length >= 2) {
player.mode = p(1)
}
broadcastPlayers()
}
if (p(0) == "START") {
broadcastServer(
"RACE_START"
)
}
if (p(0) == "LOBBY") {
player.mode = "LOBBY"
player.ready = false
broadcastPlayers()
}
}
}
}
} catch {
case _: Throwable =>
}
if (player != null) {
broadcastServer(
"SYSTEM|" +
cleanText(player.name) +
" left."
)
removeServerClient(
player.id
)
} else {
try {
socket.close()
} catch {
case _: Throwable =>
}
}
}
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 NETWORK
========================================================= */
def disconnectClient(): Unit = {
clientConnected = false
online = false
ready = false
raceStarted = false
try {
if (clientSocket != null) {
clientSocket.close()
}
} catch {
case _: Throwable =>
}
clientSocket = null
clientOut = null
clientIn = null
remotePlayers.synchronized {
remotePlayers.clear()
}
}
def connectToServer(
host: String,
port: Int
): Boolean = {
disconnectClient()
try {
val socket =
new Socket()
socket.connect(
new java.net.InetSocketAddress(
host,
port
),
3500
)
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
}
}
def sendClient(
message: String
): Unit = {
if (!clientConnected) {
return
}
try {
clientOut.println(message)
clientOut.flush()
} catch {
case _: Throwable =>
clientConnected = false
online = false
}
}
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"
)
}
}
}
}
)
}
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 ? ID " +
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
startGame()
}
}
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()
}
def sendGameState(): Unit = {
if (
!online ||
!clientConnected
) {
return
}
val now =
System.currentTimeMillis()
if (
now - lastNetworkSend < 80L
) {
return
}
lastNetworkSend = now
sendClient(
"STATE|" +
clientId +
"|" +
playerName +
"|" +
carX +
"|" +
gameDistance +
"|" +
gameSpeed +
"|" +
gear +
"|" +
modeText() +
"|" +
ready
)
}
def sendChat(
text: String
): Unit = {
if (
text.trim.isEmpty
) {
return
}
if (clientConnected) {
sendClient(
"CHAT|" +
clientId +
"|" +
cleanText(text)
)
} else {
addLobbyChat(
"LOCAL: " +
text
)
}
}
/* =========================================================
LOBBY UPDATE
========================================================= */
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 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
========================================================= */
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
gear = "N"
lights = false
highBeam = false
paused = false
gameRunning = true
traffic.clear()
var i = 0
while (i < 12) {
traffic +=
TrafficCar(
i % 3,
0.15 + i.toDouble * 0.09,
35.0 +
(i % 5) * 10.0,
getTrafficKind(i),
i % 8 == 0
)
i += 1
}
playTone(
820,
120
)
}
def getTrafficKind(
i: Int
): String = {
if (i % 5 == 0) {
"TRUCK"
} else if (i % 3 == 0) {
"BUS"
} else {
"CAR"
}
}
def startGame(): Unit = {
resetGame()
showCard("game")
if (
gamePanel != null
) {
gamePanel.requestFocusInWindow()
}
}
def updateGame(): Unit = {
if (
!gameRunning ||
paused
) {
return
}
val now =
System.currentTimeMillis()
val dtRaw =
(
now - lastTick
).toDouble / 1000.0
val dt =
Math.min(
0.06,
dtRaw
)
lastTick = now
updateControls(dt)
updateTraffic(dt)
detectTrafficCollision()
updateMission()
timePhase =
(
gameDistance /
2500.0
).toInt % 4
sendGameState()
}
def updateControls(
dt: Double
): Unit = {
val left =
keys.contains(KeyEvent.VK_A) ||
keys.contains(KeyEvent.VK_LEFT)
val right =
keys.contains(KeyEvent.VK_D) ||
keys.contains(KeyEvent.VK_RIGHT)
val up =
keys.contains(KeyEvent.VK_W) ||
keys.contains(KeyEvent.VK_UP)
val down =
keys.contains(KeyEvent.VK_S) ||
keys.contains(KeyEvent.VK_DOWN)
if (left) {
steering -=
2.7 * dt
} else if (right) {
steering +=
2.7 * dt
} else {
if (steering > 0.0) {
steering -= 1.5 * dt
}
if (steering < 0.0) {
steering += 1.5 * dt
}
}
if (steering > 1.0) {
steering = 1.0
}
if (steering < -1.0) {
steering = -1.0
}
val gas =
throttle || up
val brake =
braking || down
var acceleration = 0.0
val currentGear =
gearNumber(gear)
if (gas) {
if (currentGear > 0) {
acceleration =
30.0 +
currentGear *
7.0
}
if (gear == "R") {
acceleration = -25.0
}
}
if (brake) {
acceleration -= 65.0
}
if (
nitro &&
fuel > 2.0 &&
gameSpeed > 10.0
) {
acceleration += 75.0
fuel -= 6.0 * dt
}
gameSpeed +=
acceleration * dt
if (!gas && !brake) {
if (gameSpeed > 0.0) {
gameSpeed -= 8.0 * dt
}
if (gameSpeed < 0.0) {
gameSpeed += 8.0 * dt
}
}
if (gameSpeed > 180.0) {
gameSpeed = 180.0
}
if (gameSpeed < -40.0) {
gameSpeed = -40.0
}
if (autoGear) {
autoShift()
}
carX +=
steering *
(
0.08 +
Math.abs(gameSpeed) *
0.0012
) *
dt
if (carX < -1.0) {
carX = -1.0
}
if (carX > 1.0) {
carX = 1.0
}
val forwardSpeed =
Math.max(
0.0,
gameSpeed
)
gameDistance +=
forwardSpeed *
dt *
0.12
fuel -=
(
0.5 +
forwardSpeed *
0.0035
) *
dt
if (fuel < 0.0) {
fuel = 0.0
}
if (fuel <= 0.0) {
gameSpeed -=
15.0 * dt
}
score +=
(
forwardSpeed *
dt *
0.12
).toInt
}
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 < 115.0) {
gear = "4"
} else {
gear = "5"
}
}
/* =========================================================
TRAFFIC
========================================================= */
def updateTraffic(
dt: Double
): Unit = {
var i = 0
while (i < traffic.length) {
val car =
traffic(i)
car.z +=
(
car.speed -
gameSpeed
) *
dt *
0.0015
if (car.z > 1.25) {
car.z = -0.15
car.lane =
Math.abs(
(
System.nanoTime() /
1000L
).toInt
) % 3
}
if (car.z < -0.4) {
car.z = 1.05
}
i += 1
}
val now =
System.currentTimeMillis()
if (
now - lastTrafficSpawn >
2300L
) {
lastTrafficSpawn = now
val lane =
Math.abs(
(
System.nanoTime() /
1000L
).toInt
) % 3
traffic +=
TrafficCar(
lane,
-0.15,
40.0 +
(
Math.abs(
(
System.nanoTime() /
1000000L
) % 80L
).toDouble
),
"CAR",
false
)
if (traffic.length > 20) {
traffic.remove(0)
}
}
}
/* =========================================================
COLLISION
========================================================= */
def detectTrafficCollision(): Unit = {
var i = 0
while (i < traffic.length) {
val car =
traffic(i)
if (
car.z > 0.82 &&
car.z < 1.02
) {
val trafficX =
laneToX(
car.lane
)
val difference =
Math.abs(
trafficX -
carX
)
if (difference < 0.22) {
val impact =
Math.abs(
gameSpeed -
car.speed
)
damage +=
4.0 +
impact *
0.07
score -= 25
if (score < 0) {
score = 0
}
car.z = -0.25
playTone(
115,
170
)
if (car.police) {
score -= 75
if (score < 0) {
score = 0
}
addFlash(
"POLICE FINE!"
)
} else {
addFlash(
"COLLISION!"
)
}
}
}
i += 1
}
if (damage > 100.0) {
damage = 100.0
gameSpeed *= 0.55
addFlash("VEHICLE DAMAGED")
}
}
/* =========================================================
MISSION
========================================================= */
def updateMission(): Unit = {
missionProgress =
Math.min(
missionTarget.toInt,
gameDistance.toInt
)
if (
gameDistance >=
missionTarget
) {
missionNumber += 1
missionTarget +=
1000.0 +
missionNumber * 300.0
score += 1000
playTone(
1000,
150
)
addFlash(
"CHECKPOINT +" +
missionNumber
)
}
}
/* =========================================================
NAVIGATION
========================================================= */
def startMultiplayer(): Unit = {
if (!clientConnected) {
addFlash(
"NOT CONNECTED"
)
return
}
multiplayer = true
soloOnline = false
ready = true
sendClient(
"MODE|MULTIPLAYER"
)
sendClient(
"READY"
)
sendClient(
"START"
)
startGame()
}
def startOnlineSolo(): Unit = {
if (!clientConnected) {
addFlash(
"NOT CONNECTED"
)
return
}
multiplayer = false
soloOnline = true
ready = true
sendClient(
"MODE|ONLINE SOLO"
)
sendClient(
"READY"
)
startGame()
}
def leaveToLobby(): Unit = {
gameRunning = false
throttle = false
braking = false
nitro = false
horn = false
if (clientConnected) {
sendClient(
"LOBBY"
)
ready = false
}
showCard("lobby")
updateLobbyPlayers()
}
def returnToMenu(): Unit = {
gameRunning = false
sendClient(
"LOBBY"
)
disconnectClient()
stopServer()
multiplayer = false
soloOnline = false
ready = false
showCard("menu")
}
def exitApplication(): Unit = {
try {
disconnectClient()
} catch {
case _: Throwable =>
}
try {
stopServer()
} catch {
case _: Throwable =>
}
try {
frame.dispose()
} catch {
case _: Throwable =>
}
}
/* =========================================================
BUTTON
========================================================= */
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
}
/* =========================================================
MAIN MENU
========================================================= */
def makeMenu(): JPanel = {
val panel =
new JPanel()
panel.setLayout(
new BoxLayout(
panel,
BoxLayout.Y_AXIS
)
)
panel.setBorder(
BorderFactory
.createEmptyBorder(
35,
80,
35,
80
)
)
val title =
new JLabel(
"REAL DRIVE X"
)
title.setFont(
new Font(
"Arial",
Font.BOLD,
42
)
)
title.setAlignmentX(
0.5f
)
panel.add(title)
val subtitle =
new JLabel(
"ONLINE ? MULTIPLAYER ? SOLO ? TRAFFIC"
)
subtitle.setAlignmentX(
0.5f
)
panel.add(subtitle)
panel.add(
javax.swing.Box
.createVerticalStrut(30)
)
val host =
makeButton(
"HOST ONLINE",
330,
55
)
host.setAlignmentX(
0.5f
)
host.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
val enteredName =
JOptionPane.showInputDialog(
frame,
"Driver name:",
"Driver"
)
if (
enteredName != null &&
enteredName.trim.nonEmpty
) {
playerName =
cleanText(
enteredName.trim
)
}
val enteredPort =
JOptionPane.showInputDialog(
frame,
"Server port:",
"5555"
)
val port =
Try(
Option(
enteredPort
).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 {
lobbyStatusLabel.setText(
"LOCAL HOST CONNECTION FAILED"
)
}
} else {
JOptionPane.showMessageDialog(
frame,
"Could not open server port."
)
}
}
}
)
panel.add(host)
panel.add(
javax.swing.Box
.createVerticalStrut(12)
)
val join =
makeButton(
"JOIN ONLINE",
330,
55
)
join.setAlignmentX(
0.5f
)
join.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
val enteredName =
JOptionPane.showInputDialog(
frame,
"Driver name:",
"Driver"
)
if (
enteredName != null &&
enteredName.trim.nonEmpty
) {
playerName =
cleanText(
enteredName.trim
)
}
val host =
JOptionPane.showInputDialog(
frame,
"Host IP:",
lastHost
)
if (
host == null ||
host.trim.isEmpty
) {
return
}
val enteredPort =
JOptionPane.showInputDialog(
frame,
"Port:",
lastPort.toString
)
val port =
Try(
Option(
enteredPort
).getOrElse(
lastPort.toString
).toInt
).getOrElse(
lastPort
)
lobbyStatusLabel.setText(
"CONNECTING..."
)
val connected =
connectToServer(
host.trim,
port
)
if (connected) {
lobbyStatusLabel.setText(
"CONNECTED TO " +
host.trim +
":" +
port
)
showCard("lobby")
} else {
lobbyStatusLabel.setText(
"CONNECTION FAILED"
)
JOptionPane.showMessageDialog(
frame,
"Connection failed.\nCheck host IP, port and firewall."
)
}
}
}
)
panel.add(join)
panel.add(
javax.swing.Box
.createVerticalStrut(12)
)
val offline =
makeButton(
"OFFLINE SOLO",
330,
55
)
offline.setAlignmentX(
0.5f
)
offline.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
online = false
multiplayer = false
soloOnline = false
startGame()
}
}
)
panel.add(offline)
panel.add(
javax.swing.Box
.createVerticalStrut(12)
)
val exit =
makeButton(
"EXIT",
330,
50
)
exit.setAlignmentX(
0.5f
)
exit.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
exitApplication()
}
}
)
panel.add(exit)
panel
}
/* =========================================================
LOBBY
========================================================= */
def makeLobby(): JPanel = {
val panel =
new JPanel()
panel.setLayout(
new BoxLayout(
panel,
BoxLayout.Y_AXIS
)
)
panel.setBorder(
BorderFactory
.createEmptyBorder(
20,
30,
20,
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(
"Choose an online mode."
)
lobbyModeLabel.setAlignmentX(
0.5f
)
panel.add(
lobbyModeLabel
)
panel.add(
javax.swing.Box
.createVerticalStrut(10)
)
val form =
new JPanel()
form.add(
new JLabel("NAME")
)
lobbyNameField =
new JTextField(
"Driver",
12
)
form.add(
lobbyNameField
)
form.add(
new JLabel("HOST")
)
lobbyHostField =
new JTextField(
"127.0.0.1",
12
)
form.add(
lobbyHostField
)
form.add(
new JLabel("PORT")
)
lobbyPortField =
new JTextField(
"5555",
7
)
form.add(
lobbyPortField
)
panel.add(form)
val modeRow =
new JPanel()
val solo =
makeButton(
"ONLINE SOLO",
175,
45
)
solo.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
playerName =
cleanText(
lobbyNameField.getText
)
if (
playerName.isEmpty
) {
playerName = "Driver"
}
startOnlineSolo()
}
}
)
modeRow.add(solo)
val multi =
makeButton(
"MULTIPLAYER",
175,
45
)
multi.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
playerName =
cleanText(
lobbyNameField.getText
)
if (
playerName.isEmpty
) {
playerName = "Driver"
}
startMultiplayer()
}
}
)
modeRow.add(multi)
val readyButton =
makeButton(
"READY",
120,
45
)
readyButton.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
ready = true
sendClient(
"READY"
)
addLobbyChat(
"[LOCAL] READY"
)
}
}
)
modeRow.add(
readyButton
)
panel.add(modeRow)
val chatLabel =
new JLabel(
"CHAT"
)
chatLabel.setAlignmentX(
0.5f
)
panel.add(chatLabel)
lobbyChatArea =
new JTextArea(
10,
62
)
lobbyChatArea.setEditable(false)
panel.add(
new JScrollPane(
lobbyChatArea
)
)
val chatRow =
new JPanel()
lobbyChatField =
new JTextField(
38
)
val sendButton =
makeButton(
"SEND",
100,
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 bottom =
new JPanel()
val retry =
makeButton(
"RETRY",
120,
42
)
retry.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
lobbyStatusLabel.setText(
"RECONNECTING..."
)
val host =
lobbyHostField
.getText
.trim
val port =
Try(
lobbyPortField
.getText
.trim
.toInt
).getOrElse(
5555
)
val connected =
connectToServer(
host,
port
)
if (connected) {
lobbyStatusLabel.setText(
"CONNECTED"
)
} else {
lobbyStatusLabel.setText(
"RECONNECT FAILED"
)
}
}
}
)
bottom.add(retry)
val menu =
makeButton(
"BACK TO MENU",
160,
42
)
menu.addActionListener(
new ActionListener {
def actionPerformed(
e: java.awt.event.ActionEvent
): Unit = {
returnToMenu()
}
}
)
bottom.add(menu)
panel.add(bottom)
panel
}
/* =========================================================
DRIVE PANEL
========================================================= */
class DrivePanel
extends JPanel {
setFocusable(true)
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_R
) {
manualReverse()
}
if (
k == KeyEvent.VK_N
) {
if (!autoGear) {
gear = "N"
}
}
if (
k == KeyEvent.VK_1
) {
if (!autoGear) {
gear = "1"
}
}
if (
k == KeyEvent.VK_2
) {
if (!autoGear) {
gear = "2"
}
}
if (
k == KeyEvent.VK_3
) {
if (!autoGear) {
gear = "3"
}
}
if (
k == KeyEvent.VK_4
) {
if (!autoGear) {
gear = "4"
}
}
if (
k == KeyEvent.VK_5
) {
if (!autoGear) {
gear = "5"
}
}
if (
k == KeyEvent.VK_P
) {
paused = !paused
}
if (
k == KeyEvent.VK_T
) {
toggleRain()
}
}
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 h =
getHeight
val w =
getWidth
if (
y > h - 95
) {
if (
x < 120
) {
steering = -1.0
return
} else if (
x < 240
) {
braking = true
return
} else if (
x > w - 240 &&
x < w - 120
) {
throttle = true
return
} else if (
x > w - 120
) {
steering = 1.0
return
}
}
if (
y < 80
) {
if (
x < 100
) {
toggleLights()
return
} else if (
x < 205
) {
toggleHighBeam()
return
} else if (
x < 315
) {
nitro = true
return
} else if (
x < 425
) {
horn = true
playTone(
560,
120
)
return
}
}
}
override def mouseReleased(
e: MouseEvent
): Unit = {
throttle = false
braking = false
nitro = false
horn = false
steering = 0.0
}
}
)
/* =======================================================
DRAW
======================================================= */
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)
drawRemoteCars(g)
drawRain(g)
drawCockpit(g)
drawHud(g)
drawControls(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,
230
)
var bottom =
new Color(
200,
230,
250
)
if (timePhase == 1) {
top =
new Color(
235,
125,
75
)
bottom =
new Color(
255,
215,
145
)
}
if (timePhase == 2) {
top =
new Color(
10,
18,
45
)
bottom =
new Color(
50,
60,
100
)
}
if (timePhase == 3) {
top =
new Color(
110,
65,
125
)
bottom =
new Color(
210,
120,
130
)
}
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 < 70) {
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,
185
)
)
g.fillOval(
x,
y,
2 + i % 3,
2 + i % 3
)
i += 1
}
}
def drawSunMoon(
g: Graphics2D
): Unit = {
if (
timePhase == 2
) {
g.setColor(
new Color(
235,
240,
255
)
)
g.fillOval(
getWidth - 145,
55,
72,
72
)
} else {
g.setColor(
new Color(
255,
220,
90
)
)
g.fillOval(
getWidth - 155,
50,
88,
88
)
}
}
/* =======================================================
ROAD
======================================================= */
def drawRoad(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
val horizon =
h / 3
g.setColor(
new Color(
55,
105,
55
)
)
g.fillRect(
0,
horizon,
w,
h - horizon
)
val topY =
horizon - 20
val bottomY =
h
val topLeft =
w / 2 - 85
val topRight =
w / 2 + 85
val bottomLeft =
w / 2 - 270
val bottomRight =
w / 2 + 270
val polygon =
new java.awt.Polygon()
polygon.addPoint(
topLeft,
topY
)
polygon.addPoint(
topRight,
topY
)
polygon.addPoint(
bottomRight,
bottomY
)
polygon.addPoint(
bottomLeft,
bottomY
)
g.setColor(
new Color(
45,
46,
50
)
)
g.fillPolygon(
polygon
)
g.setColor(
new Color(
235,
230,
210
)
)
g.setStroke(
new BasicStroke(
7.0f
)
)
g.drawLine(
topLeft,
topY,
bottomLeft,
bottomY
)
g.drawLine(
topRight,
topY,
bottomRight,
bottomY
)
drawLane(
g,
topLeft,
topRight,
bottomLeft,
bottomRight,
topY,
bottomY,
1.0 / 3.0
)
drawLane(
g,
topLeft,
topRight,
bottomLeft,
bottomRight,
topY,
bottomY,
2.0 / 3.0
)
drawRoadside(
g,
horizon
)
}
def drawLane(
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 < 10) {
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 =
interpolateX(
tl,
tr,
bl,
br,
topY,
bottomY,
y1,
fraction
)
val x2 =
interpolateX(
tl,
tr,
bl,
br,
topY,
bottomY,
y2,
fraction
)
g.setStroke(
new BasicStroke(
(
2.0 +
i * 0.7
).toFloat
)
)
g.drawLine(
x1,
y1,
x2,
y2
)
}
i += 1
}
}
def interpolateX(
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
}
def drawRoadside(
g: Graphics2D,
horizon: Int
): Unit = {
var i = 0
while (i < 9) {
val y =
horizon +
25 +
i * 58
val left =
60 +
i * 14
val right =
getWidth -
75 -
i * 14
g.setColor(
new Color(
35,
90,
40
)
)
g.fillRect(
left,
y,
8,
45
)
g.fillOval(
left - 18,
y - 25,
45,
38
)
g.fillRect(
right,
y,
8,
45
)
g.fillOval(
right - 18,
y - 25,
45,
38
)
i += 1
}
}
/* =======================================================
TRAFFIC DRAW
======================================================= */
def drawTraffic(
g: Graphics2D
): Unit = {
var i = 0
while (
i < traffic.length
) {
drawTrafficCar(
g,
traffic(i)
)
i += 1
}
}
def drawTrafficCar(
g: Graphics2D,
car: TrafficCar
): Unit = {
val w =
getWidth
val h =
getHeight
val horizon =
h / 3
val z =
Math.max(
0.0,
Math.min(
1.0,
car.z
)
)
val y =
horizon +
(
h - horizon
) *
z
val roadHalf =
90.0 +
170.0 *
z
val cx =
w / 2.0 +
laneToX(
car.lane
) *
roadHalf
val cw =
20.0 +
48.0 *
z
val ch =
16.0 +
66.0 *
z
val x =
cx -
cw / 2.0
val top =
y -
ch / 2.0
var body =
new Color(
220,
35,
45
)
if (
car.kind == "TRUCK"
) {
body =
new Color(
190,
130,
55
)
}
if (
car.kind == "BUS"
) {
body =
new Color(
55,
110,
210
)
}
if (
car.police
) {
body =
new Color(
235,
235,
240
)
}
g.setColor(body)
g.fillRoundRect(
x.toInt,
top.toInt,
cw.toInt,
ch.toInt,
10,
10
)
g.setColor(
new Color(
30,
45,
60
)
)
g.fillRect(
(
x +
cw * 0.18
).toInt,
(
top +
ch * 0.25
).toInt,
(
cw * 0.64
).toInt,
(
ch * 0.27
).toInt
)
if (
car.police
) {
g.setColor(
Color.RED
)
g.fillRect(
(
x +
cw * 0.18
).toInt,
(
top +
ch * 0.12
).toInt,
(
cw * 0.27
).toInt,
(
ch * 0.12
).toInt
)
g.setColor(
Color.BLUE
)
g.fillRect(
(
x +
cw * 0.55
).toInt,
(
top +
ch * 0.12
).toInt,
(
cw * 0.27
).toInt,
(
ch * 0.12
).toInt
)
}
}
/* =======================================================
REMOTE PLAYERS
======================================================= */
def drawRemoteCars(
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) < 700.0
) {
val z =
Math.max(
0.20,
Math.min(
1.0,
0.62 +
relative /
1400.0
)
)
val roadHalf =
90.0 +
170.0 *
z
val cx =
getWidth / 2.0 +
p.x *
roadHalf
val y =
getHeight / 3 +
(
getHeight -
getHeight / 3
) *
z
val cw =
25.0 +
35.0 *
z
val ch =
34.0 +
45.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(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
11
)
)
g.drawString(
p.name,
(
cx -
25
).toInt,
(
y -
ch / 2.0 -
6
).toInt
)
}
i += 1
}
}
}
/* =======================================================
RAIN
======================================================= */
def drawRain(
g: Graphics2D
): Unit = {
if (!rain) {
return
}
g.setColor(
new Color(
180,
210,
255,
125
)
)
var i = 0
while (i < 130) {
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 + 17
)
i += 1
}
}
/* =======================================================
COCKPIT
======================================================= */
def drawCockpit(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
g.setColor(
new Color(
15,
18,
23,
220
)
)
g.fillRoundRect(
w / 2 - 245,
h - 165,
490,
150,
28,
28
)
g.setColor(
new Color(
5,
8,
10
)
)
g.fillOval(
w / 2 - 125,
h - 130,
105,
105
)
g.fillOval(
w / 2 + 25,
h - 130,
105,
105
)
g.setColor(
Color.WHITE
)
g.setStroke(
new BasicStroke(
7.0f
)
)
g.drawOval(
w / 2 - 125,
h - 130,
105,
105
)
g.drawOval(
w / 2 + 25,
h - 130,
105,
105
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
15
)
)
g.drawString(
"KM/H",
w / 2 - 106,
h - 40
)
g.drawString(
"GEAR",
w / 2 + 50,
h - 105
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
28
)
)
g.drawString(
Math.abs(
gameSpeed
).toInt.toString,
w / 2 - 104,
h - 70
)
g.drawString(
gear,
w / 2 + 67,
h - 65
)
}
/* =======================================================
HUD
======================================================= */
def drawHud(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
8,
12,
20,
220
)
)
g.fillRoundRect(
15,
95,
285,
205,
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
)
drawGPS(g)
}
/* =======================================================
GPS
======================================================= */
def drawGPS(
g: Graphics2D
): Unit = {
val x =
getWidth - 185
val y =
95
g.setColor(
new Color(
8,
18,
22,
235
)
)
g.fillRoundRect(
x,
y,
165,
185,
18,
18
)
g.setColor(
new Color(
60,
155,
80
)
)
g.fillRoundRect(
x + 18,
y + 18,
129,
118,
18,
18
)
g.setColor(
new Color(
35,
35,
35
)
)
g.fillRect(
x + 76,
y + 18,
25,
116
)
g.setColor(
Color.WHITE
)
g.fillOval(
x + 85,
y + 88,
9,
9
)
g.setColor(
Color.RED
)
g.fillOval(
x + 100,
y + 48,
11,
11
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
13
)
)
g.drawString(
"GPS / MAP",
x + 42,
y + 157
)
g.setFont(
new Font(
"Arial",
Font.PLAIN,
11
)
)
g.drawString(
"DESTINATION",
x + 39,
y + 176
)
}
/* =======================================================
TOUCH BUTTONS
======================================================= */
def drawControls(
g: Graphics2D
): Unit = {
val w =
getWidth
val h =
getHeight
drawControlButton(
g,
18,
h - 88,
92,
58,
"LEFT"
)
drawControlButton(
g,
122,
h - 88,
92,
58,
"BRAKE"
)
drawControlButton(
g,
w - 214,
h - 88,
92,
58,
"GAS"
)
drawControlButton(
g,
w - 110,
h - 88,
92,
58,
"RIGHT"
)
drawControlButton(
g,
18,
18,
88,
48,
"LIGHT"
)
drawControlButton(
g,
116,
18,
88,
48,
"BEAM"
)
drawControlButton(
g,
214,
18,
88,
48,
"NITRO"
)
drawControlButton(
g,
312,
18,
88,
48,
"HORN"
)
}
def drawControlButton(
g: Graphics2D,
x: Int,
y: Int,
w: Int,
h: Int,
text: String
): Unit = {
g.setColor(
new Color(
15,
20,
30,
200
)
)
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 textWidth =
g.getFontMetrics
.stringWidth(text)
g.drawString(
text,
x +
(
w -
textWidth
) / 2,
y +
h / 2 +
5
)
}
/* =======================================================
PAUSE
======================================================= */
def drawPause(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
0,
0,
0,
175
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
54
)
)
g.drawString(
"PAUSED",
getWidth / 2 - 105,
getHeight / 2
)
}
/* =======================================================
FLASH
======================================================= */
def drawFlash(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
0,
0,
0,
175
)
)
g.fillRoundRect(
getWidth / 2 - 170,
72,
340,
55,
17,
17
)
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,
107
)
}
}
/* =========================================================
CREATE UI
========================================================= */
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"
)
/* =========================================================
GAME TIMER
========================================================= */
val gameTimer =
new javax.swing.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
}
}
}
)
gameTimer.start()
/* =========================================================
WINDOW FOCUS
========================================================= */
frame.addWindowFocusListener(
new java.awt.event.WindowAdapter {
override def windowGainedFocus(
e: java.awt.event.WindowEvent
): Unit = {
if (
gamePanel != null
) {
gamePanel.requestFocusInWindow()
}
}
override def windowClosing(
e: java.awt.event.WindowEvent
): Unit = {
exitApplication()
}
}
)