Files
thing/GameFields.java
2026-05-13 21:58:16 +00:00

786 lines
31 KiB
Java

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.awt.event.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
public class GameFields extends JPanel implements ActionListener, KeyListener {
// ── Instance Fields ────────────────────────────────────────────────────
private final int PANEL_WIDTH = 640;
private final int PANEL_HEIGHT = 360;
private final int MAX_Y = PANEL_HEIGHT - 40; // bottom bound for players
public int gustWidth = 80;
public int gustHeight = 200;
// Menus / navigation
public JPanel container;
public CardLayout cardLayout;
public String playerName1;
public String playerName2;
// Element collections
public ArrayList<Fluid> fluids;
public ArrayList<Gust> windBoxes;
public ArrayList<Rock> rocks;
public ArrayList<Wall> walls;
// Game-state flags
private boolean isRunning = false;
private boolean gameOver = false;
// Physics
private int bearVelocityY = 0; // separate Y-velocities so players don't share state
private int sealVelocityY = 0;
private final int gravity = 1;
private final int JUMP_FORCE = -12; // negative = upward in screen-space
// UI
private JProgressBar bearYProgress;
private JProgressBar sealYProgress;
private JLabel timerLabel; // shows countdown on screen
// Timers
private Timer gameTimer;
private Timer playTimer;
private AtomicInteger secondsRemaining; // tracked so we can read it in paintComponent
// Players
public Player bear;
public Player seal;
private int animationCounter = 0;
public Image[] bearSprites;
public Image[] sealSprites;
// ── Win zone (goal door) ───────────────────────────────────────────────
// Both players must reach their respective door to win.
// bearDoor is safe for bear; sealDoor is safe for seal.
private Rectangle bearDoor;
private Rectangle sealDoor;
private boolean bearWon = false;
private boolean sealWon = false;
// ══════════════════════════════════════════════════════════════════════
// Inner classes
// ══════════════════════════════════════════════════════════════════════
/** A controllable character. player==false → bear; player==true → seal. */
public class Player {
public int x, y;
public int speed = 5;
public Rectangle hitbox = new Rectangle(5, 10, 20, 30);
public boolean player = false; // false = bear, true = seal
public boolean onGround = false;
// Animation
public Image[] walkFrames;
public int currentFrame = 0;
// Input flags
public boolean left, right, up;
public Player(Image[] frames, boolean isSeal) {
this.walkFrames = frames;
this.player = isSeal;
}
public Image getCurrentImage() {
if (walkFrames == null || walkFrames.length == 0) return null;
return walkFrames[currentFrame];
}
}
public class Fluid {
public int x, y;
public int width = 80;
public int height = 30;
public Image img;
public boolean player = false; // false → dangerous to bear; true → dangerous to seal
public Fluid(Image img) { this.img = img; }
}
public class Gust {
public int x, y;
public int width = gustWidth;
public int height = gustHeight;
public Image img;
public Gust(Image img) { this.img = img; }
}
/** Pushable rock obstacle. */
public class Rock {
public Image img;
public boolean collision;
public Rectangle hitbox;
public int velocityY = 0; // rocks obey gravity too
public Rock() {
this.img = null;
this.collision = false;
this.hitbox = new Rectangle();
}
public Rock(Image img, int x, int y, int width, int height) {
this.img = img;
this.collision = false;
this.hitbox = new Rectangle(x, y, width, height);
}
public int getX() { return hitbox.x; }
public int getY() { return hitbox.y; }
public int getWidth() { return hitbox.width; }
public int getHeight() { return hitbox.height; }
public void setPosition(int x, int y) { hitbox.setLocation(x, y); }
public void draw(Graphics g) {
if (img != null) {
g.drawImage(img, hitbox.x, hitbox.y, hitbox.width, hitbox.height, null);
} else {
g.setColor(new Color(139, 90, 43));
g.fillRect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
g.setColor(new Color(100, 60, 20));
g.drawRect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
}
}
@Override
public String toString() {
return "Rock{hitbox=" + hitbox + ", collision=" + collision + "}";
}
}
/** Static impassable wall / platform. */
public class Wall {
public Image img;
public boolean collision;
public Rectangle hitbox;
public Wall() {
this.img = null;
this.collision = false;
this.hitbox = new Rectangle();
}
public Wall(Image img, int x, int y, int width, int height) {
this.img = img;
this.collision = false;
this.hitbox = new Rectangle(x, y, width, height);
}
public int getX() { return hitbox.x; }
public int getY() { return hitbox.y; }
public int getWidth() { return hitbox.width; }
public int getHeight() { return hitbox.height; }
public void setPosition(int x, int y) { hitbox.setLocation(x, y); }
public void draw(Graphics g) {
if (img != null) {
g.drawImage(img, hitbox.x, hitbox.y, hitbox.width, hitbox.height, null);
} else {
g.setColor(Color.GRAY);
g.fillRect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
g.setColor(Color.DARK_GRAY);
g.drawRect(hitbox.x, hitbox.y, hitbox.width, hitbox.height);
}
}
@Override
public String toString() {
return "Wall{hitbox=" + hitbox + ", collision=" + collision + "}";
}
}
// ══════════════════════════════════════════════════════════════════════
// Constructor
// ══════════════════════════════════════════════════════════════════════
Image bearIdle = new ImageIcon("bearIdle.png").getImage();
Image bearWalk1 = new ImageIcon("bearWalk1.png").getImage();
Image bearWalk2 = new ImageIcon("bearWalk2.png").getImage();
Image bearWalk3 = new ImageIcon("bearWalk3.png").getImage();
Image sealIdle = new ImageIcon("sealIdle.png").getImage();
Image sealWalk1 = new ImageIcon("sealWalk1.png").getImage();
Image sealWalk2 = new ImageIcon("sealWalk2.png").getImage();
public GameFields() {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setFocusable(true);
setBackground(new Color(30, 30, 60));
this.addKeyListener(this);
setFocusTraversalKeysEnabled(false);
// Progress bars (show height progress)
bearYProgress = new JProgressBar(0, MAX_Y);
sealYProgress = new JProgressBar(0, MAX_Y);
bearYProgress.setForeground(new Color(139, 90, 43)); // brown for bear
sealYProgress.setForeground(new Color(70, 130, 180)); // steel blue for seal
bearYProgress.setPreferredSize(new Dimension(100, 14));
sealYProgress.setPreferredSize(new Dimension(100, 14));
timerLabel = new JLabel("Time: ");
timerLabel.setForeground(Color.WHITE);
timerLabel.setFont(new Font("Arial", Font.BOLD, 16));
JPanel hud = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 2));
hud.setOpaque(false);
hud.add(new JLabel("Bear:") {{ setForeground(Color.WHITE); }});
hud.add(bearYProgress);
hud.add(timerLabel);
hud.add(new JLabel("Seal:") {{ setForeground(Color.WHITE); }});
hud.add(sealYProgress);
setLayout(new BorderLayout());
add(hud, BorderLayout.NORTH);
// Pre-allocate element pools
fluids = new ArrayList<>();
windBoxes = new ArrayList<>();
rocks = new ArrayList<>();
walls = new ArrayList<>();
// 6 fluid slots (indices 0-2 = bear-fluid; 3-5 = seal-fluid)
for (int i = 0; i < 6; i++) {
Fluid f = new Fluid(null);
f.player = (i >= 3); // 0-2 dangerous to bear (false), 3-5 dangerous to seal (true)
f.x = -9999; f.y = -9999; // park off-screen until placed
fluids.add(f);
}
// 5 wall slots
for (int i = 0; i < 5; i++) {
Wall w = new Wall(null, -9999, -9999, 120, 20);
walls.add(w);
}
// 3 rock slots
for (int i = 0; i < 3; i++) {
Rock r = new Rock(null, -9999, -9999, 30, 30);
rocks.add(r);
}
// 2 gust slots
for (int i = 0; i < 2; i++) {
Gust g = new Gust(null);
g.x = -9999; g.y = -9999;
windBoxes.add(g);
}
// Players
bearSprites = new Image[4];
bearSprites[0] = bearIdle;
bearSprites[1] = bearWalk1;
bearSprites[2] = bearWalk2;
bearSprites[3] = bearWalk3;
sealSprites = new Image[3];
sealSprites[0] = sealIdle;
sealSprites[1] = sealWalk1;
sealSprites[2] = sealWalk2;
bear = new Player(bearSprites, false);
seal = new Player(sealSprites, true);
//setupKeyBindings();
// 60 fps game loop
gameTimer = new Timer(1000 / 60, this);
}
// ══════════════════════════════════════════════════════════════════════
// Navigation helpers
// ══════════════════════════════════════════════════════════════════════
public void setContainer(JPanel container, CardLayout layout) {
this.container = container;
this.cardLayout = layout;
}
/**
* Called by a level button in LvlManager / MainMenu.
* Resets state, assigns player names, and switches to the chosen card.
*/
public void levelStart(String name1, String name2, String level) {
playerName1 = name1;
playerName2 = name2;
gameOver = false;
bearWon = false;
sealWon = false;
bearVelocityY = 0;
sealVelocityY = 0;
isRunning = true;
gameTimer.start();
cardLayout.show(container, level);
// Ensure the panel can receive focus and request it
this.setFocusable(true);
this.requestFocus();
this.requestFocusInWindow();
repaint();
}
// ══════════════════════════════════════════════════════════════════════
// Input
// ══════════════════════════════════════════════════════════════════════
/*private void setupKeyBindings() {
InputMap im = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = this.getActionMap();
// BEAR — Arrow keys
bindKey(im, am, KeyEvent.VK_LEFT, "bear_left", bear);
bindKey(im, am, KeyEvent.VK_RIGHT, "bear_right", bear);
bindKey(im, am, KeyEvent.VK_UP, "bear_up", bear);
// SEAL — WASD
bindKey(im, am, KeyEvent.VK_A, "seal_left", seal);
bindKey(im, am, KeyEvent.VK_D, "seal_right", seal);
bindKey(im, am, KeyEvent.VK_W, "seal_up", seal);
}
private void bindKey(InputMap im, ActionMap am, int keyCode, String name, Player p) {
// 1. Create KeyStrokes for both Press and Release
KeyStroke press = KeyStroke.getKeyStroke(keyCode, 0, false);
KeyStroke release = KeyStroke.getKeyStroke(keyCode, 0, true);
// 2. Put them in the InputMap with UNIQUE action keys
im.put(press, name + "_P");
im.put(release, name + "_R");
// 3. Map those action keys to your ToggleMovementAction in the ActionMap
// name.split("_")[1] extracts "left", "right", or "up" from "bear_left"
String direction = name.split("_")[1];
am.put(name + "_P", new ToggleMovementAction(p, direction, true));
am.put(name + "_R", new ToggleMovementAction(p, direction, false));
/*im.put(KeyStroke.getKeyStroke(keyCode, 0, false), name + "_press");
am.put(name + "_press", new ToggleMovementAction(p, name.split("_")[1], true));
im.put(KeyStroke.getKeyStroke(keyCode, 0, true), name + "_release");
am.put(name + "_release", new ToggleMovementAction(p, name.split("_")[1], false));
}
public class ToggleMovementAction extends AbstractAction {
private final Player player;
private final String direction;
private final boolean pressed;
public ToggleMovementAction(Player player, String direction, boolean pressed) {
this.player = player;
this.direction = direction;
this.pressed = pressed;
}
@Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case "left" -> player.left = pressed;
case "right" -> player.right = pressed;
case "up" -> player.up = pressed;
}
}
}*/
// ══════════════════════════════════════════════════════════════════════
// Game loop
// ══════════════════════════════════════════════════════════════════════
@Override
public void actionPerformed(ActionEvent e) {
if (isRunning) {
move();
repaint();
if (gameOver) {
gameTimer.stop();
if (playTimer != null) playTimer.stop();
}
// Check win condition
if (bearWon && sealWon) {
gameOver = true;
isRunning = false;
}
}
}
// ══════════════════════════════════════════════════════════════════════
// Physics & movement
// ══════════════════════════════════════════════════════════════════════
public void move() {
if (bear == null || seal == null) return;
updatePlayerPhysics(bear);
updatePlayerPhysics(seal);
// Sync progress bars (invert: higher up = lower y value = more progress)
bearYProgress.setValue(MAX_Y - Math.max(0, bear.y));
sealYProgress.setValue(MAX_Y - Math.max(0, seal.y));
// Fluid collision — wrong fluid type kills the player
for (Fluid f : fluids) {
if (checkCollision(bear, f)) { gameOver = true; return; }
if (checkCollision(seal, f)) { gameOver = true; return; }
}
// Gust — push player upward
for (Gust g : windBoxes) {
if (checkCollision(bear, g)) bearVelocityY = Math.min(bearVelocityY, -8);
if (checkCollision(seal, g)) sealVelocityY = Math.min(sealVelocityY, -8);
}
// Win-door checks
if (bearDoor != null && bear.hitbox.intersects(bearDoor)) bearWon = true;
if (sealDoor != null && seal.hitbox.intersects(sealDoor)) sealWon = true;
}
private void updatePlayerPhysics(Player p) {
int oldX = p.x;
int oldY = p.y;
// ── Horizontal movement ──
boolean moved = false;
if (p.left) { p.x -= p.speed; moved = true; System.out.println("left key pressed");}
if (p.right) { p.x += p.speed; moved = true; }
p.hitbox.setLocation(p.x + 5, p.y + 10); // offset to keep hitbox centred in sprite
if (checkSolidCollision(p)) {
p.x = oldX;
p.hitbox.setLocation(p.x + 5, p.y + 10);
}
// ── Vertical movement (gravity + jump) ──
int vel = (p == bear) ? bearVelocityY : sealVelocityY;
vel += gravity;
// Jump: only allowed when standing on something
if (p.up && p.onGround) {
vel = JUMP_FORCE;
p.onGround = false;
}
p.y += vel;
p.hitbox.setLocation(p.x + 5, p.y + 10);
p.onGround = false;
if (checkSolidCollision(p)) {
if (vel > 0) {
// Landing on top of a surface
p.y = oldY;
vel = 0;
p.onGround = true;
} else {
// Bumped head on ceiling
p.y = oldY;
vel = 0;
}
p.hitbox.setLocation(p.x + 5, p.y + 10);
}
// Store velocity back
if (p == bear) bearVelocityY = vel;
else sealVelocityY = vel;
// Screen boundary clamping
p.x = Math.max(0, Math.min(p.x, PANEL_WIDTH - 30));
p.y = Math.max(0, Math.min(p.y, PANEL_HEIGHT - 40));
if (p.y >= PANEL_HEIGHT - 40) {
p.onGround = true;
if (p == bear) bearVelocityY = 0;
else sealVelocityY = 0;
}
p.hitbox.setLocation(p.x + 5, p.y + 10);
// ── Animation ──
if (moved && p.walkFrames != null && p.walkFrames.length > 1) {
animationCounter++;
if (animationCounter % 10 == 0)
p.currentFrame = (p.currentFrame + 1) % p.walkFrames.length;
} else {
p.currentFrame = 0;
}
}
// ── Solid-collision helpers ────────────────────────────────────────────
public boolean checkSolidCollision(Player p) {
for (Wall w : walls) {
if (w.hitbox.x < -1000) continue; // parked off-screen
if (p.hitbox.intersects(w.hitbox)) return true;
}
for (Rock r : rocks) {
if (r.hitbox.x < -1000) continue;
if (p.hitbox.intersects(r.hitbox)) {
// Try to push the rock horizontally
int moveX = 0;
if (p.left) moveX = -p.speed;
if (p.right) moveX = p.speed;
if (moveX != 0 && canRockMove(r, moveX, 0)) {
r.hitbox.x += moveX;
return false; // rock moved, player not blocked
}
return true;
}
}
return false;
}
private boolean canRockMove(Rock r, int dx, int dy) {
Rectangle future = new Rectangle(r.hitbox.x + dx, r.hitbox.y + dy,
r.hitbox.width, r.hitbox.height);
for (Wall w : walls) {
if (w.hitbox.x < -1000) continue;
if (future.intersects(w.hitbox)) return false;
}
for (Rock other : rocks) {
if (other != r && other.hitbox.x > -1000 && future.intersects(other.hitbox))
return false;
}
return future.x >= 0 && future.x + future.width <= PANEL_WIDTH;
}
// ── Collision checkers ─────────────────────────────────────────────────
/** Fluid is dangerous only when the player type matches the fluid type. */
public boolean checkCollision(Player a, Fluid b) {
if (a.hitbox == null || b.x < -1000) return false;
// player==false (bear) is harmed by fluid.player==false; same for seal
if (a.player != b.player) return false;
Rectangle fluidRect = new Rectangle(b.x, b.y, b.width, b.height);
return a.hitbox.intersects(fluidRect);
}
public boolean checkCollision(Player a, Gust b) {
if (b.x < -1000) return false;
Rectangle gustRect = new Rectangle(b.x, b.y, b.width, b.height);
return a.hitbox.intersects(gustRect);
}
// ══════════════════════════════════════════════════════════════════════
// Placement helpers (used by LvlManager)
// ══════════════════════════════════════════════════════════════════════
public void placePlayer(Player play, int x, int y) {
play.x = x; play.y = y;
play.hitbox.setLocation(x + 5, y + 10);
}
public void placeFluid(Fluid flu, int x, int y) {
flu.x = x; flu.y = y;
}
public void placeGust(Gust gus, int x, int y) {
gus.x = x; gus.y = y;
}
public void placeRock(Rock rock, int x, int y) { rock.setPosition(x, y); }
public void placeWall(Wall wall, int x, int y) { wall.setPosition(x, y); }
/** Optionally place win doors (call from LvlManager per level). */
public void placeDoors(int bx, int by, int sx, int sy) {
bearDoor = new Rectangle(bx, by, 30, 50);
sealDoor = new Rectangle(sx, sy, 30, 50);
}
// ══════════════════════════════════════════════════════════════════════
// Difficulty / timer
// ══════════════════════════════════════════════════════════════════════
public void setDifficulty(String in) {
if (in.equals("Easy")) CountDown(new AtomicInteger(150));
else if (in.equals("Medium")) CountDown(new AtomicInteger(120));
else CountDown(new AtomicInteger(100));
}
/** Starts (or restarts) the countdown and updates timerLabel each second. */
public void CountDown(AtomicInteger secs) {
secondsRemaining = secs;
if (playTimer != null) playTimer.stop();
playTimer = new Timer(1000, e -> {
if (secondsRemaining.get() > 0) {
timerLabel.setText("Time: " + secondsRemaining.getAndDecrement());
System.out.println(secondsRemaining);
} else {
gameOver = true;
((Timer) e.getSource()).stop();
timerLabel.setText("Time: 0");
}
});
playTimer.start();
}
@Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
// BEAR Controls
if (code == KeyEvent.VK_LEFT) {
bear.left = true;
System.out.println("left pressed");
}
if (code == KeyEvent.VK_RIGHT) bear.right = true;
if (code == KeyEvent.VK_UP) bear.up = true;
// SEAL Controls
if (code == KeyEvent.VK_A) seal.left = true;
if (code == KeyEvent.VK_D) seal.right = true;
if (code == KeyEvent.VK_W) seal.up = true;
// Global Commands
if (code == KeyEvent.VK_ESCAPE) cardLayout.show(container, "MENU");
}
@Override
public void keyReleased(KeyEvent e) {
int code = e.getKeyCode();
// BEAR Controls
if (code == KeyEvent.VK_LEFT) bear.left = false;
if (code == KeyEvent.VK_RIGHT) bear.right = false;
if (code == KeyEvent.VK_UP) bear.up = false;
// SEAL Controls
if (code == KeyEvent.VK_A) seal.left = false;
if (code == KeyEvent.VK_D) seal.right = false;
if (code == KeyEvent.VK_W) seal.up = false;
}
@Override
public void keyTyped(KeyEvent e) {
// Not needed for movement, but required by interface
}
// ══════════════════════════════════════════════════════════════════════
// Rendering
// ══════════════════════════════════════════════════════════════════════
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
// HUD overlay
if (isRunning) {
g.setFont(new Font("Arial", Font.BOLD, 14));
// Player-name tags above their characters
g.setColor(new Color(210, 140, 60));
if (bear != null)
g.drawString(playerName1 != null ? playerName1 : "Bear", bear.x - 5, bear.y - 4);
g.setColor(new Color(70, 160, 220));
if (seal != null)
g.drawString(playerName2 != null ? playerName2 : "Seal", seal.x - 5, seal.y - 4);
}
if (gameOver) {
// Semi-transparent overlay
g.setColor(new Color(0, 0, 0, 160));
g.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT);
g.setFont(new Font("Arial", Font.BOLD, 36));
if (bearWon && sealWon) {
g.setColor(new Color(255, 215, 0));
drawCentered(g, "YOU WIN!", PANEL_HEIGHT / 2 - 20);
} else {
g.setColor(new Color(255, 80, 80));
drawCentered(g, "GAME OVER", PANEL_HEIGHT / 2 - 20);
}
g.setFont(new Font("Arial", Font.PLAIN, 18));
g.setColor(Color.WHITE);
drawCentered(g, "Press R to restart or ESC for menu", PANEL_HEIGHT / 2 + 20);
}
}
private void drawCentered(Graphics g, String text, int y) {
FontMetrics fm = g.getFontMetrics();
g.drawString(text, (PANEL_WIDTH - fm.stringWidth(text)) / 2, y);
}
protected void draw(Graphics g) {
// Background
g.setColor(new Color(30, 30, 60));
g.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT);
// Win doors
if (bearDoor != null) {
g.setColor(new Color(210, 140, 60, 180));
g.fillRect(bearDoor.x, bearDoor.y, bearDoor.width, bearDoor.height);
g.setColor(Color.WHITE);
g.drawRect(bearDoor.x, bearDoor.y, bearDoor.width, bearDoor.height);
g.setFont(new Font("Arial", Font.BOLD, 9));
g.drawString("BEAR", bearDoor.x, bearDoor.y - 3);
}
if (sealDoor != null) {
g.setColor(new Color(70, 160, 220, 180));
g.fillRect(sealDoor.x, sealDoor.y, sealDoor.width, sealDoor.height);
g.setColor(Color.WHITE);
g.drawRect(sealDoor.x, sealDoor.y, sealDoor.width, sealDoor.height);
g.setFont(new Font("Arial", Font.BOLD, 9));
g.drawString("SEAL", sealDoor.x, sealDoor.y - 3);
}
// Walls and rocks
for (Wall w : walls) if (w.hitbox.x > -1000) w.draw(g);
for (Rock r : rocks) if (r.hitbox.x > -1000) r.draw(g);
// Gusts (semi-transparent column)
g.setColor(new Color(200, 255, 200, 60));
for (Gust gust : windBoxes) {
if (gust.x > -1000) {
g.fillRect(gust.x, gust.y, gust.width, gust.height);
g.setColor(new Color(150, 255, 150, 120));
g.drawRect(gust.x, gust.y, gust.width, gust.height);
g.setColor(new Color(200, 255, 200, 60));
}
}
// Fluids
for (Fluid f : fluids) {
if (f.x > -1000) {
// bear-fluid = brown/red; seal-fluid = blue
if (!f.player) g.setColor(new Color(180, 80, 0, 200));
else g.setColor(new Color(0, 120, 220, 200));
g.fillRect(f.x, f.y, f.width, f.height);
g.setColor(Color.WHITE);
g.drawRect(f.x, f.y, f.width, f.height);
}
}
// Players
if (bear != null) {
if (bear.getCurrentImage() != null) {
g.drawImage(bear.getCurrentImage(), bear.x, bear.y, 30, 40, null);
} else {
// Fallback: coloured rectangle
g.setColor(new Color(210, 140, 60));
g.fillRoundRect(bear.x, bear.y, 30, 40, 8, 8);
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 9));
g.drawString("BEAR", bear.x, bear.y + 22);
}
}
if (seal != null) {
if (seal.getCurrentImage() != null) {
g.drawImage(seal.getCurrentImage(), seal.x, seal.y, 30, 40, null);
} else {
g.setColor(new Color(70, 160, 220));
g.fillRoundRect(seal.x, seal.y, 30, 40, 8, 8);
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 9));
g.drawString("SEAL", seal.x, seal.y + 22);
}
}
}
}