Code Sketch
bhavesh shahare
Category: Programming
// ============================================================
// OPERATION SINDOOR - PART 2
// ULTRA LEGENDARY 90 SECOND CINEMATIC EDITION
// ASTRONAUT + ARMY HERO FINAL STORY
// CLEAN KOJO SCALA VERSION
// ============================================================
cleari()
originBottomLeft()
// ============================================================
// GAME STATES
// ============================================================
var gameState = 0
// 0 = Secret Briefing
// 1 = Astronaut + Army Entry
// 2 = 90 Second Defense
// 3 = Final Threat
// 4 = Final Reunion
// 5 = Black Fade
// 6 = THE END
// ============================================================
// STORY
// ============================================================
var storyTimer = 0.0
var missionTimer = 90.0
var endingTimer = 0.0
// ============================================================
// LETTER
// ============================================================
var letterTyped = 0
var letterTimer = 0.0
val missionLetter =
"TOP SECRET DIRECTIVE\n" +
"OPERATION SINDOOR - PART 2\n\n" +
"Commander,\n\n" +
"A critical defense alert has been activated.\n" +
"Your mission is to protect the defense line for 90 seconds.\n" +
"Work alongside the Army Hero and hold your position.\n\n" +
"Click START MISSION when ready."
// ============================================================
// ASTRONAUT
// ============================================================
var astronautX = -60.0
var astronautY = 38.0
var astronautWalking = true
// ============================================================
// ARMY HERO
// ============================================================
var armyX = cwidth + 60.0
var armyY = 38.0
var armyWalking = true
// ============================================================
// TANK
// ============================================================
var tankX = cwidth / 2.0
val tankY = 35.0
var cannonAngle = math.Pi / 2.0
// ============================================================
// LAUNCHER
// ============================================================
var launcherX = cwidth / 2.0 - 220.0
val launcherY = 150.0
var launcherAngle = math.Pi / 2.0
var selectedType = 1
// ============================================================
// FIRE
// ============================================================
var fireCooldown = 0
var launchCooldown = 0
var muzzleFlash = 0
// ============================================================
// GENERAL ANIMATION
// ============================================================
var worldTime = 0.0
var skyTime = 0.0
var flagTime = 0.0
var particleTime = 0.0
var waveTime = 0.0
var slowMotion = false
// ============================================================
// FINALE VARIABLES
// IMPORTANT: THESE MUST BE GLOBAL
// ============================================================
var finaleX = cwidth / 2.0
var finaleY = cheight + 150.0
var finaleTime = 0.0
// ============================================================
// FINAL REUNION
// ============================================================
var finalWalk = 0.0
var finalLight = 0.0
var finalGlow = 0.0
// ============================================================
// END SCREEN
// ============================================================
var endFade = 0.0
var endStars = 0.0
var endSweep = -200.0
// ============================================================
// AUDIO
// ============================================================
var audioEnabled = false
try {
setNoteInstrument(Instrument.PIANO)
audioEnabled = true
} catch {
case _: Exception =>
}
def playFx(
note: Int,
duration: Int
): Unit = {
if (audioEnabled) {
try {
playNote(
note,
duration
)
} catch {
case _: Exception =>
}
}
}
// ============================================================
// STARS
// ============================================================
val starCount = 220
val starX =
Array.fill(starCount)(
math.random * cwidth
)
val starY =
Array.fill(starCount)(
130.0 +
math.random * (cheight - 140.0)
)
val starSize =
Array.fill(starCount)(
0.7 +
math.random * 2.5
)
// ============================================================
// AMBIENT GLOW PARTICLE
// ============================================================
class GlowParticle(
x0: Double,
y0: Double,
rr: Int,
gg: Int,
bb: Int
) {
var x = x0
var y = y0
var vx =
randomDouble(
-1.0,
1.0
)
var vy =
randomDouble(
0.3,
1.8
)
var life = 255.0
def step(): Unit = {
x += vx
y += vy
vx +=
math.sin(
particleTime +
x * 0.01
) * 0.01
life -= 1.8
if (y > cheight + 20.0) {
y = 110.0
}
if (x < -20.0) {
x = cwidth + 20.0
}
if (x > cwidth + 20.0) {
x = -20.0
}
}
def view(
c: CanvasDraw
): Unit = {
if (life > 0.0) {
c.fill(
rr,
gg,
bb,
life.toInt
)
c.noStroke()
val size =
2.0 +
math.abs(
math.sin(
particleTime * 2.0
)
) * 2.0
c.ellipse(
x,
y,
size,
size
)
}
}
}
val glowParticles =
ArrayBuffer.empty[GlowParticle]
for (i <- 0 until 90) {
glowParticles.append(
new GlowParticle(
randomDouble(0.0, cwidth),
randomDouble(120.0, cheight),
255,
190,
70
)
)
}
// ============================================================
// EXPLOSION PARTICLE
// ============================================================
class ExplosionParticle(
x0: Double,
y0: Double,
rr: Int,
gg: Int,
bb: Int
) {
var x = x0
var y = y0
var vx =
randomDouble(
-7.0,
7.0
)
var vy =
randomDouble(
-7.0,
7.0
)
var life = 255.0
def step(): Unit = {
x += vx
y += vy
vx *= 0.93
vy *= 0.93
life -= 9.0
}
def view(
c: CanvasDraw
): Unit = {
if (life > 0.0) {
c.fill(
rr,
gg,
bb,
life.toInt
)
c.noStroke()
c.ellipse(
x,
y,
3.0,
3.0
)
}
}
}
val explosionParticles =
ArrayBuffer.empty[ExplosionParticle]
// ============================================================
// SHOCKWAVE
// ============================================================
class Shockwave(
val x: Double,
val y: Double
) {
var radius = 6.0
var alpha = 230.0
var active = true
def step(): Unit = {
radius += 6.0
alpha -= 11.0
if (alpha <= 0.0) {
active = false
}
}
def view(
c: CanvasDraw
): Unit = {
if (active) {
c.noFill()
c.stroke(
255,
180,
55,
alpha.toInt
)
c.strokeWeight(3)
c.ellipse(
x,
y,
radius * 2.0,
radius * 2.0
)
}
}
}
val shockwaves =
ArrayBuffer.empty[Shockwave]
// ============================================================
// SHELL
// ============================================================
class Shell(
var x: Double,
var y: Double,
val vx: Double,
val vy: Double
) {
var active = true
def step(): Unit = {
x += vx
y += vy
if (
x < -60.0 ||
x > cwidth + 60.0 ||
y < -60.0 ||
y > cheight + 60.0
) {
active = false
}
}
def view(
c: CanvasDraw
): Unit = {
c.fill(
255,
210,
90
)
c.noStroke()
c.ellipse(
x,
y,
9,
9
)
}
}
val shells =
ArrayBuffer.empty[Shell]
// ============================================================
// ENEMY TARGET
// ============================================================
class EnemyTarget(
var x: Double,
var y: Double,
val fall: Double,
val mode: Int
) {
var active = true
var hit = false
def step(): Unit = {
if (!hit) {
y += fall
if (mode == 2) {
x +=
math.sin(
worldTime * 2.0 +
y * 0.02
) * 1.5
}
if (mode == 3) {
x +=
math.cos(
worldTime * 2.5 +
y * 0.03
) * 2.0
}
if (y < -40.0) {
active = false
}
}
}
def view(
c: CanvasDraw
): Unit = {
if (!hit) {
c.pushMatrix()
c.translate(
x,
y
)
c.rotate(
-math.Pi / 2.0
)
c.fill(
190,
45,
45
)
c.stroke(
70,
15,
15
)
c.strokeWeight(1)
c.rect(
-17,
-6,
34,
12
)
c.fill(
60,
60,
60
)
c.noStroke()
c.ellipse(
17,
0,
10,
10
)
c.popMatrix()
}
}
}
val enemyTargets =
ArrayBuffer.empty[EnemyTarget]
var spawnTimer = 0
// ============================================================
// STRATEGIC MISSILE
// ============================================================
class StrategicMissile(
var x: Double,
var y: Double,
val speed: Double,
var angle: Double,
val mode: Int
) {
var active = true
def step(): Unit = {
if (mode == 2) {
angle += 0.009
}
if (mode == 3) {
angle +=
math.sin(
worldTime * 3.0
) * 0.015
}
x +=
math.cos(angle) *
speed
y +=
math.sin(angle) *
speed
if (
x < -100.0 ||
x > cwidth + 100.0 ||
y < -100.0 ||
y > cheight + 100.0
) {
active = false
}
}
def view(
c: CanvasDraw
): Unit = {
c.pushMatrix()
c.translate(
x,
y
)
c.rotate(
angle
)
c.fill(
210,
220,
230
)
c.stroke(
50,
50,
65
)
c.strokeWeight(1)
c.rect(
-18,
-6,
36,
12
)
c.fill(
225,
45,
45
)
c.noStroke()
c.ellipse(
18,
0,
12,
12
)
c.popMatrix()
}
}
val strategicMissiles =
ArrayBuffer.empty[StrategicMissile]
// ============================================================
// FIREWORK
// ============================================================
val tricolor =
Array(
cm.rgb(255, 153, 51),
cm.rgb(255, 255, 255),
cm.rgb(18, 136, 7)
)
class FireParticle(
var x: Double,
var y: Double,
val col: Color
) {
var vx =
randomDouble(
-4.0,
4.0
)
var vy =
randomDouble(
-4.0,
4.0
)
var life = 255.0
def step(): Unit = {
x += vx
y += vy
vy -= 0.04
vx *= 0.96
vy *= 0.96
life -= 5.0
}
def view(
c: CanvasDraw
): Unit = {
if (life > 0.0) {
c.stroke(
col
)
c.strokeWeight(2)
c.point(
x,
y
)
}
}
}
class Firework {
val centerX =
randomDouble(
60.0,
cwidth - 60.0
)
val centerY =
180.0 +
randomDouble(
100.0,
230.0
)
val color =
tricolor(
random(
tricolor.length
)
)
var radius = 0.0
var active = true
def step(): Unit = {
radius += 7.0
if (radius > 120.0) {
active = false
}
}
def view(
c: CanvasDraw
): Unit = {
if (active) {
for (
i <- 0 until 32
) {
val a =
i *
math.Pi *
2.0 /
32.0
val rr =
radius *
(
0.55 +
math.abs(
math.sin(
worldTime +
i
)
) * 0.45
)
c.fill(
color.getRed,
color.getGreen,
color.getBlue,
180
)
c.noStroke()
c.ellipse(
centerX +
math.cos(a) * rr,
centerY +
math.sin(a) * rr,
3,
3
)
}
}
}
}
val fireworks =
ArrayBuffer.empty[Firework]
// ============================================================
// IMPACT
// ============================================================
def createImpact(
x: Double,
y: Double
): Unit = {
shockwaves.append(
new Shockwave(
x,
y
)
)
for (
i <- 0 until 18
) {
explosionParticles.append(
new ExplosionParticle(
x,
y,
255,
random(80, 230),
30
)
)
}
playFx(
52,
90
)
}
// ============================================================
// CONTROLS
// ============================================================
def handleControls(): Unit = {
if (
gameState == 2 &&
isMousePressed
) {
val mx =
mouseX
val my =
mouseY
if (
mx >= 20 &&
mx <= 75 &&
my >= 90 &&
my <= 125
) {
tankX -= 4.0
}
else if (
mx >= 80 &&
mx <= 135 &&
my >= 90 &&
my <= 125
) {
tankX += 4.0
}
else if (
mx >= 140 &&
mx <= 190 &&
my >= 90 &&
my <= 125
) {
cannonAngle -= 0.04
}
else if (
mx >= 195 &&
mx <= 245 &&
my >= 90 &&
my <= 125
) {
cannonAngle += 0.04
}
else if (
mx >= 250 &&
mx <= 305 &&
my >= 90 &&
my <= 125
) {
if (
fireCooldown <= 0
) {
val sx =
tankX +
math.cos(
cannonAngle
) * 68.0
val sy =
tankY +
46.0 +
math.sin(
cannonAngle
) * 68.0
shells.append(
new Shell(
sx,
sy,
math.cos(
cannonAngle
) * 10.0,
math.sin(
cannonAngle
) * 10.0
)
)
fireCooldown = 11
muzzleFlash = 6
playFx(
66,
50
)
}
}
else if (
mx >= 315 &&
mx <= 365 &&
my >= 90 &&
my <= 125
) {
launcherAngle -= 0.04
}
else if (
mx >= 370 &&
mx <= 420 &&
my >= 90 &&
my <= 125
) {
launcherAngle += 0.04
}
else if (
mx >= 430 &&
mx <= 475 &&
my >= 90 &&
my <= 125
) {
selectedType = 1
}
else if (
mx >= 480 &&
mx <= 525 &&
my >= 90 &&
my <= 125
) {
selectedType = 2
}
else if (
mx >= 530 &&
mx <= 575 &&
my >= 90 &&
my <= 125
) {
selectedType = 3
}
else if (
mx >= 580 &&
mx <= 660 &&
my >= 90 &&
my <= 125
) {
if (
launchCooldown <= 0
) {
var speed =
7.0
if (
selectedType == 2
) {
speed = 6.0
}
if (
selectedType == 3
) {
speed = 8.0
}
val lx =
launcherX +
math.cos(
launcherAngle
) * 55.0
val ly =
launcherY +
math.sin(
launcherAngle
) * 55.0
strategicMissiles.append(
new StrategicMissile(
lx,
ly,
speed,
launcherAngle,
selectedType
)
)
launchCooldown = 18
playFx(
55,
65
)
}
}
}
}
// ============================================================
// COLLISIONS
// ============================================================
def checkCollisions(): Unit = {
repeatFor(
enemyTargets
) { enemy =>
if (!enemy.hit) {
repeatFor(
shells
) { shell =>
if (
shell.active &&
math.abs(
shell.x -
enemy.x
) < 28.0 &&
math.abs(
shell.y -
enemy.y
) < 28.0
) {
enemy.hit = true
shell.active = false
createImpact(
enemy.x,
enemy.y
)
}
}
repeatFor(
strategicMissiles
) { missile =>
if (
missile.active &&
math.abs(
missile.x -
enemy.x
) < 34.0 &&
math.abs(
missile.y -
enemy.y
) < 34.0
) {
enemy.hit = true
missile.active = false
createImpact(
enemy.x,
enemy.y
)
}
}
}
}
}
// ============================================================
// UPDATE OBJECTS
// ============================================================
def updateObjects(): Unit = {
shells.filterInPlace(
_.active
)
repeatFor(
shells
)(
_.step()
)
strategicMissiles.filterInPlace(
_.active
)
repeatFor(
strategicMissiles
)(
_.step()
)
enemyTargets.filterInPlace(
_.active
)
repeatFor(
enemyTargets
)(
_.step()
)
glowParticles.foreach(
_.step()
)
explosionParticles.filterInPlace(
_.life > 0.0
)
repeatFor(
explosionParticles
)(
_.step()
)
shockwaves.filterInPlace(
_.active
)
repeatFor(
shockwaves
)(
_.step()
)
fireworks.filterInPlace(
_.active
)
repeatFor(
fireworks
)(
_.step()
)
}
// ============================================================
// MAIN UPDATE
// ============================================================
def updateState(): Unit = {
worldTime += 0.04
skyTime += 0.025
flagTime += 0.08
particleTime += 0.06
waveTime += 0.04
if (
fireCooldown > 0
) {
fireCooldown -= 1
}
if (
launchCooldown > 0
) {
launchCooldown -= 1
}
if (
muzzleFlash > 0
) {
muzzleFlash -= 1
}
// ----------------------------------------------------------
// LETTER
// ----------------------------------------------------------
if (
gameState == 0
) {
letterTimer += 0.04
if (
letterTimer >= 0.035 &&
letterTyped < missionLetter.length
) {
letterTyped += 1
letterTimer = 0.0
}
return
}
// ----------------------------------------------------------
// ENTRY
// ----------------------------------------------------------
if (
gameState == 1
) {
if (
astronautX <
cwidth / 2.0 - 55.0
) {
astronautX += 2.5
}
else {
astronautWalking = false
}
if (
armyX >
cwidth / 2.0 + 55.0
) {
armyX -= 2.5
}
else {
armyWalking = false
}
if (
astronautWalking == false &&
armyWalking == false
) {
gameState = 2
playFx(
72,
140
)
}
return
}
// ----------------------------------------------------------
// 90 SECOND MISSION
// ----------------------------------------------------------
if (
gameState == 2
) {
handleControls()
missionTimer -= 0.033
updateObjects()
checkCollisions()
// Wave difficulty increases with time
spawnTimer += 1
var spawnDelay = 72
if (
missionTimer < 60.0
) {
spawnDelay = 58
}
if (
missionTimer < 30.0
) {
spawnDelay = 44
}
if (
spawnTimer >= spawnDelay
) {
spawnTimer = 0
val ex =
randomDouble(
80.0,
cwidth - 80.0
)
var mode = 1
if (
randomDouble(1.0) > 0.65
) {
mode = 2
}
if (
missionTimer < 35.0 &&
randomDouble(1.0) > 0.65
) {
mode = 3
}
var speed =
-0.45
if (
missionTimer < 30.0
) {
speed = -0.52
}
enemyTargets.append(
new EnemyTarget(
ex,
cheight + 25.0,
speed,
mode
)
)
}
// Fireworks
if (
randomDouble(1.0) < 0.025
) {
fireworks.append(
new Firework()
)
}
// Tank limits
if (
tankX < 60.0
) {
tankX = 60.0
}
if (
tankX >
cwidth - 60.0
) {
tankX =
cwidth - 60.0
}
if (
cannonAngle < 0.20
) {
cannonAngle =
0.20
}
if (
cannonAngle >
math.Pi - 0.20
) {
cannonAngle =
math.Pi - 0.20
}
if (
launcherAngle < -math.Pi
) {
launcherAngle =
-math.Pi
}
if (
launcherAngle > math.Pi
) {
launcherAngle =
math.Pi
}
// 90 seconds completed
if (
missionTimer <= 0.0
) {
missionTimer = 0.0
gameState = 3
finaleX =
tankX
finaleY =
cheight + 150.0
finaleTime = 0.0
playFx(
42,
140
)
}
return
}
// ----------------------------------------------------------
// FINAL THREAT
// ----------------------------------------------------------
if (
gameState == 3
) {
finaleTime += 0.04
finaleX +=
(
tankX -
finaleX
) * 0.055
finaleY -=
6.0
if (
randomDouble(1.0) < 0.14
) {
explosionParticles.append(
new ExplosionParticle(
finaleX,
finaleY + 18.0,
255,
90,
20
)
)
}
updateObjects()
if (
finaleY <
tankY + 25.0
) {
createImpact(
tankX,
tankY + 35.0
)
slowMotion = true
gameState = 4
endingTimer = 0.0
playFx(
38,
220
)
}
return
}
// ----------------------------------------------------------
// FINAL REUNION
// ----------------------------------------------------------
if (
gameState == 4
) {
endingTimer += 0.04
finalLight += 0.04
finalGlow += 0.05
updateObjects()
if (
finalWalk < 62.0
) {
finalWalk += 0.8
}
if (
endingTimer >= 7.0
) {
gameState = 5
endFade = 0.0
endStars = 0.0
endSweep = -200.0
}
return
}
// ----------------------------------------------------------
// BLACK FADE
// ----------------------------------------------------------
if (
gameState == 5
) {
endFade += 0.012
endStars += 0.01
endSweep += 2.0
if (
endFade > 1.0
) {
endFade = 1.0
}
if (
endSweep >
cwidth + 250.0
) {
endSweep = -250.0
}
if (
endFade >= 1.0
) {
gameState = 6
}
return
}
// ----------------------------------------------------------
// THE END
// ----------------------------------------------------------
if (
gameState == 6
) {
endStars += 0.008
endSweep += 2.0
if (
endSweep >
cwidth + 250.0
) {
endSweep = -250.0
}
return
}
}
// ============================================================
// BACKGROUND
// ============================================================
def drawBackground(
c: CanvasDraw
): Unit = {
c.background(
5,
8,
18
)
for (
i <- 0 until starCount
) {
val alpha =
(
60 +
math.abs(
math.sin(
skyTime * 3.0 +
i
)
) * 180
).toInt
c.fill(
255,
255,
230,
alpha
)
c.noStroke()
c.ellipse(
starX(i),
starY(i),
starSize(i),
starSize(i)
)
}
c.fill(
28,
90,
48
)
c.rect(
0,
0,
cwidth,
105
)
c.fill(
30,
105,
200
)
c.rect(
0,
0,
cwidth,
14
)
}
// ============================================================
// FLAG
// ============================================================
def drawFlag(
c: CanvasDraw
): Unit = {
val fx =
cwidth - 165.0
val fy =
cheight - 160.0
val fw =
135.0
val fh =
22.0
c.stroke(
220,
220,
220
)
c.strokeWeight(5)
c.line(
fx,
fy - 120.0,
fx,
fy + 70.0
)
var x = 0.0
while (
x < fw
) {
val offset =
math.sin(
flagTime +
x * 0.035
) * 7.0
c.strokeWeight(4)
c.stroke(
255,
153,
51
)
c.line(
fx + x,
fy + 44.0 + offset,
fx + x,
fy + 66.0 + offset
)
c.stroke(
255,
255,
255
)
c.line(
fx + x,
fy + 22.0 + offset,
fx + x,
fy + 44.0 + offset
)
c.stroke(
18,
136,
7
)
c.line(
fx + x,
fy + offset,
fx + x,
fy + 22.0 + offset
)
x += 4.0
}
val chakraX =
fx +
fw / 2.0
val chakraY =
fy +
33.0 +
math.sin(
flagTime
) * 7.0
c.noFill()
c.stroke(
0,
0,
140
)
c.strokeWeight(2)
c.ellipse(
chakraX,
chakraY,
18,
18
)
}
// ============================================================
// ASTRONAUT DRAW
// ============================================================
def drawAstronaut(
c: CanvasDraw,
x: Double,
y: Double
): Unit = {
var leg = 0.0
if (astronautWalking) {
leg =
math.sin(
worldTime * 10.0
) * 3.5
}
c.fill(
0,
0,
0,
70
)
c.noStroke()
c.ellipse(
x,
y - 7.0,
25,
7
)
// body
c.fill(
235,
238,
245
)
c.stroke(
120,
140,
165
)
c.strokeWeight(2)
c.rect(
x - 7.0,
y,
14,
21
)
// helmet
c.ellipse(
x,
y + 28.0,
20,
20
)
// visor
c.fill(
20,
150,
220
)
c.noStroke()
c.ellipse(
x,
y + 28.0,
13,
9
)
// legs
c.stroke(
215,
220,
228
)
c.strokeWeight(4)
c.line(
x - 3,
y,
x - 6.0 - leg,
y - 11.0
)
c.line(
x + 3,
y,
x + 6.0 + leg,
y - 11.0
)
// arms
c.line(
x - 7,
y + 17,
x - 13,
y + 7
)
c.line(
x + 7,
y + 17,
x + 13,
y + 7
)
}
// ============================================================
// ARMY PERSON DRAW
// ============================================================
def drawArmyPerson(
c: CanvasDraw,
x: Double,
y: Double
): Unit = {
var walk = 0.0
if (armyWalking) {
walk =
math.sin(
worldTime * 10.0
) * 4.0
}
c.fill(
0,
0,
0,
70
)
c.noStroke()
c.ellipse(
x,
y - 7.0,
28,
7
)
// boots
c.fill(
15,
15,
18
)
c.rect(
x - 8.0 - walk,
y - 10.0,
7,
4
)
c.rect(
x + 1.0 + walk,
y - 10.0,
7,
4
)
// uniform
c.fill(
70,
100,
50
)
c.stroke(
35,
55,
30
)
c.strokeWeight(1)
c.rect(
x - 10.0,
y,
20,
28
)
// head
c.fill(
205,
155,
120
)
c.noStroke()
c.ellipse(
x,
y + 39.0,
18,
18
)
// cap
c.fill(
50,
75,
35
)
c.rect(
x - 10.0,
y + 47.0,
20,
6
)
// legs
c.stroke(
60,
85,
45
)
c.strokeWeight(5)
c.line(
x - 5.0,
y,
x - 7.0 - walk,
y - 12.0
)
c.line(
x + 5.0,
y,
x + 7.0 + walk,
y - 12.0
)
// arms
c.line(
x - 10,
y + 22,
x - 16,
y + 8
)
c.line(
x + 10,
y + 22,
x + 16,
y + 8
)
}
// ============================================================
// TANK DRAW
// ============================================================
def drawTank(
c: CanvasDraw
): Unit = {
if (
gameState == 4 ||
gameState == 5 ||
gameState == 6
) {
return
}
c.fill(
0,
0,
0,
80
)
c.noStroke()
c.ellipse(
tankX,
tankY - 4,
120,
16
)
// tracks
c.fill(
25,
27,
30
)
c.stroke(
10,
10,
12
)
c.strokeWeight(2)
c.rect(
tankX - 52.0,
tankY,
104,
24
)
// wheels
c.fill(
75,
80,
75
)
for (
i <- -4 to 4
) {
c.ellipse(
tankX + i * 11.0,
tankY + 12.0,
9,
9
)
}
// hull
c.fill(
55,
88,
47
)
c.rect(
tankX - 42.0,
tankY + 24.0,
84,
22
)
// turret
c.fill(
43,
74,
40
)
c.ellipse(
tankX,
tankY + 46.0,
46,
25
)
// cannon
val barrel = 68.0
val tipX =
tankX +
math.cos(
cannonAngle
) *
barrel
val tipY =
tankY +
46.0 +
math.sin(
cannonAngle
) *
barrel
c.stroke(
25,
50,
20
)
c.strokeWeight(8)
c.line(
tankX,
tankY + 46.0,
tipX,
tipY
)
if (
muzzleFlash > 0
) {
c.noStroke()
c.fill(
255,
170,
30,
220
)
c.ellipse(
tipX,
tipY,
45,
45
)
c.fill(
255,
245,
170,
230
)
c.ellipse(
tipX,
tipY,
20,
20
)
}
}
// ============================================================
// LAUNCHER DRAW
// ============================================================
def drawLauncher(
c: CanvasDraw
): Unit = {
c.fill(
45,
48,
55
)
c.stroke(
20,
22,
26
)
c.strokeWeight(2)
c.ellipse(
launcherX,
launcherY,
58,
58
)
c.pushMatrix()
c.translate(
launcherX,
launcherY
)
c.rotate(
launcherAngle
)
c.fill(
62,
78,
70
)
c.rect(
-14,
-16,
62,
32
)
c.fill(
220,
30,
30
)
c.noStroke()
c.rect(
16,
-16,
8,
32
)
c.popMatrix()
}
// ============================================================
// CONTROLS
// ============================================================
def drawControls(
c: CanvasDraw
): Unit = {
c.fill(
10,
16,
30,
240
)
c.stroke(
0,
255,
205
)
c.strokeWeight(1)
c.rect(
15,
88,
665,
48
)
c.fill(
55,
55,
85
)
c.rect(20, 96, 50, 28)
c.rect(75, 96, 50, 28)
c.fill(
255,
255,
255
)
c.text(
"TANK-",
29,
114
)
c.text(
"TANK+",
84,
114
)
c.fill(
75,
42,
42
)
c.rect(
130,
96,
50,
28
)
c.rect(
185,
96,
50,
28
)
c.fill(
255,
255,
255
)
c.text(
"CANN-",
136,
114
)
c.text(
"CANN+",
191,
114
)
c.fill(
205,
70,
0
)
c.rect(
240,
96,
58,
28
)
c.fill(
255,
255,
255
)
c.text(
"FIRE",
256,
114
)
c.fill(
40,
90,
90
)
c.rect(
310,
96,
45,
28
)
c.rect(
360,
96,
45,
28
)
c.fill(
255,
255,
255
)
c.text(
"M-",
323,
114
)
c.text(
"M+",
373,
114
)
c.fill(
70,
30,
110
)
c.rect(
415,
96,
45,
28
)
c.rect(
465,
96,
45,
28
)
c.rect(
515,
96,
45,
28
)
c.fill(
255,
255,
255
)
c.text(
"A",
433,
114
)
c.text(
"B",
483,
114
)
c.text(
"C",
533,
114
)
c.fill(
145,
0,
180
)
c.rect(
565,
96,
90,
28
)
c.fill(
255,
255,
255
)
c.text(
"LAUNCH",
580,
114
)
}
// ============================================================
// HUD
// ============================================================
def drawHUD(
c: CanvasDraw
): Unit = {
c.fill(
8,
14,
30,
235
)
c.stroke(
255,
215,
0
)
c.strokeWeight(1.5)
c.rect(
18,
cheight - 58,
420,
44
)
c.fill(
255,
215,
0
)
c.text(
s"MISSION TIME: ${missionTimer.toInt}s",
30,
cheight - 34
)
c.fill(
120,
255,
200
)
c.text(
"90 SECOND DEFENSE",
190,
cheight - 34
)
// progress
c.fill(
30,
40,
60
)
c.rect(
450,
cheight - 50,
190,
14
)
val progress =
missionTimer / 90.0
c.fill(
255,
153,
51
)
c.rect(
450,
cheight - 50,
190 * progress,
14
)
}
// ============================================================
// LETTER
// ============================================================
def drawLetter(
c: CanvasDraw
): Unit = {
c.background(
3,
6,
14
)
val lx =
cwidth / 2.0 - 270.0
val ly =
cheight / 2.0 - 150.0
c.fill(
15,
25,
45,
250
)
c.stroke(
0,
255,
200
)
c.strokeWeight(
2.5
)
c.rect(
lx,
ly,
540,
320
)
c.fill(
255,
215,
0
)
c.text(
"OPERATION SINDOOR - PART 2",
lx + 145,
ly + 25
)
val shown =
missionLetter.substring(
0,
letterTyped
)
val lines =
shown.split(
"\n"
)
var yy =
ly + 55.0
for (
line <- lines
) {
c.fill(
225,
240,
255
)
c.text(
line,
lx + 20,
yy
)
yy += 17.0
}
if (
letterTyped >=
missionLetter.length
) {
val bx =
lx + 170.0
val by =
ly - 55.0
c.fill(
0,
180,
120
)
c.stroke(
255,
255,
255
)
c.strokeWeight(2)
c.rect(
bx,
by,
200,
36
)
c.fill(
255,
255,
255
)
c.text(
"START MISSION",
bx + 47,
by + 23
)
if (
isMousePressed &&
mouseX >= bx &&
mouseX <= bx + 200 &&
mouseY >= by &&
mouseY <= by + 36
) {
gameState = 1
playFx(
72,
120
)
}
}
}
// ============================================================
// FINAL THREAT DRAW
// ============================================================
def drawFinalThreat(
c: CanvasDraw
): Unit = {
if (
gameState != 3
) {
return
}
for (
i <- 0 until 15
) {
c.fill(
255,
80,
20,
120 - i * 6
)
c.noStroke()
c.ellipse(
finaleX,
finaleY + i * 11,
10 - i * 0.4,
10 - i * 0.4
)
}
c.pushMatrix()
c.translate(
finaleX,
finaleY
)
c.rotate(
-math.Pi / 2.0
)
c.fill(
240,
70,
45
)
c.stroke(
100,
20,
20
)
c.strokeWeight(2)
c.rect(
-30,
-10,
60,
20
)
c.fill(
255,
225,
100
)
c.noStroke()
c.ellipse(
30,
0,
20,
20
)
c.popMatrix()
}
// ============================================================
// FINAL REUNION
// ============================================================
def drawFinalReunion(
c: CanvasDraw
): Unit = {
c.background(
5,
6,
10
)
val glow =
25 +
math.abs(
math.sin(
finalGlow
)
) * 35
c.fill(
255,
185,
80,
glow.toInt
)
c.noStroke()
c.ellipse(
cwidth / 2.0,
320,
430,
260
)
// tricolor horizon
c.strokeWeight(
5
)
c.stroke(
255,
153,
51,
180
)
c.line(
100,
120,
cwidth - 100,
120
)
c.stroke(
255,
255,
255,
150
)
c.line(
100,
112,
cwidth - 100,
112
)
c.stroke(
18,
136,
7,
180
)
c.line(
100,
104,
cwidth - 100,
104
)
val ax =
cwidth / 2.0 -
65.0 +
finalWalk
val bx =
cwidth / 2.0 +
65.0 -
finalWalk
if (
finalWalk < 62.0
) {
drawAstronaut(
c,
ax,
135
)
drawArmyPerson(
c,
bx,
135
)
}
else {
drawAstronaut(
c,
cwidth / 2.0 - 18.0,
135
)
drawArmyPerson(
c,
cwidth / 2.0 + 18.0,
135
)
c.fill(
255,
255,
255,
80
)
c.noStroke()
c.ellipse(
cwidth / 2.0,
225,
95,
95
)
c.fill(
255,
225,
150,
200
)
c.text(
"MISSION OVER",
cwidth / 2.0 - 55,
300
)
c.fill(
245,
245,
245
)
c.text(
"A final salute in silence.",
cwidth / 2.0 - 70,
275
)
}
}
// ============================================================
// BLACK FADE
// ============================================================
def drawFade(
c: CanvasDraw
): Unit = {
c.background(
0,
0,
0
)
val alpha =
(
endFade * 255.0
).toInt
c.fill(
255,
255,
255,
alpha
)
c.text(
"THE END",
cwidth / 2.0 - 40.0,
cheight / 2.0 + 60.0
)
c.fill(
255,
255,
255,
alpha
)
c.text(
"THE END OF THE ASTRONAUT",
cwidth / 2.0 - 120.0,
cheight / 2.0 + 20.0
)
c.text(
"AND THE ARMY HERO",
cwidth / 2.0 - 105.0,
cheight / 2.0 - 10.0
)
}
// ============================================================
// FINAL END SCREEN
// ============================================================
def drawEnd(
c: CanvasDraw
): Unit = {
c.background(
0,
0,
0
)
// stars
for (
i <- 0 until 90
) {
val sx =
(
i * 83.0 +
endStars *
(8.0 + i % 4)
) %
(cwidth + 30.0)
val sy =
55.0 +
(
i * 47
) %
(cheight - 120.0)
val sa =
(
60 +
math.abs(
math.sin(
endStars +
i
)
) * 170
).toInt
c.fill(
255,
255,
255,
sa
)
c.noStroke()
c.ellipse(
sx,
sy,
1.8,
1.8
)
}
// light sweep
c.fill(
255,
255,
255,
16
)
c.rect(
endSweep,
0,
85,
cheight
)
// THE END
c.fill(
255,
255,
255
)
c.text(
"THE END",
cwidth / 2.0 - 42.0,
cheight / 2.0 + 100.0
)
// Story title
c.text(
"THE END OF THE ASTRONAUT",
cwidth / 2.0 - 125.0,
cheight / 2.0 + 48.0
)
c.text(
"AND THE ARMY HERO",
cwidth / 2.0 - 105.0,
cheight / 2.0 + 18.0
)
// Tribute
c.fill(
255,
255,
255
)
c.text(
"PRAY FOR OUR ARMY SOLDIERS",
cwidth / 2.0 - 138.0,
cheight / 2.0 - 30.0
)
c.fill(
225,
225,
225
)
c.text(
"We remember their courage, service and sacrifice.",
cwidth / 2.0 - 155.0,
cheight / 2.0 - 68.0
)
c.fill(
255,
255,
255
)
c.text(
"JAI HIND",
cwidth / 2.0 - 35.0,
cheight / 2.0 - 115.0
)
c.text(
"SALUTE TO OUR SOLDIERS",
cwidth / 2.0 - 83.0,
cheight / 2.0 - 152.0
)
// tricolor
c.strokeWeight(4)
c.stroke(
255,
153,
51
)
c.line(
cwidth / 2.0 - 155.0,
75.0,
cwidth / 2.0 + 155.0,
75.0
)
c.stroke(
255,
255,
255
)
c.line(
cwidth / 2.0 - 155.0,
68.0,
cwidth / 2.0 + 155.0,
68.0
)
c.stroke(
18,
136,
7
)
c.line(
cwidth / 2.0 - 155.0,
61.0,
cwidth / 2.0 + 155.0,
61.0
)
}
// ============================================================
// VIEW STATE
// ============================================================
def viewState(
c: CanvasDraw
): Unit = {
// ----------------------------------------------------------
// LETTER
// ----------------------------------------------------------
if (
gameState == 0
) {
drawLetter(c)
return
}
// ----------------------------------------------------------
// FINAL REUNION
// ----------------------------------------------------------
if (
gameState == 4
) {
drawFinalReunion(c)
return
}
// ----------------------------------------------------------
// BLACK FADE
// ----------------------------------------------------------
if (
gameState == 5
) {
drawFade(c)
return
}
// ----------------------------------------------------------
// FINAL SCREEN
// ----------------------------------------------------------
if (
gameState == 6
) {
drawEnd(c)
return
}
// ----------------------------------------------------------
// NORMAL BACKGROUND
// ----------------------------------------------------------
drawBackground(c)
drawFlag(c)
repeatFor(
glowParticles
)(
_.view(c)
)
repeatFor(
fireworks
)(
_.view(c)
)
// ----------------------------------------------------------
// ENTRY
// ----------------------------------------------------------
if (
gameState == 1
) {
drawAstronaut(
c,
astronautX,
astronautY
)
drawArmyPerson(
c,
armyX,
armyY
)
c.fill(
255,
215,
90
)
c.text(
"ASTRONAUT + ARMY DEFENSE TEAM",
cwidth / 2.0 - 105.0,
cheight - 55.0
)
return
}
// ----------------------------------------------------------
// MAIN MISSION
// ----------------------------------------------------------
drawLauncher(c)
drawTank(c)
repeatFor(
shells
)(
_.view(c)
)
repeatFor(
strategicMissiles
)(
_.view(c)
)
repeatFor(
enemyTargets
)(
_.view(c)
)
repeatFor(
explosionParticles
)(
_.view(c)
)
repeatFor(
shockwaves
)(
_.view(c)
)
if (
gameState == 2
) {
drawControls(c)
drawHUD(c)
}
if (
gameState == 3
) {
drawFinalThreat(c)
c.fill(
255,
80,
50
)
c.text(
"FINAL THREAT",
cwidth / 2.0 - 45.0,
cheight - 45.0
)
}
}
// ============================================================
// MAIN LOOP
// ============================================================
animateWithCanvasDraw { c =>
updateState()
viewState(c)
}