Code Sketch
cfxf
Category: Programming
import java.awt.BasicStroke
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Cursor
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.Font
import java.awt.GradientPaint
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.Point
import java.awt.RenderingHints
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.awt.event.MouseMotionAdapter
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import java.awt.geom.Path2D
import java.util.Random
import javax.swing.DefaultListModel
import javax.swing.JButton
import javax.swing.JColorChooser
import javax.swing.JFileChooser
import javax.swing.JFrame
import javax.swing.JLabel
import javax.swing.JList
import javax.swing.JOptionPane
import javax.swing.JPanel
import javax.swing.JScrollPane
import javax.swing.JSplitPane
import javax.swing.JTabbedPane
import javax.swing.JTextArea
import javax.swing.JTextField
import javax.swing.ListSelectionModel
import javax.swing.SwingConstants
import javax.swing.SwingUtilities
import javax.swing.Timer
import javax.swing.WindowConstants
import scala.collection.mutable.ArrayBuffer
// ============================================================
// ULTRA LEGEND X9
// DRAW + OBJECT + SHAPE + ANIMATION + GAMES STUDIO
// FULL STABLE VERSION
// ============================================================
// ============================================================
// CONSTANTS
// ============================================================
val CANVAS_W: Int = 1000
val CANVAS_H: Int = 650
val CENTER_X: Int = CANVAS_W / 2
val CENTER_Y: Int = CANVAS_H / 2
val RANDOM_GEN =
new Random()
// ============================================================
// DATA MODELS
// ============================================================
case class DrawItem(
points: Vector[Point],
stroke: Color,
fill: Color,
filled: Boolean,
name: String,
visible: Boolean,
locked: Boolean
)
case class PaddleLine(
p1: Point,
p2: Point
)
case class GameInfo(
name: String,
mode: String,
description: String
)
// ============================================================
// GLOBAL DRAW STATE
// ============================================================
var drawItems =
ArrayBuffer[DrawItem]()
var undoStack =
ArrayBuffer[ArrayBuffer[DrawItem]]()
var redoStack =
ArrayBuffer[ArrayBuffer[DrawItem]]()
var activeTool: String =
"PENCIL"
var strokeColor: Color =
new Color(25, 90, 220)
var fillColor: Color =
new Color(255, 180, 40)
var brushWidth: Float =
5.0f
var showGrid: Boolean =
true
var snapToGrid: Boolean =
false
var selectedIndex: Int =
-1
var drawingNow: Boolean =
false
var movingNow: Boolean =
false
var resizingNow: Boolean =
false
var resizeHandle: String =
""
var animationFrames =
ArrayBuffer[ArrayBuffer[DrawItem]]()
var animationClipboard =
ArrayBuffer[DrawItem]()
var animationCurrentFrame: Int =
0
var animationFps: Int =
8
var animationStudioOpen: Boolean =
false
var lastMousePoint: Point =
new Point(0, 0)
var originalPoints: Vector[Point] =
Vector.empty
var tempPoints =
ArrayBuffer[Point]()
var generatedCode: String =
""
var generatedAnimationCode: String =
""
var mainFrame: JFrame =
null
var canvas: JPanel =
null
var codeArea: JTextArea =
null
var statusLabel: JLabel =
null
var objectSearch: JTextField =
null
var objectList: JList[String] =
null
var shapeList: JList[String] =
null
// ============================================================
// UI HELPERS
// ============================================================
def button(text: String): JButton = {
val b =
new JButton(text)
b.setFocusable(false)
b.setFont(
new Font(
"Arial",
Font.BOLD,
11
)
)
b
}
def setStatus(text: String): Unit = {
if (statusLabel != null) {
statusLabel.setText(text)
}
}
def repaintCanvas(): Unit = {
if (canvas != null) {
canvas.repaint()
}
}
// ============================================================
// HISTORY
// ============================================================
def cloneItems(
source: ArrayBuffer[DrawItem]
): ArrayBuffer[DrawItem] = {
val output =
ArrayBuffer[DrawItem]()
var i: Int = 0
while (i < source.length) {
val item =
source(i)
val pts =
item.points.map { p =>
new Point(
p.x,
p.y
)
}
output +=
item.copy(
points = pts
)
i += 1
}
output
}
def saveHistory(): Unit = {
undoStack +=
cloneItems(drawItems)
redoStack.clear()
while (undoStack.length > 60) {
undoStack.remove(0)
}
}
def undo(): Unit = {
if (undoStack.nonEmpty) {
redoStack +=
cloneItems(drawItems)
val previous =
undoStack.last
undoStack.remove(
undoStack.length - 1
)
drawItems.clear()
drawItems ++=
cloneItems(previous)
selectedIndex =
-1
repaintCanvas()
setStatus(
"UNDO COMPLETE"
)
} else {
setStatus(
"NOTHING TO UNDO"
)
}
}
def redo(): Unit = {
if (redoStack.nonEmpty) {
undoStack +=
cloneItems(drawItems)
val nextState =
redoStack.last
redoStack.remove(
redoStack.length - 1
)
drawItems.clear()
drawItems ++=
cloneItems(nextState)
selectedIndex =
-1
repaintCanvas()
setStatus(
"REDO COMPLETE"
)
} else {
setStatus(
"NOTHING TO REDO"
)
}
}
// ============================================================
// POINT HELPERS
// ============================================================
def distance(
a: Point,
b: Point
): Double = {
math.hypot(
b.x - a.x,
b.y - a.y
)
}
def snapped(value: Int): Int = {
if (snapToGrid) {
(value / 10) * 10
} else {
value
}
}
def canvasPoint(
e: MouseEvent
): Point = {
val x =
math.max(
0,
math.min(
CANVAS_W - 1,
snapped(e.getX)
)
)
val y =
math.max(
0,
math.min(
CANVAS_H - 1,
snapped(e.getY)
)
)
new Point(
x,
y
)
}
def closed(
points: Iterable[Point]
): Boolean = {
val seq =
points.toVector
seq.length >= 3 &&
distance(
seq.head,
seq.last
) <= 45.0
}
// ============================================================
// BOUNDS
// ============================================================
def boundsOf(
item: DrawItem
): java.awt.Rectangle = {
if (item.points.isEmpty) {
new java.awt.Rectangle(
0,
0,
1,
1
)
} else {
var minX =
item.points.head.x
var maxX =
item.points.head.x
var minY =
item.points.head.y
var maxY =
item.points.head.y
var i: Int = 1
while (i < item.points.length) {
val p =
item.points(i)
if (p.x < minX) {
minX = p.x
}
if (p.x > maxX) {
maxX = p.x
}
if (p.y < minY) {
minY = p.y
}
if (p.y > maxY) {
maxY = p.y
}
i += 1
}
new java.awt.Rectangle(
minX,
minY,
math.max(
1,
maxX - minX
),
math.max(
1,
maxY - minY
)
)
}
}
// ============================================================
// SEGMENT HIT TEST
// ============================================================
def segmentDistance(
p: Point,
a: Point,
b: Point
): Double = {
val dx: Double =
b.x - a.x
val dy: Double =
b.y - a.y
if (
dx == 0.0 &&
dy == 0.0
) {
distance(
p,
a
)
} else {
val dot: Double =
(p.x - a.x) * dx +
(p.y - a.y) * dy
val len2: Double =
dx * dx +
dy * dy
val t: Double =
math.max(
0.0,
math.min(
1.0,
dot / len2
)
)
val cx: Double =
a.x + t * dx
val cy: Double =
a.y + t * dy
math.hypot(
p.x - cx,
p.y - cy
)
}
}
// ============================================================
// POLYGON HIT TEST
// ============================================================
def insidePolygon(
p: Point,
points: Seq[Point]
): Boolean = {
if (points.length < 3) {
false
} else {
var inside: Boolean =
false
var j: Int =
points.length - 1
var i: Int =
0
while (i < points.length) {
val xi =
points(i).x
val yi =
points(i).y
val xj =
points(j).x
val yj =
points(j).y
val hit =
((yi > p.y) != (yj > p.y)) &&
(
p.x <
(
xj - xi
).toDouble *
(
p.y - yi
).toDouble /
(
yj - yi +
0.000001
) +
xi
)
if (hit) {
inside =
!inside
}
j =
i
i += 1
}
inside
}
}
// ============================================================
// OBJECT HIT TEST
// ============================================================
def findObject(
p: Point
): Int = {
var result: Int =
-1
var i: Int =
drawItems.length - 1
while (
i >= 0 &&
result < 0
) {
val item =
drawItems(i)
if (item.visible) {
val r =
boundsOf(item)
val expanded =
new java.awt.Rectangle(
r.x - 14,
r.y - 14,
r.width + 28,
r.height + 28
)
if (expanded.contains(p)) {
if (
item.filled &&
closed(item.points) &&
insidePolygon(
p,
item.points.toSeq
)
) {
result =
i
} else {
var j: Int =
1
while (
j < item.points.length &&
result < 0
) {
if (
segmentDistance(
p,
item.points(j - 1),
item.points(j)
) < 16.0
) {
result =
i
}
j += 1
}
}
}
}
i -= 1
}
result
}
// ============================================================
// RESIZE HANDLES
// ============================================================
def handles(
r: java.awt.Rectangle
): Array[(String, Point)] = {
val mx =
r.x + r.width / 2
val my =
r.y + r.height / 2
Array(
("TL", new Point(r.x, r.y)),
("TM", new Point(mx, r.y)),
("TR", new Point(r.x + r.width, r.y)),
("ML", new Point(r.x, my)),
("MR", new Point(r.x + r.width, my)),
("BL", new Point(r.x, r.y + r.height)),
("BM", new Point(mx, r.y + r.height)),
("BR", new Point(r.x + r.width, r.y + r.height))
)
}
def resizeHandle(
item: DrawItem,
p: Point
): String = {
val r =
boundsOf(item)
val expanded =
new java.awt.Rectangle(
r.x - 8,
r.y - 8,
r.width + 16,
r.height + 16
)
val hs =
handles(expanded)
var result =
""
var i: Int =
0
while (
i < hs.length &&
result == ""
) {
if (
distance(
p,
hs(i)._2
) <= 13.0
) {
result =
hs(i)._1
}
i += 1
}
result
}
// ============================================================
// MOVE
// ============================================================
def moveSelected(
dx: Int,
dy: Int
): Unit = {
if (
selectedIndex >= 0 &&
selectedIndex < drawItems.length
) {
val item =
drawItems(selectedIndex)
if (!item.locked) {
val moved =
item.points.map { p =>
new Point(
p.x + dx,
p.y + dy
)
}
drawItems(selectedIndex) =
item.copy(
points = moved
)
}
}
}
// ============================================================
// RESIZE
// ============================================================
def resizeSelected(
p: Point
): Unit = {
if (
selectedIndex >= 0 &&
selectedIndex < drawItems.length &&
originalPoints.nonEmpty
) {
val item =
drawItems(selectedIndex)
var minX =
originalPoints.head.x
var maxX =
originalPoints.head.x
var minY =
originalPoints.head.y
var maxY =
originalPoints.head.y
var i: Int =
1
while (
i < originalPoints.length
) {
val q =
originalPoints(i)
if (q.x < minX) {
minX =
q.x
}
if (q.x > maxX) {
maxX =
q.x
}
if (q.y < minY) {
minY =
q.y
}
if (q.y > maxY) {
maxY =
q.y
}
i += 1
}
val oldW =
math.max(
1,
maxX - minX
)
val oldH =
math.max(
1,
maxY - minY
)
var newMinX =
minX
var newMaxX =
maxX
var newMinY =
minY
var newMaxY =
maxY
if (resizeHandle == "TL") {
newMinX = p.x
newMinY = p.y
} else if (resizeHandle == "TM") {
newMinY = p.y
} else if (resizeHandle == "TR") {
newMaxX = p.x
newMinY = p.y
} else if (resizeHandle == "ML") {
newMinX = p.x
} else if (resizeHandle == "MR") {
newMaxX = p.x
} else if (resizeHandle == "BL") {
newMinX = p.x
newMaxY = p.y
} else if (resizeHandle == "BM") {
newMaxY = p.y
} else if (resizeHandle == "BR") {
newMaxX = p.x
newMaxY = p.y
}
val newW =
math.max(
5,
newMaxX - newMinX
)
val newH =
math.max(
5,
newMaxY - newMinY
)
val scaled =
originalPoints.map { q =>
val nx =
newMinX +
(
(
q.x - minX
).toDouble *
newW /
oldW
).toInt
val ny =
newMinY +
(
(
q.y - minY
).toDouble *
newH /
oldH
).toInt
new Point(
nx,
ny
)
}
drawItems(selectedIndex) =
item.copy(
points = scaled
)
}
}
// ============================================================
// GEOMETRY
// ============================================================
def polygon(
sides: Int,
radius: Int,
start: Double
): Vector[Point] = {
val out =
ArrayBuffer[Point]()
var i: Int =
0
while (i <= sides) {
val angle =
start +
i.toDouble *
2.0 *
math.Pi /
sides.toDouble
out +=
new Point(
CENTER_X +
(
math.cos(angle) *
radius
).toInt,
CENTER_Y +
(
math.sin(angle) *
radius
).toInt
)
i += 1
}
out.toVector
}
def ellipse(
rx: Int,
ry: Int
): Vector[Point] = {
val out =
ArrayBuffer[Point]()
var deg: Int =
0
while (deg <= 360) {
val a =
deg *
math.Pi /
180.0
out +=
new Point(
CENTER_X +
(
math.cos(a) *
rx
).toInt,
CENTER_Y +
(
math.sin(a) *
ry
).toInt
)
deg += 3
}
out.toVector
}
def star(
count: Int,
radius: Int
): Vector[Point] = {
val out =
ArrayBuffer[Point]()
var i: Int =
0
while (
i <= count * 2
) {
val angle =
-math.Pi / 2.0 +
i.toDouble *
math.Pi /
count.toDouble
val rr =
if (i % 2 == 0) {
radius
} else {
(radius * 0.45).toInt
}
out +=
new Point(
CENTER_X +
(
math.cos(angle) *
rr
).toInt,
CENTER_Y +
(
math.sin(angle) *
rr
).toInt
)
i += 1
}
out.toVector
}
def heart(): Vector[Point] = {
val out =
ArrayBuffer[Point]()
var i: Int =
0
while (i <= 180) {
val t =
i *
math.Pi /
180.0
val x =
16.0 *
math.pow(
math.sin(t),
3
)
val y =
13.0 *
math.cos(t) -
5.0 *
math.cos(2.0 * t) -
2.0 *
math.cos(3.0 * t) -
math.cos(4.0 * t)
out +=
new Point(
CENTER_X +
(x * 13.0).toInt,
CENTER_Y -
(y * 13.0).toInt
)
i += 1
}
out.toVector
}
// ============================================================
// SHAPE LIBRARY
// ============================================================
val shapes =
Array(
"LINE",
"SQUARE",
"RECTANGLE",
"CIRCLE",
"ELLIPSE",
"TRIANGLE",
"DIAMOND",
"PENTAGON",
"HEXAGON",
"OCTAGON",
"STAR",
"STAR 6",
"HEART",
"ARROW",
"PLUS",
"CLOUD",
"SUN",
"MOON",
"RING",
"WAVE",
"SPIRAL",
"BURST",
"SNOWFLAKE"
)
var shapeIndex: Int =
0
val shapeModel =
new DefaultListModel[String]()
while (
shapeIndex < shapes.length
) {
shapeModel.addElement(
shapes(shapeIndex)
)
shapeIndex += 1
}
// ============================================================
// SHAPE GENERATOR
// ============================================================
def shapeGeometry(
name: String
): Vector[Point] = {
val n =
name.toUpperCase
if (n == "LINE") {
Vector(
new Point(100,120),
new Point(900,530)
)
} else if (n == "SQUARE") {
Vector(
new Point(300,150),
new Point(700,150),
new Point(700,500),
new Point(300,500),
new Point(300,150)
)
} else if (n == "RECTANGLE") {
Vector(
new Point(170,210),
new Point(830,210),
new Point(830,440),
new Point(170,440),
new Point(170,210)
)
} else if (n == "CIRCLE") {
ellipse(
210,
210
)
} else if (n == "ELLIPSE") {
ellipse(
290,
160
)
} else if (n == "TRIANGLE") {
polygon(
3,
250,
-math.Pi / 2.0
)
} else if (n == "DIAMOND") {
Vector(
new Point(CENTER_X,70),
new Point(860,CENTER_Y),
new Point(CENTER_X,580),
new Point(140,CENTER_Y),
new Point(CENTER_X,70)
)
} else if (n == "PENTAGON") {
polygon(
5,
240,
-math.Pi / 2.0
)
} else if (n == "HEXAGON") {
polygon(
6,
235,
0.0
)
} else if (n == "OCTAGON") {
polygon(
8,
235,
math.Pi / 8.0
)
} else if (n == "STAR") {
star(
5,
250
)
} else if (n == "STAR 6") {
star(
6,
250
)
} else if (n == "HEART") {
heart()
} else if (n == "ARROW") {
Vector(
new Point(130,245),
new Point(620,245),
new Point(620,135),
new Point(870,325),
new Point(620,515),
new Point(620,405),
new Point(130,405),
new Point(130,245)
)
} else if (n == "PLUS") {
Vector(
new Point(430,90),
new Point(570,90),
new Point(570,240),
new Point(720,240),
new Point(720,410),
new Point(570,410),
new Point(570,560),
new Point(430,560),
new Point(430,410),
new Point(280,410),
new Point(280,240),
new Point(430,240),
new Point(430,90)
)
} else if (n == "CLOUD") {
Vector(
new Point(160,470),
new Point(200,360),
new Point(330,270),
new Point(450,300),
new Point(535,195),
new Point(665,220),
new Point(790,320),
new Point(840,390),
new Point(810,470),
new Point(160,470)
)
} else if (n == "SUN") {
star(
16,
245
)
} else if (n == "MOON") {
ellipse(
225,
225
)
} else if (n == "RING") {
ellipse(
225,
225
)
} else if (n == "WAVE") {
val out =
ArrayBuffer[Point]()
var x: Int =
30
while (x <= 970) {
out +=
new Point(
x,
CENTER_Y +
(
math.sin(
(x - 30) * 0.045
) * 100.0
).toInt
)
x += 5
}
out.toVector
} else if (n == "SPIRAL") {
val out =
ArrayBuffer[Point]()
var angle: Double =
0.0
var radius: Double =
4.0
while (
angle < math.Pi * 8.0
) {
out +=
new Point(
CENTER_X +
(
math.cos(angle) *
radius
).toInt,
CENTER_Y +
(
math.sin(angle) *
radius
).toInt
)
angle += 0.08
radius += 1.5
}
out.toVector
} else if (n == "BURST") {
star(
20,
250
)
} else if (n == "SNOWFLAKE") {
Vector(
new Point(CENTER_X,70),
new Point(CENTER_X,580),
new Point(240,160),
new Point(760,490),
new Point(240,490),
new Point(760,160)
)
} else {
ellipse(
220,
160
)
}
}
// ============================================================
// OBJECT LIBRARY
// ============================================================
val objects =
Array(
"CAR",
"SPORTS CAR",
"TRUCK",
"BUS",
"TRAIN",
"AIRPLANE",
"JET",
"HELICOPTER",
"ROCKET",
"BICYCLE",
"MOTORBIKE",
"BOAT",
"SHIP",
"SUBMARINE",
"DRONE",
"UFO",
"HOUSE",
"VILLA",
"CASTLE",
"TOWER",
"BRIDGE",
"CITY",
"TREE",
"PALM TREE",
"FLOWER",
"CACTUS",
"MOUNTAIN",
"VOLCANO",
"ISLAND",
"RAINBOW",
"SUN",
"MOON",
"PLANET",
"EARTH",
"SATURN",
"BOY",
"GIRL",
"PERSON",
"ROBOT",
"ASTRONAUT",
"KING",
"QUEEN",
"CAT",
"DOG",
"BIRD",
"FISH",
"SHARK",
"WHALE",
"DOLPHIN",
"LION",
"TIGER",
"ELEPHANT",
"HORSE",
"RABBIT",
"PANDA",
"MONKEY",
"FOX",
"DEER",
"BUTTERFLY",
"BEE",
"SNAKE",
"TURTLE",
"APPLE",
"BANANA",
"MANGO",
"PIZZA",
"BURGER",
"CAKE",
"GUITAR",
"PIANO",
"CAMERA",
"COMPUTER",
"PHONE",
"BOOK",
"CLOCK",
"KEY",
"LAMP",
"CHAIR",
"TABLE",
"STAR",
"HEART",
"DIAMOND",
"CROWN",
"TROPHY",
"GIFT",
"BALLOON",
"UMBRELLA",
"FOOTBALL",
"BASKETBALL",
"CRICKET BAT",
"CRICKET BALL",
"FUTURE CAR",
"CYBER CITY",
"NEON TOWER",
"TIME MACHINE"
)
val objectModel =
new DefaultListModel[String]()
var objectInit: Int =
0
while (
objectInit < objects.length
) {
objectModel.addElement(
objects(objectInit)
)
objectInit += 1
}
// ============================================================
// OBJECT GEOMETRY
// ============================================================
def objectGeometry(
name: String
): Vector[Point] = {
val n =
name.toUpperCase
if (n.indexOf("CAR") >= 0) {
Vector(
new Point(100,470),
new Point(215,365),
new Point(390,355),
new Point(470,245),
new Point(660,245),
new Point(775,355),
new Point(900,370),
new Point(950,470),
new Point(100,470)
)
} else if (
n == "HOUSE" ||
n == "VILLA" ||
n == "CASTLE"
) {
Vector(
new Point(220,555),
new Point(220,300),
new Point(500,70),
new Point(780,300),
new Point(780,555),
new Point(220,555)
)
} else if (
n.indexOf("TREE") >= 0
) {
Vector(
new Point(455,580),
new Point(545,580),
new Point(545,350),
new Point(680,270),
new Point(600,150),
new Point(500,215),
new Point(400,150),
new Point(320,270),
new Point(455,350),
new Point(455,580)
)
} else if (n == "BIRD") {
Vector(
new Point(180,350),
new Point(365,215),
new Point(500,300),
new Point(660,180),
new Point(820,330),
new Point(630,415),
new Point(450,400),
new Point(310,470),
new Point(180,350)
)
} else if (
n == "FISH" ||
n == "SHARK" ||
n == "WHALE" ||
n == "DOLPHIN"
) {
Vector(
new Point(120,325),
new Point(300,210),
new Point(650,210),
new Point(865,325),
new Point(650,440),
new Point(300,440),
new Point(120,325)
)
} else if (
n.indexOf("ROCKET") >= 0 ||
n == "UFO"
) {
Vector(
new Point(425,525),
new Point(450,220),
new Point(500,60),
new Point(550,220),
new Point(575,525),
new Point(500,600),
new Point(425,525)
)
} else if (
n.indexOf("ROBOT") >= 0
) {
Vector(
new Point(350,555),
new Point(350,300),
new Point(330,300),
new Point(330,130),
new Point(670,130),
new Point(670,300),
new Point(650,300),
new Point(650,555),
new Point(350,555)
)
} else if (
n.indexOf("MOUNTAIN") >= 0
) {
Vector(
new Point(40,560),
new Point(280,205),
new Point(430,400),
new Point(620,75),
new Point(960,560),
new Point(40,560)
)
} else if (n == "FLOWER") {
Vector(
new Point(500,585),
new Point(500,315),
new Point(390,225),
new Point(500,115),
new Point(610,225),
new Point(500,315),
new Point(500,585)
)
} else if (n == "STAR") {
star(
5,
250
)
} else if (n == "HEART") {
heart()
} else if (n == "CROWN") {
Vector(
new Point(230,490),
new Point(195,155),
new Point(390,295),
new Point(500,80),
new Point(610,295),
new Point(805,155),
new Point(770,490),
new Point(230,490)
)
} else {
polygon(
6,
230,
0.0
)
}
}
// ============================================================
// ADD OBJECTS
// ============================================================
def addShape(
name: String
): Unit = {
saveHistory()
val pts =
shapeGeometry(name)
drawItems +=
DrawItem(
pts,
strokeColor,
fillColor,
closed(pts),
name,
true,
false
)
selectedIndex =
drawItems.length - 1
activeTool =
"SELECT"
repaintCanvas()
setStatus(
"SHAPE ADDED: " + name
)
}
def addObject(
name: String
): Unit = {
saveHistory()
val pts =
objectGeometry(name)
drawItems +=
DrawItem(
pts,
strokeColor,
fillColor,
closed(pts),
name,
true,
false
)
selectedIndex =
drawItems.length - 1
activeTool =
"SELECT"
repaintCanvas()
setStatus(
"OBJECT ADDED: " + name
)
}
// ============================================================
// FILL
// ============================================================
def fillSelected(): Unit = {
if (
selectedIndex >= 0 &&
selectedIndex < drawItems.length
) {
val item =
drawItems(selectedIndex)
if (
closed(item.points)
) {
saveHistory()
drawItems(selectedIndex) =
item.copy(
fill = fillColor,
filled = true
)
repaintCanvas()
setStatus(
"FILL COMPLETE"
)
} else {
setStatus(
"CLOSED SHAPE REQUIRED"
)
}
} else {
setStatus(
"SELECT OBJECT FIRST"
)
}
}
// ============================================================
// DELETE / CLEAR
// ============================================================
def deleteSelected(): Unit = {
if (
selectedIndex >= 0 &&
selectedIndex < drawItems.length
) {
saveHistory()
drawItems.remove(
selectedIndex
)
selectedIndex =
-1
repaintCanvas()
setStatus(
"OBJECT DELETED"
)
}
}
def clearAll(): Unit = {
if (drawItems.nonEmpty) {
saveHistory()
}
drawItems.clear()
tempPoints.clear()
selectedIndex =
-1
if (codeArea != null) {
codeArea.setText(
"// CANVAS CLEARED"
)
}
repaintCanvas()
setStatus(
"CANVAS CLEARED"
)
}
// ============================================================
// COLOR NAME
// ============================================================
def colorName(
c: Color
): String = {
val r =
c.getRed
val g =
c.getGreen
val b =
c.getBlue
if (
r > 220 &&
g < 100 &&
b < 100
) {
"red"
} else if (
r < 100 &&
g > 170 &&
b < 120
) {
"green"
} else if (
r < 100 &&
g < 160 &&
b > 170
) {
"blue"
} else if (
r > 220 &&
g > 170 &&
b < 130
) {
"yellow"
} else if (
r > 220 &&
g > 100 &&
b < 120
) {
"orange"
} else if (
r > 160 &&
b > 160
) {
"magenta"
} else if (
r > 170 &&
g > 170 &&
b > 170
) {
"white"
} else {
"black"
}
}
// ============================================================
// GENERATE KOJO CODE
// ============================================================
def generateCode(): Unit = {
val sb =
new StringBuilder()
sb.append(
"// ULTRA LEGEND GENERATED KOJO CODE\n"
)
sb.append(
"cleari()\n"
)
sb.append(
"setAnimationDelay(5)\n\n"
)
if (drawItems.isEmpty) {
sb.append(
"// NOTHING DRAWN\n"
)
} else {
var i: Int =
0
while (
i < drawItems.length
) {
val item =
drawItems(i)
if (
item.visible &&
item.points.nonEmpty
) {
sb.append(
"// " +
item.name +
"\n"
)
sb.append(
"setPenColor(" +
colorName(
item.stroke
) +
")\n"
)
sb.append(
"penUp()\n"
)
sb.append(
"setPosition(" +
(
item.points.head.x -
CENTER_X
) +
", " +
(
CENTER_Y -
item.points.head.y
) +
")\n"
)
sb.append(
"penDown()\n"
)
var j: Int =
1
while (
j < item.points.length
) {
sb.append(
"lineTo(" +
(
item.points(j).x -
CENTER_X
) +
", " +
(
CENTER_Y -
item.points(j).y
) +
")\n"
)
j += 1
}
if (item.filled && item.points.length >= 3) {
sb.append(
"setFillColor(" +
item.fill.getRed + "," +
item.fill.getGreen + "," +
item.fill.getBlue +
")\n"
)
sb.append(
"// Fill RGB: " +
item.fill.getRed + "," +
item.fill.getGreen + "," +
item.fill.getBlue +
"\n"
)
}
sb.append(
"\n"
)
}
i += 1
}
}
generatedCode =
sb.toString
codeArea.setText(
generatedCode
)
setStatus(
"DRAW CODE GENERATED"
)
}
// ============================================================
// COPY / SAVE
// ============================================================
def copyText(
text: String
): Unit = {
try {
val clipboard =
java.awt.Toolkit
.getDefaultToolkit
.getSystemClipboard
clipboard.setContents(
new java.awt.datatransfer.StringSelection(
text
),
null
)
setStatus(
"COPIED"
)
} catch {
case _: Exception =>
setStatus(
"COPY FAILED"
)
}
}
def saveText(
text: String
): Unit = {
val chooser =
new JFileChooser()
chooser.setDialogTitle(
"SAVE SCALA CODE"
)
if (
chooser.showSaveDialog(
mainFrame
) ==
JFileChooser.APPROVE_OPTION
) {
try {
var file =
chooser.getSelectedFile
if (
!file.getName
.toLowerCase
.endsWith(".scala")
) {
file =
new java.io.File(
file.getAbsolutePath +
".scala"
)
}
val out =
new java.io.PrintWriter(
file
)
out.write(text)
out.close()
setStatus(
"FILE SAVED"
)
} catch {
case _: Exception =>
setStatus(
"SAVE FAILED"
)
}
}
}
// ============================================================
// ANIMATION CODE
// ============================================================
def generateAnimationCode(): Unit = {
val sb =
new StringBuilder()
sb.append(
"// ULTRA LEGEND ANIMATION CODE\n"
)
sb.append(
"cleari()\n"
)
sb.append(
"val p = Picture.circle(40)\n"
)
sb.append(
"p.setPosition(0,0)\n"
)
sb.append(
"draw(p)\n\n"
)
sb.append(
"animate {\n"
)
sb.append(
" p.forward(3)\n"
)
sb.append(
" p.rotate(3)\n"
)
sb.append(
"}\n"
)
generatedAnimationCode =
sb.toString
codeArea.setText(
generatedAnimationCode
)
setStatus(
"ANIMATION CODE GENERATED"
)
}
// ============================================================
// SIMPLE 2D / PSEUDO 3D PREVIEW
// ============================================================
def openPreview3D(): Unit = {
val frame =
new JFrame(
"ULTRA 2D / 3D PREVIEW"
)
frame.setDefaultCloseOperation(
WindowConstants.DISPOSE_ON_CLOSE
)
frame.setSize(
900,
700
)
frame.setLocationRelativeTo(
mainFrame
)
var angle: Double =
0.0
var running: Boolean =
true
val preview =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
g.setColor(
new Color(
8,
12,
28
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
val cx =
getWidth / 2
val cy =
getHeight / 2
val size3D =
180
val depth =
(
math.sin(angle) *
45
).toInt
val left =
cx - size3D
val top =
cy - size3D
val right =
cx + size3D
val bottom =
cy + size3D
g.setColor(
new Color(
40,
160,
240
)
)
g.fillRect(
left,
top,
size3D * 2,
size3D * 2
)
g.setColor(
new Color(
20,
100,
180
)
)
val side =
new java.awt.Polygon()
side.addPoint(
right,
top
)
side.addPoint(
right + depth,
top - 30
)
side.addPoint(
right + depth,
bottom - 30
)
side.addPoint(
right,
bottom
)
g.fillPolygon(
side
)
g.setColor(
new Color(
30,
120,
205
)
)
val topFace =
new java.awt.Polygon()
topFace.addPoint(
left,
top
)
topFace.addPoint(
right,
top
)
topFace.addPoint(
right + depth,
top - 30
)
topFace.addPoint(
left + depth,
top - 30
)
g.fillPolygon(
topFace
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
22
)
)
g.drawString(
"PSEUDO 3D LIVE PREVIEW",
25,
35
)
g.drawString(
"Rotate / Animate",
25,
65
)
}
}
val previewTimer: Timer =
new Timer(
30,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (running) {
angle +=
0.06
preview.repaint()
}
}
}
)
previewTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
running =
false
previewTimer.stop()
}
}
)
frame.add(
preview
)
frame.setVisible(
true
)
}
// ============================================================
// GAME DATABASE
// ============================================================
val gameDatabase =
Array(
GameInfo(
"SLING BALL",
"MOUSE",
"Drag and release the ball toward the target."
),
GameInfo(
"PONG",
"MOUSE",
"Move the paddle and keep the ball alive."
),
GameInfo(
"SNAKE",
"KEYBOARD",
"Use arrows or WASD to collect food."
),
GameInfo(
"BREAKOUT",
"MOUSE",
"Move the paddle and break blocks."
),
GameInfo(
"FLAPPY BIRD",
"MOUSE",
"Click to flap and pass the obstacles."
),
GameInfo(
"CAR DODGE",
"KEYBOARD",
"Move left and right to avoid traffic."
),
GameInfo(
"SPACE SHOOTER",
"KEYBOARD",
"Move and fire at falling enemies."
),
GameInfo(
"TARGET RUSH",
"MOUSE",
"Click targets as quickly as possible."
),
GameInfo(
"BALL CATCH",
"MOUSE",
"Move the basket and catch falling balls."
),
GameInfo(
"MAZE BALL",
"MOUSE",
"Drag the ball through a maze."
),
GameInfo(
"TIC TAC TOE",
"MOUSE",
"Play against a simple computer."
),
GameInfo(
"KEYBOARD RUNNER",
"KEYBOARD",
"Run, jump, collect coins and avoid enemies."
),
GameInfo(
"KEYBOARD SPACE",
"KEYBOARD",
"Control a spaceship and shoot enemies."
),
GameInfo(
"PLATFORM QUEST",
"KEYBOARD",
"Jump, collect coins and reach the goal."
),
GameInfo(
"DODGE FIELD",
"MOUSE",
"Move around and avoid falling enemies."
)
)
// ============================================================
// GAME WINDOW
// ============================================================
def gameFrame(
title: String
): JFrame = {
val frame =
new JFrame(title)
frame.setDefaultCloseOperation(
WindowConstants.DISPOSE_ON_CLOSE
)
frame.setSize(
900,
650
)
frame.setLocationRelativeTo(
mainFrame
)
frame
}
// ============================================================
// GAME 1 - SLING BALL
// ============================================================
def playSlingBall(): Unit = {
val frame =
gameFrame(
"SLING BALL - MOUSE"
)
var ballX: Double =
110.0
var ballY: Double =
510.0
var vx: Double =
0.0
var vy: Double =
0.0
var launched: Boolean =
false
var won: Boolean =
false
val target =
new java.awt.Rectangle(
760,
90,
70,
70
)
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setPaint(
new GradientPaint(
0,
0,
new Color(30,100,160),
0,
getHeight,
new Color(10,20,40)
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.RED
)
g.fillOval(
target.x,
target.y,
target.width,
target.height
)
g.setColor(
Color.YELLOW
)
g.fillOval(
ballX.toInt - 10,
ballY.toInt - 10,
20,
20
)
g.setColor(
Color.WHITE
)
g.drawString(
if (won)
"YOU WIN!"
else
"MOVE MOUSE + RELEASE TO LAUNCH",
20,
30
)
}
}
panel.addMouseListener(
new MouseAdapter {
override def mouseReleased(
e: MouseEvent
): Unit = {
if (!launched) {
vx =
(
ballX -
e.getX
) * 0.08
vy =
(
ballY -
e.getY
) * 0.08
launched =
true
}
}
}
)
val timer: Timer =
new Timer(
25,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (
launched &&
!won
) {
vy +=
0.25
ballX +=
vx
ballY +=
vy
if (
ballX < 10 ||
ballX > frame.getWidth - 10
) {
vx =
-vx
}
if (ballY < 10) {
vy =
-vy
}
if (
ballY >
frame.getHeight + 20
) {
ballX =
110
ballY =
510
vx =
0
vy =
0
launched =
false
}
if (
target.contains(
ballX.toInt,
ballY.toInt
)
) {
won =
true
}
panel.repaint()
}
}
}
)
timer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
timer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 2 - PONG
// ============================================================
def playPong(): Unit = {
val frame =
gameFrame(
"PONG - MOUSE"
)
var paddleX: Double =
350.0
var ballX: Double =
420.0
var ballY: Double =
280.0
var vx: Double =
5.0
var vy: Double =
4.0
var score: Int =
0
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
Color.BLACK
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.WHITE
)
g.fillOval(
ballX.toInt - 10,
ballY.toInt - 10,
20,
20
)
g.fillRoundRect(
paddleX.toInt,
getHeight - 60,
150,
20,
10,
10
)
g.drawString(
"SCORE: " + score,
20,
30
)
}
}
panel.addMouseMotionListener(
new MouseMotionAdapter {
override def mouseMoved(
e: MouseEvent
): Unit = {
paddleX =
math.max(
0,
math.min(
panel.getWidth - 150,
e.getX - 75
)
)
}
}
)
val pongTimer: Timer =
new Timer(
25,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
ballX +=
vx
ballY +=
vy
if (
ballX < 10 ||
ballX > panel.getWidth - 10
) {
vx =
-vx
}
if (ballY < 10) {
vy =
-vy
}
if (
ballY >
panel.getHeight - 85 &&
ballX >= paddleX &&
ballX <= paddleX + 150
) {
vy =
-math.abs(vy)
score +=
1
}
if (
ballY >
panel.getHeight
) {
ballX =
panel.getWidth / 2.0
ballY =
panel.getHeight / 2.0
vx =
5.0
vy =
-4.0
score =
0
}
panel.repaint()
}
}
)
pongTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
pongTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 3 - KEYBOARD SNAKE
// ============================================================
def playSnake(): Unit = {
val frame =
gameFrame(
"SNAKE - KEYBOARD"
)
val cell: Int =
25
val body =
ArrayBuffer[Point](
new Point(10,10),
new Point(9,10),
new Point(8,10)
)
var food =
new Point(
16,
10
)
var dirX: Int =
1
var dirY: Int =
0
var score: Int =
0
var running: Boolean =
true
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
new Color(15,50,25)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
new Color(
255,
255,
255,
20
)
)
var gx: Int =
0
while (
gx <= getWidth
) {
g.drawLine(
gx,
0,
gx,
getHeight
)
gx +=
cell
}
var gy: Int =
0
while (
gy <= getHeight
) {
g.drawLine(
0,
gy,
getWidth,
gy
)
gy +=
cell
}
g.setColor(
Color.RED
)
g.fillOval(
food.x * cell + 3,
food.y * cell + 3,
cell - 6,
cell - 6
)
var i: Int =
0
while (
i < body.length
) {
g.setColor(
if (i == 0)
new Color(80,250,100)
else
new Color(35,175,65)
)
g.fillRoundRect(
body(i).x * cell + 2,
body(i).y * cell + 2,
cell - 4,
cell - 4,
8,
8
)
i +=
1
}
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
18
)
)
g.drawString(
"ARROWS / WASD SCORE: " + score,
15,
25
)
if (!running) {
g.setFont(
new Font(
"Arial",
Font.BOLD,
42
)
)
g.drawString(
"GAME OVER",
290,
300
)
}
}
}
panel.setFocusable(
true
)
panel.addKeyListener(
new KeyAdapter {
override def keyPressed(
e: KeyEvent
): Unit = {
val k =
e.getKeyCode
if (
k == KeyEvent.VK_LEFT ||
k == KeyEvent.VK_A
) {
if (dirX != 1) {
dirX =
-1
dirY =
0
}
} else if (
k == KeyEvent.VK_RIGHT ||
k == KeyEvent.VK_D
) {
if (dirX != -1) {
dirX =
1
dirY =
0
}
} else if (
k == KeyEvent.VK_UP ||
k == KeyEvent.VK_W
) {
if (dirY != 1) {
dirX =
0
dirY =
-1
}
} else if (
k == KeyEvent.VK_DOWN ||
k == KeyEvent.VK_S
) {
if (dirY != -1) {
dirX =
0
dirY =
1
}
}
}
}
)
val snakeTimer: Timer =
new Timer(
110,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (
running &&
body.nonEmpty
) {
val head =
body.head
val nx =
head.x +
dirX
val ny =
head.y +
dirY
val cols =
math.max(
1,
panel.getWidth / cell
)
val rows =
math.max(
1,
panel.getHeight / cell
)
if (
nx < 0 ||
ny < 0 ||
nx >= cols ||
ny >= rows
) {
running =
false
} else {
val newHead =
new Point(
nx,
ny
)
var hitSelf: Boolean =
false
var i: Int =
0
while (
i < body.length
) {
if (
body(i).x == newHead.x &&
body(i).y == newHead.y
) {
hitSelf =
true
}
i +=
1
}
if (hitSelf) {
running =
false
} else {
body.prepend(
newHead
)
if (
newHead.x == food.x &&
newHead.y == food.y
) {
score +=
1
var foundFood: Boolean =
false
while (!foundFood) {
val fx =
(
RANDOM_GEN.nextDouble() *
cols
).toInt
val fy =
(
RANDOM_GEN.nextDouble() *
rows
).toInt
foundFood =
true
var q: Int =
0
while (
q < body.length
) {
if (
body(q).x == fx &&
body(q).y == fy
) {
foundFood =
false
}
q +=
1
}
if (foundFood) {
food =
new Point(
fx,
fy
)
}
}
} else {
body.remove(
body.length - 1
)
}
}
}
panel.repaint()
}
}
}
)
snakeTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
snakeTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
panel.requestFocusInWindow()
}
// ============================================================
// GAME 4 - BREAKOUT
// ============================================================
def playBreakout(): Unit = {
val frame =
gameFrame(
"BREAKOUT - MOUSE"
)
var paddleX: Double =
350.0
var bx: Double =
450.0
var by: Double =
420.0
var vx: Double =
4.0
var vy: Double =
-4.0
var score: Int =
0
val blocks =
ArrayBuffer[Point]()
var r: Int =
0
while (
r < 5
) {
var c: Int =
0
while (
c < 10
) {
blocks +=
new Point(
40 + c * 75,
70 + r * 32
)
c +=
1
}
r +=
1
}
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
new Color(18,18,40)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.WHITE
)
g.fillOval(
bx.toInt - 9,
by.toInt - 9,
18,
18
)
g.setColor(
Color.CYAN
)
g.fillRoundRect(
paddleX.toInt,
getHeight - 55,
150,
18,
10,
10
)
g.setColor(
Color.ORANGE
)
var i: Int =
0
while (
i < blocks.length
) {
g.fillRect(
blocks(i).x,
blocks(i).y,
65,
22
)
i +=
1
}
g.setColor(
Color.WHITE
)
g.drawString(
"SCORE: " + score,
20,
28
)
}
}
panel.addMouseMotionListener(
new MouseMotionAdapter {
override def mouseMoved(
e: MouseEvent
): Unit = {
paddleX =
math.max(
0,
math.min(
panel.getWidth - 150,
e.getX - 75
)
)
}
}
)
val breakoutTimer: Timer =
new Timer(
25,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
bx +=
vx
by +=
vy
if (
bx < 10 ||
bx > panel.getWidth - 10
) {
vx =
-vx
}
if (by < 10) {
vy =
-vy
}
if (
by >
panel.getHeight - 75 &&
bx >= paddleX &&
bx <= paddleX + 150
) {
vy =
-math.abs(vy)
}
var hitBlock: Int =
-1
var i: Int =
0
while (
i < blocks.length
) {
if (
bx >= blocks(i).x &&
bx <= blocks(i).x + 65 &&
by >= blocks(i).y &&
by <= blocks(i).y + 22
) {
hitBlock =
i
}
i +=
1
}
if (
hitBlock >= 0
) {
blocks.remove(
hitBlock
)
vy =
-vy
score +=
1
}
if (
by >
panel.getHeight
) {
bx =
panel.getWidth / 2.0
by =
panel.getHeight / 2.0
vx =
4.0
vy =
-4.0
score =
0
}
panel.repaint()
}
}
)
breakoutTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
breakoutTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 5 - FLAPPY
// ============================================================
def playFlappy(): Unit = {
val frame =
gameFrame(
"FLAPPY BIRD - MOUSE"
)
var birdY: Double =
280.0
var birdV: Double =
0.0
var pipeX: Double =
850.0
var gap: Double =
290.0
var score: Int =
0
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setPaint(
new GradientPaint(
0,
0,
new Color(70,190,255),
0,
getHeight,
Color.WHITE
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.YELLOW
)
g.fillOval(
150,
birdY.toInt,
45,
35
)
g.setColor(
Color.GREEN
)
g.fillRect(
pipeX.toInt,
0,
70,
gap.toInt - 85
)
g.fillRect(
pipeX.toInt,
gap.toInt + 85,
70,
getHeight
)
g.setColor(
Color.BLACK
)
g.drawString(
"CLICK TO FLAP SCORE: " + score,
20,
30
)
}
}
panel.addMouseListener(
new MouseAdapter {
override def mousePressed(
e: MouseEvent
): Unit = {
birdV =
-9.0
}
}
)
val flappyTimer: Timer =
new Timer(
25,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
birdV +=
0.4
birdY +=
birdV
pipeX -=
5.0
if (
pipeX <
-80
) {
pipeX =
panel.getWidth + 20
gap =
170 +
RANDOM_GEN.nextDouble() *
220
score +=
1
}
if (
birdY < 0 ||
birdY > panel.getHeight
) {
birdY =
280
birdV =
0
pipeX =
panel.getWidth
score =
0
}
panel.repaint()
}
}
)
flappyTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
flappyTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 6 - KEYBOARD CAR
// ============================================================
def playKeyboardCar(): Unit = {
val frame =
gameFrame(
"CAR DODGE - KEYBOARD"
)
var playerX: Double =
420.0
var enemyX: Double =
240.0
var enemyY: Double =
-120.0
var score: Int =
0
var running: Boolean =
true
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
new Color(45,45,50)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.WHITE
)
var roadY: Int =
0
while (
roadY < getHeight
) {
g.fillRect(
getWidth / 2 - 5,
roadY,
10,
60
)
roadY +=
100
}
g.setColor(
Color.BLUE
)
g.fillRoundRect(
playerX.toInt,
getHeight - 125,
60,
100,
18,
18
)
g.setColor(
Color.RED
)
g.fillRoundRect(
enemyX.toInt,
enemyY.toInt,
60,
100,
18,
18
)
g.setColor(
Color.WHITE
)
g.drawString(
"LEFT / RIGHT SCORE: " + score,
15,
28
)
if (!running) {
g.setFont(
new Font(
"Arial",
Font.BOLD,
42
)
)
g.drawString(
"GAME OVER",
300,
300
)
}
}
}
panel.setFocusable(
true
)
panel.addKeyListener(
new KeyAdapter {
override def keyPressed(
e: KeyEvent
): Unit = {
val k =
e.getKeyCode
if (
k == KeyEvent.VK_LEFT ||
k == KeyEvent.VK_A
) {
playerX -=
15
} else if (
k == KeyEvent.VK_RIGHT ||
k == KeyEvent.VK_D
) {
playerX +=
15
}
playerX =
math.max(
0,
math.min(
panel.getWidth - 60,
playerX
)
)
}
}
)
val carTimer: Timer =
new Timer(
25,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (running) {
enemyY +=
7
if (
enemyY >
panel.getHeight
) {
enemyY =
-100
enemyX =
RANDOM_GEN.nextDouble() *
math.max(
1,
panel.getWidth - 60
)
score +=
1
}
val hit =
enemyX + 60 >
playerX &&
enemyX <
playerX + 60 &&
enemyY + 100 >
panel.getHeight - 125 &&
enemyY <
panel.getHeight
if (hit) {
running =
false
}
panel.repaint()
}
}
}
)
carTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
carTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
panel.requestFocusInWindow()
}
// ============================================================
// GAME 7 - KEYBOARD SPACE
// ============================================================
def playKeyboardSpace(): Unit = {
val frame =
gameFrame(
"SPACE SHOOTER - KEYBOARD"
)
var shipX: Double =
420.0
var enemyX: Double =
350.0
var enemyY: Double =
60.0
var laserX: Double =
-100.0
var laserY: Double =
-100.0
var score: Int =
0
var running: Boolean =
true
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
new Color(5,10,30)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.WHITE
)
var s: Int =
0
while (
s < 90
) {
g.fillOval(
(
s * 83
) %
math.max(
1,
getWidth
),
(
s * 47
) %
math.max(
1,
getHeight
),
2,
2
)
s +=
1
}
g.setColor(
Color.CYAN
)
val ship =
new java.awt.Polygon()
ship.addPoint(
shipX.toInt,
getHeight - 70
)
ship.addPoint(
shipX.toInt + 25,
getHeight - 130
)
ship.addPoint(
shipX.toInt + 50,
getHeight - 70
)
g.fillPolygon(
ship
)
g.setColor(
Color.RED
)
g.fillOval(
enemyX.toInt,
enemyY.toInt,
45,
35
)
if (
laserY >
-40
) {
g.setColor(
Color.YELLOW
)
g.fillRect(
laserX.toInt,
laserY.toInt,
6,
20
)
}
g.setColor(
Color.WHITE
)
g.drawString(
"LEFT / RIGHT + SPACE SCORE: " +
score,
15,
28
)
if (!running) {
g.setFont(
new Font(
"Arial",
Font.BOLD,
38
)
)
g.drawString(
"MISSION OVER",
285,
300
)
}
}
}
panel.setFocusable(
true
)
panel.addKeyListener(
new KeyAdapter {
override def keyPressed(
e: KeyEvent
): Unit = {
val k =
e.getKeyCode
if (
k == KeyEvent.VK_LEFT ||
k == KeyEvent.VK_A
) {
shipX -=
12
} else if (
k == KeyEvent.VK_RIGHT ||
k == KeyEvent.VK_D
) {
shipX +=
12
} else if (
k == KeyEvent.VK_SPACE
) {
laserX =
shipX + 22
laserY =
panel.getHeight - 150
}
shipX =
math.max(
0,
math.min(
panel.getWidth - 50,
shipX
)
)
}
}
)
val spaceTimer: Timer =
new Timer(
25,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (running) {
enemyY +=
2
laserY -=
12
if (
laserX >= enemyX &&
laserX <= enemyX + 45 &&
laserY <= enemyY + 35 &&
laserY >= enemyY - 20
) {
score +=
1
enemyX =
RANDOM_GEN.nextDouble() *
math.max(
1,
panel.getWidth - 45
)
enemyY =
50
}
if (
enemyY >
panel.getHeight
) {
enemyY =
50
}
panel.repaint()
}
}
}
)
spaceTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
spaceTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
panel.requestFocusInWindow()
}
// ============================================================
// GAME 8 - TARGET RUSH
// ============================================================
def playTargetRush(): Unit = {
val frame =
gameFrame(
"TARGET RUSH - MOUSE"
)
var tx: Double =
350.0
var ty: Double =
250.0
var score: Int =
0
var timeLeft: Int =
20
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
new Color(20,25,60)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.RED
)
g.fillOval(
tx.toInt,
ty.toInt,
80,
80
)
g.setColor(
Color.WHITE
)
g.drawOval(
tx.toInt + 18,
ty.toInt + 18,
44,
44
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
20
)
)
g.drawString(
"SCORE: " +
score +
" TIME: " +
timeLeft,
20,
30
)
}
}
panel.addMouseListener(
new MouseAdapter {
override def mousePressed(
e: MouseEvent
): Unit = {
if (
e.getX >= tx &&
e.getX <= tx + 80 &&
e.getY >= ty &&
e.getY <= ty + 80 &&
timeLeft > 0
) {
score +=
1
tx =
RANDOM_GEN.nextDouble() *
math.max(
1,
panel.getWidth - 80
)
ty =
70 +
RANDOM_GEN.nextDouble() *
math.max(
1,
panel.getHeight - 150
)
panel.repaint()
}
}
}
)
val targetTimer: Timer =
new Timer(
1000,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
timeLeft -=
1
if (
timeLeft <=
0
) {
timeLeft =
20
score =
0
}
panel.repaint()
}
}
)
targetTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
targetTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 9 - BALL CATCH
// ============================================================
def playBallCatch(): Unit = {
val frame =
gameFrame(
"BALL CATCH - MOUSE"
)
var basketX: Double =
350.0
var ballX: Double =
200.0
var ballY: Double =
-50.0
var score: Int =
0
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setPaint(
new GradientPaint(
0,
0,
new Color(25,120,220),
0,
getHeight,
new Color(170,235,255)
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.YELLOW
)
g.fillOval(
ballX.toInt,
ballY.toInt,
35,
35
)
g.setColor(
new Color(150,85,20)
)
g.fillRoundRect(
basketX.toInt,
getHeight - 80,
150,
35,
15,
15
)
g.setColor(
Color.WHITE
)
g.drawString(
"SCORE: " + score,
20,
30
)
}
}
panel.addMouseMotionListener(
new MouseMotionAdapter {
override def mouseMoved(
e: MouseEvent
): Unit = {
basketX =
math.max(
0,
math.min(
panel.getWidth - 150,
e.getX - 75
)
)
}
}
)
val catchTimer: Timer =
new Timer(
30,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
ballY +=
6
if (
ballY >
panel.getHeight - 110
) {
if (
ballX + 35 >= basketX &&
ballX <= basketX + 150
) {
score +=
1
}
ballX =
RANDOM_GEN.nextDouble() *
math.max(
1,
panel.getWidth - 35
)
ballY =
-40
}
panel.repaint()
}
}
)
catchTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
catchTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 10 - MAZE BALL
// ============================================================
def playMazeBall(): Unit = {
val frame =
gameFrame(
"MAZE BALL - MOUSE"
)
var ballX: Double =
70.0
var ballY: Double =
70.0
val target =
new java.awt.Rectangle(
730,
500,
60,
60
)
val walls =
Array(
new java.awt.Rectangle(
170,
90,
520,
20
),
new java.awt.Rectangle(
170,
90,
20,
340
),
new java.awt.Rectangle(
420,
220,
20,
330
),
new java.awt.Rectangle(
680,
90,
20,
430
)
)
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
Color.WHITE
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
new Color(75,75,85)
)
var i: Int =
0
while (
i < walls.length
) {
g.fillRect(
walls(i).x,
walls(i).y,
walls(i).width,
walls(i).height
)
i +=
1
}
g.setColor(
Color.GREEN
)
g.fillOval(
target.x,
target.y,
target.width,
target.height
)
g.setColor(
Color.RED
)
g.fillOval(
ballX.toInt,
ballY.toInt,
35,
35
)
g.setColor(
Color.BLACK
)
g.drawString(
"DRAG RED BALL TO GREEN TARGET",
20,
30
)
}
}
panel.addMouseMotionListener(
new MouseMotionAdapter {
override def mouseDragged(
e: MouseEvent
): Unit = {
val nx =
e.getX - 17
val ny =
e.getY - 17
val probe =
new java.awt.Rectangle(
nx,
ny,
35,
35
)
var hitWall: Boolean =
false
var i: Int =
0
while (
i < walls.length
) {
if (
walls(i).intersects(
probe
)
) {
hitWall =
true
}
i +=
1
}
if (!hitWall) {
ballX =
math.max(
0,
math.min(
panel.getWidth - 35,
nx
)
)
ballY =
math.max(
0,
math.min(
panel.getHeight - 35,
ny
)
)
}
if (
target.contains(
ballX.toInt + 17,
ballY.toInt + 17
)
) {
JOptionPane.showMessageDialog(
frame,
"YOU WIN!"
)
}
panel.repaint()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 11 - TIC TAC TOE
// ============================================================
def playTicTacToe(): Unit = {
val frame =
gameFrame(
"TIC TAC TOE"
)
val board =
Array.fill[Int](
9
)(
0
)
var humanTurn: Boolean =
true
var finished: Boolean =
false
def win(
player: Int
): Boolean = {
val lines =
Array(
Array(0,1,2),
Array(3,4,5),
Array(6,7,8),
Array(0,3,6),
Array(1,4,7),
Array(2,5,8),
Array(0,4,8),
Array(2,4,6)
)
var result: Boolean =
false
var i: Int =
0
while (
i < lines.length
) {
if (
board(lines(i)(0)) == player &&
board(lines(i)(1)) == player &&
board(lines(i)(2)) == player
) {
result =
true
}
i +=
1
}
result
}
def full(): Boolean = {
var result: Boolean =
true
var i: Int =
0
while (
i < 9
) {
if (
board(i) == 0
) {
result =
false
}
i +=
1
}
result
}
def computerMove(): Int = {
var chosen: Int =
-1
var i: Int =
0
while (
i < 9 &&
chosen < 0
) {
if (
board(i) == 0
) {
chosen =
i
}
i +=
1
}
chosen
}
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setColor(
new Color(
245,
248,
255
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.BLACK
)
g.setStroke(
new BasicStroke(
6
)
)
var i: Int =
1
while (
i < 3
) {
g.drawLine(
i * getWidth / 3,
0,
i * getWidth / 3,
getHeight
)
g.drawLine(
0,
i * getHeight / 3,
getWidth,
i * getHeight / 3
)
i +=
1
}
g.setFont(
new Font(
"Arial",
Font.BOLD,
82
)
)
i =
0
while (
i < 9
) {
if (
board(i) != 0
) {
g.setColor(
if (
board(i) == 1
)
Color.BLUE
else
Color.RED
)
val col =
i % 3
val row =
i / 3
g.drawString(
if (
board(i) == 1
)
"X"
else
"O",
col *
getWidth / 3 +
65,
row *
getHeight / 3 +
105
)
}
i +=
1
}
g.setColor(
Color.DARK_GRAY
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
18
)
)
g.drawString(
if (finished)
"GAME OVER"
else if (humanTurn)
"YOUR TURN"
else
"COMPUTER TURN",
20,
28
)
}
}
panel.addMouseListener(
new MouseAdapter {
override def mousePressed(
e: MouseEvent
): Unit = {
if (
humanTurn &&
!finished
) {
val cellW =
math.max(
1,
panel.getWidth / 3
)
val cellH =
math.max(
1,
panel.getHeight / 3
)
val col =
math.max(
0,
math.min(
2,
e.getX / cellW
)
)
val row =
math.max(
0,
math.min(
2,
e.getY / cellH
)
)
val idx =
row * 3 + col
if (
board(idx) == 0
) {
board(idx) =
1
if (
win(1) ||
full()
) {
finished =
true
} else {
humanTurn =
false
val ai =
computerMove()
if (
ai >= 0
) {
board(ai) =
2
}
if (
win(2) ||
full()
) {
finished =
true
} else {
humanTurn =
true
}
}
panel.repaint()
}
}
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
}
// ============================================================
// GAME 12 - KEYBOARD RUNNER
// ============================================================
def playKeyboardRunner(): Unit = {
val frame =
gameFrame(
"KEYBOARD RUNNER"
)
var px: Double =
100.0
var py: Double =
430.0
var velY: Double =
0.0
var score: Int =
0
var jumping: Boolean =
false
var running: Boolean =
true
val coins =
ArrayBuffer[Point](
new Point(260,390),
new Point(470,320),
new Point(670,390),
new Point(820,280)
)
val enemies =
ArrayBuffer[Point](
new Point(530,450),
new Point(760,450)
)
val panel =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setPaint(
new GradientPaint(
0,
0,
new Color(70,180,250),
0,
getHeight,
new Color(210,240,255)
)
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
new Color(65,180,75)
)
g.fillRect(
0,
getHeight - 100,
getWidth,
100
)
g.setColor(
Color.BLUE
)
g.fillRoundRect(
px.toInt,
py.toInt,
45,
65,
10,
10
)
g.setColor(
Color.YELLOW
)
var i: Int =
0
while (
i < coins.length
) {
g.fillOval(
coins(i).x,
coins(i).y,
25,
25
)
i +=
1
}
g.setColor(
Color.RED
)
i =
0
while (
i < enemies.length
) {
g.fillRect(
enemies(i).x,
enemies(i).y,
45,
45
)
i +=
1
}
g.setColor(
Color.WHITE
)
g.drawString(
"A/D or ARROWS SPACE = JUMP SCORE: " +
score,
15,
28
)
if (!running) {
g.setColor(
Color.BLACK
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
g.setColor(
Color.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
42
)
)
g.drawString(
"GAME OVER",
300,
300
)
}
}
}
panel.setFocusable(
true
)
panel.addKeyListener(
new KeyAdapter {
override def keyPressed(
e: KeyEvent
): Unit = {
if (!running) {
return
}
val k =
e.getKeyCode
if (
k == KeyEvent.VK_LEFT ||
k == KeyEvent.VK_A
) {
px -=
12
} else if (
k == KeyEvent.VK_RIGHT ||
k == KeyEvent.VK_D
) {
px +=
12
} else if (
k == KeyEvent.VK_SPACE ||
k == KeyEvent.VK_UP
) {
if (!jumping) {
velY =
-11
jumping =
true
}
}
px =
math.max(
0,
math.min(
panel.getWidth - 45,
px
)
)
}
}
)
val runnerTimer: Timer =
new Timer(
30,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
if (running) {
velY +=
0.55
py +=
velY
val ground =
panel.getHeight - 165
if (
py >= ground
) {
py =
ground
velY =
0
jumping =
false
}
var i: Int =
coins.length - 1
while (
i >= 0
) {
val c =
coins(i)
val got =
px + 45 > c.x &&
px < c.x + 25 &&
py + 65 > c.y &&
py < c.y + 25
if (got) {
coins.remove(i)
score +=
10
}
i -=
1
}
i =
0
while (
i < enemies.length
) {
enemies(i).x -=
3
if (
enemies(i).x < -60
) {
enemies(i).x =
panel.getWidth +
100 +
i * 200
}
val hit =
px + 45 > enemies(i).x &&
px < enemies(i).x + 45 &&
py + 65 > enemies(i).y &&
py < enemies(i).y + 45
if (hit) {
running =
false
}
i +=
1
}
panel.repaint()
}
}
}
)
runnerTimer.start()
frame.addWindowListener(
new java.awt.event.WindowAdapter {
override def windowClosed(
e: java.awt.event.WindowEvent
): Unit = {
runnerTimer.stop()
}
}
)
frame.add(
panel
)
frame.setVisible(
true
)
panel.requestFocusInWindow()
}
// ============================================================
// GAME ROUTER
// ============================================================
def launchGame(
name: String
): Unit = {
if (
name == "SLING BALL"
) {
playSlingBall()
} else if (
name == "PONG"
) {
playPong()
} else if (
name == "SNAKE"
) {
playSnake()
} else if (
name == "BREAKOUT"
) {
playBreakout()
} else if (
name == "FLAPPY BIRD"
) {
playFlappy()
} else if (
name == "CAR DODGE"
) {
playKeyboardCar()
} else if (
name == "SPACE SHOOTER"
) {
playKeyboardSpace()
} else if (
name == "TARGET RUSH"
) {
playTargetRush()
} else if (
name == "BALL CATCH"
) {
playBallCatch()
} else if (
name == "MAZE BALL"
) {
playMazeBall()
} else if (
name == "TIC TAC TOE"
) {
playTicTacToe()
} else {
playKeyboardRunner()
}
}
// ============================================================
// GAME STUDIO
// ============================================================
def openGameStudio(): Unit = {
val frame =
new JFrame(
"ULTRA LEGEND GAME STUDIO"
)
frame.setDefaultCloseOperation(
WindowConstants.DISPOSE_ON_CLOSE
)
frame.setSize(
1250,
780
)
frame.setLocationRelativeTo(
mainFrame
)
val model =
new DefaultListModel[String]()
var i: Int =
0
while (
i < gameDatabase.length
) {
model.addElement(
gameDatabase(i).name
)
i +=
1
}
val list =
new JList[String](
model
)
list.setSelectionMode(
ListSelectionModel.SINGLE_SELECTION
)
list.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
val search =
new JTextField(
14
)
val searchButton =
button(
"SEARCH"
)
val playButton =
button(
"PLAY"
)
val codeButton =
button(
"GAME CODE"
)
val closeButton =
button(
"CLOSE"
)
val info =
new JTextArea()
info.setEditable(
false
)
info.setFont(
new Font(
"Monospaced",
Font.PLAIN,
13
)
)
info.setText(
"ULTRA LEGEND GAME STUDIO\n\n" +
"MOUSE + KEYBOARD GAMES\n\n" +
"DOUBLE CLICK A GAME TO PLAY.\n" +
"SELECT GAME AND PRESS GAME CODE.\n"
)
val searchPanel =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
searchPanel.add(
new JLabel(
"SEARCH:"
)
)
searchPanel.add(
search
)
searchPanel.add(
searchButton
)
val left =
new JPanel(
new BorderLayout()
)
left.add(
new JLabel(
"GAME LIBRARY",
SwingConstants.CENTER
),
BorderLayout.NORTH
)
left.add(
searchPanel,
BorderLayout.CENTER
)
left.add(
new JScrollPane(
list
),
BorderLayout.SOUTH
)
val right =
new JPanel(
new BorderLayout()
)
right.add(
new JLabel(
"GAME INFORMATION",
SwingConstants.CENTER
),
BorderLayout.NORTH
)
right.add(
new JScrollPane(
info
),
BorderLayout.CENTER
)
val split =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
left,
right
)
split.setDividerLocation(
380
)
list.addListSelectionListener(
new javax.swing.event.ListSelectionListener {
override def valueChanged(
e: javax.swing.event.ListSelectionEvent
): Unit = {
if (
!e.getValueIsAdjusting
) {
val selected =
list.getSelectedValue
if (
selected != null
) {
var j: Int =
0
while (
j < gameDatabase.length
) {
if (
gameDatabase(j).name ==
selected
) {
info.setText(
"GAME\n\n" +
gameDatabase(j).name +
"\n\nMODE: " +
gameDatabase(j).mode +
"\n\n" +
gameDatabase(j).description
)
}
j +=
1
}
}
}
}
}
)
searchButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val q =
search.getText
.trim
.toLowerCase
model.clear()
var j: Int =
0
while (
j < gameDatabase.length
) {
if (
q.isEmpty ||
gameDatabase(j).name
.toLowerCase
.indexOf(q) >= 0
) {
model.addElement(
gameDatabase(j).name
)
}
j +=
1
}
}
}
)
playButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val selected =
list.getSelectedValue
if (
selected != null
) {
launchGame(
selected
)
} else {
JOptionPane.showMessageDialog(
frame,
"SELECT A GAME FIRST"
)
}
}
}
)
list.addMouseListener(
new MouseAdapter {
override def mouseClicked(
e: MouseEvent
): Unit = {
if (
e.getClickCount == 2
) {
val selected =
list.getSelectedValue
if (
selected != null
) {
launchGame(
selected
)
}
}
}
}
)
codeButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val selected =
list.getSelectedValue
if (
selected != null
) {
generatedAnimationCode =
"// GAME SOURCE: " +
selected +
"\n\n" +
"cleari()\n" +
"setAnimationDelay(5)\n\n" +
"// Launch from ULTRA LEGEND GAME STUDIO."
val codeFrame =
new JFrame(
"GAME CODE"
)
codeFrame.setDefaultCloseOperation(
WindowConstants.DISPOSE_ON_CLOSE
)
codeFrame.setSize(
820,
580
)
codeFrame.setLocationRelativeTo(
frame
)
val area =
new JTextArea(
generatedAnimationCode
)
area.setFont(
new Font(
"Monospaced",
Font.PLAIN,
13
)
)
val copyGame =
button(
"COPY"
)
copyGame.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
copyText(
area.getText
)
}
}
)
codeFrame.add(
new JScrollPane(
area
),
BorderLayout.CENTER
)
codeFrame.add(
copyGame,
BorderLayout.SOUTH
)
codeFrame.setVisible(
true
)
} else {
JOptionPane.showMessageDialog(
frame,
"SELECT A GAME FIRST"
)
}
}
}
)
closeButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
frame.dispose()
}
}
)
val bottom =
new JPanel(
new FlowLayout(
FlowLayout.CENTER
)
)
bottom.add(
playButton
)
bottom.add(
codeButton
)
bottom.add(
closeButton
)
frame.setLayout(
new BorderLayout()
)
frame.add(
split,
BorderLayout.CENTER
)
frame.add(
bottom,
BorderLayout.SOUTH
)
frame.setVisible(
true
)
}
// ============================================================
// MAIN CANVAS
// ============================================================
canvas =
new JPanel {
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
g.setColor(
Color.WHITE
)
g.fillRect(
0,
0,
getWidth,
getHeight
)
if (showGrid) {
g.setColor(
new Color(
0,
0,
0,
20
)
)
var x: Int =
0
while (
x <= CANVAS_W
) {
g.drawLine(
x,
0,
x,
CANVAS_H
)
x +=
50
}
var y: Int =
0
while (
y <= CANVAS_H
) {
g.drawLine(
0,
y,
CANVAS_W,
y
)
y +=
50
}
}
var i: Int =
0
while (
i < drawItems.length
) {
val item =
drawItems(i)
if (
item.visible &&
item.points.length >= 2
) {
if (
item.filled &&
item.points.length >= 3
) {
val path =
new Path2D.Double()
path.moveTo(
item.points.head.x,
item.points.head.y
)
var j: Int =
1
while (
j < item.points.length
) {
path.lineTo(
item.points(j).x,
item.points(j).y
)
j +=
1
}
path.closePath()
g.setColor(
item.fill
)
g.fill(
path
)
}
g.setColor(
item.stroke
)
g.setStroke(
new BasicStroke(
brushWidth,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND
)
)
var j: Int =
1
while (
j < item.points.length
) {
g.drawLine(
item.points(j - 1).x,
item.points(j - 1).y,
item.points(j).x,
item.points(j).y
)
j +=
1
}
}
i +=
1
}
if (
tempPoints.length >= 2
) {
g.setColor(
strokeColor
)
g.setStroke(
new BasicStroke(
brushWidth,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND
)
)
var j: Int =
1
while (
j < tempPoints.length
) {
g.drawLine(
tempPoints(j - 1).x,
tempPoints(j - 1).y,
tempPoints(j).x,
tempPoints(j).y
)
j +=
1
}
}
if (
selectedIndex >= 0 &&
selectedIndex < drawItems.length &&
activeTool == "SELECT"
) {
val r =
boundsOf(
drawItems(
selectedIndex
)
)
val box =
new java.awt.Rectangle(
r.x - 8,
r.y - 8,
r.width + 16,
r.height + 16
)
g.setColor(
new Color(
0,
105,
240
)
)
g.setStroke(
new BasicStroke(
2
)
)
g.drawRect(
box.x,
box.y,
box.width,
box.height
)
val hs =
handles(
box
)
var h: Int =
0
while (
h < hs.length
) {
val hp =
hs(h)._2
g.setColor(
Color.WHITE
)
g.fillRect(
hp.x - 5,
hp.y - 5,
10,
10
)
g.setColor(
new Color(
0,
105,
240
)
)
g.drawRect(
hp.x - 5,
hp.y - 5,
10,
10
)
h +=
1
}
}
}
}
canvas.setFocusable(
true
)
// ============================================================
// CANVAS MOUSE INPUT
// ============================================================
canvas.addMouseListener(
new MouseAdapter {
override def mousePressed(
e: MouseEvent
): Unit = {
val p =
canvasPoint(e)
if (
activeTool == "SELECT"
) {
val hit =
findObject(p)
if (
selectedIndex >= 0 &&
selectedIndex < drawItems.length
) {
val handle =
resizeHandle(
drawItems(
selectedIndex
),
p
)
if (
handle != ""
) {
if (
!drawItems(
selectedIndex
).locked
) {
saveHistory()
resizingNow =
true
movingNow =
false
resizeHandle =
handle
originalPoints =
drawItems(
selectedIndex
).points
lastMousePoint =
p
}
} else if (
hit == selectedIndex
) {
if (
!drawItems(
selectedIndex
).locked
) {
saveHistory()
movingNow =
true
resizingNow =
false
lastMousePoint =
p
}
} else {
selectedIndex =
hit
}
} else {
selectedIndex =
hit
}
repaintCanvas()
} else if (
activeTool == "ERASER"
) {
val hit =
findObject(p)
if (
hit >= 0 &&
!drawItems(hit).locked
) {
saveHistory()
drawItems.remove(
hit
)
selectedIndex =
-1
repaintCanvas()
setStatus(
"ERASED"
)
}
} else if (
activeTool == "BUCKET"
) {
val hit =
findObject(p)
if (
hit >= 0
) {
selectedIndex =
hit
fillSelected()
} else {
setStatus(
"CLICK A CLOSED OBJECT"
)
}
} else {
saveHistory()
tempPoints.clear()
tempPoints +=
p
drawingNow =
true
selectedIndex =
-1
setStatus(
"DRAWING..."
)
}
}
override def mouseReleased(
e: MouseEvent
): Unit = {
if (
activeTool == "SELECT"
) {
movingNow =
false
resizingNow =
false
repaintCanvas()
} else if (
drawingNow
) {
tempPoints +=
canvasPoint(e)
if (
tempPoints.length >= 2
) {
drawItems +=
DrawItem(
tempPoints.toVector,
strokeColor,
fillColor,
closed(
tempPoints.toVector
),
"FREEHAND",
true,
false
)
selectedIndex =
drawItems.length - 1
}
tempPoints.clear()
drawingNow =
false
repaintCanvas()
setStatus(
"DRAWING RECORDED"
)
}
}
}
)
// ============================================================
// CANVAS DRAG INPUT
// ============================================================
canvas.addMouseMotionListener(
new MouseMotionAdapter {
override def mouseDragged(
e: MouseEvent
): Unit = {
val p =
canvasPoint(e)
if (
activeTool == "SELECT"
) {
if (
movingNow &&
selectedIndex >= 0
) {
moveSelected(
p.x -
lastMousePoint.x,
p.y -
lastMousePoint.y
)
lastMousePoint =
p
repaintCanvas()
} else if (
resizingNow &&
selectedIndex >= 0
) {
resizeSelected(
p
)
repaintCanvas()
}
} else if (
drawingNow
) {
if (
tempPoints.isEmpty ||
distance(
tempPoints.last,
p
) >= 2.0
) {
tempPoints +=
p
repaintCanvas()
}
}
}
}
)
// ============================================================
// TOOLBAR BUTTONS
// ============================================================
val pencilBtn =
button(
"PENCIL"
)
val selectBtn =
button(
"SELECT"
)
val eraserBtn =
button(
"ERASER"
)
val bucketBtn =
button(
"BUCKET"
)
val undoBtn =
button(
"UNDO"
)
val redoBtn =
button(
"REDO"
)
val clearBtn =
button(
"CLEAR"
)
val drawCodeBtn =
button(
"DRAW CODE"
)
val animationBtn =
button(
"ANIMATION"
)
val animationStudioBtn =
button(
"ANIMATION DRAW"
)
val preview3DBtn =
button(
"2D / 3D"
)
val copyBtn =
button(
"COPY"
)
val saveBtn =
button(
"SAVE"
)
val gamesBtn =
button(
"GAMES"
)
val gridBtn =
button(
"GRID"
)
val snapBtn =
button(
"SNAP"
)
val strokeBtn =
button(
"STROKE"
)
val fillBtn =
button(
"FILL"
)
// ============================================================
// TOOLBAR
// ============================================================
val toolbar =
new JPanel(
new FlowLayout(
FlowLayout.LEFT,
3,
3
)
)
toolbar.add(
pencilBtn
)
toolbar.add(
selectBtn
)
toolbar.add(
eraserBtn
)
toolbar.add(
bucketBtn
)
toolbar.add(
undoBtn
)
toolbar.add(
redoBtn
)
toolbar.add(
clearBtn
)
toolbar.add(
drawCodeBtn
)
toolbar.add(
animationBtn
)
toolbar.add(
preview3DBtn
)
toolbar.add(
copyBtn
)
toolbar.add(
saveBtn
)
toolbar.add(
gamesBtn
)
toolbar.add(
gridBtn
)
toolbar.add(
snapBtn
)
toolbar.add(
strokeBtn
)
toolbar.add(
fillBtn
)
// ============================================================
// OBJECT / SHAPE LIST UI
// ============================================================
objectList =
new JList[String](
objectModel
)
objectList.setFont(
new Font(
"Arial",
Font.BOLD,
13
)
)
shapeList =
new JList[String](
shapeModel
)
shapeList.setFont(
new Font(
"Arial",
Font.BOLD,
13
)
)
objectSearch =
new JTextField(
12
)
val objectSearchBtn =
button(
"SEARCH"
)
val objectSearchPanel =
new JPanel(
new FlowLayout(
FlowLayout.LEFT
)
)
objectSearchPanel.add(
objectSearch
)
objectSearchPanel.add(
objectSearchBtn
)
val objectPanel =
new JPanel(
new BorderLayout()
)
objectPanel.add(
new JLabel(
"OBJECT LIBRARY",
SwingConstants.CENTER
),
BorderLayout.NORTH
)
objectPanel.add(
objectSearchPanel,
BorderLayout.CENTER
)
objectPanel.add(
new JScrollPane(
objectList
),
BorderLayout.SOUTH
)
val shapePanel =
new JPanel(
new BorderLayout()
)
shapePanel.add(
new JLabel(
"SHAPE LIBRARY",
SwingConstants.CENTER
),
BorderLayout.NORTH
)
shapePanel.add(
new JScrollPane(
shapeList
),
BorderLayout.CENTER
)
val libraryTabs =
new JTabbedPane()
libraryTabs.addTab(
"OBJECTS",
objectPanel
)
libraryTabs.addTab(
"SHAPES",
shapePanel
)
// ============================================================
// SEARCH
// ============================================================
objectSearchBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val q =
objectSearch
.getText
.trim
.toLowerCase
objectModel.clear()
var i: Int =
0
while (
i < objects.length
) {
if (
q.isEmpty ||
objects(i)
.toLowerCase
.indexOf(q) >= 0
) {
objectModel.addElement(
objects(i)
)
}
i +=
1
}
}
}
)
// ============================================================
// DOUBLE CLICK OBJECT
// ============================================================
objectList.addMouseListener(
new MouseAdapter {
override def mouseClicked(
e: MouseEvent
): Unit = {
if (
e.getClickCount == 2
) {
val selected =
objectList.getSelectedValue
if (
selected != null
) {
addObject(
selected
)
}
}
}
}
)
// ============================================================
// DOUBLE CLICK SHAPE
// ============================================================
shapeList.addMouseListener(
new MouseAdapter {
override def mouseClicked(
e: MouseEvent
): Unit = {
if (
e.getClickCount == 2
) {
val selected =
shapeList.getSelectedValue
if (
selected != null
) {
addShape(
selected
)
}
}
}
}
)
// ============================================================
// CODE AREA
// ============================================================
codeArea =
new JTextArea()
codeArea.setEditable(
false
)
codeArea.setFont(
new Font(
"Monospaced",
Font.PLAIN,
13
)
)
codeArea.setText(
"// DRAW SOMETHING\n" +
"// THEN PRESS DRAW CODE"
)
// ============================================================
// PANELS
// ============================================================
val canvasPanel =
new JPanel(
new BorderLayout()
)
canvasPanel.add(
new JLabel(
"DRAWING CANVAS",
SwingConstants.CENTER
),
BorderLayout.NORTH
)
canvasPanel.add(
canvas,
BorderLayout.CENTER
)
val codePanel =
new JPanel(
new BorderLayout()
)
codePanel.add(
new JLabel(
"GENERATED KOJO / SCALA CODE",
SwingConstants.CENTER
),
BorderLayout.NORTH
)
codePanel.add(
new JScrollPane(
codeArea
),
BorderLayout.CENTER
)
val canvasCodeSplit =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
canvasPanel,
codePanel
)
canvasCodeSplit.setDividerLocation(
660
)
val mainSplit =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
libraryTabs,
canvasCodeSplit
)
mainSplit.setDividerLocation(
285
)
// ============================================================
// STATUS
// ============================================================
statusLabel =
new JLabel(
"ULTRA LEGEND X9 READY"
)
statusLabel.setFont(
new Font(
"Arial",
Font.BOLD,
13
)
)
// ============================================================
// MAIN FRAME
// ============================================================
mainFrame =
new JFrame(
"ULTRA LEGEND DRAW + GAME STUDIO X9"
)
mainFrame.setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
mainFrame.setSize(
1650,
900
)
mainFrame.setMinimumSize(
new Dimension(
1100,
650
)
)
mainFrame.setLocationRelativeTo(
null
)
mainFrame.setLayout(
new BorderLayout(
4,
4
)
)
mainFrame.add(
toolbar,
BorderLayout.NORTH
)
mainFrame.add(
mainSplit,
BorderLayout.CENTER
)
mainFrame.add(
statusLabel,
BorderLayout.SOUTH
)
// ============================================================
// TOOL EVENTS
// ============================================================
pencilBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
activeTool =
"PENCIL"
selectedIndex =
-1
canvas.setCursor(
Cursor.getPredefinedCursor(
Cursor.CROSSHAIR_CURSOR
)
)
setStatus(
"PENCIL MODE"
)
canvas.requestFocusInWindow()
}
}
)
selectBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
activeTool =
"SELECT"
canvas.setCursor(
Cursor.getPredefinedCursor(
Cursor.DEFAULT_CURSOR
)
)
setStatus(
"SELECT / MOVE / RESIZE"
)
repaintCanvas()
canvas.requestFocusInWindow()
}
}
)
eraserBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
activeTool =
"ERASER"
selectedIndex =
-1
canvas.setCursor(
Cursor.getPredefinedCursor(
Cursor.CROSSHAIR_CURSOR
)
)
setStatus(
"ERASER MODE"
)
canvas.requestFocusInWindow()
}
}
)
bucketBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
activeTool =
"BUCKET"
canvas.setCursor(
Cursor.getPredefinedCursor(
Cursor.HAND_CURSOR
)
)
setStatus(
"BUCKET MODE"
)
canvas.requestFocusInWindow()
}
}
)
undoBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
undo()
}
}
)
redoBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
redo()
}
}
)
clearBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
clearAll()
}
}
)
drawCodeBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
generateCode()
}
}
)
animationBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
generateAnimationCode()
}
}
)
animationStudioBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
openFrameAnimationStudio()
}
}
)
preview3DBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
openPreview3D()
}
}
)
copyBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
copyText(
codeArea.getText
)
}
}
)
saveBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
saveText(
codeArea.getText
)
}
}
)
gamesBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
openGameStudio()
}
}
)
gridBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
showGrid =
!showGrid
repaintCanvas()
if (showGrid)
setStatus("GRID ON")
else
setStatus("GRID OFF")
}
}
)
snapBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
snapToGrid =
!snapToGrid
if (snapToGrid)
setStatus("SNAP ON")
else
setStatus("SNAP OFF")
}
}
)
strokeBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val selected =
JColorChooser.showDialog(
mainFrame,
"CHOOSE STROKE COLOR",
strokeColor
)
if (
selected != null
) {
strokeColor =
selected
setStatus(
"STROKE COLOR CHANGED"
)
}
}
}
)
fillBtn.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val selected =
JColorChooser.showDialog(
mainFrame,
"CHOOSE FILL COLOR",
fillColor
)
if (
selected != null
) {
fillColor =
selected
if (
selectedIndex >= 0
) {
fillSelected()
} else {
setStatus(
"FILL COLOR CHANGED"
)
}
}
}
}
)
// ============================================================
// KEYBOARD SHORTCUTS
// ============================================================
canvas.addKeyListener(
new KeyAdapter {
override def keyPressed(
e: KeyEvent
): Unit = {
val k: Int =
e.getKeyCode
if (
e.isControlDown &&
k == KeyEvent.VK_Z
) {
undo()
} else if (
e.isControlDown &&
k == KeyEvent.VK_Y
) {
redo()
} else if (
e.isControlDown &&
k == KeyEvent.VK_G
) {
generateCode()
} else if (
e.isControlDown &&
k == KeyEvent.VK_S
) {
saveText(
codeArea.getText
)
} else if (
e.isControlDown &&
k == KeyEvent.VK_C
) {
copyText(
codeArea.getText
)
} else if (
e.isControlDown &&
k == KeyEvent.VK_D
) {
openGameStudio()
} else if (
k == KeyEvent.VK_DELETE
) {
deleteSelected()
} else if (
k == KeyEvent.VK_P
) {
activeTool =
"PENCIL"
setStatus(
"PENCIL"
)
} else if (
k == KeyEvent.VK_V
) {
activeTool =
"SELECT"
setStatus(
"SELECT"
)
} else if (
k == KeyEvent.VK_B
) {
activeTool =
"BUCKET"
setStatus(
"BUCKET"
)
} else if (
k == KeyEvent.VK_E
) {
activeTool =
"ERASER"
setStatus(
"ERASER"
)
} else if (
k == KeyEvent.VK_F1
) {
JOptionPane.showMessageDialog(
mainFrame,
"ULTRA LEGEND X9\n\n" +
"P = Pencil\n" +
"V = Select\n" +
"B = Bucket\n" +
"E = Eraser\n" +
"DELETE = Delete\n\n" +
"CTRL+Z = Undo\n" +
"CTRL+Y = Redo\n" +
"CTRL+G = Draw Code\n" +
"CTRL+C = Copy\n" +
"CTRL+S = Save\n" +
"CTRL+D = Game Studio\n\n" +
"SELECT + DRAG = MOVE\n" +
"SELECT + HANDLE = RESIZE",
"SHORTCUTS",
JOptionPane.INFORMATION_MESSAGE
)
}
}
}
)
// ============================================================
// ADVANCED FRAME-BY-FRAME ANIMATION DRAW STUDIO
// ============================================================
val animationCommandWords = Array(
"NONE","MOVE LEFT","MOVE RIGHT","MOVE UP","MOVE DOWN","JUMP","BOUNCE",
"WALK","RUN","FLOAT","FALL","RISE","SHAKE","WIGGLE","WAVE","DANCE","SPIN",
"ROTATE LEFT","ROTATE RIGHT","FLIP HORIZONTAL","FLIP VERTICAL","SCALE UP","SCALE DOWN",
"PULSE","SWING","ORBIT","ORBIT REVERSE","CIRCLE MOVE","SPIRAL MOVE","FIGURE EIGHT",
"ZIGZAG","JELLY","HEARTBEAT","TWINKLE","SPARKLE","VIBRATE","TREMBLE","TILT LEFT",
"TILT RIGHT","ROCK","SPRING","POP","SLIDE LEFT","SLIDE RIGHT","SLIDE UP","SLIDE DOWN",
"FLY","SOAR","LAND","TAKE OFF","CAR DRIVE","CAR REVERSE","WHEEL SPIN","BALL ROLL",
"BALL BOUNCE","CLOUD DRIFT","RAIN FALL","SNOW FALL","FIRE FLICKER","WATER WAVE",
"TREE SWAY","LEAF FALL","BIRD FLY","BUTTERFLY FLUTTER","FISH SWIM","CLOCK TICK",
"CANDLE FLICKER","LIGHT FLASH","MAGIC APPEAR","MAGIC DISAPPEAR","TELEPORT","PORTAL",
"LASER","EXPLODE","PARTICLE BURST","RING PULSE","STAR SPIN","MOON ORBIT","PLANET ORBIT",
"SUN PULSE","ELECTRIC","NEON PULSE","GLITCH","CYBER MOVE","FUTURE DRIVE","TIME WARP",
"TIME REWIND","SPEED UP","SLOW MOTION","EASE IN","EASE OUT","EASE IN OUT","SPRING IN",
"SPRING OUT","ELASTIC","RANDOM SHAKE","RANDOM MOVE","REVERSE","PING PONG","POSE","STEP",
"CHASE","PATROL","FOLLOW"
)
def animationClone(source: ArrayBuffer[DrawItem]): ArrayBuffer[DrawItem] = cloneItems(source)
def animationSnapshot(): Unit = {
if (animationFrames.isEmpty) animationFrames += animationClone(drawItems)
else animationFrames(animationCurrentFrame) = animationClone(drawItems)
}
def animationLoad(index: Int): Unit = {
if (animationFrames.nonEmpty) {
val safe = math.max(0, math.min(animationFrames.length - 1, index))
drawItems.clear()
drawItems ++= animationClone(animationFrames(safe))
selectedIndex = -1
animationCurrentFrame = safe
repaintCanvas()
}
}
def animationNextFrame(): Unit = {
animationSnapshot()
animationFrames += ArrayBuffer[DrawItem]()
animationCurrentFrame = animationFrames.length - 1
drawItems.clear()
selectedIndex = -1
repaintCanvas()
}
def animationCopy(): Unit = {
animationClipboard = animationClone(drawItems)
}
def animationPaste(): Unit = {
if (animationClipboard.nonEmpty) {
drawItems.clear()
drawItems ++= animationClone(animationClipboard)
selectedIndex = -1
repaintCanvas()
}
}
def animationTransform(source: ArrayBuffer[DrawItem], command: String, step: Int): ArrayBuffer[DrawItem] = {
val out = animationClone(source)
var minX = 0.0
var maxX = 0.0
var minY = 0.0
var maxY = 0.0
var n = 0
var i = 0
while (i < out.length) {
var j = 0
while (j < out(i).points.length) {
val p = out(i).points(j)
if (n == 0) { minX = p.x; maxX = p.x; minY = p.y; maxY = p.y }
else { minX = math.min(minX,p.x); maxX = math.max(maxX,p.x); minY = math.min(minY,p.y); maxY = math.max(maxY,p.y) }
n += 1
j += 1
}
i += 1
}
val cx = (minX + maxX) / 2.0
val cy = (minY + maxY) / 2.0
i = 0
while (i < out.length) {
val item = out(i)
val pts = item.points.map { p =>
var nx = p.x.toDouble
var ny = p.y.toDouble
if (command == "MOVE LEFT" || command == "SLIDE LEFT") nx -= step * 5
else if (command == "MOVE RIGHT" || command == "SLIDE RIGHT") nx += step * 5
else if (command == "MOVE UP" || command == "RISE" || command == "SLIDE UP") ny -= step * 4
else if (command == "MOVE DOWN" || command == "FALL" || command == "SLIDE DOWN") ny += step * 4
else if (command == "JUMP" || command == "BOUNCE" || command == "SPRING") ny -= math.sin(step.toDouble/11.0*math.Pi)*55
else if (command == "SCALE UP" || command == "PULSE") { val k=1+math.sin(step.toDouble/11.0*math.Pi)*0.10; nx=cx+(nx-cx)*k; ny=cy+(ny-cy)*k }
else if (command == "SCALE DOWN") { val k=1-math.sin(step.toDouble/11.0*math.Pi)*0.08; nx=cx+(nx-cx)*k; ny=cy+(ny-cy)*k }
else if (command == "ROTATE LEFT" || command == "SPIN" || command == "STAR SPIN") { val a=-step*7.0*math.Pi/180.0; val dx=nx-cx; val dy=ny-cy; nx=cx+dx*math.cos(a)-dy*math.sin(a); ny=cy+dx*math.sin(a)+dy*math.cos(a) }
else if (command == "ROTATE RIGHT" || command == "TILT RIGHT") { val a=step*7.0*math.Pi/180.0; val dx=nx-cx; val dy=ny-cy; nx=cx+dx*math.cos(a)-dy*math.sin(a); ny=cy+dx*math.sin(a)+dy*math.cos(a) }
else if (command == "FLIP HORIZONTAL") nx=cx-(nx-cx)
else if (command == "FLIP VERTICAL") ny=cy-(ny-cy)
else if (command == "WAVE" || command == "WIGGLE" || command == "TREE SWAY" || command == "WATER WAVE") ny += math.sin(p.x*0.05+step*0.5)*10
else if (command == "SHAKE" || command == "VIBRATE" || command == "TREMBLE" || command == "RANDOM SHAKE") { nx += math.sin(step*2.4)*10; ny += math.cos(step*2.0)*7 }
else if (command == "SWING" || command == "ROCK" || command == "TILT LEFT") { val a=math.sin(step.toDouble/11.0*math.Pi)*10*math.Pi/180.0; val dx=nx-cx; val dy=ny-cy; nx=cx+dx*math.cos(a)-dy*math.sin(a); ny=cy+dx*math.sin(a)+dy*math.cos(a) }
else if (command == "ORBIT" || command == "CIRCLE MOVE" || command == "MOON ORBIT" || command == "PLANET ORBIT") { nx += math.cos(step*0.35)*12; ny += math.sin(step*0.35)*8 }
else if (command == "ORBIT REVERSE") { nx += math.cos(-step*0.35)*12; ny += math.sin(-step*0.35)*8 }
else if (command == "SPIRAL MOVE") { val r=step*1.5; nx += math.cos(step*0.45)*r; ny += math.sin(step*0.45)*r }
else if (command == "FIGURE EIGHT") { nx += math.sin(step*0.35)*14; ny += math.sin(step*0.70)*9 }
else if (command == "ZIGZAG") { nx += if (step%2==0) 12 else -12; ny -= 4 }
else if (command == "FLY" || command == "SOAR" || command == "BIRD FLY") { nx += step*4; ny -= math.sin(step*0.45)*15 }
else if (command == "CAR DRIVE" || command == "FUTURE DRIVE") nx += step*6
else if (command == "CAR REVERSE") nx -= step*6
else if (command == "BALL ROLL" || command == "WHEEL SPIN") { nx += step*5; ny += math.sin(step*0.65)*4 }
else if (command == "CLOUD DRIFT") nx += step*2
else if (command == "RAIN FALL" || command == "SNOW FALL" || command == "LEAF FALL") ny += step*4
else if (command == "FIRE FLICKER" || command == "CANDLE FLICKER") ny += math.sin(step*0.9)*5
else if (command == "GLITCH" || command == "CYBER MOVE") nx += (if (step%3==0) 12 else -5)
else if (command == "TIME WARP") { nx=cx+(nx-cx)*(1+step*0.01); ny=cy+(ny-cy)*(1+step*0.01) }
else if (command == "ELASTIC" || command == "JELLY") { nx=cx+(nx-cx)*(1+math.sin(step*0.7)*0.08); ny=cy+(ny-cy)*(1-math.sin(step*0.7)*0.05) }
new Point(nx.toInt,ny.toInt)
}.toVector
out(i)=item.copy(points=pts)
i += 1
}
out
}
def generatedAnimationItemsCode(items: ArrayBuffer[DrawItem], indent: String): String = {
val sb=new StringBuilder()
var i=0
while(i<items.length){
val item=items(i)
if(item.visible && item.points.length>=2){
sb.append(indent+"// "+item.name.replace("\"","'")+"\n")
sb.append(indent+"val pts"+i+" = Array(")
var j=0
while(j<item.points.length){
if(j>0) sb.append(",")
sb.append("new Point("+item.points(j).x+","+item.points(j).y+")")
j+=1
}
sb.append(")\n")
if(item.filled && item.points.length>=3){
sb.append(indent+"g.setColor(new Color("+item.fill.getRed+","+item.fill.getGreen+","+item.fill.getBlue+"))\n")
sb.append(indent+"val fx"+i+" = pts"+i+".map(_.x)\n")
sb.append(indent+"val fy"+i+" = pts"+i+".map(_.y)\n")
sb.append(indent+"g.fillPolygon(fx"+i+",fy"+i+",fx"+i+".length)\n")
}
sb.append(indent+"g.setColor(new Color("+item.stroke.getRed+","+item.stroke.getGreen+","+item.stroke.getBlue+"))\n")
sb.append(indent+"g.setStroke(new BasicStroke(3))\n")
sb.append(indent+"var k"+i+" = 1\n")
sb.append(indent+"while (k"+i+" < pts"+i+".length) {\n")
sb.append(indent+" g.drawLine(pts"+i+"(k"+i+"-1).x,pts"+i+"(k"+i+"-1).y,pts"+i+"(k"+i+" ).x,pts"+i+"(k"+i+" ).y)\n")
sb.append(indent+" k"+i+" += 1\n")
sb.append(indent+"}\n")
}
i+=1
}
sb.toString
}
def generateFrameAnimationStudioCode(): Unit = {
animationSnapshot()
val sb=new StringBuilder()
sb.append("import java.awt.BasicStroke\nimport java.awt.Color\nimport java.awt.Dimension\nimport java.awt.Graphics2D\nimport java.awt.Point\nimport java.awt.RenderingHints\nimport java.awt.event.ActionEvent\nimport javax.swing.JFrame\nimport javax.swing.JPanel\nimport javax.swing.Timer\nimport javax.swing.WindowConstants\n\n")
sb.append("val W = "+CANVAS_W+"\nval H = "+CANVAS_H+"\nvar frameNo = 0\n")
sb.append("val w = new JFrame(\"GENERATED FRAME ANIMATION\")\n")
sb.append("w.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)\n")
sb.append("w.setSize(new Dimension(W,H))\nw.setLocationRelativeTo(null)\n")
sb.append("val panel = new JPanel {\n")
sb.append(" override def paintComponent(graphics: java.awt.Graphics): Unit = {\n")
sb.append(" super.paintComponent(graphics)\n")
sb.append(" val g = graphics.asInstanceOf[Graphics2D]\n")
sb.append(" g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON)\n")
sb.append(" g.setColor(Color.WHITE)\ng.fillRect(0,0,getWidth,getHeight)\n")
var f=0
while(f<animationFrames.length){
sb.append(" if (frameNo == "+f+") {\n")
sb.append(generatedAnimationItemsCode(animationFrames(f)," "))
sb.append(" }\n")
f+=1
}
sb.append(" g.setColor(Color.DARK_GRAY)\n")
sb.append(" g.drawString(\"FRAME \"+(frameNo+1)+\" / "+animationFrames.length+"\",12,20)\n")
sb.append(" }\n}\n")
sb.append("val timer = new Timer("+(math.max(20,1000/animationFps))+",new javax.swing.AbstractAction {\n")
sb.append(" override def actionPerformed(e: ActionEvent): Unit = { frameNo += 1; if(frameNo >= "+animationFrames.length+") frameNo=0; panel.repaint() }\n")
sb.append("})\n")
sb.append("w.add(panel)\nw.setVisible(true)\ntimer.start()\n")
generatedAnimationCode=sb.toString
if(codeArea!=null) codeArea.setText(generatedAnimationCode)
setStatus("ANIMATION CODE GENERATED: "+animationFrames.length+" FRAMES")
}
def openFrameAnimationStudio(): Unit = {
animationFrames.clear()
animationClipboard.clear()
animationCurrentFrame=0
animationFrames += animationClone(drawItems)
val win=new JFrame("ULTRA ANIMATION DRAW STUDIO")
win.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)
win.setSize(1450,900)
win.setLocationRelativeTo(mainFrame)
var drawing=false
var temp=ArrayBuffer[Point]()
var playing=false
var previewIndex=0
var timer: Timer=null
val frameLabel=new JLabel("FRAME 1 / 1")
val info=new JLabel("Draw here -> COPY DRAW -> NEXT -> PASTE DRAW -> edit -> NEXT -> DONE",SwingConstants.CENTER)
val editor=new JPanel {
override def paintComponent(graphics: Graphics): Unit = {
super.paintComponent(graphics)
val g=graphics.asInstanceOf[Graphics2D]
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON)
g.setColor(new Color(225,228,238))
g.fillRect(0,0,getWidth,getHeight)
g.translate(90,50)
g.setColor(Color.WHITE)
g.fillRect(0,0,CANVAS_W,CANVAS_H)
g.setColor(new Color(232,235,242))
var gx=0; while(gx<=CANVAS_W){g.drawLine(gx,0,gx,CANVAS_H);gx+=50}
var gy=0; while(gy<=CANVAS_H){g.drawLine(0,gy,CANVAS_W,gy);gy+=50}
val source=if(playing && animationFrames.nonEmpty) animationFrames(previewIndex) else drawItems
var i=0
while(i<source.length){
val item=source(i)
if(item.visible && item.points.length>=2){
if(item.filled && item.points.length>=3){
g.setColor(item.fill); val xs=item.points.map(_.x).toArray; val ys=item.points.map(_.y).toArray; g.fillPolygon(xs,ys,xs.length)
}
g.setColor(item.stroke); g.setStroke(new BasicStroke(math.max(1.0f,brushWidth)))
var j=1; while(j<item.points.length){g.drawLine(item.points(j-1).x,item.points(j-1).y,item.points(j).x,item.points(j).y);j+=1}
if(i==selectedIndex && !playing){val r=boundsOf(item);g.setColor(new Color(20,120,255,150));g.drawRect(r.x-5,r.y-5,r.width+10,r.height+10)}
}
i+=1
}
if(drawing && temp.nonEmpty){g.setColor(strokeColor);g.setStroke(new BasicStroke(math.max(1.0f,brushWidth),BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));var j=1;while(j<temp.length){g.drawLine(temp(j-1).x,temp(j-1).y,temp(j).x,temp(j).y);j+=1}}
}
addMouseListener(new MouseAdapter{
override def mousePressed(e:MouseEvent):Unit={
if(!playing && e.getX>=90 && e.getY>=50){drawing=true;temp.clear();temp+=new Point(math.max(0,math.min(CANVAS_W-1,e.getX-90)),math.max(0,math.min(CANVAS_H-1,e.getY-50)));repaint()}
}
override def mouseReleased(e:MouseEvent):Unit={
if(drawing && temp.length>=2){drawItems+=DrawItem(temp.toVector,strokeColor,fillColor,closed(temp),"FRAME STROKE",true,false);selectedIndex=drawItems.length-1}
temp.clear();drawing=false;repaint();repaintCanvas()
}
})
addMouseMotionListener(new MouseMotionAdapter{
override def mouseDragged(e:MouseEvent):Unit={if(drawing && !playing){temp+=new Point(math.max(0,math.min(CANVAS_W-1,e.getX-90)),math.max(0,math.min(CANVAS_H-1,e.getY-50)));repaint()}}
})
}
val copyB=button("COPY DRAW")
val pasteB=button("PASTE DRAW")
val prevB=button("PREVIOUS")
val nextB=button("NEXT")
val clearB=button("CLEAR FRAME")
val playB=button("PLAY")
val codeB=button("ANIMATION GENERATE CODE")
val doneB=button("DONE")
val delB=button("DELETE SELECTED")
val strokeB=button("DRAW COLOR")
val fillB=button("FILL COLOR")
val minusB=button("FPS -")
val plusB=button("FPS +")
val fpsL=new JLabel("FPS: "+animationFps)
val commandField=new JTextField(16)
val commandCombo=new javax.swing.JComboBox[String](animationCommandWords)
val commandB=button("AUTO FRAMES")
val top=new JPanel(new FlowLayout(FlowLayout.LEFT,4,4))
top.add(frameLabel);top.add(copyB);top.add(pasteB);top.add(prevB);top.add(nextB);top.add(clearB);top.add(delB);top.add(playB);top.add(codeB);top.add(doneB);top.add(strokeB);top.add(fillB);top.add(fpsL);top.add(minusB);top.add(plusB)
val cmd=new JPanel(new FlowLayout(FlowLayout.LEFT,4,4))
cmd.add(new JLabel("ANIMATION WORD:"));cmd.add(commandCombo);cmd.add(commandField);cmd.add(commandB)
val south=new JPanel(new BorderLayout())
south.add(cmd,BorderLayout.NORTH);south.add(info,BorderLayout.SOUTH)
win.setLayout(new BorderLayout());win.add(top,BorderLayout.NORTH);win.add(new JScrollPane(editor),BorderLayout.CENTER);win.add(south,BorderLayout.SOUTH)
def refresh():Unit={frameLabel.setText("FRAME "+(animationCurrentFrame+1)+" / "+math.max(1,animationFrames.length));editor.repaint()}
copyB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={animationCopy();info.setText("COPIED "+animationClipboard.length+" drawings")}})
pasteB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={animationPaste();refresh();info.setText("PASTED DRAWING")}})
prevB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={animationSnapshot();if(animationCurrentFrame>0){animationCurrentFrame-=1;animationLoad(animationCurrentFrame)};refresh()}})
nextB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={animationNextFrame();refresh()}})
clearB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={drawItems.clear();selectedIndex = -1;editor.repaint();info.setText("CURRENT FRAME CLEARED")}})
delB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={deleteSelected();editor.repaint()}})
strokeB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={val c=JColorChooser.showDialog(win,"DRAW COLOR",strokeColor);if(c!=null)strokeColor=c}})
fillB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={val c=JColorChooser.showDialog(win,"FILL COLOR",fillColor);if(c!=null){fillColor=c;if(selectedIndex>=0)fillSelected();editor.repaint()}}})
playB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={
animationSnapshot();playing=!playing
if(playing){previewIndex=0;if(timer!=null)timer.stop();timer=new Timer(math.max(20,1000/animationFps),new ActionListener{override def actionPerformed(e:ActionEvent):Unit={if(animationFrames.nonEmpty){previewIndex+=1;if(previewIndex>=animationFrames.length)previewIndex=0;editor.repaint()}}});timer.start();playB.setText("STOP")}
else{if(timer!=null)timer.stop();playB.setText("PLAY");animationLoad(animationCurrentFrame);editor.repaint()}
}})
codeB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={if(timer!=null)timer.stop();playing=false;playB.setText("PLAY");generateFrameAnimationStudioCode();info.setText("ANIMATION CODE GENERATED")}})
doneB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={if(timer!=null)timer.stop();playing=false;generateFrameAnimationStudioCode();info.setText("DONE - CODE IS IN MAIN CODE PANEL")}})
minusB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={animationFps=math.max(2,animationFps-1);fpsL.setText("FPS: "+animationFps)}})
plusB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={animationFps=math.min(30,animationFps+1);fpsL.setText("FPS: "+animationFps)}})
commandB.addActionListener(new ActionListener{override def actionPerformed(e:ActionEvent):Unit={
val typed=commandField.getText.trim.toUpperCase
val selected=commandCombo.getSelectedItem
val command=if(typed.nonEmpty)typed else if(selected!=null)selected.toString.toUpperCase else "NONE"
if(command!="NONE"){
animationSnapshot()
var base=animationClone(drawItems)
var step=1
while(step<=12){val next=animationTransform(base,command,step);animationFrames+=animationClone(next);base=next;step+=1}
animationCurrentFrame=animationFrames.length-1;drawItems.clear();drawItems++=animationClone(base);selectedIndex = -1;refresh();info.setText("AUTO CREATED 12 FRAMES: "+command)
}
}})
win.addWindowListener(new java.awt.event.WindowAdapter{override def windowClosed(e:java.awt.event.WindowEvent):Unit={if(timer!=null)timer.stop()}})
refresh();win.setVisible(true)
}
// ============================================================
// START
// ============================================================
SwingUtilities.invokeLater(
new Runnable {
override def run(): Unit = {
mainFrame.setVisible(
true
)
canvas.requestFocusInWindow()
setStatus(
"ULTRA LEGEND X9 READY"
)
}
}
)