Code Sketch
yoiiiiiiii
Category: Programming
import javax.swing._
import java.awt._
import java.awt.event._
import java.io._
import java.net._
import java.util.concurrent._
import scala.collection.mutable
import scala.util.Random
val PORT = 5555
// ============================================================
// DATA
// ============================================================
case class PlayerData(
var name: String,
var x: Double,
var distance: Double,
var speed: Double,
var gear: String,
var racing: Boolean,
var ready: Boolean
)
case class TrafficCar(
var lane: Int,
var z: Double,
var speed: Double,
var kind: Int,
var body: Color
)
// ============================================================
// ONLINE SERVER
// ============================================================
class DriveServer {
case class Client(
id: Int,
socket: Socket,
in: BufferedReader,
out: PrintWriter,
var name: String = "Player",
var x: Double = 0.0,
var distance: Double = 0.0,
var speed: Double = 0.0,
var gear: String = "N",
var racing: Boolean = false,
var ready: Boolean = false
)
val clients =
new ConcurrentHashMap[Int, Client]()
val pool =
Executors.newCachedThreadPool()
var server: ServerSocket = null
@volatile var running = false
var nextId = 9000
def start(): Unit = synchronized {
if (running)
return
try {
server =
new ServerSocket(PORT)
running = true
val thread =
new Thread(
new Runnable {
def run(): Unit = {
while (running) {
try {
val s =
server.accept()
s.setTcpNoDelay(true)
nextId += 1
val id =
nextId
val in =
new BufferedReader(
new InputStreamReader(
s.getInputStream
)
)
val out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
s.getOutputStream
)
),
true
)
val client =
Client(
id,
s,
in,
out
)
clients.put(
id,
client
)
out.println(
"WELCOME|" +
id
)
broadcastPlayers()
pool.submit(
new Runnable {
def run(): Unit =
readClient(client)
}
)
} catch {
case _: Throwable =>
}
}
}
},
"DRIVE_SERVER"
)
thread.setDaemon(true)
thread.start()
} catch {
case _: Throwable =>
running = false
}
}
def safeName(
s: String
): String = {
val n =
Option(s)
.getOrElse("Player")
.trim
.replace("|", " ")
if (n.isEmpty)
"Player"
else
n.take(18)
}
def safeText(
s: String
): String = {
Option(s)
.getOrElse("")
.replace("\n", " ")
.replace("\r", " ")
.replace("|", "/")
.take(120)
}
def readClient(
c: Client
): Unit = {
try {
var line: String = null
while ({
line = c.in.readLine()
line != null
}) {
val p =
line.split("\\|", -1)
if (p.length > 0) {
if (
p(0) == "HELLO" &&
p.length >= 2
) {
c.name =
safeName(p(1))
broadcastPlayers()
} else if (
p(0) == "STATE" &&
p.length >= 7
) {
try {
c.x =
p(1).toDouble
c.distance =
p(2).toDouble
c.speed =
p(3).toDouble
c.gear =
p(4)
c.racing =
p(5) == "1"
c.ready =
p(6) == "1"
broadcastPlayer(c)
} catch {
case _: Throwable =>
}
} else if (
p(0) == "CHAT" &&
p.length >= 2
) {
val txt =
safeText(
p.drop(1)
.mkString("|")
)
broadcast(
"CHAT|" +
c.id +
"|" +
c.name +
"|" +
txt
)
} else if (
p(0) == "RACE"
) {
c.racing = true
c.ready = true
broadcastPlayers()
}
}
}
} catch {
case _: Throwable =>
}
clients.remove(
c.id
)
broadcast(
"LEAVE|" +
c.id
)
broadcastPlayers()
try {
c.socket.close()
} catch {
case _: Throwable =>
}
}
def broadcastPlayer(
c: Client
): Unit = {
broadcast(
"PLAYER|" +
c.id +
"|" +
c.name +
"|" +
c.x +
"|" +
c.distance +
"|" +
c.speed +
"|" +
c.gear +
"|" +
(if (c.racing) "1" else "0") +
"|" +
(if (c.ready) "1" else "0")
)
}
def broadcastPlayers(): Unit = {
val it =
clients.values().iterator()
while (it.hasNext) {
broadcastPlayer(
it.next()
)
}
}
def broadcast(
msg: String
): Unit = {
val it =
clients.values().iterator()
while (it.hasNext) {
val c =
it.next()
try {
c.out.println(
msg
)
} catch {
case _: Throwable =>
}
}
}
}
// ============================================================
// GLOBAL SERVER
// ============================================================
val driveServer =
new DriveServer
driveServer.start()
// ============================================================
// MAIN WINDOW
// ============================================================
class DriveWindow
extends JFrame {
setTitle(
"REALISTIC DRIVE ONLINE"
)
setSize(
1280,
820
)
setLocationRelativeTo(
null
)
setResizable(false)
setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
val cards =
new CardLayout
val root =
new JPanel(cards)
val menu =
new StartScreen
val lobby =
new OnlineLobby
val cockpit =
new CockpitScreen
root.add(
menu,
"MENU"
)
root.add(
lobby,
"LOBBY"
)
root.add(
cockpit,
"GAME"
)
add(root)
menu.hostPressed =
(name: String) => {
cockpit.startPlayer(
name,
"127.0.0.1",
true
)
lobby.configure(
name,
"127.0.0.1"
)
cards.show(
root,
"LOBBY"
)
lobby.requestFocusInWindow()
}
menu.joinPressed =
(name: String, host: String) => {
lobby.configure(
name,
host
)
cards.show(
root,
"LOBBY"
)
lobby.connect(
name,
host
)
lobby.requestFocusInWindow()
}
menu.offlinePressed =
(name: String) => {
cockpit.startPlayer(
name,
"",
false
)
cards.show(
root,
"GAME"
)
cockpit.requestFocusInWindow()
}
lobby.playPressed =
() => {
cockpit.startPlayer(
lobby.playerName,
lobby.serverHost,
lobby.serverHost.nonEmpty
)
cards.show(
root,
"GAME"
)
cockpit.requestFocusInWindow()
}
lobby.exitPressed =
() => {
lobby.disconnect()
cards.show(
root,
"MENU"
)
menu.requestFocusInWindow()
}
setVisible(
true
)
}
// ============================================================
// START SCREEN
// ============================================================
class StartScreen
extends JPanel {
var hostPressed:
String => Unit =
_ => ()
var joinPressed:
(String, String) => Unit =
(_, _) => ()
var offlinePressed:
String => Unit =
_ => ()
val nameField =
new JTextField(
"Player"
)
val ipField =
new JTextField(
"127.0.0.1"
)
setLayout(
null
)
setBackground(
new Color(
5,
10,
18
)
)
val title =
new JLabel(
"REALISTIC DRIVE",
SwingConstants.CENTER
)
title.setForeground(
new Color(
0,
235,
255
)
)
title.setFont(
new Font(
"Arial",
Font.BOLD,
58
)
)
title.setBounds(
250,
70,
780,
70
)
add(title)
val sub =
new JLabel(
"ONLINE MULTIPLAYER ? COCKPIT ? CHAT ? RACE",
SwingConstants.CENTER
)
sub.setForeground(
Color.WHITE
)
sub.setFont(
new Font(
"Arial",
Font.PLAIN,
17
)
)
sub.setBounds(
300,
140,
680,
35
)
add(sub)
addLabel(
"YOUR NAME",
390,
225
)
nameField.setBounds(
520,
215,
300,
40
)
add(nameField)
addLabel(
"SERVER IP",
390,
280
)
ipField.setBounds(
520,
270,
300,
40
)
add(ipField)
val host =
createButton(
"HOST + PLAY ONLINE",
390,
335
)
host.addActionListener(
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit = {
hostPressed(
name()
)
}
}
)
add(host)
val join =
createButton(
"JOIN ONLINE SERVER",
390,
400
)
join.addActionListener(
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit = {
joinPressed(
name(),
ipField.getText.trim
)
}
}
)
add(join)
val offline =
createButton(
"OFFLINE DRIVE",
390,
465
)
offline.addActionListener(
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit = {
offlinePressed(
name()
)
}
}
)
add(offline)
val info =
new JLabel(
"<html><center>" +
"1. HOST PC ? HOST + PLAY ONLINE<br>" +
"2. OTHER PC ? enter HOST PC IP ? JOIN ONLINE SERVER<br>" +
"3. After joining, lobby shows player names and status<br>" +
"4. CHAT = messages ? RACE = start race" +
"</center></html>",
SwingConstants.CENTER
)
info.setForeground(
new Color(
190,
205,
215
)
)
info.setFont(
new Font(
"Arial",
Font.PLAIN,
13
)
)
info.setBounds(
330,
545,
620,
105
)
add(info)
def name(): String = {
val n =
nameField
.getText
.trim
.replace("|", " ")
if (n.isEmpty)
"Player"
else
n.take(18)
}
def addLabel(
text: String,
x: Int,
y: Int
): Unit = {
val l =
new JLabel(
text
)
l.setForeground(
Color.WHITE
)
l.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
l.setBounds(
x,
y,
120,
30
)
add(l)
}
def createButton(
text: String,
x: Int,
y: Int
): JButton = {
val b =
new JButton(
text
)
b.setBounds(
x,
y,
500,
52
)
b.setFont(
new Font(
"Arial",
Font.BOLD,
17
)
)
b.setForeground(
Color.WHITE
)
b.setBackground(
new Color(
15,
48,
62
)
)
b.setFocusPainted(
false
)
b
}
}
// ============================================================
// ONLINE LOBBY
// ============================================================
class OnlineLobby
extends JPanel {
var playerName =
"Player"
var serverHost =
""
var playPressed:
() => Unit =
() => ()
var exitPressed:
() => Unit =
() => ()
val playerList =
new JTextArea
val chat =
new JTextArea
val chatInput =
new JTextField
var socket:
Socket = null
var in:
BufferedReader = null
var out:
PrintWriter = null
var connected =
false
val remote =
mutable.Map[
Int,
PlayerData
]()
setLayout(
null
)
setBackground(
new Color(
7,
12,
19
)
)
val title =
new JLabel(
"ONLINE LOBBY",
SwingConstants.CENTER
)
title.setForeground(
new Color(
0,
235,
255
)
)
title.setFont(
new Font(
"Arial",
Font.BOLD,
36
)
)
title.setBounds(
360,
25,
560,
55
)
add(title)
playerList.setEditable(
false
)
playerList.setBackground(
new Color(
12,
20,
28
)
)
playerList.setForeground(
Color.WHITE
)
playerList.setFont(
new Font(
"Monospaced",
Font.PLAIN,
15
)
)
val playerScroll =
new JScrollPane(
playerList
)
playerScroll.setBounds(
70,
120,
500,
450
)
add(
playerScroll
)
chat.setEditable(
false
)
chat.setBackground(
new Color(
12,
20,
28
)
)
chat.setForeground(
new Color(
230,
235,
240
)
)
chat.setFont(
new Font(
"Arial",
Font.PLAIN,
13
)
)
val chatScroll =
new JScrollPane(
chat
)
chatScroll.setBounds(
620,
120,
520,
340
)
add(
chatScroll
)
chatInput.setBounds(
620,
475,
390,
40
)
add(
chatInput
)
val send =
new JButton(
"SEND"
)
send.setBounds(
1020,
475,
120,
40
)
add(
send
)
send.addActionListener(
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit =
sendChat()
}
)
val race =
new JButton(
"? START RACE"
)
race.setBounds(
620,
545,
250,
55
)
race.setFont(
new Font(
"Arial",
Font.BOLD,
18
)
)
add(
race
)
race.addActionListener(
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit = {
if (connected) {
send(
"RACE"
)
}
playPressed()
}
}
)
val play =
new JButton(
"ENTER COCKPIT"
)
play.setBounds(
890,
545,
250,
55
)
play.setFont(
new Font(
"Arial",
Font.BOLD,
18
)
)
add(
play
)
play.addActionListener(
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit =
playPressed()
}
)
val exit =
new JButton(
"EXIT"
)
exit.setBounds(
70,
600,
250,
45
)
add(
exit
)
exit.addActionListener(
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit =
exitPressed()
}
)
def configure(
name: String,
host: String
): Unit = {
playerName =
name
serverHost =
host
updateList()
}
def connect(
name: String,
host: String
): Unit = {
playerName =
name
serverHost =
host
disconnect()
if (host.isEmpty) {
updateList()
return
}
try {
socket =
new Socket()
socket.connect(
new InetSocketAddress(
host,
PORT
),
4000
)
socket.setTcpNoDelay(
true
)
in =
new BufferedReader(
new InputStreamReader(
socket.getInputStream
)
)
out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream
)
),
true
)
connected =
true
send(
"HELLO|" +
playerName
)
val t =
new Thread(
new Runnable {
def run(): Unit =
readServer()
}
)
t.setDaemon(
true
)
t.start()
} catch {
case _: Throwable =>
connected =
false
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
JOptionPane.showMessageDialog(
OnlineLobby.this,
"Connection failed.\n" +
"Use HOST + PLAY on the first PC.\n" +
"Other PCs must use the host PC's IP."
)
}
}
)
}
}
def send(
s: String
): Unit = {
if (
out != null
) {
try {
out.println(
s
)
} catch {
case _: Throwable =>
connected =
false
}
}
}
def readServer(): Unit = {
try {
var line:
String = null
while ({
line =
in.readLine()
line != null
}) {
val p =
line.split(
"\\|",
-1
)
if (
p.length >= 8 &&
p(0) == "PLAYER"
) {
try {
val id =
p(1).toInt
remote.synchronized {
remote.update(
id,
PlayerData(
p(2),
p(3).toDouble,
p(4).toDouble,
p(5).toDouble,
p(6),
p(7) == "1",
p.length >= 9 &&
p(8) == "1"
)
)
}
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit =
updateList()
}
)
} catch {
case _: Throwable =>
}
} else if (
p.length >= 4 &&
p(0) == "CHAT"
) {
val msg =
p(2) +
": " +
p.drop(3).mkString("|")
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
chat.append(
msg +
"\n"
)
chat.setCaretPosition(
chat.getDocument.getLength
)
}
}
)
} else if (
p.length >= 2 &&
p(0) == "LEAVE"
) {
try {
remote.synchronized {
remote.remove(
p(1).toInt
)
}
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit =
updateList()
}
)
} catch {
case _: Throwable =>
}
}
}
} catch {
case _: Throwable =>
}
connected =
false
}
def sendChat(): Unit = {
val text =
chatInput
.getText
.trim
if (
text.nonEmpty
) {
if (connected) {
send(
"CHAT|" +
text.take(120)
)
} else {
chat.append(
playerName +
": " +
text.take(120) +
"\n"
)
}
chatInput.setText(
""
)
}
}
def updateList(): Unit = {
val sb =
new StringBuilder
sb.append(
"ONLINE PLAYERS\n"
)
sb.append(
"===============================\n"
)
if (
remote.isEmpty
) {
sb.append(
playerName +
" ? READY\n"
)
} else {
val arr =
remote.synchronized {
remote.toArray
}
for (
e <- arr
) {
val p =
e._2
sb.append(
p.name +
(
if (p.racing)
" ? RACING"
else if (p.ready)
" ? READY"
else
" ? WAITING"
) +
"\n"
)
}
}
sb.append(
"\nYOU: " +
playerName
)
sb.append(
"\nSERVER: " +
(
if (serverHost.isEmpty)
"OFFLINE"
else
serverHost
)
)
playerList.setText(
sb.toString
)
}
def disconnect(): Unit = {
connected =
false
if (
socket != null
) {
try {
socket.close()
} catch {
case _: Throwable =>
}
}
socket =
null
in =
null
out =
null
remote.clear()
}
}
// ============================================================
// COCKPIT GAME
// ============================================================
class CockpitScreen
extends JPanel {
var playerName =
"Player"
var serverHost =
""
var online =
false
var playerId =
-1
var socket:
Socket = null
var in:
BufferedReader = null
var out:
PrintWriter = null
var speed =
0.0
var rpm =
900.0
var steering =
0.0
var gear =
"N"
var fuel =
100.0
var damage =
0.0
var nitro =
100.0
var distance =
0.0
var score =
0
var racing =
false
var headlights =
false
var mapVisible =
true
var paused =
false
var roadOffset =
0.0
var smoke =
0.0
var flash =
0.0
val rnd =
new Random
val keys =
mutable.Set[Int]()
val players =
mutable.Map[
Int,
PlayerData
]()
val traffic =
mutable.ArrayBuffer[
TrafficCar
]()
setFocusable(
true
)
createTraffic()
setupKeys()
// ==========================================================
// START
// ==========================================================
def startPlayer(
name: String,
host: String,
onlineMode: Boolean
): Unit = {
playerName =
name
serverHost =
host
online =
onlineMode
resetGame()
if (
onlineMode
) {
connect()
} else {
disconnect()
}
}
// ==========================================================
// NETWORK
// ==========================================================
def connect(): Unit = {
disconnect()
try {
socket =
new Socket()
socket.connect(
new InetSocketAddress(
serverHost,
PORT
),
4000
)
socket.setTcpNoDelay(
true
)
in =
new BufferedReader(
new InputStreamReader(
socket.getInputStream
)
)
out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream
)
),
true
)
online =
true
send(
"HELLO|" +
playerName
)
val t =
new Thread(
new Runnable {
def run(): Unit =
readNetwork()
},
"COCKPIT_NETWORK"
)
t.setDaemon(
true
)
t.start()
} catch {
case _: Throwable =>
online =
false
}
}
def send(
s: String
): Unit = {
if (
out != null
) {
try {
out.println(
s
)
} catch {
case _: Throwable =>
online =
false
}
}
}
def readNetwork(): Unit = {
try {
var line:
String = null
while ({
line =
in.readLine()
line != null
}) {
val p =
line.split(
"\\|",
-1
)
if (
p.length >= 2 &&
p(0) == "WELCOME"
) {
try {
playerId =
p(1).toInt
} catch {
case _: Throwable =>
}
} else if (
p.length >= 8 &&
p(0) == "PLAYER"
) {
try {
val id =
p(1).toInt
if (
id != playerId
) {
players.synchronized {
players.update(
id,
PlayerData(
p(2),
p(3).toDouble,
p(4).toDouble,
p(5).toDouble,
p(6),
p(7) == "1",
p.length >= 9 &&
p(8) == "1"
)
)
}
}
} catch {
case _: Throwable =>
}
} else if (
p.length >= 2 &&
p(0) == "LEAVE"
) {
try {
players.synchronized {
players.remove(
p(1).toInt
)
}
} catch {
case _: Throwable =>
}
}
}
} catch {
case _: Throwable =>
}
online =
false
}
var lastNetwork =
0L
def sendState(): Unit = {
if (
!online ||
out == null
)
return
val now =
System.currentTimeMillis()
if (
now -
lastNetwork <
70
)
return
lastNetwork =
now
send(
"STATE|" +
playerX +
"|" +
distance +
"|" +
speed +
"|" +
gear +
"|" +
(
if (racing)
"1"
else
"0"
) +
"|" +
"1"
)
}
def disconnect(): Unit = {
online =
false
if (
socket != null
) {
try {
socket.close()
} catch {
case _: Throwable =>
}
}
socket =
null
in =
null
out =
null
players.clear()
}
// ==========================================================
// GAME
// ==========================================================
var playerX =
0.0
def resetGame(): Unit = {
playerX =
0.0
speed =
0.0
rpm =
900.0
steering =
0.0
gear =
"N"
fuel =
100.0
damage =
0.0
nitro =
100.0
distance =
0.0
score =
0
roadOffset =
0.0
smoke =
0.0
flash =
0.0
racing =
false
paused =
false
createTraffic()
}
def setupKeys(): Unit = {
val codes =
Array(
KeyEvent.VK_LEFT,
KeyEvent.VK_RIGHT,
KeyEvent.VK_UP,
KeyEvent.VK_DOWN,
KeyEvent.VK_A,
KeyEvent.VK_D,
KeyEvent.VK_W,
KeyEvent.VK_S,
KeyEvent.VK_SPACE,
KeyEvent.VK_1,
KeyEvent.VK_2,
KeyEvent.VK_3,
KeyEvent.VK_4,
KeyEvent.VK_5,
KeyEvent.VK_R,
KeyEvent.VK_N,
KeyEvent.VK_E,
KeyEvent.VK_H,
KeyEvent.VK_M,
KeyEvent.VK_ESCAPE,
KeyEvent.VK_F
)
val im =
getInputMap(
JComponent.WHEN_IN_FOCUSED_WINDOW
)
val am =
getActionMap()
for (
k <- codes
) {
val down =
"DOWN_" +
k
val up =
"UP_" +
k
im.put(
KeyStroke.getKeyStroke(
k,
0,
false
),
down
)
im.put(
KeyStroke.getKeyStroke(
k,
0,
true
),
up
)
am.put(
down,
new AbstractAction {
def actionPerformed(
e: ActionEvent
): Unit =
keyDown(k)
}
)
am.put(
up,
new AbstractAction {
def actionPerformed(
e: ActionEvent
): Unit =
keys -= k
}
)
}
}
def keyDown(
k: Int
): Unit = {
if (
k ==
KeyEvent.VK_ESCAPE
) {
paused =
!paused
return
}
if (
k ==
KeyEvent.VK_H
) {
headlights =
!headlights
return
}
if (
k ==
KeyEvent.VK_M
) {
mapVisible =
!mapVisible
return
}
if (
k ==
KeyEvent.VK_F
) {
racing =
true
send(
"RACE"
)
return
}
keys +=
k
if (
k ==
KeyEvent.VK_R
)
gear =
"R"
else if (
k ==
KeyEvent.VK_N
)
gear =
"N"
else if (
k ==
KeyEvent.VK_1
)
gear =
"1"
else if (
k ==
KeyEvent.VK_2
)
gear =
"2"
else if (
k ==
KeyEvent.VK_3
)
gear =
"3"
else if (
k ==
KeyEvent.VK_4
)
gear =
"4"
else if (
k ==
KeyEvent.VK_5
)
gear =
"5"
}
// ==========================================================
// TRAFFIC
// ==========================================================
def createTraffic(): Unit = {
traffic.clear()
for (
i <- 0 until 18
) {
traffic +=
TrafficCar(
rnd.nextInt(3) - 1,
0.18 +
i * 0.075 +
rnd.nextDouble() *
0.12,
0.25 +
rnd.nextDouble() *
0.75,
rnd.nextInt(3),
randomColor()
)
}
}
def randomColor(): Color = {
val c =
Array(
new Color(
220,
40,
45
),
new Color(
35,
125,
225
),
new Color(
245,
210,
50
),
new Color(
235,
235,
235
),
new Color(
45,
47,
52
),
new Color(
40,
175,
100
)
)
c(
rnd.nextInt(
c.length
)
)
}
// ==========================================================
// GAME LOOP
// ==========================================================
val timer =
new Timer(
16,
new ActionListener {
def actionPerformed(
e: ActionEvent
): Unit = {
updateGame(
0.016
)
repaint()
}
}
)
timer.start()
def updateGame(
dt: Double
): Unit = {
if (
paused
)
return
val left =
keys.contains(
KeyEvent.VK_LEFT
) ||
keys.contains(
KeyEvent.VK_A
)
val right =
keys.contains(
KeyEvent.VK_RIGHT
) ||
keys.contains(
KeyEvent.VK_D
)
val accel =
keys.contains(
KeyEvent.VK_UP
) ||
keys.contains(
KeyEvent.VK_W
)
val brake =
keys.contains(
KeyEvent.VK_DOWN
) ||
keys.contains(
KeyEvent.VK_S
) ||
keys.contains(
KeyEvent.VK_SPACE
)
val boost =
keys.contains(
KeyEvent.VK_E
) &&
nitro > 0 &&
speed > 20
var targetSteer =
0.0
if (
left &&
!right
)
targetSteer =
-1.0
else if (
right &&
!left
)
targetSteer =
1.0
steering +=
(
targetSteer -
steering
) *
0.14
playerX +=
steering *
(
4.8 +
Math.abs(speed) *
0.025
)
if (
playerX <
-315
)
playerX =
-315
if (
playerX >
315
)
playerX =
315
var ratio =
0.0
if (
gear ==
"1"
)
ratio =
2.70
else if (
gear ==
"2"
)
ratio =
1.95
else if (
gear ==
"3"
)
ratio =
1.40
else if (
gear ==
"4"
)
ratio =
1.00
else if (
gear ==
"5"
)
ratio =
0.78
if (
gear ==
"R"
) {
if (
accel
)
speed -=
90 *
dt
} else if (
gear ==
"N"
) {
speed *=
0.997
} else {
if (
accel
)
speed +=
68 *
ratio *
dt
}
if (
boost
) {
speed +=
135 *
dt
nitro -=
30 *
dt
} else {
nitro +=
6.5 *
dt
}
if (
nitro <
0
)
nitro =
0
if (
nitro >
100
)
nitro =
100
if (
brake
) {
speed -=
145 *
dt
speed *=
0.972
if (
speed <
0
)
speed =
0
}
speed -=
speed *
0.012 *
dt
if (
speed >
250
)
speed =
250
if (
speed <
-70
)
speed =
-70
if (
gear ==
"N"
) {
if (
accel
)
rpm =
3000
else
rpm =
850
} else {
var rf =
19.0
if (
gear ==
"1"
)
rf =
48.0
else if (
gear ==
"2"
)
rf =
34.0
else if (
gear ==
"3"
)
rf =
26.0
rpm =
900 +
Math.abs(speed) *
rf
if (
rpm >
7000
)
rpm =
7000
}
if (
accel
) {
fuel -=
0.008 +
Math.abs(speed) *
0.000022
}
if (
boost
)
fuel -=
0.02
if (
fuel <
0
)
fuel =
0
if (
fuel ==
0
) {
gear =
"N"
speed *=
0.99
}
roadOffset +=
speed *
dt *
0.45
if (
speed >
0
) {
distance +=
speed *
dt *
0.90
score +=
(
speed *
dt *
0.08
).toInt
}
for (
t <- traffic
) {
val rel =
Math.abs(speed) *
0.0021 -
t.speed *
0.00115
t.z -=
rel *
dt *
15
if (
t.z <
0.06 ||
t.z >
1.5
) {
t.lane =
rnd.nextInt(3) - 1
t.z =
1.10 +
rnd.nextDouble() *
0.38
t.speed =
0.25 +
rnd.nextDouble() *
0.75
}
if (
t.z >
0.13 &&
t.z <
0.29
) {
val tx =
t.lane *
205.0
if (
Math.abs(
playerX -
tx
) <
76 &&
Math.abs(speed) >
22
) {
damage +=
16 +
Math.abs(speed) *
0.08
score =
Math.max(
0,
score -
70
)
speed *=
0.28
smoke =
1.0
flash =
1.0
t.z =
1.35
if (
damage >=
100
) {
damage =
100
paused =
true
}
}
}
}
smoke *=
0.965
flash *=
0.90
sendState()
}
// ==========================================================
// DRAW
// ==========================================================
override def paintComponent(
g0: Graphics
): Unit = {
super.paintComponent(
g0
)
val g =
g0.asInstanceOf[
Graphics2D
]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
drawSky(g)
drawWorld(g)
drawRoad(g)
for (
t <- traffic.sortBy(
_.z
).reverse
) {
drawTraffic(
g,
t
)
}
drawOnlinePlayers(
g
)
if (
headlights
)
drawHeadlights(g)
drawCockpit(
g
)
drawHUD(
g
)
if (
mapVisible
)
drawMap(
g
)
if (
smoke >
0.08
)
drawSmoke(
g
)
if (
flash >
0.02
)
drawFlash(
g
)
if (
paused
)
drawPause(
g
)
if (
damage >=
100
)
drawDestroyed(
g
)
}
// ==========================================================
// SKY
// ==========================================================
def drawSky(
g: Graphics2D
): Unit = {
val sky =
new GradientPaint(
0,
0,
new Color(
13,
25,
46
),
0,
350,
new Color(
112,
145,
162
)
)
g.setPaint(
sky
)
g.fillRect(
0,
0,
1280,
350
)
g.setColor(
new Color(
240,
240,
225
)
)
g.fillOval(
1040,
60,
70,
70
)
drawCloud(
g,
90,
95,
1.0
)
drawCloud(
g,
535,
115,
0.8
)
drawCloud(
g,
810,
90,
1.05
)
}
def drawCloud(
g: Graphics2D,
x: Int,
y: Int,
s: Double
): Unit = {
g.setColor(
new Color(
210,
220,
225,
110
)
)
g.fillOval(
x,
y,
(80 * s).toInt,
(36 * s).toInt
)
g.fillOval(
x +
(30 * s).toInt,
y -
(20 * s).toInt,
(65 * s).toInt,
(45 * s).toInt
)
g.fillOval(
x +
(67 * s).toInt,
y,
(75 * s).toInt,
(36 * s).toInt
)
}
// ==========================================================
// WORLD
// ==========================================================
def drawWorld(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
28,
80,
38
)
)
g.fillRect(
0,
340,
1280,
480
)
val hills =
new Polygon()
hills.addPoint(
0,
360
)
hills.addPoint(
150,
300
)
hills.addPoint(
300,
350
)
hills.addPoint(
470,
292
)
hills.addPoint(
650,
356
)
hills.addPoint(
815,
298
)
hills.addPoint(
1010,
350
)
hills.addPoint(
1200,
300
)
hills.addPoint(
1280,
375
)
hills.addPoint(
1280,
440
)
hills.addPoint(
0,
440
)
g.setColor(
new Color(
42,
75,
52
)
)
g.fillPolygon(
hills
)
for (
i <- 0 until 12
) {
drawTree(
g,
25 +
i * 108,
300 +
(
i % 3
) * 12,
0.68
)
drawTree(
g,
1255 -
i * 108,
300 +
(
i % 2
) * 15,
0.67
)
}
}
def drawTree(
g: Graphics2D,
x: Int,
y: Int,
s: Double
): Unit = {
g.setColor(
new Color(
90,
60,
35
)
)
g.fillRect(
x - 6,
y,
12,
58
)
g.setColor(
new Color(
28,
103,
42
)
)
g.fillOval(
x -
(50 * s).toInt,
y -
(68 * s).toInt,
(100 * s).toInt,
(88 * s).toInt
)
}
// ==========================================================
// ROAD
// ==========================================================
def drawRoad(
g: Graphics2D
): Unit = {
val h =
340
val road =
new Polygon()
road.addPoint(
500,
h
)
road.addPoint(
780,
h
)
road.addPoint(
1240,
760
)
road.addPoint(
40,
760
)
g.setColor(
new Color(
47,
48,
52
)
)
g.fillPolygon(
road
)
g.setColor(
new Color(
190,
190,
190
)
)
g.setStroke(
new BasicStroke(
5
)
)
g.drawLine(
500,
h,
40,
760
)
g.drawLine(
780,
h,
1240,
760
)
var y =
h +
5 -
(
roadOffset.toInt %
92
)
while (
y <
760
) {
val p =
Math.max(
0.0,
Math.min(
1.0,
(
y -
h
).toDouble /
(
760 -
h
)
)
)
val left =
500 -
460 *
p
val right =
780 +
460 *
p
val lane =
(
right -
left
) /
3.0
g.setColor(
new Color(
250,
220,
75
)
)
for (
n <- 1 to 2
) {
val x =
(
left +
lane *
n
).toInt
g.drawLine(
x,
y,
x,
Math.min(
760,
y +
20 +
(
p *
85
).toInt
)
)
}
y +=
58 +
(
p *
100
).toInt
}
}
// ==========================================================
// TRAFFIC DRAW
// ==========================================================
def drawTraffic(
g: Graphics2D,
t: TrafficCar
): Unit = {
val z =
Math.max(
0.17,
t.z
)
val scale =
1.0 /
z
val cx =
(
640 +
t.lane *
205 *
scale
).toInt
val cy =
(
340 +
(
1.2 -
z
) *
330
).toInt
val w =
Math.max(
20,
(
56 *
scale
).toInt
)
val h =
Math.max(
30,
(
108 *
scale
).toInt
)
g.setColor(
new Color(
0,
0,
0,
80
)
)
g.fillOval(
cx -
w /
2,
cy +
h /
2,
w +
24,
14
)
g.setColor(
Color.BLACK
)
g.fillRoundRect(
cx -
w /
2,
cy -
h /
2,
w,
h,
12,
12
)
g.setColor(
t.body
)
g.fillRoundRect(
cx -
w /
2 +
3,
cy -
h /
2 +
3,
w -
6,
h -
6,
10,
10
)
g.setColor(
new Color(
55,
82,
100
)
)
g.fillRoundRect(
cx -
w /
2 +
7,
cy -
h /
4,
w -
14,
h /
4,
6,
6
)
g.setColor(
new Color(
255,
35,
35
)
)
g.fillRect(
cx -
w /
2 +
6,
cy +
h /
2 -
15,
9,
7
)
g.fillRect(
cx +
w /
2 -
15,
cy +
h /
2 -
15,
9,
7
)
}
// ==========================================================
// ONLINE CARS
// ==========================================================
def drawOnlinePlayers(
g: Graphics2D
): Unit = {
val arr =
players.synchronized {
players.toArray
}
for (
e <- arr
) {
val p =
e._2
val delta =
p.distance -
distance
if (
Math.abs(
delta
) <
850
) {
val relative =
Math.max(
0.15,
Math.min(
1.18,
0.72 -
delta /
900.0
)
)
val scale =
1.0 /
Math.max(
0.18,
relative
)
val cx =
(
640 +
p.x *
scale
).toInt
val cy =
(
350 +
(
1.13 -
relative
) *
340
).toInt
val w =
Math.max(
22,
(
62 *
scale
).toInt
)
val h =
Math.max(
35,
(
112 *
scale
).toInt
)
g.setColor(
new Color(
0,
235,
255,
42
)
)
g.fillOval(
cx -
w,
cy -
h /
2 -
10,
w *
2,
h +
20
)
g.setColor(
Color.BLACK
)
g.fillRoundRect(
cx -
w /
2,
cy -
h /
2,
w,
h,
13,
13
)
g.setColor(
new Color(
35,
160,
235
)
)
g.fillRoundRect(
cx -
w /
2 +
3,
cy -
h /
2 +
3,
w -
6,
h -
6,
11,
11
)
g.setColor(
if (
p.racing
)
new Color(
255,
225,
50
)
else
new Color(
0,
245,
255
)
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
Math.max(
8,
(
12 *
scale
).toInt
)
)
)
g.drawString(
p.name +
(
if (
p.racing
)
" ? RACING"
else
""
),
cx -
w /
2,
cy -
h /
2 -
7
)
}
}
}
// ==========================================================
// COCKPIT
// ==========================================================
def drawCockpit(
g: Graphics2D
): Unit = {
// windshield frame
g.setColor(
new Color(
12,
15,
19
)
)
g.fillRect(
0,
305,
22,
345
)
g.fillRect(
1258,
305,
22,
345
)
g.fillRect(
0,
295,
1280,
18
)
// dashboard
g.setColor(
new Color(
11,
14,
18
)
)
g.fillRect(
0,
545,
1280,
105
)
// driver head
g.setColor(
new Color(
220,
180,
150
)
)
g.fillOval(
460,
392,
46,
46
)
// driver shirt
g.setColor(
new Color(
35,
80,
145
)
)
g.fillRoundRect(
450,
435,
80,
98,
18,
18
)
// seat belt
g.setColor(
new Color(
235,
220,
180
)
)
g.setStroke(
new BasicStroke(
4
)
)
g.drawLine(
462,
442,
516,
515
)
// steering wheel
val sx =
640
val sy =
620
g.setColor(
new Color(
28,
28,
31
)
)
g.setStroke(
new BasicStroke(
12
)
)
g.drawOval(
sx -
76,
sy -
76,
152,
152
)
val a =
steering *
Math.toRadians(
60
)
val wx =
sx +
(
Math.sin(a) *
60
).toInt
val wy =
sy -
(
Math.cos(a) *
60
).toInt
g.setStroke(
new BasicStroke(
8
)
)
g.drawLine(
sx,
sy,
wx,
wy
)
g.setColor(
new Color(
105,
105,
110
)
)
g.fillOval(
sx -
13,
sy -
13,
26,
26
)
// display
g.setColor(
Color.BLACK
)
g.fillRoundRect(
500,
552,
280,
65,
12,
12
)
g.setColor(
new Color(
0,
245,
180
)
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
28
)
)
g.drawString(
Math.abs(
speed
).toInt +
" KM/H",
525,
585
)
g.setColor(
new Color(
0,
225,
255
)
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
18
)
)
g.drawString(
"GEAR " +
gear,
675,
585
)
// pedals
val accel =
keys.contains(
KeyEvent.VK_UP
) ||
keys.contains(
KeyEvent.VK_W
)
val brake =
keys.contains(
KeyEvent.VK_DOWN
) ||
keys.contains(
KeyEvent.VK_S
) ||
keys.contains(
KeyEvent.VK_SPACE
)
g.setColor(
if (accel)
new Color(
0,
230,
160
)
else
new Color(
60,
65,
70
)
)
g.fillRoundRect(
820,
555,
110,
36,
8,
8
)
g.setColor(
if (brake)
new Color(
255,
55,
55
)
else
new Color(
60,
65,
70
)
)
g.fillRoundRect(
945,
555,
110,
36,
8,
8
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
10
)
)
g.drawString(
"ACCELERATOR",
840,
578
)
g.drawString(
"BRAKE",
980,
578
)
}
// ==========================================================
// HEADLIGHTS
// ==========================================================
def drawHeadlights(
g: Graphics2D
): Unit = {
val cx =
(
640 +
playerX
).toInt
val left =
new Polygon()
left.addPoint(
cx -
65,
550
)
left.addPoint(
cx -
20,
550
)
left.addPoint(
cx -
290,
340
)
left.addPoint(
cx -
160,
340
)
val right =
new Polygon()
right.addPoint(
cx +
20,
550
)
right.addPoint(
cx +
65,
550
)
right.addPoint(
cx +
160,
340
)
right.addPoint(
cx +
290,
340
)
g.setColor(
new Color(
255,
245,
175,
45
)
)
g.fillPolygon(
left
)
g.fillPolygon(
right
)
}
// ==========================================================
// HUD
// ==========================================================
def drawHUD(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
4,
8,
13,
230
)
)
g.fillRect(
0,
0,
960,
70
)
g.setColor(
if (online)
new Color(
0,
245,
160
)
else
new Color(
255,
80,
70
)
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
12
)
)
g.drawString(
if (online)
"? ONLINE"
else
"? OFFLINE",
20,
24
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
15
)
)
g.drawString(
playerName +
" #" +
playerId,
110,
24
)
g.drawString(
"SPEED " +
Math.abs(
speed
).toInt +
" KM/H",
305,
24
)
g.drawString(
"DIST " +
distance.toInt +
" M",
460,
24
)
g.drawString(
"SCORE " +
score,
585,
24
)
g.drawString(
"PLAYERS " +
players.synchronized {
players.size
},
680,
24
)
g.setColor(
if (racing)
new Color(
255,
225,
50
)
else
Color.WHITE
)
g.drawString(
if (racing)
"? RACING"
else
"F = RACE",
790,
24
)
g.setColor(
new Color(
40,
45,
50
)
)
g.fillRect(
305,
44,
365,
10
)
g.setColor(
new Color(
0,
230,
160
)
)
g.fillRect(
305,
44,
Math.min(
365,
(
distance /
5000.0 *
365
).toInt
),
10
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.PLAIN,
11
)
)
g.drawString(
"5000 M RACE",
690,
51
)
}
// ==========================================================
// MAP
// ==========================================================
def drawMap(
g: Graphics2D
): Unit = {
val x =
990
val y =
90
g.setColor(
new Color(
5,
9,
14,
240
)
)
g.fillRoundRect(
x,
y,
250,
210,
18,
18
)
g.setColor(
new Color(
0,
230,
255
)
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
g.drawString(
"LIVE GPS",
x +
18,
y +
25
)
g.setColor(
new Color(
70,
75,
80
)
)
g.setStroke(
new BasicStroke(
25,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND
)
)
g.drawLine(
x +
125,
y +
48,
x +
125,
y +
160
)
val progress =
Math.min(
1.0,
distance /
5000.0
)
g.setColor(
new Color(
0,
240,
160
)
)
g.setStroke(
new BasicStroke(
4
)
)
g.drawLine(
x +
125,
y +
48,
x +
125,
y +
48 +
(
progress *
112
).toInt
)
g.setColor(
new Color(
255,
235,
50
)
)
g.fillOval(
x +
119,
y +
43 +
(
progress *
112
).toInt,
12,
12
)
g.setColor(
new Color(
255,
60,
60
)
)
g.fillOval(
x +
118,
y +
153,
14,
14
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.PLAIN,
10
)
)
g.drawString(
"YOU",
x +
148,
y +
68
)
g.drawString(
"ONLINE: " +
players.synchronized {
players.size
},
x +
18,
y +
190
)
}
// ==========================================================
// SMOKE / FLASH
// ==========================================================
def drawSmoke(
g: Graphics2D
): Unit = {
val cx =
(
640 +
playerX
).toInt
for (
i <- 0 until 7
) {
g.setColor(
new Color(
210,
210,
210,
Math.max(
15,
95 -
i *
11
)
)
)
val s =
22 +
i *
7
g.fillOval(
cx -
35 +
rnd.nextInt(
55
),
640 -
i *
14,
s,
s
)
}
}
def drawFlash(
g: Graphics2D
): Unit = {
val a =
Math.max(
0,
Math.min(
120,
(
flash *
120
).toInt
)
)
g.setColor(
new Color(
255,
40,
30,
a
)
)
g.fillRect(
0,
0,
1280,
650
)
}
// ==========================================================
// PAUSE / DESTROYED
// ==========================================================
def drawPause(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
0,
0,
0,
175
)
)
g.fillRect(
0,
0,
1280,
650
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
56
)
)
g.drawString(
"PAUSED",
520,
325
)
g.setFont(
new Font(
"Arial",
Font.PLAIN,
18
)
)
g.drawString(
"PRESS ESC TO CONTINUE",
480,
368
)
}
def drawDestroyed(
g: Graphics2D
): Unit = {
g.setColor(
new Color(
0,
0,
0,
185
)
)
g.fillRect(
0,
0,
1280,
650
)
g.setColor(
new Color(
255,
45,
45
)
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
48
)
)
g.drawString(
"VEHICLE DESTROYED",
405,
315
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
19
)
)
g.drawString(
"PRESS ENTER TO RESTART",
485,
370
)
}
}
// ============================================================
// AUTO START
// ============================================================
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
new DriveWindow
}
}
)