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