Code Sketch
sniper_0718
Category: Art
import java.awt.{Color, Font, Graphics2D, RenderingHints}
import java.awt.event.{KeyEvent, KeyListener, ActionListener, ActionEvent}
import javax.swing.{JFrame, JPanel, Timer}
import scala.math._
object Realistic5DayGrannyGame {
val ScreenWidth = 640
val ScreenHeight = 480
// 1 = Wall, 0 = Floor, 2 = Door, 3 = Bed
// Items: 4 = Screwdriver, 5 = Hammer, 6 = Safe Key, 7 = Chit Code
val mapWidth = 8
val mapHeight = 8
var map: Array[Array[Int]] = Array(
Array(1,1,1,1,1,1,2,1), // Top Wall with Door (2)
Array(1,0,0,0,0,0,4,1), // Screwdriver (4)
Array(1,3,3,0,0,0,0,1), // Bed (3) on Left
Array(1,3,3,0,0,0,5,1), // Hammer (5)
Array(1,0,0,0,0,0,6,1), // Safe Key (6)
Array(1,0,0,0,0,0,7,1), // Chit Code (7)
Array(1,0,0,0,0,0,0,1),
Array(1,1,1,1,1,1,1,1)
)
// Game States & 5-Day System
var gameStarted = false
var gameOver = false
var gameEscaped = false
var showingStairs = false // Brown stairs view before final escape
var currentDay = 1
var showingDayScreen = false
var dayScreenTimer = 0
// Player Settings & Inventory (4 items required)
var playerX = 2.0
var playerY = 5.0
var playerA = 0.0
val fov = Pi / 3.0
var isHiding = false
var hasScrewdriver = false
var hasHammer = false
var hasSafeKey = false
var hasChitCode = false
// Granny AI (Starts in hole/corner)
var grannyX = 6.5
var grannyY = 6.5
var grannyChasing = false
val grannySpeed = 0.015
// Controls
var wPressed = false
var sPressed = false
var aPressed = false
var dPressed = false
var leftKey = false
var rightKey = false
// GUI Canvas
val frame = new JFrame("3D Granny Horror - Items & Brown Stairs")
val panel = new JPanel {
override def paintComponent(gNative: java.awt.Graphics): Unit = {
super.paintComponent(gNative)
val g = gNative.asInstanceOf[Graphics2D]
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
if (!gameStarted) {
g.setColor(Color.BLACK)
g.fillRect(0, 0, ScreenWidth, ScreenHeight)
g.setColor(new Color(180, 20, 20))
g.setFont(new Font("Arial", Font.BOLD, 32))
g.drawString("GRANNY: COLLECT ITEMS & ESCAPE", ScreenWidth / 2 - 240, ScreenHeight / 2 - 80)
g.setColor(Color.LIGHT_GRAY)
g.setFont(new Font("Arial", Font.PLAIN, 15))
g.drawString("Press ENTER to Start", ScreenWidth / 2 - 80, ScreenHeight / 2 - 30)
g.drawString("W,A,S,D : Walk/Run | Left/Right : Turn", ScreenWidth / 2 - 150, ScreenHeight / 2 + 10)
g.drawString("E : Pick Items / Open Door | R : Hide in Bed", ScreenWidth / 2 - 160, ScreenHeight / 2 + 40)
g.drawString("Need: Screwdriver, Hammer, Safe Key, Chit Code", ScreenWidth / 2 - 180, ScreenHeight / 2 + 75)
} else if (showingDayScreen) {
g.setColor(Color.BLACK)
g.fillRect(0, 0, ScreenWidth, ScreenHeight)
g.setColor(new Color(100, 100, 100))
g.setFont(new Font("Serif", Font.BOLD, 48))
g.drawString(s"DAY $currentDay", ScreenWidth / 2 - 80, ScreenHeight / 2)
} else if (gameOver) {
g.setColor(new Color(30, 0, 0))
g.fillRect(0, 0, ScreenWidth, ScreenHeight)
g.setColor(Color.RED)
g.setFont(new Font("Arial", Font.BOLD, 36))
g.drawString("GAME OVER!", ScreenWidth / 2 - 120, ScreenHeight / 2 - 30)
g.setFont(new Font("Arial", Font.PLAIN, 20))
g.drawString("Granny caught you on Day 5!", ScreenWidth / 2 - 130, ScreenHeight / 2 + 10)
g.setColor(Color.WHITE)
g.setFont(new Font("Arial", Font.PLAIN, 16))
g.drawString("Press ENTER to Restart", ScreenWidth / 2 - 90, ScreenHeight / 2 + 60)
} else if (showingStairs) {
// --- BROWN STAIRS VIEW ---
g.setColor(new Color(30, 15, 5))
g.fillRect(0, 0, ScreenWidth, ScreenHeight)
// Draw Brown Stairs steps
g.setColor(new Color(120, 70, 30))
for (i <- 0 until 6) {
g.fillRect(120 + i * 45, 100 + i * 45, ScreenWidth - 240 - i * 90, 40)
g.setColor(new Color(90, 50, 20))
g.drawRect(120 + i * 45, 100 + i * 45, ScreenWidth - 240 - i * 90, 40)
g.setColor(new Color(140, 85, 35))
}
g.setColor(Color.YELLOW)
g.setFont(new Font("Arial", Font.BOLD, 24))
g.drawString("STAIRS DOWN TO FREEDOM!", ScreenWidth / 2 - 160, 50)
g.setFont(new Font("Arial", Font.PLAIN, 16))
g.drawString("Press 'E' to go down the stairs and escape!", ScreenWidth / 2 - 160, ScreenHeight - 50)
} else if (gameEscaped) {
g.setColor(new Color(20, 40, 20))
g.fillRect(0, 0, ScreenWidth, ScreenHeight)
g.setColor(Color.GREEN)
g.setFont(new Font("Arial", Font.BOLD, 32))
g.drawString("YOU ESCAPED THE HOUSE!", ScreenWidth / 2 - 180, ScreenHeight / 2 - 20)
g.setColor(Color.WHITE)
g.setFont(new Font("Arial", Font.PLAIN, 18))
g.drawString("Press ENTER to Play Again", ScreenWidth / 2 - 120, ScreenHeight / 2 + 40)
} else {
// Ceiling & Floor
g.setColor(new Color(60, 40, 20))
g.fillRect(0, 0, ScreenWidth, ScreenHeight / 2)
g.setColor(new Color(70, 70, 75))
g.fillRect(0, ScreenHeight / 2, ScreenWidth, ScreenHeight / 2)
val depthBuffer = new Array[Double](ScreenWidth)
for (x <- 0 until ScreenWidth) {
val rayAngle = (playerA - fov / 2.0) + (x.toDouble / ScreenWidth.toDouble) * fov
var distanceToWall = 0.0
var hitWall = false
var wallType = 1
val eyeX = cos(rayAngle)
val eyeY = sin(rayAngle)
while (!hitWall && distanceToWall < 10.0) {
distanceToWall += 0.03
val checkX = (playerX + eyeX * distanceToWall).toInt
val checkY = (playerY + eyeY * distanceToWall).toInt
if (checkX < 0 || checkX >= mapWidth || checkY < 0 || checkY >= mapHeight) {
hitWall = true
distanceToWall = 10.0
} else if (map(checkY)(checkX) > 0) {
hitWall = true
wallType = map(checkY)(checkX)
}
}
val correctedDist = distanceToWall * cos(rayAngle - playerA)
depthBuffer(x) = correctedDist
val ceiling = (ScreenHeight / 2.0) - (ScreenHeight / correctedDist)
val floor = ScreenHeight - ceiling
val wallHeight = floor - ceiling
val brightness = max(0, min(180, (180 * (1.0 - correctedDist / 8.5)).toInt))
wallType match {
case 1 => g.setColor(new Color((brightness * 0.8).toInt, (brightness * 0.55).toInt, (brightness * 0.3).toInt)) // Wall
case 2 => g.setColor(new Color(40, 15, 5)) // Very Dark Brown Door
case 3 => g.setColor(new Color((brightness * 0.6).toInt, (brightness * 0.35).toInt, (brightness * 0.18).toInt)) // Bed
case 4 => g.setColor(Color.ORANGE) // Screwdriver
case 5 => g.setColor(Color.CYAN) // Hammer
case 6 => g.setColor(Color.YELLOW) // Safe Key
case 7 => g.setColor(Color.PINK) // Chit Code
case _ => g.setColor(Color.BLACK)
}
g.fillRect(x, ceiling.toInt, 1, wallHeight.toInt)
}
val ex = grannyX - playerX
val ey = grannyY - playerY
var grannyAngle = atan2(ey, ex) - playerA
while (grannyAngle < -Pi) grannyAngle += 2 * Pi
while (grannyAngle > Pi) grannyAngle -= 2 * Pi
val grannyDist = sqrt(ex * ex + ey * ey)
if (grannyDist > 0.2 && abs(grannyAngle) < fov) {
val screenX = (ScreenWidth / 2.0 * (1.0 + tan(grannyAngle) / tan(fov / 2.0))).toInt
val spriteSize = min(ScreenWidth, (ScreenHeight / grannyDist).toInt)
val spriteTop = (ScreenHeight / 2) - spriteSize / 2
val startX = max(0, screenX - spriteSize / 2)
val endX = min(ScreenWidth - 1, screenX + spriteSize / 2)
for (x <- startX to endX) {
if (x >= 0 && x < ScreenWidth && depthBuffer(x) > grannyDist) {
val gShade = max(20, min(200, (200 * (1.0 - grannyDist / 8.0)).toInt))
g.setColor(new Color(gShade, gShade, (gShade * 0.9).toInt))
g.fillRect(x, spriteTop, 1, spriteSize)
if (grannyChasing) g.setColor(Color.RED) else g.setColor(Color.WHITE)
val eyeSize = max(2, spriteSize / 8)
g.fillRect(x, spriteTop + spriteSize / 5, 1, eyeSize)
}
}
}
// Crosshair
g.setColor(Color.WHITE)
g.drawLine(ScreenWidth / 2 - 5, ScreenHeight / 2, ScreenWidth / 2 + 5, ScreenHeight / 2)
g.drawLine(ScreenWidth / 2, ScreenHeight / 2 - 5, ScreenWidth / 2, ScreenHeight / 2 + 5)
g.setColor(Color.RED)
g.setFont(new Font("Arial", Font.BOLD, 16))
g.drawString(s"DAY $currentDay / 5", ScreenWidth - 110, 30)
if (isHiding) {
g.setColor(Color.YELLOW)
g.setFont(new Font("Arial", Font.BOLD, 14))
g.drawString("[ HIDDEN IN BED - PRESS 'R' TO GET OUT ]", ScreenWidth / 2 - 150, ScreenHeight - 20)
}
if (grannyChasing) {
g.setColor(Color.RED)
g.setFont(new Font("Arial", Font.BOLD, 15))
g.drawString("GRANNY IS CHASING YOU!", ScreenWidth / 2 - 110, 40)
}
// Inventory HUD
g.setColor(Color.WHITE)
g.setFont(new Font("Arial", Font.BOLD, 12))
val sdc = if (hasScrewdriver) "YES" else "NO"
val hmc = if (hasHammer) "YES" else "NO"
val skc = if (hasSafeKey) "YES" else "NO"
val ccc = if (hasChitCode) "YES" else "NO"
g.drawString(s"Items -> Screwdriver: $sdc | Hammer: $hmc | SafeKey: $skc | Chit: $ccc", 15, 25)
}
}
}
def updateGame(): Unit = {
if (showingDayScreen) {
dayScreenTimer -= 1
if (dayScreenTimer <= 0) {
showingDayScreen = false
}
} else if (gameStarted && !gameOver && !gameEscaped && !showingStairs) {
val moveSpeed = 0.09
val rotSpeed = 0.04
if (!isHiding) {
if (leftKey) playerA -= rotSpeed
if (rightKey) playerA += rotSpeed
var newX = playerX
var newY = playerY
if (wPressed) { newX += cos(playerA) * moveSpeed; newY += sin(playerA) * moveSpeed }
if (sPressed) { newX -= cos(playerA) * moveSpeed; newY -= sin(playerA) * moveSpeed }
if (aPressed) { newX += sin(playerA) * moveSpeed; newY -= cos(playerA) * moveSpeed }
if (dPressed) { newX -= sin(playerA) * moveSpeed; newY += cos(playerA) * moveSpeed }
val checkCellX = map(playerY.toInt)(newX.toInt)
val checkCellY = map(newY.toInt)(playerX.toInt)
if (checkCellY == 0 || checkCellY == 3) playerY = newY
if (checkCellX == 0 || checkCellX == 3) playerX = newX
}
val distToPlayer = sqrt(pow(playerX - grannyX, 2) + pow(playerY - grannyY, 2))
if (distToPlayer < 3.0 && !isHiding) {
grannyChasing = true
}
if (grannyChasing) {
val dx = playerX - grannyX
val dy = playerY - grannyY
val dist = sqrt(dx * dx + dy * dy)
if (dist > 0.4) {
val grannyNewX = grannyX + (dx / dist) * grannySpeed
val grannyNewY = grannyY + (dy / dist) * grannySpeed
if (map(grannyY.toInt)(grannyNewX.toInt) == 0 || map(grannyY.toInt)(grannyNewX.toInt) == 3) grannyX = grannyNewX
if (map(grannyNewY.toInt)(grannyX.toInt) == 0 || map(grannyNewY.toInt)(grannyX.toInt) == 3) grannyY = grannyNewY
}
}
if (distToPlayer < 0.5 && !isHiding) {
if (currentDay < 5) {
currentDay += 1
respawnPlayerForNextDay()
} else {
gameOver = true
}
}
}
panel.repaint()
}
val gameLoop: Timer = new Timer(16, new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
updateGame()
}
})
def respawnPlayerForNextDay(): Unit = {
playerX = 2.0
playerY = 5.0
playerA = 0.0
grannyX = 6.5
grannyY = 6.5
grannyChasing = false
isHiding = false
showingStairs = false
showingDayScreen = true
dayScreenTimer = 120
}
def handleAction(): Unit = {
if (showingStairs) {
gameEscaped = true
showingStairs = false
return
}
val frontX = (playerX + cos(playerA) * 0.8).toInt
val frontY = (playerY + sin(playerA) * 0.8).toInt
if (frontX >= 0 && frontX < mapWidth && frontY >= 0 && frontY < mapHeight) {
val cell = map(frontY)(frontX)
if (cell == 4) { hasScrewdriver = true; map(frontY)(frontX) = 0 }
else if (cell == 5) { hasHammer = true; map(frontY)(frontX) = 0 }
else if (cell == 6) { hasSafeKey = true; map(frontY)(frontX) = 0 }
else if (cell == 7) { hasChitCode = true; map(frontY)(frontX) = 0 }
else if (cell == 2) {
// Check if all 4 items are collected
if (hasScrewdriver && hasHammer && hasSafeKey && hasChitCode) {
showingStairs = true // Open door to show brown stairs
}
}
}
}
def toggleHide(): Unit = {
val currentCell = map(playerY.toInt)(playerX.toInt)
if (currentCell == 3 || isHiding) {
isHiding = !isHiding
if (isHiding) {
grannyChasing = false
}
}
}
def restartGame(): Unit = {
currentDay = 1
hasScrewdriver = false
hasHammer = false
hasSafeKey = false
hasChitCode = false
gameOver = false
gameEscaped = false
showingStairs = false
gameStarted = true
// Reset items on map
map(1)(6) = 4
map(3)(6) = 5
map(4)(6) = 6
map(5)(6) = 7
respawnPlayerForNextDay()
}
def run(): Unit = {
panel.setPreferredSize(new java.awt.Dimension(ScreenWidth, ScreenHeight))
panel.setFocusable(true)
panel.addKeyListener(new KeyListener {
override def keyTyped(e: KeyEvent): Unit = {}
override def keyPressed(e: KeyEvent): Unit = {
e.getKeyCode match {
case KeyEvent.VK_ENTER => if (!gameStarted || gameOver || gameEscaped) restartGame()
case KeyEvent.VK_W => wPressed = true
case KeyEvent.VK_S => sPressed = true
case KeyEvent.VK_A => aPressed = true
case KeyEvent.VK_D => dPressed = true
case KeyEvent.VK_LEFT => leftKey = true
case KeyEvent.VK_RIGHT => rightKey = true
case KeyEvent.VK_E => handleAction()
case KeyEvent.VK_R => toggleHide()
case _ =>
}
}
override def keyReleased(e: KeyEvent): Unit = {
e.getKeyCode match {
case KeyEvent.VK_W => wPressed = false
case KeyEvent.VK_S => sPressed = false
case KeyEvent.VK_A => aPressed = false
case KeyEvent.VK_D => dPressed = false
case KeyEvent.VK_LEFT => leftKey = false
case KeyEvent.VK_RIGHT => rightKey = false
case _ =>
}
}
})
frame.setContentPane(panel)
frame.pack()
frame.setLocationRelativeTo(null)
frame.setVisible(true)
panel.requestFocusInWindow()
gameLoop.start()
}
def main(args: Array[String]): Unit = {
run()
}
}
Realistic5DayGrannyGame.run()