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; } }