diff --git a/src/MainGame.java b/src/MainGame.java new file mode 100644 index 0000000..399f421 --- /dev/null +++ b/src/MainGame.java @@ -0,0 +1,548 @@ +package cn.edu.caztc.sokobangame; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.nio.file.Path; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * 游戏主体 + */ +public class MainGame extends JFrame implements MapConfig { + + int[][][] map1 = new int[18][18][1];// 地图数组 + int playerx = 0;//玩家坐标 + int playery = 0;//玩家坐标 + boolean diy = false;// + int level = 1;//关卡 + //游戏面板 + JPanel panel; + GetLevelDialog dialog; + private ImageIcon[] currentIcons; + private static MainGame instance; + + private UpdateThread updateThread; + + public static MainGame getInstance() { + return instance; + } + + public MainGame() { + // TODO Auto-generated constructor stub + instance = this; + currentIcons = ThemeSwitcher.getCurrentThemeIcons().clone(); // 从ThemeSwitcher获取当前主题 + this.setTitle("推箱子"); + this.setSize(900, 950); + this.setLayout(new FlowLayout()); + this.setDefaultCloseOperation(3); + //设置窗体居中 + this.setLocationRelativeTo(null); + //不可拉伸 + this.setResizable(false); + + JMenuBar menuBar = new JMenuBar(); + this.add(menuBar, BorderLayout.NORTH); + + JMenu menu = new JMenu("菜单"); + menuBar.add(menu); + + JMenuItem menuItem = new JMenuItem("选关"); + menuItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + // 传入 false 表示是单人模式 + dialog = new GetLevelDialog(false); // 修改这里 + if (dialog.getValue() != 0) { + diy = dialog.isdiy(); + level = dialog.getValue(); + GetMAP(level, diy); + } + } + }); + menu.add(menuItem); + + // 添加双人模式菜单项 + // 修改双人模式菜单项 + JMenuItem dualModeItem = new JMenuItem("双人模式"); + dualModeItem.addActionListener(e -> { + DualGame dualGame = new DualGame(); + dualGame.setVisible(true); + this.dispose(); // 完全关闭单人模式窗口 + System.out.println("切换到双人模式,关闭单人模式"); // 添加切换信息 + }); + menu.add(dualModeItem); + + + JMenuItem menuItem_1 = new JMenuItem("重新开始"); + menuItem_1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + //重新开始 + GetMAP(level,diy); + } + }); + menu.add(menuItem_1); + + JMenu customMenu = new JMenu("自定义"); + menuBar.add(customMenu); + + JMenuItem editorItem = new JMenuItem("地图编辑器"); + editorItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + new CreatMap(); + } + }); + customMenu.add(editorItem); + + // 添加主题切换菜单项 + JMenuItem themeItem = ThemeSwitcher.createThemeMenuItem(); + customMenu.add(themeItem); + + // 添加音乐菜单 + JMenu musicMenu = new JMenu("音乐"); + menuBar.add(musicMenu); + + // 音乐开关菜单项 + JMenuItem toggleMusicItem = new JMenuItem("开启音乐"); + // 音乐切换菜单项 + JMenuItem changeMusicItem = new JMenuItem("切换音乐"); + + toggleMusicItem.addActionListener(e -> { + MusicPlayer player = MusicPlayer.getInstance(); + if (player.isMuted()) { + // 使用getter方法替代直接访问字段 + if (player.getCurrentTrackIndex() == -1) { + player.playMusic(0); + } else { + player.toggleMute(); + } + toggleMusicItem.setText("关闭音乐"); + } else { + player.toggleMute(); + toggleMusicItem.setText("开启音乐"); + } + }); + + changeMusicItem.addActionListener(e -> { + MusicPlayer.getInstance().nextTrack(); + }); + + musicMenu.add(toggleMusicItem); + musicMenu.add(changeMusicItem); + + JMenu aboutMenu = new JMenu("关于"); + JMenuItem aboutItem = new JMenuItem("玩法说明"); + aboutItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog(null, + "用键盘的方向键控制人物,将箱子推到指定位置", + "关于", JOptionPane.PLAIN_MESSAGE); + } + }); + aboutMenu.add(aboutItem); + menuBar.add(aboutMenu); + + GetMAP(level, diy); + //创建游戏面板 + panel = setpanel(); + + this.add(panel); + this.setVisible(true); + + //安装键盘监听器 + PanelListenner plis = new PanelListenner(); + this.addKeyListener(plis); + + //启动刷新面板线程 + updateThread = new UpdateThread(panel); + updateThread.start(); + + // 窗口关闭时停止音乐 + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + MusicPlayer.getInstance().stopMusic(); + if (updateThread != null) { + updateThread.stopRunning(); + } + } + }); + } + + // 更新主题的方法 + public void updateTheme(ImageIcon[] newIcons) { + currentIcons = newIcons.clone(); + panel.repaint(); + } + + // 修改GetGameImage方法使用当前主题图标 + Image GetGameImage(int i) { + if (i >= 0 && i < currentIcons.length) { + return currentIcons[i].getImage(); + } + return null; + } + + /** + *读取地图数据 + * + * @param level 关卡 + * @param diy + */ + void GetMAP(int level, boolean diy) { + String stringdiy = ""; + if (diy) { + stringdiy = "diy"; + } + try { + DataInputStream in = new DataInputStream( + new BufferedInputStream(new FileInputStream(PATH + "\\" + stringdiy + level + ".map"))); + int i = in.readInt(); + int j = in.readInt(); + for (int ii = 0; ii < i; ii++) { + for (int jj = 0; jj < j; jj++) { + map1[ii][jj][0] = in.readInt(); + if (map1[ii][jj][0] == 5) { + playerx = ii; + playery = jj; + map1[ii][jj][0] = 1; + } + + } + } + } catch (Exception e) { + // TODO: handle exception + } + } + + /** + * 自定义内部游戏面板类 + */ + class MyPanel extends JPanel { + @Override + public void paint(Graphics g) { + super.paint(g); + for (int i = 0; i < MAP_HEIGHT / SOUREC_HEIGHT; i++) { + for (int j = 0; j < MAP_WIDTH / SOUREC_WIDTH; j++) { + g.drawImage(GetGameImage(map1[i][j][0]), getDrawX(j), getDrawY(i), SOUREC_WIDTH, SOUREC_HEIGHT, + null); + } + } + g.drawImage(icon106.getImage(), getDrawX(playery), getDrawY(playerx), SOUREC_WIDTH, SOUREC_HEIGHT, null); + } + + // 将数组下标转化成对应的图片左上角坐标 + public int getDrawX(int j) { + int x = j * 50; + return x; + } + + //将数组下标转化成对应的图片左上角坐标 + public int getDrawY(int i) { + int y = i * 50; + return y; + } + } + + /** + * 设置游戏面板 + */ + public JPanel setpanel() { + JPanel panel = new MyPanel(); + panel.setPreferredSize(new Dimension(MAP_WIDTH, MAP_HEIGHT)); + panel.setLayout(null); + panel.setBackground(Color.black); + return panel; + } + + /** + * 获取到数字对应的图片 + * + * @param i 数字 + * @return + */ +// Image GetGameImage(int i) { +// if (i > 5) { +// i = 0; +// } +// return allicons[i].getImage(); +// } + + /** + * 内部游戏按键监听类 + */ + class PanelListenner extends KeyAdapter { + //当按键按下 + public void keyPressed(KeyEvent e) { + int code = e.getKeyCode(); + switch (code) { + case KeyEvent.VK_UP: + if (check(1)) { + Move(1); + } + break; + case KeyEvent.VK_DOWN: + if (check(3)) { + Move(3); + } + break; + case KeyEvent.VK_LEFT: + if (check(2)) { + Move(2); + } + break; + case KeyEvent.VK_RIGHT: + if (check(4)) { + Move(4); + } + break; +// case 87:// W 键 +// if (check(1)) { +// Move(1); +// } +// break; +// case 83:// S 键 +// if (check(3)) { +// Move(3); +// } +// break; +// case 65:// A 键 +// if (check(2)) { +// Move(2); +// } +// break; +// case 68:// D 键 +// if (check(4)) { +// Move(4); +// } +// break; + default: + break; + + } + if (IsSuccess()) { + GetNextMap(); + } + } + + } + + /** + * 检查能不能走,前方有墙或者两个箱子超界等 + * + * @param direction 方向 1234wasd + * @return + */ + boolean check(int direction) { + switch (direction) { + case 1: + //判断1.超界 2.前面是墙 3.前面是箱子或者空箱子 再前面是箱子或者墙或者空箱子 + if (playerx == 0) { + return false; + } else if (map1[playerx - 1][playery][0] == 0) { + return false; + } else if (map1[playerx - 1][playery][0] == 2 || map1[playerx - 1][playery][0] == 3) { + if (map1[playerx - 2][playery][0] == 2 || map1[playerx - 2][playery][0] == 3 + || map1[playerx - 2][playery][0] == 0) { + return false; + } + } + break; + case 2: + if (playery == 0) { + return false; + } else if (map1[playerx][playery - 1][0] == 0) { + return false; + } else if (map1[playerx][playery - 1][0] == 2 || map1[playerx][playery - 1][0] == 3) { + if (map1[playerx][playery - 2][0] == 2 || map1[playerx][playery - 2][0] == 3 + || map1[playerx][playery - 2][0] == 0) { + return false; + } + } + break; + case 3: + if (playerx == 17) { + return false; + } else if (map1[playerx + 1][playery][0] == 0) { + return false; + } else if (map1[playerx + 1][playery][0] == 2 || map1[playerx + 1][playery][0] == 3) { + if (map1[playerx + 2][playery][0] == 2 || map1[playerx + 2][playery][0] == 3 + || map1[playerx + 2][playery][0] == 0) { + return false; + } + } + break; + case 4: + if (playery == 17) { + return false; + } else if (map1[playerx][playery + 1][0] == 0) { + return false; + } else if (map1[playerx][playery + 1][0] == 2 || map1[playerx][playery + 1][0] == 3) { + if (map1[playerx][playery + 2][0] == 2 || map1[playerx][playery + 2][0] == 3 + || map1[playerx][playery + 2][0] == 0) { + return false; + } + } + break; + default: + break; + } + return true; + } + + /** + * 角色移动 + * @param direction 方向 1234wasd + */ + private void Move(int direction) { + switch (direction) { + case 1: // 上 + if (map1[playerx - 1][playery][0] == 2 || map1[playerx - 1][playery][0] == 3) { + // 保存目标位置原始状态 + int targetOriginal = map1[playerx - 2][playery][0]; + + // 移动箱子 + if (targetOriginal == 4) { // 目标位置是点位 + map1[playerx - 2][playery][0] = 3; // 变为已到位箱子 + } else { + map1[playerx - 2][playery][0] = 2; // 变为普通箱子 + } + + // 恢复原来位置 + if (map1[playerx - 1][playery][0] == 3) { // 如果移动的是已到位箱子 + map1[playerx - 1][playery][0] = 4; // 恢复为点位 + } else { + map1[playerx - 1][playery][0] = 1; // 恢复为地板 + } + } + playerx -= 1; + break; + + case 2: // 左 + if (map1[playerx][playery - 1][0] == 2 || map1[playerx][playery - 1][0] == 3) { + int targetOriginal = map1[playerx][playery - 2][0]; + + if (targetOriginal == 4) { + map1[playerx][playery - 2][0] = 3; + } else { + map1[playerx][playery - 2][0] = 2; + } + + if (map1[playerx][playery - 1][0] == 3) { + map1[playerx][playery - 1][0] = 4; + } else { + map1[playerx][playery - 1][0] = 1; + } + } + playery -= 1; + break; + + case 3: // 下 + if (map1[playerx + 1][playery][0] == 2 || map1[playerx + 1][playery][0] == 3) { + int targetOriginal = map1[playerx + 2][playery][0]; + + if (targetOriginal == 4) { + map1[playerx + 2][playery][0] = 3; + } else { + map1[playerx + 2][playery][0] = 2; + } + + if (map1[playerx + 1][playery][0] == 3) { + map1[playerx + 1][playery][0] = 4; + } else { + map1[playerx + 1][playery][0] = 1; + } + } + playerx += 1; + break; + + case 4: // 右 + if (map1[playerx][playery + 1][0] == 2 || map1[playerx][playery + 1][0] == 3) { + int targetOriginal = map1[playerx][playery + 2][0]; + + if (targetOriginal == 4) { + map1[playerx][playery + 2][0] = 3; + } else { + map1[playerx][playery + 2][0] = 2; + } + + if (map1[playerx][playery + 1][0] == 3) { + map1[playerx][playery + 1][0] = 4; + } else { + map1[playerx][playery + 1][0] = 1; + } + } + playery += 1; + break; + } + } + + + /** + * 检查是否成功,即地图上没有空箱子 + * @return + */ + boolean IsSuccess() { + for (int i = 0; i < MAP_WIDTH/SOUREC_WIDTH; i++) { + for (int j = 0; j < MAP_HEIGHT/SOUREC_HEIGHT; j++) { + if (map1[i][j][0] == 2) { // 只检查空箱子(2),不检查已到位箱子(3) + return false; + } + } + } + return true; + } + + + /** + *闯过本关,开启下一关 + */ + void GetNextMap() { + if (diy) { + if (Utils.IsExistence(PATH + "\\diy" + (level+1) + ".map")) { + JOptionPane.showMessageDialog(null,"恭喜通关!即将进入下一关", "成功", JOptionPane.INFORMATION_MESSAGE); + level++; + GetMAP(level, diy); + }else { + Success(); + } + } else { + + if (Utils.IsExistence(PATH + "\\" + (level+1) + ".map")) { + JOptionPane.showMessageDialog(null, "恭喜通关!即将进入下一关", "成功", JOptionPane.INFORMATION_MESSAGE); + level++; + GetMAP(level, diy); + }else { + Success(); + } + } + } + + /** + * 全部闯关成功,闯关成功即弹出提示且调到第一关或者其他操作 + */ + void Success() { + JOptionPane.showMessageDialog(null, "恭喜你,全部闯关成功!", "全部通关", JOptionPane.PLAIN_MESSAGE); + level=1; + GetMAP(level, diy); + } +}