import java.util.ArrayList; import java.util.HashMap; import java.io.*; import java.awt.*; import java.awt.event.*; 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 int FRICTION = 1; static final int MAXYVELO = 15; static final int MAXXVELO = 5; static final int totalLevels = 10; static int[] numAm = {4,5,0,0,0,0,0,0,0,0}; //game objects Player player; ArrayList collidables; ArrayList collectables; Flag flag; ArrayList enemies; ArrayList projectiles; //game vars int boardWidth; int boardHeight; int tileSize; Timer gameTimer; HashMap 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 numbers; 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(); 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; 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; //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){ player.yVelo = -15; player.onGround = false; jumpPressed = true; player.airJumps = 0; } else if (player.curPower == 1 && player.airJumps < 1){ 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 > boardHeight){ 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++; } } } 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){ 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(); } } //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); projectiles.remove(p); break; } } } enemies.removeIf(e -> !e.alive); } public void loadLevel(int level) { projectiles.clear(); 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; } g.translate(-cameraX, -cameraY); if (currentLevel > totalLevels) { g.drawImage(winImg, boardWidth/2 - 100, boardHeight/2 - 50, null); g.drawImage(pressRImg, boardWidth/2 - 80, boardHeight/2 + 60, null); return; } 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); //flag counter int amOnes = player.numAmendments % 10; int amTens = player.numAmendments / 10; if (currentLevel > 0 && player.numAmendments < numAm[currentLevel-1]){ 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); g.drawImage(numbers.get(numAm[currentLevel-1]),flag.x+55,flag.y-30,null); g.drawImage(amendmentImg,flag.x+85,flag.y-30,null); } g.translate(cameraX, cameraY); 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); 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); g.drawImage(slash,363,12,null); g.drawImage(numbers.get(numAm[currentLevel-1]),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(); return; } return; } 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){ } }