Files
chess/American-Identity-Project/Platformer.java
CoolGuy27 a97e122e58 Add a new platformer game with various levels and enemies
The agent has updated the game's core logic, including player movement, enemy AI, collision detection, and level loading. New sprites and sound effects have been integrated, along with the addition of a Chess game. The replit.nix file has been configured to include jdk21 and neovim.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: f6819c21-e85d-45ac-acde-604db2cfa4fe
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 32bc3e83-133e-4485-96b6-ff3d548bcbfd
Replit-Helium-Checkpoint-Created: true
2026-04-20 21:19:57 +00:00

591 lines
17 KiB
Java

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import javax.sound.sampled.*;
import javax.swing.*;
public class Platformer extends JPanel implements KeyListener, ActionListener {
// constants
static final int GRAVITY = 1;
static final Color playerColor = Color.RED;
static final Color tileColor = Color.BLUE;
static final Color SKY = new Color(135, 206, 235);
static final Color NIGHT_SKY = new Color(4, 26, 64);
static final int FRICTION = 1;
static final int MAXYVELO = 15;
static final int MAXXVELO = 5;
static final int totalLevels = 10;
static int[] numAm = {4, 5, 10, 18, 6, 6, 5, 5, 6, 23};
// game objects
Player player;
ArrayList<Collidable> collidables;
ArrayList<Collectable> collectables;
Flag flag;
ArrayList<Enemy> enemies;
ArrayList<Projectile> projectiles;
// game vars
int boardWidth;
int boardHeight;
int tileSize;
int enemiesKilled;
Timer gameTimer;
HashMap<Integer, Boolean> pressedKeys;
boolean jumpPressed;
int cameraX, cameraY;
int currentLevel;
boolean allCollected;
boolean gameOver;
boolean gameStarted;
Image heart, emptyHeart, slash, amendmentImg, powerImg, pressRImg, endImg, winImg, titleImg;
ArrayList<Image> numbers;
Clip jumpSound, shootSound, hitSound, collectSound;
public Platformer(int boardWidth, int boardHeight, int tileSize) {
// setup game
this.boardWidth = boardWidth;
this.boardHeight = boardHeight;
this.tileSize = tileSize;
setPreferredSize(new Dimension(this.boardWidth, this.boardHeight));
addKeyListener(this);
this.setFocusable(true);
this.setLayout(null);
pressedKeys = new HashMap<>();
jumpPressed = false;
gameOver = false;
gameStarted = false;
setBackground(SKY);
// setup objects
heart = new ImageIcon("Sprites/Hearts/heart.png").getImage();
titleImg = new ImageIcon("Sprites/Title.png").getImage();
emptyHeart = new ImageIcon("Sprites/Hearts/emptyHeart.png").getImage();
endImg = new ImageIcon("Sprites/end.png").getImage();
pressRImg = new ImageIcon("Sprites/PressR.png").getImage();
winImg = new ImageIcon("Sprites/win.png").getImage();
jumpSound = loadClip("Sounds/jump.wav");
shootSound = loadClip("Sounds/shoot.wav");
hitSound = loadClip("Sounds/hit.wav");
collectSound = loadClip("Sounds/collect.wav");
gameTimer = new Timer(15, this);
player = new Player(-20, 0, tileSize, tileSize);
collidables = new ArrayList<>();
collectables = new ArrayList<>();
enemies = new ArrayList<>();
projectiles = new ArrayList<>();
flag = new Flag(-20, 0, tileSize, tileSize);
cameraX = 0;
cameraY = 0;
currentLevel = 0;
enemiesKilled = 0;
numbers = new ArrayList<>();
for (int i = 0; i < 10; i++)
numbers.add((new ImageIcon("Sprites/Numbers/" + i + ".png")).getImage());
slash = new ImageIcon("Sprites/Numbers/Slash.png").getImage();
amendmentImg = new ImageIcon("Sprites/Amendment.png").getImage();
powerImg = new ImageIcon("Sprites/Powerup1.png").getImage();
gameTimer.start();
// if i wanna add a button
/*
* JButton gameStart = new JButton("Start Game");
* gameStart.addActionListener(e -> {
* loadLevel(currentLevel);
* gameTimer.start();
* this.remove(gameStart);
* this.revalidate();
* this.repaint();
* });
* gameStart.setBorderPainted(false);
* gameStart.setFocusPainted(false);
* gameStart.setBounds(193,200,114,15);
* gameStart.setForeground(new Color(52, 152, 219));
* this.add(gameStart);
*/
}
// gameloop
public void gameLoop() {
if (currentLevel > totalLevels) return;
// lvl 3 arena
if (currentLevel == 4 || currentLevel == 10) {
boolean empty = currentLevel == 4 ? enemies.isEmpty() : enemies.size() <= 5;
if (enemiesKilled < 100 * (currentLevel == 4 ? 1 : 4) && empty) {
Random rand = new Random();
int xOff = rand.nextInt(700);
for (int i = 0; i < 10; i++) {
xOff = rand.nextInt(700);
if (xOff + 80 <= player.x && player.x <= xOff + 140) xOff += 60;
enemies.add(new Enemy(100 + xOff, 280, 20, 20, currentLevel));
}
} else if (enemiesKilled >= 100 * (currentLevel == 4 ? 1 : 4)) {
collidables.removeIf(c -> c.y == 300 && c.x >= 440 && c.x <= 520);
}
}
// camera
cameraX = player.x - boardWidth / 2;
cameraX = Math.max(0, cameraX);
cameraY = player.y - boardHeight / 2;
// cameraY = Math.max(0, cameraY);
// win
allCollected = player.numAmendments >= numAm[currentLevel - 1];
if (player.collidesWith(flag) && allCollected) {
currentLevel++;
player.health = 3;
if (currentLevel > totalLevels) {
gameTimer.stop();
System.out.println("You win!");
return;
} else {
loadLevel(currentLevel);
}
return;
}
// keys
if (Math.abs(player.xVelo) < MAXXVELO) {
if (isKeyPressed(KeyEvent.VK_D) || isKeyPressed(KeyEvent.VK_RIGHT)) {
player.xVelo += 1;
} else if (isKeyPressed(KeyEvent.VK_A) || isKeyPressed(KeyEvent.VK_LEFT)) {
player.xVelo -= 1;
}
}
if (!isKeyPressed(KeyEvent.VK_D)
&& !isKeyPressed(KeyEvent.VK_RIGHT)
&& !isKeyPressed(KeyEvent.VK_A)
&& !isKeyPressed(KeyEvent.VK_LEFT)) { // friction
if (player.xVelo > 0) {
player.xVelo = Math.max(0, player.xVelo - FRICTION);
} else if (player.xVelo < 0) {
player.xVelo = Math.min(0, player.xVelo + FRICTION);
}
}
// jump
boolean jumpKeyDown = (isKeyPressed(KeyEvent.VK_W) || isKeyPressed(KeyEvent.VK_UP));
if (jumpKeyDown && !jumpPressed) {
if (player.onGround) {
playClip(jumpSound);
player.yVelo = -15;
player.onGround = false;
jumpPressed = true;
player.airJumps = 0;
} else if (player.curPower == 1 && player.airJumps < 1) {
playClip(jumpSound);
player.yVelo = -15;
player.airJumps++;
jumpPressed = true;
}
}
if (!jumpKeyDown) {
jumpPressed = false;
}
// gravity
if (player.yVelo < MAXYVELO) {
player.yVelo += GRAVITY;
}
// fall out of world
if (player.y > 1400) { // 1400/20 = 70 rows to work with per level
loadLevel(currentLevel);
player.health--;
}
for (Collectable c : collectables) {
if (c instanceof Powerup) {
Powerup pu = (Powerup) c;
if (pu.yVelo < MAXYVELO && !pu.onGround) {
pu.yVelo += GRAVITY;
}
pu.moveY(pu.yVelo);
for (Collidable col : collidables) {
if (pu.collidesWith(col)) {
pu.yVelo = 0;
pu.onGround = true;
pu.y = ((Tile) col).y - pu.height;
pu.rect.y = pu.y;
}
}
if (player.collidesWith(pu)) {
player.curPower = pu.id;
player.powerTimer = Player.POWER_DURATION;
}
} else if (c instanceof Amendment) {
Amendment am = (Amendment) c;
if (player.collidesWith(am)) {
player.numAmendments++;
playClip(collectSound);
}
}
}
collectables.removeIf(c -> player.collidesWith(c));
// update x
player.moveX(player.xVelo);
// collision with all tiles x
for (Collidable c : collidables) {
if (player.collidesWith(c)) {
player.onCollideX(c);
}
}
// update y
player.moveY(player.yVelo);
// assume not on ground
player.onGround = false;
// collision with all tiles y
for (Collidable c : collidables) {
if (player.collidesWith(c)) {
player.onCollideY(c, collectables);
}
}
// Powerup timer
if (player.curPower > 0) {
player.powerTimer--;
if (player.powerTimer <= 0) {
player.curPower = 0;
}
}
// update facing
if (player.xVelo > 0) player.facing = 1;
else if (player.xVelo < 0) player.facing = -1;
// shoot cooldown
if (player.shootCooldown > 0) player.shootCooldown--;
// invincibility timer
if (player.invincibleTimer > 0) player.invincibleTimer--;
// shoot
// projectiles
if (isKeyPressed(KeyEvent.VK_SPACE) && player.shootCooldown == 0) {
playClip(shootSound);
int projX = player.facing == 1 ? player.x + player.width : player.x - 10;
projectiles.add(new Projectile(projX, player.y, tileSize, 10, currentLevel, player.facing));
player.shootCooldown = Player.SHOOT_COOLDOWN;
}
projectiles.removeIf(p -> p.x < -50 + cameraX || p.x > boardWidth + cameraX + 200);
for (Projectile p : new ArrayList<>(projectiles)) {
p.move();
// projectile hits tile
for (Collidable c : collidables) {
if (p.collidesWith(c)) {
projectiles.remove(p);
break;
}
}
}
// update enemies
for (Enemy e : enemies) {
e.patrol(collidables);
// enemy hits player
if (player.collidesWith(e)) {
player.takeDamage();
playClip(hitSound);
}
}
// die
if (player.health <= 0) {
System.out.print("Game Over - You Died!");
gameOver = true;
gameTimer.stop();
}
// projectile hits enemy
for (Projectile p : new ArrayList<>(projectiles)) {
for (Enemy e : new ArrayList<>(enemies)) {
if (p.collidesWith(e)) {
enemies.remove(e);
enemiesKilled++;
projectiles.remove(p);
break;
}
}
}
enemies.removeIf(e -> !e.alive);
}
public void loadLevel(int level) {
projectiles.clear();
enemiesKilled = 0;
try {
LevelLoader.load(tileSize, collidables, collectables, enemies, flag, player, level);
player.reset();
player.setLevel(level);
cameraX = 0;
cameraY = 0;
} catch (IOException e) {
System.out.println("Could not load level " + level);
}
}
// paintComponent
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
// draw function
public void draw(Graphics g) {
// gameover screen
if (gameOver) {
g.drawImage(endImg, boardWidth / 2 - 100, boardHeight / 2 - 150, null);
g.drawImage(pressRImg, boardWidth / 2 - 80, boardHeight / 2, null);
return;
}
if (currentLevel > totalLevels) {
g.drawImage(winImg, boardWidth / 2 - 100, boardHeight / 2 - 50, null);
g.drawImage(pressRImg, boardWidth / 2 - 80, boardHeight / 2 + 60, null);
return;
}
if (currentLevel == 6) {
this.setBackground(NIGHT_SKY);
}
if (currentLevel != 6) {
this.setBackground(SKY);
}
g.translate(-cameraX, -cameraY);
player.draw(g);
for (Collidable c : collidables) c.draw(g);
for (Collectable c : collectables) c.draw(g);
for (Enemy e : enemies) e.draw(g);
for (Projectile p : projectiles) p.draw(g);
flag.draw(g);
if (currentLevel == 2) {
g.drawString("Take a leap of faith....", flag.x - 25, flag.y - 400);
}
if (currentLevel == 4 || currentLevel == 10) {
g.drawString("Kill " + 100 * (currentLevel == 4 ? 1 : 4) + " of them...", 220, 200);
}
if (currentLevel == 10) {
g.drawString("Final Level.... Time for the gauntlet", 80, 80);
}
// flag counter
int amOnes = player.numAmendments % 10;
int amTens = player.numAmendments / 10;
if (currentLevel > 0 && player.numAmendments < numAm[currentLevel - 1]) {
int lvlAmOnes = numAm[currentLevel - 1] % 10;
int lvlAmTens = numAm[currentLevel - 1] / 10;
if (amTens > 0) g.drawImage(numbers.get(amTens), flag.x - 20, flag.y - 30, null);
g.drawImage(numbers.get(amOnes), flag.x + 5, flag.y - 30, null);
g.drawImage(slash, flag.x + 28, flag.y - 32, null);
if (lvlAmTens > 0) {
g.drawImage(numbers.get(lvlAmTens), flag.x + 55, flag.y - 30, null);
g.drawImage(numbers.get(lvlAmOnes), flag.x + 80, flag.y - 30, null);
g.drawImage(amendmentImg, flag.x + 110, flag.y - 30, null);
} else {
g.drawImage(numbers.get(lvlAmOnes), flag.x + 55, flag.y - 30, null);
g.drawImage(amendmentImg, flag.x + 85, flag.y - 30, null);
}
}
g.translate(cameraX, cameraY);
if (currentLevel == 8) {
BufferedImage darkness =
new BufferedImage(boardWidth, boardHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = darkness.createGraphics();
// fill entire overlay with fully opaque black
g2.setColor(new Color(0, 0, 0, 255));
g2.fillRect(0, 0, boardWidth + 1000, boardHeight + 1000);
// player's position in screen coordinates
int screenX = (player.x + player.width / 2) - cameraX;
int screenY = (player.y + player.height / 2) - cameraY;
// cut circle centered on player
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
g2.fillOval(screenX - 80, screenY - 100, 160, 160);
g2.dispose();
// draw overlay in screen space (after translate reset)
g.drawImage(darkness, 0, 0, null);
}
int modAmt = 2000;
int curTime = (int) System.currentTimeMillis() % modAmt;
curTime = Math.abs(curTime);
// start screen
int startTime = 0;
if (curTime >= 0 && curTime <= modAmt / 4 - 1) startTime = 0;
else if (curTime >= modAmt / 4 && curTime <= modAmt / 2 - 1) startTime = 1;
else if (curTime >= modAmt / 2 && curTime <= modAmt * 3 / 4 - 1) startTime = 2;
else if (curTime >= modAmt * 3 / 4 && curTime <= modAmt - 1) startTime = 3;
if (currentLevel == 0) {
g.drawImage(titleImg, boardWidth / 2 - 150, boardHeight / 2 - 200, null);
g.drawString("An American Identity Project", boardWidth / 2 - 75, boardHeight / 2 - 40);
String text = "Press P to Start!";
int xBase = 340;
int yBase = 400;
int spacing = 8;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
// x moves linearly
int x = xBase + (i * spacing);
// y uses a sine wave
// Math.sin takes radians. We use (startTime + i) to give each letter a
// different phase.
double waveOffset = Math.sin(startTime + i * 0.5) * 10;
int y = yBase + (int) waveOffset;
g.drawString(String.valueOf(c), x, y);
}
}
if (currentLevel > 0) {
// draw hearts:
int heartTime = 0;
if (curTime >= 0 && curTime <= modAmt / 2 - 1) heartTime = 1;
else if (curTime >= modAmt / 2 && curTime <= modAmt - 1) heartTime = 0;
for (int i = 0; i < player.health; i++) {
g.drawImage(heart, (((i + 1) * 20) - 10) + heartTime, 10 + heartTime * 2, null);
}
for (int i = 0; i < 3 - player.health; i++) {
g.drawImage(emptyHeart, (50 - (i * 20)) + heartTime, 10 + heartTime * 2, null);
}
// draw amendments counter in top right
if (amTens > 0) g.drawImage(numbers.get(amTens), 315, 10, null);
g.drawImage(numbers.get(amOnes), 340, 10, null);
int lvlAmOnes = numAm[currentLevel - 1] % 10;
int lvlAmTens = numAm[currentLevel - 1] / 10;
g.drawImage(slash, 363, 12, null);
if (lvlAmTens > 0) {
g.drawImage(numbers.get(lvlAmTens), 390, 10, null);
g.drawImage(numbers.get(lvlAmOnes), 415, 10, null);
g.drawImage(amendmentImg, 445, 10, null);
} else {
g.drawImage(numbers.get(lvlAmOnes), 390, 10, null);
g.drawImage(amendmentImg, 420, 10, null);
}
}
// draw powerup timer
if (player.curPower == 1) {
int secs = player.powerTimer / 66;
int tens = secs / 10;
int ones = secs % 10;
if (tens > 0) {
g.drawImage(numbers.get(tens), 184, 10, null);
}
g.drawImage(numbers.get(ones), 205, 10, null);
g.drawImage(powerImg, 220, 10, null);
}
}
// is key pressed
public boolean isKeyPressed(int key) {
return pressedKeys.getOrDefault(key, false);
}
// every tick
@Override
public void actionPerformed(ActionEvent e) {
if (gameStarted) {
gameLoop();
}
repaint();
}
// check for key presses
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_P) {
if (currentLevel == 0) {
currentLevel = 1;
loadLevel(currentLevel);
gameStarted = true;
}
gameTimer.start();
return;
}
if (e.getKeyCode() == KeyEvent.VK_R) {
if (gameOver) {
gameTimer.stop();
loadLevel(1);
currentLevel = 1;
jumpPressed = false;
player.health = 3;
gameOver = false;
repaint();
gameTimer.start();
gameStarted = true;
}
}
if (e.getKeyCode() == KeyEvent.VK_O) {
currentLevel++;
loadLevel(currentLevel);
}
pressedKeys.put(e.getKeyCode(), true);
}
@Override
public void keyReleased(KeyEvent e) {
pressedKeys.put(e.getKeyCode(), false);
}
// dont need
@Override
public void keyTyped(KeyEvent e) {}
public Clip loadClip(String path) {
try {
AudioInputStream audio = AudioSystem.getAudioInputStream(new File(path));
Clip clip = AudioSystem.getClip();
clip.open(audio);
return clip;
} catch (Exception e) {
System.out.println("Could not load sound: " + path);
return null;
}
}
public void playClip(Clip clip) {
if (clip != null) {
clip.setFramePosition(0); // rewind to start
clip.start();
}
}
}