diff --git a/src/DualGame.java b/src/DualGame.java new file mode 100644 index 0000000..3b611d6 --- /dev/null +++ b/src/DualGame.java @@ -0,0 +1,376 @@ +package cn.edu.caztc.sokobangame; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; + +public class DualGame extends JFrame implements MapConfig { + private int[][][] map1; + private int player1x = -1, player1y = -1; + private int player2x = -1, player2y = -1; + private int level = 1; + private boolean diy = false; + + private JPanel panel; + private static DualGame instance; + private ImageIcon[] currentIcons; + private UpdateThread updateThread; + + public static DualGame getInstance() { + return instance; + } + + public DualGame() { + instance = this; + currentIcons = ThemeSwitcher.getCurrentThemeIcons().clone(); + initUI(); + loadInitialMap(); + setVisible(true); + } + + public void updateTheme(ImageIcon[] newIcons) { + currentIcons = newIcons.clone(); + panel.repaint(); + } + + private void initUI() { + setTitle("推箱子-双人模式"); + setSize(900, 950); + setLayout(new FlowLayout()); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + setResizable(false); + + initMenu(); + initGamePanel(); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + cleanupResources(); + System.out.println("双人模式程序终止"); // 添加终止信息 + } + }); + } + + private void initMenu() { + JMenuBar menuBar = new JMenuBar(); + JMenu menu = new JMenu("菜单"); + + JMenuItem levelItem = new JMenuItem("选关"); + levelItem.addActionListener(e -> selectLevel()); + menu.add(levelItem); + + JMenuItem restartItem = new JMenuItem("重新开始"); + restartItem.addActionListener(e -> GetMAP(level, diy)); + menu.add(restartItem); + + JMenuItem singleModeItem = new JMenuItem("切换到单人模式"); + singleModeItem.addActionListener(e -> switchToSingleMode()); + menu.add(singleModeItem); + + menuBar.add(menu); + setJMenuBar(menuBar); + } + + private void initGamePanel() { + panel = new DualPanel(); + panel.setPreferredSize(new Dimension(MAP_WIDTH, MAP_HEIGHT)); + panel.setBackground(Color.BLACK); + add(panel); + + panel.setFocusable(true); + panel.requestFocusInWindow(); + panel.addKeyListener(new DualKeyListener()); + } + + private void loadInitialMap() { + GetMAP(1, false); + updateThread = new UpdateThread(panel); + updateThread.start(); + } + + private void cleanupResources() { + MusicPlayer.getInstance().stopMusic(); + if (updateThread != null) { + updateThread.stopRunning(); + } + } + + void GetMAP(int level, boolean diy) { + String prefix = diy ? "双人diy" : "双人"; + String filePath = PATH + "\\" + prefix + level + ".map"; + + try { + File mapFile = new File(filePath); + loadMapData(filePath); + validateSpawnPoints(); + } catch (Exception e) { + JOptionPane.showMessageDialog(null, + "地图加载失败: " + e.getMessage() + "\n文件路径: " + filePath, + "错误", JOptionPane.ERROR_MESSAGE); + } + + refreshGameDisplay(); + } + + private void loadMapData(String filePath) throws IOException { + try (DataInputStream in = new DataInputStream( + new BufferedInputStream(new FileInputStream(filePath)))) { + + resetPlayerPositions(); + int rows = in.readInt(); + int cols = in.readInt(); + map1 = new int[rows][cols][1]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + int cellValue = in.readInt(); + map1[i][j][0] = cellValue; + + if (cellValue == 5) { // 玩家一出生点 + player1x = i; + player1y = j; + map1[i][j][0] = 1; // 出生点变为空地 + } else if (cellValue == 6) { // 玩家二出生点 + player2x = i; + player2y = j; + map1[i][j][0] = 1; // 出生点变为空地 + } + } + } + } + } + + private void validateSpawnPoints() { + if (player1x == -1 || player2x == -1) { + JOptionPane.showMessageDialog(null, + "地图必须包含两个出生点(5-玩家一,6-玩家二)", "错误", JOptionPane.ERROR_MESSAGE); + } + } + + class DualPanel extends JPanel { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + g.setColor(Color.BLACK); + g.fillRect(0, 0, getWidth(), getHeight()); + + if (map1 != null) { + for (int i = 0; i < map1.length; i++) { + for (int j = 0; j < map1[0].length; j++) { + int cellValue = map1[i][j][0]; + if (cellValue >= 0 && cellValue < currentIcons.length) { // 使用currentIcons + Image img = currentIcons[cellValue].getImage(); + if (img != null) { + g.drawImage(img, j*SOUREC_WIDTH, i*SOUREC_HEIGHT, + SOUREC_WIDTH, SOUREC_HEIGHT, this); + } + } + } + } + } + + // 绘制玩家(同样使用currentIcons) + if (player1x >= 0 && player1y >= 0) { + g.drawImage(currentIcons[5].getImage(), player1y*SOUREC_WIDTH, player1x*SOUREC_HEIGHT, + SOUREC_WIDTH, SOUREC_HEIGHT, this); + } + if (player2x >= 0 && player2y >= 0) { + g.drawImage(currentIcons[6].getImage(), player2y*SOUREC_WIDTH, player2x*SOUREC_HEIGHT, + SOUREC_WIDTH, SOUREC_HEIGHT, this); + } + } + } + + class DualKeyListener extends KeyAdapter { + @Override + public void keyPressed(KeyEvent e) { + switch (e.getKeyCode()) { + case KeyEvent.VK_UP: tryMove(1, 1); break; + case KeyEvent.VK_DOWN: tryMove(1, 3); break; + case KeyEvent.VK_LEFT: tryMove(1, 2); break; + case KeyEvent.VK_RIGHT: tryMove(1, 4); break; + case KeyEvent.VK_W: tryMove(2, 1); break; + case KeyEvent.VK_S: tryMove(2, 3); break; + case KeyEvent.VK_A: tryMove(2, 2); break; + case KeyEvent.VK_D: tryMove(2, 4); break; + } + panel.repaint(); + } + + private void tryMove(int playerNum, int direction) { + if (canMove(playerNum, direction)) { + if (isPushingBox(playerNum, direction)) { + if (canPushBox(playerNum, direction)) { + movePlayer(playerNum, direction); + } + } else { + movePlayer(playerNum, direction); + } + + if (IsSuccess()) { + Success(); + } + } + } + } + + // 其他辅助方法... + private void movePlayer(int playerNum, int direction) { + int x = playerNum == 1 ? player1x : player2x; + int y = playerNum == 1 ? player1y : player2y; + + int newX = x, newY = y; + int frontX = x, frontY = y; + int beyondX = x, beyondY = y; + + switch (direction) { + case 1: newX--; frontX--; beyondX -= 2; break; + case 2: newY--; frontY--; beyondY -= 2; break; + case 3: newX++; frontX++; beyondX += 2; break; + case 4: newY++; frontY++; beyondY += 2; break; + } + + if ((playerNum == 1 && newX == player2x && newY == player2y) || + (playerNum == 2 && newX == player1x && newY == player1y)) { + return; + } + + if (map1[frontX][frontY][0] == 2 || map1[frontX][frontY][0] == 3) { + if ((beyondX == player1x && beyondY == player1y) || + (beyondX == player2x && beyondY == player2y)) { + return; + } + + map1[beyondX][beyondY][0] = (map1[beyondX][beyondY][0] == 4) ? 3 : 2; + map1[frontX][frontY][0] = (map1[frontX][frontY][0] == 3) ? 4 : 1; + } + + if (playerNum == 1) { + player1x = newX; + player1y = newY; + } else { + player2x = newX; + player2y = newY; + } + } + + boolean IsSuccess() { + for (int i = 0; i < map1.length; i++) { + for (int j = 0; j < map1[0].length; j++) { + if (map1[i][j][0] == 2) { + return false; + } + } + } + return true; + } + + void Success() { + String prefix = diy ? "双人diy" : "双人"; + // 先检查是否是最后一关 + if (!new File(PATH + "\\" + prefix + (level+1) + ".map").exists()) { + // 是最后一关 + JOptionPane.showMessageDialog(null, + "恭喜您已通关所有关卡!", + "全部通关", JOptionPane.INFORMATION_MESSAGE); + level = 1; + GetMAP(level, diy); + } else { + // 不是最后一关 + JOptionPane.showMessageDialog(null, + "恭喜通关!即将进入下一关", + "成功", JOptionPane.INFORMATION_MESSAGE); + GetNextMap(); + } + } + + void GetNextMap() { + String prefix = diy ? "双人diy" : "双人"; + level++; + GetMAP(level, diy); + } + + private void refreshGameDisplay() { + panel.repaint(); + panel.requestFocusInWindow(); + } + + private void selectLevel() { + GetLevelDialog dialog = new GetLevelDialog(true); + if (dialog.getValue() != 0) { + diy = dialog.isdiy(); + level = dialog.getValue(); + GetMAP(level, diy); + } + } + + private void switchToSingleMode() { + new MainGame().setVisible(true); + dispose(); + } + + private void resetPlayerPositions() { + player1x = player1y = player2x = player2y = -1; + } + + private boolean canMove(int playerNum, int direction) { + int x = playerNum == 1 ? player1x : player2x; + int y = playerNum == 1 ? player1y : player2y; + + switch (direction) { + case 1: return x > 0 && map1[x-1][y][0] != 0; + case 2: return y > 0 && map1[x][y-1][0] != 0; + case 3: return x < map1.length-1 && map1[x+1][y][0] != 0; + case 4: return y < map1[0].length-1 && map1[x][y+1][0] != 0; + } + return false; + } + + private boolean isPushingBox(int playerNum, int direction) { + int x = playerNum == 1 ? player1x : player2x; + int y = playerNum == 1 ? player1y : player2y; + + switch (direction) { + case 1: return x > 0 && (map1[x-1][y][0] == 2 || map1[x-1][y][0] == 3); + case 2: return y > 0 && (map1[x][y-1][0] == 2 || map1[x][y-1][0] == 3); + case 3: return x < map1.length-1 && (map1[x+1][y][0] == 2 || map1[x+1][y][0] == 3); + case 4: return y < map1[0].length-1 && (map1[x][y+1][0] == 2 || map1[x][y+1][0] == 3); + } + return false; + } + + private boolean canPushBox(int playerNum, int direction) { + int x = playerNum == 1 ? player1x : player2x; + int y = playerNum == 1 ? player1y : player2y; + + int frontX = x, frontY = y; + int beyondX = x, beyondY = y; + + switch (direction) { + case 1: frontX--; beyondX -= 2; break; + case 2: frontY--; beyondY -= 2; break; + case 3: frontX++; beyondX += 2; break; + case 4: frontY++; beyondY += 2; break; + } + + if (frontX < 0 || frontY < 0 || + frontX >= map1.length || frontY >= map1[0].length || + (map1[frontX][frontY][0] != 2 && map1[frontX][frontY][0] != 3)) { + return false; + } + + if (beyondX < 0 || beyondY < 0 || + beyondX >= map1.length || beyondY >= map1[0].length || + map1[beyondX][beyondY][0] == 0 || + map1[beyondX][beyondY][0] == 2 || + map1[beyondX][beyondY][0] == 3) { + return false; + } + + return true; + } +}