diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..38f5d98 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM openjdk:8-slim-buster + +COPY . . +RUN apt update && \ + apt install -y vim git && \ + apt install -y libxext6 libxrender1 libxtst6 libfreetype6 libfontconfig1 && \ + echo "Done!" + +# Dev environment: +# docker run -d --name bom-ai -e DISPLAY="host.docker.internal:0" -v /tmp/.X11-unix:/tmp/.X11-unix -v $HOME:/root openjdk:8-slim-buster sleep 999999999 \ No newline at end of file diff --git a/res/levels/Level1.txt b/res/levels/Level1.txt index 6f00589..254063f 100644 --- a/res/levels/Level1.txt +++ b/res/levels/Level1.txt @@ -10,7 +10,7 @@ # # # # #*# # # #*#*# # # # # # #* ** * * # # #*# # # # # # #*# # # # # # # -# * * * # +#a * * * # ############################### diff --git a/res/levels/Level2.txt b/res/levels/Level2.txt index f76a558..b01dcf7 100644 --- a/res/levels/Level2.txt +++ b/res/levels/Level2.txt @@ -10,7 +10,7 @@ # # # # #*# # # # # # #*# #*# # # **** *1 # # # # # # # # # # #*# # # # #*# -# ** * * 3 # +#a ** * * 3 # ############################### diff --git a/res/levels/Level3.txt b/res/levels/Level3.txt new file mode 100644 index 0000000..5a6ca2f --- /dev/null +++ b/res/levels/Level3.txt @@ -0,0 +1,14 @@ +3 13 31 +############################### +#p * *3 * ** * * *# +# # # # #*# # #*# # # # # #*# # +# * * *** ** # * ** # +# #*# # # # #4# #b#1# # # # #*# +# * ** * **# +# # #*# # # # # #*#*#*#*# # # # +# * * * *x* * 4 # +# # # # #*# # # # # # #*# #*# # +# **** *2 # +# # # # # # # # # #*# # # # #*# +# ** ** * 5 # +############################### diff --git a/src/uet/oop/bomberman/Board.java b/src/uet/oop/bomberman/Board.java index 4130228..2cb6fd1 100644 --- a/src/uet/oop/bomberman/Board.java +++ b/src/uet/oop/bomberman/Board.java @@ -1,94 +1,186 @@ package uet.oop.bomberman; +import uet.oop.bomberman.base.IEntityManager; +import uet.oop.bomberman.base.IGameInfoManager; +import uet.oop.bomberman.base.IMessageManager; import uet.oop.bomberman.entities.Entity; import uet.oop.bomberman.entities.Message; import uet.oop.bomberman.entities.bomb.Bomb; import uet.oop.bomberman.entities.bomb.FlameSegment; import uet.oop.bomberman.entities.character.Bomber; import uet.oop.bomberman.entities.character.Character; +import uet.oop.bomberman.entities.tile.item.Item; import uet.oop.bomberman.exceptions.LoadLevelException; import uet.oop.bomberman.graphics.IRender; import uet.oop.bomberman.graphics.Screen; import uet.oop.bomberman.input.Keyboard; import uet.oop.bomberman.level.FileLevelLoader; import uet.oop.bomberman.level.LevelLoader; +import uet.oop.bomberman.sound.Sound; +import uet.oop.bomberman.utils.Global; import java.awt.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; /** * Quản lý thao tác điều khiển, load level, render các màn hình của game */ -public class Board implements IRender { +public class Board implements IRender, IEntityManager, IMessageManager, IGameInfoManager { protected LevelLoader _levelLoader; protected Game _game; protected Keyboard _input; protected Screen _screen; - + public Entity[] _entities; public List _characters = new ArrayList<>(); protected List _bombs = new ArrayList<>(); private List _messages = new ArrayList<>(); - - private int _screenToShow = -1; //1:endgame, 2:changelevel, 3:paused - + private List _activeItems = new ArrayList<>(); + + private Character player; + private Character player2; + + @Override + public List getActiveItems() { + return getPlayer().getActiveItems().collect(Collectors.toList()); + } + + private int _screenToShow = -1; // 1:endgame, 2:changelevel, 3:paused + private int _time = Game.TIME; private int _points = Game.POINTS; - + public Board(Game game, Keyboard input, Screen screen) { _game = game; _input = input; _screen = screen; - - loadLevel(1); //start in level 1 + + loadLevel(Global.gameLevel); // start in level 1 + } + + private void snapCameraToPlayer() { + int xScroll = Screen.calculateXOffset(this, getPlayer()); + Screen.setOffset(xScroll, 0); } - + @Override public void update() { - if( _game.isPaused() ) return; - + if (_game.isPaused()) + return; + updateEntities(); updateCharacters(); updateBombs(); updateMessages(); + updateActiveItems(); detectEndGame(); - + + snapCameraToPlayer(); + processPlayerInput(); + processPlayer2Input(); + for (int i = 0; i < _characters.size(); i++) { Character a = _characters.get(i); - if(a.isRemoved()) _characters.remove(i); + if (a.isRemoved()) + _characters.remove(i); + } + } + + private void processPlayerInput() { + Character player = getPlayer(); + if (!player.isAlive()) + return; + + processPlayerInputMove(player); + + if (player instanceof Bomber) { + Bomber bomber = (Bomber) player; + if (_input.player1_space) + bomber.placeBomb(); + } + } + + private void processPlayer2Input() { + Character player2 = getPlayer2(); + if (!player2.isAlive()) + return; + + processPlayer2InputMove(player2); + + if (player2 instanceof Bomber) { + Bomber bomber2 = (Bomber) player2; + if (_input.player2_space) + bomber2.placeBomb(); + } + } + + private void processPlayerInputMove(Character player) { + int xa = 0, ya = 0; + if (_input.player1_up) + ya--; + if (_input.player1_down) + ya++; + if (_input.player1_left) + xa--; + if (_input.player1_right) + xa++; + + if (xa != 0 || ya != 0) { + player.move(xa * player.getSpeed(), ya * player.getSpeed()); + player.setMoving(true); + } else { + player.setMoving(false); + } + } + + private void processPlayer2InputMove(Character player2) { + int xa = 0, ya = 0; + if (_input.player2_up) + ya--; + if (_input.player2_down) + ya++; + if (_input.player2_left) + xa--; + if (_input.player2_right) + xa++; + + if (xa != 0 || ya != 0) { + player2.move(xa * player2.getSpeed(), ya * player2.getSpeed()); + player2.setMoving(true); + } else { + player2.setMoving(false); } } @Override public void render(Screen screen) { - if( _game.isPaused() ) return; - - //only render the visible part of screen - int x0 = Screen.xOffset >> 4; //tile precision, -> left X + if (_game.isPaused()) + return; + + // only render the visible part of screen + int x0 = Screen.xOffset >> 4; // tile precision, -> left X int x1 = (Screen.xOffset + screen.getWidth() + Game.TILES_SIZE) / Game.TILES_SIZE; // -> right X int y0 = Screen.yOffset >> 4; - int y1 = (Screen.yOffset + screen.getHeight()) / Game.TILES_SIZE; //render one tile plus to fix black margins - + int y1 = (Screen.yOffset + screen.getHeight()) / Game.TILES_SIZE; // render one tile plus to fix black margins + for (int y = y0; y < y1; y++) { for (int x = x0; x < x1; x++) { _entities[x + y * _levelLoader.getWidth()].render(screen); } } - + renderBombs(screen); renderCharacter(screen); - + } - + public void nextLevel() { - Game.setBombRadius(1); - Game.setBombRate(1); - Game.setBomberSpeed(1.0); loadLevel(_levelLoader.getLevel() + 1); } - + public void loadLevel(int level) { _time = Game.TIME; _screenToShow = 2; @@ -97,38 +189,34 @@ public void loadLevel(int level) { _characters.clear(); _bombs.clear(); _messages.clear(); - + try { _levelLoader = new FileLevelLoader(this, level); _entities = new Entity[_levelLoader.getHeight() * _levelLoader.getWidth()]; - + _levelLoader.createEntities(); } catch (LoadLevelException e) { endGame(); } } - + protected void detectEndGame() { - if(_time <= 0) + if (_time <= 0) endGame(); } - + public void endGame() { _screenToShow = 1; _game.resetScreenDelay(); _game.pause(); } - - public boolean detectNoEnemies() {// phat hien enemies - int total = 0; - for (int i = 0; i < _characters.size(); i++) { - if(_characters.get(i) instanceof Bomber == false) - ++total; - } - - return total == 0; + + @Override + public boolean isEnemyCleared() { + return !_characters.stream() + .anyMatch(character -> character != getPlayer()); } - + public void drawScreen(Graphics g) { switch (_screenToShow) { case 1: @@ -142,177 +230,204 @@ public void drawScreen(Graphics g) { break; } } - + + @Override public Entity getEntity(double x, double y, Character m) { - + Entity res = null; - - res = getFlameSegmentAt((int)x, (int)y); - if( res != null) return res; - + + res = getFlameSegmentAt((int) x, (int) y); + if (res != null) + return res; + res = getBombAt(x, y); - if( res != null) return res; - - res = getCharacterAtExcluding((int)x, (int)y, m); - if( res != null) return res; - - res = getEntityAt((int)x, (int)y); - + if (res != null) + return res; + + res = getCharacterAtExcluding((int) x, (int) y, m); + if (res != null) + return res; + + res = getEntityAt((int) x, (int) y); + return res; } - + + @Override public List getBombs() { return _bombs; } - + + @Override public Bomb getBombAt(double x, double y) { Iterator bs = _bombs.iterator(); Bomb b; - while(bs.hasNext()) { + while (bs.hasNext()) { b = bs.next(); - if(b.getX() == (int)x && b.getY() == (int)y) + if (b.getX() == (int) x && b.getY() == (int) y) return b; } - + return null; } - public Bomber getBomber() { - Iterator itr = _characters.iterator(); - - Character cur; - while(itr.hasNext()) { - cur = itr.next(); - - if(cur instanceof Bomber) - return (Bomber) cur; - } - - return null; + @Override + public Character getPlayer() { + return this.player; + } + + public Character getPlayer2() { + return this.player2; } - + + @Override public Character getCharacterAtExcluding(int x, int y, Character a) { Iterator itr = _characters.iterator(); - + Character cur; - while(itr.hasNext()) { + while (itr.hasNext()) { cur = itr.next(); - if(cur == a) { + if (cur == a) { continue; } - - if(cur.getXTile() == x && cur.getYTile() == y) { + + if (cur.getXTile() == x && cur.getYTile() == y) { return cur; } - + } - + return null; } - + + @Override public FlameSegment getFlameSegmentAt(int x, int y) { Iterator bs = _bombs.iterator(); Bomb b; - while(bs.hasNext()) { + while (bs.hasNext()) { b = bs.next(); - + FlameSegment e = b.flameAt(x, y); - if(e != null) { + if (e != null) { return e; } } - + return null; } - + + @Override public Entity getEntityAt(double x, double y) { - return _entities[(int)x + (int)y * _levelLoader.getWidth()]; + return _entities[(int) x + (int) y * _levelLoader.getWidth()]; + } + + @Override + public void addActiveItem(Item item) { + _activeItems.add(item); } - + + @Override public void addEntity(int pos, Entity e) { _entities[pos] = e; } - + + @Override public void addCharacter(Character e) { _characters.add(e); } - + + @Override public void addBomb(Bomb e) { _bombs.add(e); } - + + @Override public void addMessage(Message e) { _messages.add(e); } protected void renderCharacter(Screen screen) { Iterator itr = _characters.iterator(); - - while(itr.hasNext()) + + while (itr.hasNext()) itr.next().render(screen); } - + protected void renderBombs(Screen screen) { Iterator itr = _bombs.iterator(); - - while(itr.hasNext()) + + while (itr.hasNext()) itr.next().render(screen); } - + public void renderMessages(Graphics g) { Message m; for (int i = 0; i < _messages.size(); i++) { m = _messages.get(i); - + g.setFont(new Font("Arial", Font.PLAIN, m.getSize())); g.setColor(m.getColor()); - g.drawString(m.getMessage(), (int)m.getX() - Screen.xOffset * Game.SCALE, (int)m.getY()); + g.drawString(m.getMessage(), (int) m.getX() - Screen.xOffset * Game.SCALE, (int) m.getY()); } } - + protected void updateEntities() { - if( _game.isPaused() ) return; + if (_game.isPaused()) + return; for (int i = 0; i < _entities.length; i++) { _entities[i].update(); } } - + protected void updateCharacters() { - if( _game.isPaused() ) return; + if (_game.isPaused()) + return; Iterator itr = _characters.iterator(); - - while(itr.hasNext() && !_game.isPaused()) + + while (itr.hasNext() && !_game.isPaused()) itr.next().update(); } - + protected void updateBombs() { - if( _game.isPaused() ) return; + if (_game.isPaused()) + return; Iterator itr = _bombs.iterator(); - - while(itr.hasNext()) + + while (itr.hasNext()) itr.next().update(); } - + + protected void updateActiveItems() { + if (_game.isPaused()) + return; + Iterator itr = _activeItems.iterator(); + + while (itr.hasNext()) + itr.next().update(); + } + protected void updateMessages() { - if( _game.isPaused() ) return; + if (_game.isPaused()) + return; Message m; int left; for (int i = 0; i < _messages.size(); i++) { m = _messages.get(i); left = m.getDuration(); - - if(left > 0) + + if (left > 0) m.setDuration(--left); else _messages.remove(i); } } + @Override public int subtractTime() { - if(_game.isPaused()) - return this._time; + if (!_game.isPaused() && _time > 0) + return --_time; else - return this._time--; + return _time; } public Keyboard getInput() { @@ -335,18 +450,21 @@ public void setShow(int i) { _screenToShow = i; } + @Override public int getTime() { return _time; } + @Override public int getPoints() { return _points; } + @Override public void addPoints(int points) { this._points += points; } - + public int getWidth() { return _levelLoader.getWidth(); } @@ -354,5 +472,31 @@ public int getWidth() { public int getHeight() { return _levelLoader.getHeight(); } - + + @Override + public void setPlayer(Character character) { + this.player = character; + } + + public void setPlayer2(Character character2) { + this.player2 = character2; + } + + @Override + public void handleOnDeath(Character character, Character killer) { + if (character.isPlayer()) { + // TODO: handle player death + Sound.play("endgame3"); + } else { + // TODO: document how to calculate message coord + double messageX = (character.getX() * Game.SCALE) + (character.getSprite().SIZE / 2 * Game.SCALE); + double messageY = (character.getY() * Game.SCALE) - (character.getSprite().SIZE / 2 * Game.SCALE); + int points = character.getPoints(); + addPoints(points); + Message msg = new Message("+" + points, messageX, messageY, 2, Color.white, 14); + addMessage(msg); + Sound.play("AA126_11"); + } + } + } diff --git a/src/uet/oop/bomberman/Game.java b/src/uet/oop/bomberman/Game.java index 53e5c53..3d050c0 100644 --- a/src/uet/oop/bomberman/Game.java +++ b/src/uet/oop/bomberman/Game.java @@ -1,8 +1,13 @@ package uet.oop.bomberman; +import uet.oop.bomberman.base.IGameInfoManager; import uet.oop.bomberman.graphics.Screen; import uet.oop.bomberman.gui.Frame; import uet.oop.bomberman.input.Keyboard; +import uet.oop.bomberman.screen.SelectGameModeScreen; +import uet.oop.bomberman.screen.SelectLevelScreen; +import uet.oop.bomberman.utils.EScreenName; +import uet.oop.bomberman.utils.Global; import java.awt.*; import java.awt.image.BufferStrategy; @@ -15,196 +20,204 @@ */ public class Game extends Canvas { - - public static final int TILES_SIZE = 16, - WIDTH = TILES_SIZE * (31 / 2), - HEIGHT = 13 * TILES_SIZE; + WIDTH = TILES_SIZE * (31 / 2), + HEIGHT = 13 * TILES_SIZE; public static int SCALE = 3; - + public static final String TITLE = "BombermanGame"; - - private static final int BOMBRATE = 1; - private static final int BOMBRADIUS = 1; - private static final double BOMBERSPEED = 1.0;//toc do bomber - + public static final int TICKS_PER_SECOND = 60; + + public static final int BOMBRATE = 1; + public static final int BOMBRADIUS = 1; + public static final double BOMBERSPEED = 1.0;// toc do bomber + public static final int TIME = 200; public static final int POINTS = 0; - + protected static int SCREENDELAY = 3; - protected static int bombRate = BOMBRATE; - protected static int bombRadius = BOMBRADIUS; - protected static double bomberSpeed = BOMBERSPEED; - - protected int _screenDelay = SCREENDELAY; - + private Keyboard _input; private boolean _running = false; private boolean _paused = true; - private Board _board; private Screen screen; private Frame _frame; - + private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); private int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); + + // game variable + private int frames; + private int updates; + private long timer; + + // game screens + private SelectLevelScreen selectLevelScreen; + private SelectGameModeScreen selectGameModeScreen; public Game(Frame frame) { _frame = frame; _frame.setTitle(TITLE); - + screen = new Screen(WIDTH, HEIGHT); _input = new Keyboard(); - + _board = new Board(this, _input, screen); addKeyListener(_input); + + initScreen(); + } - private void renderGame() { - BufferStrategy bs = getBufferStrategy(); - if(bs == null) { - createBufferStrategy(3); - return; - } - + private void renderGame(Graphics g) { screen.clear(); - + _board.render(screen); - + for (int i = 0; i < pixels.length; i++) { pixels[i] = screen._pixels[i]; } - Graphics g = bs.getDrawGraphics(); - g.drawImage(image, 0, 0, getWidth(), getHeight(), null); _board.renderMessages(g); - - g.dispose(); - bs.show(); } - private void renderScreen() { + private void renderScreen(Graphics g) { + screen.clear(); + _board.drawScreen(g); + } + + private void initScreen() { + Global.currentScreen = EScreenName.SELECT_GAME_MODE; + + this.selectGameModeScreen = new SelectGameModeScreen(); + this.selectLevelScreen = new SelectLevelScreen(_board); + } + + private void update() { + _input.update(); + switch (Global.currentScreen) { + case GAME_PLAY_SCREEN: + _board.update(); + if (_input.pause) { // Kiểm tra nếu phím "p" được nhấn + _board.setShow(3); // Hiển thị màn hình tạm dừng + _paused = true; // Đặt trạng thái game là tạm dừng + return; + } + break; + case SELECT_LEVEL_SCREEN: + // TODO: call select level screen update + selectLevelScreen.update(); + break; + case SELECT_GAME_MODE: + selectGameModeScreen.update(); + break; + } + } + + private void showScreen() { BufferStrategy bs = getBufferStrategy(); - if(bs == null) { + if (bs == null) { createBufferStrategy(3); return; } - - screen.clear(); - Graphics g = bs.getDrawGraphics(); - - _board.drawScreen(g); + + switch (Global.currentScreen) { + case GAME_PLAY_SCREEN: + if (_paused) { + if (_screenDelay <= 0) { + _board.setShow(-1); + _paused = false; + } + + renderScreen(g); + } else { + renderGame(g); + } + + if (_input.resume) { + _paused = false; + _board.setShow(-1); + } + frames++; + if (System.currentTimeMillis() - timer > 1000) { + _frame.setTime(_board.subtractTime()); + _frame.setPoints(_board.getPoints()); + _frame.renderItemTime(); + timer += 1000; + _frame.setTitle(TITLE + " | " + updates + " rate, " + frames + " fps"); + updates = 0; + frames = 0; + + if (_board.getShow() == 2) + --_screenDelay; + } + break; + case SELECT_LEVEL_SCREEN: + // TODO: render select level screen + if (Global.currentScreen != Global.previousScreen) { + selectLevelScreen.setInput(_input); + } + selectLevelScreen.drawScreen(g); + break; + case SELECT_GAME_MODE: + if (Global.currentScreen != Global.previousScreen) { + selectGameModeScreen.setInput(_input); + } + selectGameModeScreen.drawScreen(g); + } g.dispose(); bs.show(); } - private void update() { - _input.update(); - _board.update(); + private void initGame() { + this.timer = System.currentTimeMillis(); + this.frames = 0; + this.updates = 0; } - public void start() { _running = true; + + initGame(); long lastTime = System.nanoTime(); - long timer = System.currentTimeMillis(); final double ns = 1000000000.0 / 60.0; //nanosecond, 60 frames per second double delta = 0; - int frames = 0; - int updates = 0; requestFocus(); - while(_running) { + while (_running) { long now = System.nanoTime(); delta += (now - lastTime) / ns; lastTime = now; - while(delta >= 1) { + while (delta >= 1) { update(); updates++; delta--; } - - if(_paused) { - if(_screenDelay <= 0) { - _board.setShow(-1); - _paused = false; - } - - renderScreen(); - } else { - renderGame(); - } - - - frames++; - if(System.currentTimeMillis() - timer > 1000) { - _frame.setTime(_board.subtractTime()); - _frame.setPoints(_board.getPoints()); - timer += 1000; - _frame.setTitle(TITLE + " | " + updates + " rate, " + frames + " fps"); - updates = 0; - frames = 0; - - if(_board.getShow() == 2) - --_screenDelay; - } + + showScreen(); } } - - public static double getBomberSpeed() { - return bomberSpeed; - } - - public static int getBombRate() { - return bombRate; - } - - public static int getBombRadius() { - return bombRadius; - } - - public static void addBomberSpeed(double i) { - bomberSpeed += i; - } - - public static void addBombRadius(int i) { - bombRadius += i; - } - - public static void addBombRate(int i) { - bombRate += i; - } public void resetScreenDelay() { _screenDelay = SCREENDELAY; } - - public Board getBoard() { - return _board; - } - public boolean isPaused() { return _paused; } - + public void pause() { - _paused = true; + _paused = !_paused; } - public static void setBombRate(int bombRate) { - Game.bombRate = bombRate; - } - public static void setBombRadius(int bombRadius) { - Game.bombRadius = bombRadius; - } + public IGameInfoManager getGameInfoManager() { + return _board; + } - public static void setBomberSpeed(double bomberSpeed) { - Game.bomberSpeed = bomberSpeed; - } } diff --git a/src/uet/oop/bomberman/base/IActiveItemManager.java b/src/uet/oop/bomberman/base/IActiveItemManager.java new file mode 100644 index 0000000..dc86201 --- /dev/null +++ b/src/uet/oop/bomberman/base/IActiveItemManager.java @@ -0,0 +1,12 @@ +package uet.oop.bomberman.base; + +import java.util.List; + +import uet.oop.bomberman.entities.tile.item.Item; + +public interface IActiveItemManager { + + public List getActiveItems(); + public void addActiveItem(Item item); + +} \ No newline at end of file diff --git a/src/uet/oop/bomberman/base/IEntityManager.java b/src/uet/oop/bomberman/base/IEntityManager.java new file mode 100644 index 0000000..de47e76 --- /dev/null +++ b/src/uet/oop/bomberman/base/IEntityManager.java @@ -0,0 +1,30 @@ +package uet.oop.bomberman.base; + +import java.util.List; + +import uet.oop.bomberman.entities.Entity; +import uet.oop.bomberman.entities.bomb.Bomb; +import uet.oop.bomberman.entities.bomb.FlameSegment; +import uet.oop.bomberman.entities.character.Character; + +public interface IEntityManager extends IActiveItemManager { + + public List getBombs(); + + public Entity getEntity(double x, double y, Character m); + public Entity getEntityAt(double x, double y); + public Bomb getBombAt(double x, double y); + public Character getCharacterAtExcluding(int x, int y, Character a); + public FlameSegment getFlameSegmentAt(int x, int y); + + public void addEntity(int pos, Entity e); + public void addCharacter(Character e); + public void addBomb(Bomb e); + + public void setPlayer(Character character); + public Character getPlayer(); + + public boolean isEnemyCleared(); + public void handleOnDeath(Character character, Character killer); + +} \ No newline at end of file diff --git a/src/uet/oop/bomberman/base/IGameInfoManager.java b/src/uet/oop/bomberman/base/IGameInfoManager.java new file mode 100644 index 0000000..ce0b57b --- /dev/null +++ b/src/uet/oop/bomberman/base/IGameInfoManager.java @@ -0,0 +1,11 @@ +package uet.oop.bomberman.base; + +public interface IGameInfoManager extends IActiveItemManager { + + public int subtractTime(); + public int getTime(); + + public int getPoints(); + public void addPoints(int points); + +} \ No newline at end of file diff --git a/src/uet/oop/bomberman/base/IMessageManager.java b/src/uet/oop/bomberman/base/IMessageManager.java new file mode 100644 index 0000000..ec01310 --- /dev/null +++ b/src/uet/oop/bomberman/base/IMessageManager.java @@ -0,0 +1,9 @@ +package uet.oop.bomberman.base; + +import uet.oop.bomberman.entities.Message; + +public interface IMessageManager { + + public void addMessage(Message e); + +} \ No newline at end of file diff --git a/src/uet/oop/bomberman/entities/Entity.java b/src/uet/oop/bomberman/entities/Entity.java index c1021cb..6e9317a 100644 --- a/src/uet/oop/bomberman/entities/Entity.java +++ b/src/uet/oop/bomberman/entities/Entity.java @@ -40,13 +40,27 @@ public Sprite getSprite() { return _sprite; } + public double getCenterX() { + return _x + _sprite.SIZE / 2; + } + + public double getCenterY() { + return _y - _sprite.SIZE / 2; + } + /** * Phương thức này được gọi để xử lý khi hai entity va chạm vào nhau * @param e * @return */ public abstract boolean collide(Entity e); - //xu li 2 entity va cham + + /** + * Check if other Entity can pass through this + * @param other + * @return + */ + public abstract boolean canBePassedThroughBy(Entity other); public double getX() { return _x; diff --git a/src/uet/oop/bomberman/entities/LayeredEntity.java b/src/uet/oop/bomberman/entities/LayeredEntity.java index e183f24..7d824a3 100644 --- a/src/uet/oop/bomberman/entities/LayeredEntity.java +++ b/src/uet/oop/bomberman/entities/LayeredEntity.java @@ -1,5 +1,6 @@ package uet.oop.bomberman.entities; +import uet.oop.bomberman.entities.tile.Tile; import uet.oop.bomberman.entities.tile.destroyable.DestroyableTile; import uet.oop.bomberman.graphics.Screen; @@ -9,11 +10,12 @@ * Chứa và quản lý nhiều Entity tại cùng một vị trí * Ví dụ: tại vị trí dấu Item, có 3 Entity [Grass, Item, Brick] */ -public class LayeredEntity extends Entity { +public class LayeredEntity extends Tile { - protected LinkedList _entities = new LinkedList<>(); + protected LinkedList _entities = new LinkedList<>(); - public LayeredEntity(int x, int y, Entity ... entities) { + public LayeredEntity(int x, int y, Tile ... entities) { + super(x, y, null); _x = x; _y = y; @@ -38,7 +40,7 @@ public void render(Screen screen) { getTopEntity().render(screen); } - public Entity getTopEntity() { + public Tile getTopEntity() { return _entities.getLast(); } @@ -51,15 +53,24 @@ private void clearRemoved() { } } - public void addBeforeTop(Entity e) { + public void addBeforeTop(Tile e) { _entities.add(_entities.size() - 1, e); } @Override public boolean collide(Entity e) { // TODO: lấy entity trên cùng ra để xử lý va chạm - return getTopEntity().collide(e); } + @Override + public boolean canBePassedThroughBy(Entity other) { + return getTopEntity().canBePassedThroughBy(other); + } + + @Override + public boolean isDestroyable() { + return getTopEntity().isDestroyable(); + } + } diff --git a/src/uet/oop/bomberman/entities/Message.java b/src/uet/oop/bomberman/entities/Message.java index 1e743e7..74cc779 100644 --- a/src/uet/oop/bomberman/entities/Message.java +++ b/src/uet/oop/bomberman/entities/Message.java @@ -1,5 +1,6 @@ package uet.oop.bomberman.entities; +import uet.oop.bomberman.Game; import uet.oop.bomberman.graphics.Screen; import java.awt.*; @@ -27,7 +28,7 @@ public Message(String message, double x, double y, int duration, Color color, in _x =x; _y = y; _message = message; - _duration = duration * 60; //seconds + _duration = duration * Game.TICKS_PER_SECOND; //seconds _color = color; _size = size; } @@ -64,6 +65,11 @@ public void render(Screen screen) { public boolean collide(Entity e) { return true; } + + @Override + public boolean canBePassedThroughBy(Entity other) { + return true; + } } diff --git a/src/uet/oop/bomberman/entities/bomb/Bomb.java b/src/uet/oop/bomberman/entities/bomb/Bomb.java index 34e1a20..b2da0a2 100644 --- a/src/uet/oop/bomberman/entities/bomb/Bomb.java +++ b/src/uet/oop/bomberman/entities/bomb/Bomb.java @@ -1,7 +1,7 @@ package uet.oop.bomberman.entities.bomb; -import uet.oop.bomberman.Board; import uet.oop.bomberman.Game; +import uet.oop.bomberman.base.IEntityManager; import uet.oop.bomberman.entities.AnimatedEntitiy; import uet.oop.bomberman.entities.Entity; import uet.oop.bomberman.entities.character.Bomber; @@ -10,120 +10,136 @@ import uet.oop.bomberman.entities.character.Character; import uet.oop.bomberman.level.Coordinates; import uet.oop.bomberman.sound.Sound; + public class Bomb extends AnimatedEntitiy { - protected double _timeToExplode = 120; //2 seconds - thoi gian phat no + protected double _timeToExplode = 120; // 2 seconds - thoi gian phat no public int _timeAfter = 20;// thoi gian de no - - protected Board _board; + + protected IEntityManager _board; protected Flame[] _flames; protected boolean _exploded = false; protected boolean _allowedToPassThru = true; - - public Bomb(int x, int y, Board board) { + + private final int bombRadius; + + public Bomb(int x, int y, int bombRadius, IEntityManager board) { _x = x; _y = y; _board = board; _sprite = Sprite.bomb; + this.bombRadius = bombRadius; } - + @Override public void update() { - if(_timeToExplode > 0) + if (_timeToExplode > 0) _timeToExplode--; else { - if(!_exploded) + if (!_exploded) explode(); else updateFlames(); - - if(_timeAfter > 0) + + if (_timeAfter > 0) _timeAfter--; else remove(); } - + animate(); } - + @Override public void render(Screen screen) { - if(_exploded) { - _sprite = Sprite.bomb_exploded2; + if (_exploded) { + _sprite = Sprite.bomb_exploded2; renderFlames(screen); } else - _sprite = Sprite.movingSprite(Sprite.bomb, Sprite.bomb_1, Sprite.bomb_2, _animate, 60); - - int xt = (int)_x << 4; - int yt = (int)_y << 4; - - screen.renderEntity(xt, yt , this); + _sprite = Sprite.movingSprite(Sprite.bomb, Sprite.bomb_1, Sprite.bomb_2, _animate, Game.TICKS_PER_SECOND); + + int xt = (int) _x << 4; + int yt = (int) _y << 4; + + screen.renderEntity(xt, yt, this); } - + public void renderFlames(Screen screen) { for (int i = 0; i < _flames.length; i++) { _flames[i].render(screen); } } - + public void updateFlames() { for (int i = 0; i < _flames.length; i++) { _flames[i].update(); } } - /** - * Xử lý Bomb nổ - */ - protected void explode() {//nổ + /** + * Xử lý Bomb nổ + */ + protected void explode() {// nổ _exploded = true; _allowedToPassThru = true; // TODO: xử lý khi Character đứng tại vị trí Bomb - Character x = _board.getCharacterAtExcluding((int)_x, (int)_y, null); - if(x != null){ - x.kill(); - } + Character x = _board.getCharacterAtExcluding((int) _x, (int) _y, null); + if (x != null) { + x.handleOnDeath(); + } // TODO: tạo các Flame - _flames = new Flame[4]; - for (int i = 0; i < _flames.length; i++) { - _flames[i] = new Flame((int) _x, (int) _y, i, Game.getBombRadius(), _board); - } - Sound.play("BOM_11_M"); + _flames = new Flame[4]; + for (int i = 0; i < _flames.length; i++) { + _flames[i] = new Flame((int) _x, (int) _y, i, bombRadius, _board); + } + Sound.play("BOM_11_M"); } - public void time_explode() { + + public void handleChainExplode() { _timeToExplode = 0; } + public FlameSegment flameAt(int x, int y) { - if(!_exploded) return null; - + if (!_exploded) + return null; + for (int i = 0; i < _flames.length; i++) { - if(_flames[i] == null) return null; + if (_flames[i] == null) + return null; FlameSegment e = _flames[i].flameSegmentAt(x, y); - if(e != null) return e; + if (e != null) + return e; } - + return null; } @Override public boolean collide(Entity e) { - // TODO: xử lý khi Bomber đi ra sau khi vừa đặt bom (_allowedToPassThru) - - if(e instanceof Bomber) { - double diffX = e.getX() - Coordinates.tileToPixel(getX()); - double diffY = e.getY() - Coordinates.tileToPixel(getY()); - - if(!(diffX >= -10 && diffX < 16 && diffY >= 1 && diffY <= 28)) { // differences to see if the player has moved out of the bomb, tested values + // Xử lý va chạm với Flame của Bomb khác: chain explosion + if (e instanceof Flame) { + handleChainExplode(); + return true; + } + return false; + } + + @Override + public boolean canBePassedThroughBy(Entity other) { + // Xử lý khi Bomber đi ra sau khi vừa đặt bom (_allowedToPassThru) + if (other instanceof Bomber) { + double diffX = other.getX() - Coordinates.tileToPixel(getX()); + double diffY = other.getY() - Coordinates.tileToPixel(getY()); + + if (!(diffX >= -10 && diffX < 16 && diffY >= 1 && diffY <= 28)) { // differences to see if the player has + // moved out of the bomb, tested values _allowedToPassThru = false; } - + return _allowedToPassThru; } - // TODO: xử lý va chạm với Flame của Bomb khác - if(e instanceof Flame ) { - time_explode(); - return true; - } + return false; } + } diff --git a/src/uet/oop/bomberman/entities/bomb/Flame.java b/src/uet/oop/bomberman/entities/bomb/Flame.java index 721bc25..4e2ac84 100644 --- a/src/uet/oop/bomberman/entities/bomb/Flame.java +++ b/src/uet/oop/bomberman/entities/bomb/Flame.java @@ -1,14 +1,15 @@ package uet.oop.bomberman.entities.bomb; -import uet.oop.bomberman.Board; +import uet.oop.bomberman.base.IEntityManager; import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.character.Bomber; -import uet.oop.bomberman.entities.character.enemy.Enemy; +import uet.oop.bomberman.entities.character.Character; +import uet.oop.bomberman.entities.tile.Tile; +import uet.oop.bomberman.entities.tile.destroyable.DestroyableTile; import uet.oop.bomberman.graphics.Screen; public class Flame extends Entity { - protected Board _board; + protected IEntityManager entityManager; protected int _direction; private int _radius; protected int xOrigin, yOrigin; @@ -21,14 +22,14 @@ public class Flame extends Entity { * @param direction là hướng của Flame * @param radius độ dài cực đại của Flame */ - public Flame(int x, int y, int direction, int radius, Board board) { + public Flame(int x, int y, int direction, int radius, IEntityManager entityManager) { xOrigin = x; yOrigin = y; _x = x; _y = y; _direction = direction; _radius = radius; - _board = board; + this.entityManager = entityManager; createFlameSegments(); } @@ -60,6 +61,10 @@ private void createFlameSegments() { case 3: x--; break; } _flameSegments[i] = new FlameSegment(x, y, _direction, last); + Entity entity = entityManager.getEntity(x, y, null); + if (entity!=null) { + entity.collide(this); + } } } @@ -78,17 +83,26 @@ private int calculatePermitedDistance() { if(_direction == 2) y++; if(_direction == 3) x--; - Entity a = _board.getEntity(x, y, null); + Entity a = entityManager.getEntity(x, y, null); if(a instanceof Bomb) ++radius; //explosion has to be below the bom - if(a.collide(this) == false) //cannot pass thru + if(!canSpawnFlameOn(a)) break; ++radius; } return radius; } + + private boolean canSpawnFlameOn(Entity entity) { + if (entity.canBePassedThroughBy(this)) return true; + if (entity instanceof Tile) { + Tile tile = (Tile) entity; + if (tile.isDestroyable()) return true; + } + return false; + } public FlameSegment flameSegmentAt(int x, int y) { for (int i = 0; i < _flameSegments.length; i++) { @@ -110,9 +124,15 @@ public void render(Screen screen) { @Override public boolean collide(Entity e) { - // TODO: xử lý va chạm với Bomber, Enemy. Chú ý đối tượng này có vị trí chính là vị trí của Bomb đã nổ - if(e instanceof Bomber) ((Bomber) e).kill(); - if(e instanceof Enemy) ((Enemy) e).kill(); - return true; + if (e instanceof Character) { + ((Character)e).handleOnDeath(); + } + return true; } + + @Override + public boolean canBePassedThroughBy(Entity other) { + return true; + } + } diff --git a/src/uet/oop/bomberman/entities/bomb/FlameSegment.java b/src/uet/oop/bomberman/entities/bomb/FlameSegment.java index 8a28539..cbbb2a5 100644 --- a/src/uet/oop/bomberman/entities/bomb/FlameSegment.java +++ b/src/uet/oop/bomberman/entities/bomb/FlameSegment.java @@ -1,8 +1,7 @@ package uet.oop.bomberman.entities.bomb; import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.character.Bomber; -import uet.oop.bomberman.entities.character.enemy.Enemy; +import uet.oop.bomberman.entities.character.Character; import uet.oop.bomberman.graphics.Screen; import uet.oop.bomberman.graphics.Sprite; @@ -69,11 +68,13 @@ public void update() {} @Override public boolean collide(Entity e) { - // TODO: xử lý khi FlameSegment va chạm với Character - if(e instanceof Bomber) ((Bomber) e).kill(); - if(e instanceof Enemy) ((Enemy) e).kill(); + if (e instanceof Character) ((Character)e).handleOnDeath(); + return true; + } + + @Override + public boolean canBePassedThroughBy(Entity other) { return true; } - } \ No newline at end of file diff --git a/src/uet/oop/bomberman/entities/character/Bomber.java b/src/uet/oop/bomberman/entities/character/Bomber.java index bcc797d..af2572a 100644 --- a/src/uet/oop/bomberman/entities/character/Bomber.java +++ b/src/uet/oop/bomberman/entities/character/Bomber.java @@ -3,17 +3,19 @@ import java.util.ArrayList; import uet.oop.bomberman.Board; import uet.oop.bomberman.Game; +import uet.oop.bomberman.base.IEntityManager; import uet.oop.bomberman.entities.Entity; import uet.oop.bomberman.entities.bomb.Bomb; import uet.oop.bomberman.graphics.Screen; import uet.oop.bomberman.graphics.Sprite; -import uet.oop.bomberman.input.Keyboard; import java.util.Iterator; import java.util.List; import uet.oop.bomberman.entities.LayeredEntity; import uet.oop.bomberman.entities.bomb.Flame; import uet.oop.bomberman.entities.character.enemy.Enemy; +import uet.oop.bomberman.entities.tile.item.BombItem; +import uet.oop.bomberman.entities.tile.item.FlameItem; import uet.oop.bomberman.entities.tile.item.Item; import uet.oop.bomberman.level.Coordinates; import uet.oop.bomberman.sound.Sound; @@ -21,43 +23,39 @@ public class Bomber extends Character { private List _bombs; - protected Keyboard _input; public static List _items = new ArrayList();//xu li Item - /** - * nếu giá trị này < 0 thì cho phép đặt đối tượng Bomb tiếp theo, - * cứ mỗi lần đặt 1 Bomb mới, giá trị này sẽ được reset về 0 và giảm dần trong mỗi lần update() - */ - protected int _timeBetweenPutBombs = 0; - - public Bomber(int x, int y, Board board) { - super(x, y, board); - _bombs = _board.getBombs(); - _input = _board.getInput(); - _sprite = Sprite.player_right; + + private final int baseBombLimit; + protected int bombCooldown = 0; + + private final int baseBombRadius; + + public int getBombCooldown() { + return bombCooldown; } - @Override - public void update() { - clearBombs(); - if (!_alive) { - afterKill(); - return; - } + private Board _board; - if (_timeBetweenPutBombs < -7500) _timeBetweenPutBombs = 0; - else _timeBetweenPutBombs--; + public Bomber(int x, int y, double baseSpeed, int baseBombLimit, int baseBombRadius, IEntityManager entityManager, Board board) { + super(x, y, baseSpeed, entityManager); + this.baseBombLimit = baseBombLimit; + this.baseBombRadius = baseBombRadius; + this._board = board; + _bombs = entityManager.getBombs(); + _sprite = Sprite.player_right; + } + @Override + public void handleUpdate() { + clearExpiredBombs(); + if (bombCooldown < -7500) bombCooldown = 0; + else bombCooldown--; animate(); - calculateMove(); - - detectPlaceBomb(); } @Override public void render(Screen screen) { - calculateXOffset(); - if (_alive) chooseSprite(); else @@ -66,40 +64,44 @@ public void render(Screen screen) { screen.renderEntity((int) _x, (int) _y - _sprite.SIZE, this); } - public void calculateXOffset() { - int xScroll = Screen.calculateXOffset(_board, this); - Screen.setOffset(xScroll, 0); + public int getBombLimit() { + int countActiveItem = (int) getActiveItems().filter(item -> item instanceof BombItem).count(); + int bombLimitBonus = countActiveItem * BombItem.BOMB_LIMIT_BONUS; + return this.baseBombLimit + bombLimitBonus; } - /** - * Kiểm tra xem có đặt được bom hay không? nếu có thì đặt bom tại vị trí hiện tại của Bomber - */ - private void detectPlaceBomb() { - // TODO: kiểm tra xem phím điều khiển đặt bom có được gõ và giá trị _timeBetweenPutBombs, Game.getBombRate() có thỏa mãn hay không - // TODO: Game.getBombRate() sẽ trả về số lượng bom có thể đặt liên tiếp tại thời điểm hiện tại - // TODO: _timeBetweenPutBombs dùng để ngăn chặn Bomber đặt 2 Bomb cùng tại 1 vị trí trong 1 khoảng thời gian quá ngắn - // TODO: nếu 3 điều kiện trên thỏa mãn thì thực hiện đặt bom bằng placeBomb() - // TODO: sau khi đặt, nhớ giảm số lượng Bomb Rate và reset _timeBetweenPutBombs về 0 - if(_input.space && Game.getBombRate() > 0 && _timeBetweenPutBombs < 0) { + public int getBombRemainingQuota() { + return getBombLimit() - _bombs.size(); + } + + public int getBombRadius() { + int countActiveItem = (int) getActiveItems().filter(item -> item instanceof FlameItem).count(); + int bombRadiusBonus = countActiveItem * FlameItem.BOMB_RADIUS_BONUS; + return this.baseBombRadius + bombRadiusBonus; + } + + public boolean placeBomb() { + if(getBombRemainingQuota() > 0 && bombCooldown < 0) { int xt = Coordinates.pixelToTile(_x + _sprite.getSize() / 2); int yt = Coordinates.pixelToTile( (_y + _sprite.getSize() / 2) - _sprite.getSize() ); //subtract half player height and minus 1 y position placeBomb(xt,yt); - Game.addBombRate(-1); - _timeBetweenPutBombs = 30; + bombCooldown = 30; + return true; } + return false; } - protected void placeBomb(int x, int y) { + public void placeBomb(int x, int y) { // TODO: thực hiện tạo đối tượng bom, đặt vào vị trí (x, y) - Bomb b = new Bomb(x, y, _board); - _board.addBomb(b); + Bomb b = new Bomb(x, y, getBombRadius(), entityManager); + entityManager.addBomb(b); Sound.play("BOM_SET"); } - private void clearBombs() { + private void clearExpiredBombs() { Iterator bs = _bombs.iterator(); Bomb b; @@ -107,43 +109,18 @@ private void clearBombs() { b = bs.next(); if (b.isRemoved()) { bs.remove(); - Game.addBombRate(1); } } } @Override - public void kill() { - if (!_alive) return; - _alive = false; - Sound.play("endgame3"); - } - - @Override - protected void afterKill() { - if (_timeAfter > 0) --_timeAfter; - else { - _board.endGame(); - } + protected void handleAfterDeath() { + _board.endGame(); } @Override protected void calculateMove() { - // TODO: xử lý nhận tín hiệu điều khiển hướng đi từ _input và gọi move() để thực hiện di chuyển - // TODO: nhớ cập nhật lại giá trị cờ _moving khi thay đổi trạng thái di chuyển - int xa = 0, ya = 0; - if(_input.up) ya--; - if(_input.down) ya++; - if(_input.left) xa--; - if(_input.right) xa++; - - if(xa != 0 || ya != 0) { - move(xa * Game.getBomberSpeed(), ya * Game.getBomberSpeed()); - _moving = true; - } else { - _moving = false; - } } @Override @@ -153,9 +130,9 @@ public boolean canMove(double x, double y) { double xt = ((_x + x) + c % 2 * 9) / Game.TILES_SIZE; //divide with tiles size to pass to tile coordinate double yt = ((_y + y) + c / 2 * 10 - 13) / Game.TILES_SIZE; //these values are the best from multiple tests - Entity a = _board.getEntity(xt, yt, this); + Entity a = entityManager.getEntity(xt, yt, this); - if(!a.collide(this)) + if(!a.canBePassedThroughBy(this)) return false; } @@ -163,37 +140,9 @@ public boolean canMove(double x, double y) { //return false; } - @Override - public void move(double xa, double ya) { - // TODO: sử dụng canMove() để kiểm tra xem có thể di chuyển tới điểm đã tính toán hay không và thực hiện thay đổi tọa độ _x, _y - // TODO: nhớ cập nhật giá trị _direction sau khi di chuyển - if(xa > 0) _direction = 1; - if(xa < 0) _direction = 3; - if(ya > 0) _direction = 2; - if(ya < 0) _direction = 0; - - if(canMove(0, ya)) { //separate the moves for the player can slide when is colliding - _y += ya; - } - - if(canMove(xa, 0)) { - _x += xa; - } - } - @Override public boolean collide(Entity e) { - // TODO: xử lý va chạm với Flame - // TODO: xử lý va chạm với Enemy - if(e instanceof Flame){ - this.kill(); - return false; - } - if(e instanceof Enemy){ - this.kill(); - return true; - } - if( e instanceof LayeredEntity) return(e.collide(this)); + if (!super.collide(e)) return false; return true; } @@ -232,4 +181,9 @@ private void chooseSprite() { break; } } + + @Override + public int getPoints() { + return 0; + } } diff --git a/src/uet/oop/bomberman/entities/character/Character.java b/src/uet/oop/bomberman/entities/character/Character.java index 52842a9..a630f79 100644 --- a/src/uet/oop/bomberman/entities/character/Character.java +++ b/src/uet/oop/bomberman/entities/character/Character.java @@ -1,29 +1,56 @@ package uet.oop.bomberman.entities.character; -import uet.oop.bomberman.Board; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + import uet.oop.bomberman.Game; +import uet.oop.bomberman.base.IEntityManager; import uet.oop.bomberman.entities.AnimatedEntitiy; +import uet.oop.bomberman.entities.Entity; +import uet.oop.bomberman.entities.LayeredEntity; +import uet.oop.bomberman.entities.bomb.Flame; +import uet.oop.bomberman.entities.tile.item.Item; +import uet.oop.bomberman.entities.tile.item.SpeedItem; import uet.oop.bomberman.graphics.Screen; +import uet.oop.bomberman.level.Coordinates; /** * Bao gồm Bomber và Enemy */ public abstract class Character extends AnimatedEntitiy { - protected Board _board; + protected final IEntityManager entityManager; protected int _direction = -1; protected boolean _alive = true; protected boolean _moving = false; - public int _timeAfter = 40; + protected int timerDeathAnimation = 40; + + private final double baseSpeed; + + private List activeItems = new ArrayList<>(); - public Character(int x, int y, Board board) { + public Character(int x, int y, double baseSpeed, IEntityManager entityManager) { _x = x; _y = y; - _board = board; + this.entityManager = entityManager; + this.baseSpeed = baseSpeed; } @Override - public abstract void update(); + public final void update() { + if (!_alive) { + if (timerDeathAnimation > 0) { + timerDeathAnimation -= 1; + } else { + handleAfterDeath(); + } + return; + } + handleUpdate(); + }; + + protected abstract void handleUpdate(); @Override public abstract void render(Screen screen); @@ -33,17 +60,44 @@ public Character(int x, int y, Board board) { */ protected abstract void calculateMove(); - protected abstract void move(double xa, double ya); + /** Check if can be moved with vector (xa, ya). + * @param xa + * @param ya + */ + public void move(double xa, double ya) { + if(xa > 0) _direction = 1; + if(xa < 0) _direction = 3; + if(ya > 0) _direction = 2; + if(ya < 0) _direction = 0; + + if(canMove(0, ya)) _y += ya; + if(canMove(xa, 0)) _x += xa; + + Entity collidingEntity = entityManager.getEntity( + Coordinates.pixelToTile(getCenterX()), + Coordinates.pixelToTile(getCenterY()), + this + ); + if (collidingEntity != null) { + this.collide(collidingEntity); + collidingEntity.collide(this); + } + } /** * Được gọi khi đối tượng bị tiêu diệt */ - public abstract void kill(); + public final void handleOnDeath() { + if(!_alive) return; + _alive = false; + // TODO: determine killer + entityManager.handleOnDeath(this, null); + } /** * Xử lý hiệu ứng bị tiêu diệt */ - protected abstract void afterKill(); + protected abstract void handleAfterDeath(); /** * Kiểm tra xem đối tượng có di chuyển tới vị trí đã tính toán hay không @@ -60,5 +114,65 @@ protected double getXMessage() { protected double getYMessage() { return (_y* Game.SCALE) - (_sprite.SIZE / 2 * Game.SCALE); } + + public boolean isPlayer() { + return entityManager.getPlayer() == this; + } + public boolean isAlive() { + return _alive; + } + + public void setMoving(boolean moving) { + this._moving = moving; + } + + public Stream getActiveItems() { + return activeItems.stream().filter(Item::isActive); + } + + public void addActiveItem(Item item) { + this.activeItems.add(item); + entityManager.addActiveItem(item); + } + + public double getSpeed() { + double speedMultiplier = 1; + for (Item item: activeItems) { + if (!item.isActive()) continue; + if (item instanceof SpeedItem) { + speedMultiplier += SpeedItem.SPEED_MULTIPLIER; + } + } + return speedMultiplier * this.baseSpeed; + } + + public abstract int getPoints(); + + @Override + public boolean collide(Entity e) { + if(e instanceof Flame){ + this.handleOnDeath(); + return false; + } + if (e instanceof Character) { + Character other = (Character) e; + if (this.isPlayer() && !other.isPlayer()) { + this.handleOnDeath(); + return false; + } + if (other.isPlayer() && !this.isPlayer()) { + other.handleOnDeath(); + return false; + } + } + if( e instanceof LayeredEntity) return(e.collide(this)); + return true; + } + + @Override + public boolean canBePassedThroughBy(Entity other) { + return true; + } + } diff --git a/src/uet/oop/bomberman/entities/character/enemy/Balloon.java b/src/uet/oop/bomberman/entities/character/enemy/Balloon.java index e0d43dc..f8a3ab1 100644 --- a/src/uet/oop/bomberman/entities/character/enemy/Balloon.java +++ b/src/uet/oop/bomberman/entities/character/enemy/Balloon.java @@ -25,11 +25,11 @@ protected void chooseSprite() { switch(_direction) { case 0: case 1: - _sprite = Sprite.movingSprite(Sprite.balloom_right1, Sprite.balloom_right2, Sprite.balloom_right3, _animate, 60); + _sprite = Sprite.movingSprite(Sprite.balloom_right1, Sprite.balloom_right2, Sprite.balloom_right3, _animate, Game.TICKS_PER_SECOND); break; case 2: case 3: - _sprite = Sprite.movingSprite(Sprite.balloom_left1, Sprite.balloom_left2, Sprite.balloom_left3, _animate, 60); + _sprite = Sprite.movingSprite(Sprite.balloom_left1, Sprite.balloom_left2, Sprite.balloom_left3, _animate, Game.TICKS_PER_SECOND); break; } } diff --git a/src/uet/oop/bomberman/entities/character/enemy/Doll.java b/src/uet/oop/bomberman/entities/character/enemy/Doll.java index dc85fce..bc60e2c 100644 --- a/src/uet/oop/bomberman/entities/character/enemy/Doll.java +++ b/src/uet/oop/bomberman/entities/character/enemy/Doll.java @@ -6,6 +6,7 @@ package uet.oop.bomberman.entities.character.enemy; import uet.oop.bomberman.Board; +import uet.oop.bomberman.Game; import uet.oop.bomberman.entities.character.enemy.ai.AILow; import uet.oop.bomberman.entities.character.enemy.ai.AIMedium; import uet.oop.bomberman.graphics.Sprite; @@ -21,7 +22,7 @@ public Doll(int x, int y, Board board) { _sprite = Sprite.balloom_left1; - _ai = new AIMedium(_board.getBomber(), this); + _ai = new AIMedium(entityManager.getPlayer(), this); _direction = _ai.calculateDirection(); } @@ -32,7 +33,7 @@ protected void chooseSprite() { case 0: case 1: if (_moving) { - _sprite = Sprite.movingSprite(Sprite.doll_right1, Sprite.doll_right2, Sprite.doll_right3, _animate, 60); + _sprite = Sprite.movingSprite(Sprite.doll_right1, Sprite.doll_right2, Sprite.doll_right3, _animate, Game.TICKS_PER_SECOND); } else { _sprite = Sprite.doll_left1; } @@ -40,7 +41,7 @@ protected void chooseSprite() { case 2: case 3: if (_moving) { - _sprite = Sprite.movingSprite(Sprite.doll_left1, Sprite.doll_left2, Sprite.doll_left3, _animate, 60); + _sprite = Sprite.movingSprite(Sprite.doll_left1, Sprite.doll_left2, Sprite.doll_left3, _animate, Game.TICKS_PER_SECOND); } else { _sprite = Sprite.doll_left1; } diff --git a/src/uet/oop/bomberman/entities/character/enemy/Enemy.java b/src/uet/oop/bomberman/entities/character/enemy/Enemy.java index 61a7b99..adb5562 100644 --- a/src/uet/oop/bomberman/entities/character/enemy/Enemy.java +++ b/src/uet/oop/bomberman/entities/character/enemy/Enemy.java @@ -1,20 +1,14 @@ package uet.oop.bomberman.entities.character.enemy; -import uet.oop.bomberman.Board; import uet.oop.bomberman.Game; +import uet.oop.bomberman.base.IEntityManager; import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.Message; -import uet.oop.bomberman.entities.bomb.Flame; -import uet.oop.bomberman.entities.character.Bomber; import uet.oop.bomberman.entities.character.Character; import uet.oop.bomberman.entities.character.enemy.ai.AI; import uet.oop.bomberman.graphics.Screen; import uet.oop.bomberman.graphics.Sprite; import uet.oop.bomberman.level.Coordinates; -import java.awt.*; -import uet.oop.bomberman.sound.Sound; - public abstract class Enemy extends Character { protected int _points; @@ -28,10 +22,10 @@ public abstract class Enemy extends Character { protected int _finalAnimation = 30; protected Sprite _deadSprite; - - public Enemy(int x, int y, Board board, Sprite dead, double speed, int points) { - super(x, y, board); - + + public Enemy(int x, int y, IEntityManager entityManager, Sprite dead, double speed, int points) { + super(x, y, speed, entityManager); + _points = points; _speed = speed; @@ -39,21 +33,14 @@ public Enemy(int x, int y, Board board, Sprite dead, double speed, int points) { rest = (MAX_STEPS - (int) MAX_STEPS) / MAX_STEPS; _steps = MAX_STEPS; - _timeAfter = 20; + timerDeathAnimation = 20; _deadSprite = dead; } @Override - public void update() { + public void handleUpdate() { animate(); - - if(!_alive) { - afterKill(); - return; - } - - if(_alive) - calculateMove(); + calculateMove(); } @Override @@ -62,11 +49,11 @@ public void render(Screen screen) { if(_alive) chooseSprite(); else { - if(_timeAfter > 0) { + if(timerDeathAnimation > 0) { _sprite = _deadSprite; _animate = 0; } else { - _sprite = Sprite.movingSprite(Sprite.mob_dead1, Sprite.mob_dead2, Sprite.mob_dead3, _animate, 60); + _sprite = Sprite.movingSprite(Sprite.mob_dead1, Sprite.mob_dead2, Sprite.mob_dead3, _animate, Game.TICKS_PER_SECOND); } } @@ -101,13 +88,6 @@ public void calculateMove() { } } - @Override - public void move(double xa, double ya) { - if(!_alive) return; - _y += ya; - _x += xa; - } - @Override public boolean canMove(double x, double y) { double xr = _x, yr = _y - 16; //subtract y to get more accurate results @@ -122,46 +102,26 @@ public boolean canMove(double x, double y) { int xx = Coordinates.pixelToTile(xr) +(int)x; int yy = Coordinates.pixelToTile(yr) +(int)y; - Entity a = _board.getEntity(xx, yy, this); //entity of the position we want to go + Entity a = entityManager.getEntity(xx, yy, this); //entity of the position we want to go - return a.collide(this); + return a.canBePassedThroughBy(this); } @Override public boolean collide(Entity e) { - if(e instanceof Flame){ - this.kill(); - return false; - } - if(e instanceof Bomber){ - ((Bomber) e).kill(); - return false; - } + if (!super.collide(e)) return false; return true; } @Override - public void kill() { - if(!_alive) return; - _alive = false; - - _board.addPoints(_points); - - Message msg = new Message("+" + _points, getXMessage(), getYMessage(), 2, Color.white, 14); - _board.addMessage(msg); - Sound.play("AA126_11"); + protected void handleAfterDeath() { + remove(); } - + protected abstract void chooseSprite(); + @Override - protected void afterKill() { - if(_timeAfter > 0) --_timeAfter; - else { - if(_finalAnimation > 0) --_finalAnimation; - else - remove(); - } + public int getPoints() { + return _points; } - - protected abstract void chooseSprite(); } diff --git a/src/uet/oop/bomberman/entities/character/enemy/Kondoria.java b/src/uet/oop/bomberman/entities/character/enemy/Kondoria.java new file mode 100644 index 0000000..478c97b --- /dev/null +++ b/src/uet/oop/bomberman/entities/character/enemy/Kondoria.java @@ -0,0 +1,40 @@ +package uet.oop.bomberman.entities.character.enemy; + +import uet.oop.bomberman.Game; +import uet.oop.bomberman.Board; +import uet.oop.bomberman.entities.character.enemy.ai.AIMedium; +import uet.oop.bomberman.graphics.Sprite; + +public class Kondoria extends Enemy { + public Kondoria(int x, int y, Board board) { + super(x, y, board, Sprite.balloom_dead, Game.BOMBERSPEED / 4, 1000); + + _sprite = Sprite.kondoria_right1; + + _ai = new AIMedium(entityManager.getPlayer(), this); + _direction = _ai.calculateDirection(); + // this._speed += random.nextDouble()/2; + } + + @Override + protected void chooseSprite() { + switch (_direction) { + case 0: + case 1: + if (_moving) + _sprite = Sprite.movingSprite(Sprite.kondoria_right1, Sprite.kondoria_right2, + Sprite.kondoria_right3, _animate, Game.TICKS_PER_SECOND); + else + _sprite = Sprite.kondoria_left1; + break; + case 2: + case 3: + if (_moving) + _sprite = Sprite.movingSprite(Sprite.kondoria_left1, Sprite.kondoria_left2, Sprite.kondoria_left3, + _animate, Game.TICKS_PER_SECOND); + else + _sprite = Sprite.kondoria_left1; + break; + } + } +} diff --git a/src/uet/oop/bomberman/entities/character/enemy/Minvo.java b/src/uet/oop/bomberman/entities/character/enemy/Minvo.java new file mode 100644 index 0000000..677f3ba --- /dev/null +++ b/src/uet/oop/bomberman/entities/character/enemy/Minvo.java @@ -0,0 +1,138 @@ +package uet.oop.bomberman.entities.character.enemy; + +import uet.oop.bomberman.Board; +import uet.oop.bomberman.Game; +import uet.oop.bomberman.entities.character.enemy.ai.AIMedium; +import uet.oop.bomberman.graphics.Sprite; + +public class Minvo extends Enemy { + private Board _board; + + public Minvo(int x, int y, Board board) { + super(x, y, board, Sprite.minvo_dead, Game.BOMBERSPEED * 1.5, 800); + _board = board; + _sprite = Sprite.minvo_right1; + + _ai = new AIMedium(_board.getPlayer(), this); + _direction = _ai.calculateDirection(); + } + + @Override + protected void chooseSprite() { + switch (_direction) { + case 0: + case 1: + if (_moving) + _sprite = Sprite.movingSprite(Sprite.minvo_right1, Sprite.minvo_right2, Sprite.minvo_right3, + _animate, Game.TICKS_PER_SECOND); + else + _sprite = Sprite.minvo_left1; + break; + case 2: + case 3: + if (_moving) + _sprite = Sprite.movingSprite(Sprite.minvo_left1, Sprite.minvo_left2, Sprite.minvo_left3, _animate, + Game.TICKS_PER_SECOND); + else + _sprite = Sprite.minvo_left1; + break; + } + } + + // // Thêm hành vi cho Minvo + + // @Override + // public void update() { + // super.update(); + // Bomber bomber = _board.getBomber(); + // if (bomber != null) { + // int bomberX = bomber.getXTile(); + // int bomberY = bomber.getYTile(); + // int minvoX = this.getXTile(); + // int minvoY = this.getYTile(); + + // double distance = Math.sqrt(Math.pow(bomberX - minvoX, 2) + Math.pow(bomberY + // - minvoY, 2)); + + // // Kiểm tra xem khoảng cách giữa Minvo và Bomber có dưới một ngưỡng nhất định + // // hay không + // if (distance <= 3) { + // moveTowardBomber(); + // placeBomb(); + // } else { + // _direction = _ai.calculateDirection(); + // // move(); + // } + // } + // } + + // private void moveTowardBomber() { + // Bomber bomber = _board.getBomber(); + // if (bomber != null) { + // int bomberX = bomber.getXTile(); + // int bomberY = bomber.getYTile(); + // int minvoX = this.getXTile(); + // int minvoY = this.getYTile(); + + // // hướng vector từ minvo đến bomber + // int dx = bomberX - minvoX; + // int dy = bomberY - minvoY; + + // // Chọn hướng đi gần nhất + // if (Math.abs(dx) > Math.abs(dy)) { + // if (dx > 0) { + // _direction = 1; // Di chuyển sang phải + // } else { + // _direction = 3; // Di chuyển sang trái + // } + // } else { + // if (dy > 0) { + // _direction = 2; // Di chuyển xuống dưới + // } else { + // _direction = 0; // Di chuyển lên trên + // } + + // } + // } + // // // di chuyển mivo + // // move(); + + // } + + // private void placeBomb() { + // if (_board.detectNoEnemies()) { + // return; + // } + // int bombRate = _board.getBombRate(); + // if (Math.random() < bombRate) { // Kiểm tra xác suất đặt bom + // int xt = Coordinates.pixelToTile(_x + Game.TILES_SIZE / 2); + // int yt = Coordinates.pixelToTile((_y + Game.TILES_SIZE / 2)); + // _board.addBomb(new Bomb(xt, yt, _board)); + // } + // } + + // private void move() { + // double xa = _x, ya = _y; + + // switch (_direction) { + // case 0: + // ya -= _speed; + // break; + // case 1: + // xa += _speed; + // break; + // case 2: + // ya += _speed; + // break; + // case 3: + // xa -= _speed; + // break; + // } + + // // Kiểm tra va chạm với tường và cập nhật tọa độ đích đến nếu cần + // if (!_board.detectWallCollision(xa, ya, _collisionRadius)) { + // _x = xa; + // _y = ya; + // } + // } +} diff --git a/src/uet/oop/bomberman/entities/character/enemy/Oneal.java b/src/uet/oop/bomberman/entities/character/enemy/Oneal.java index 3ad4b23..608255f 100644 --- a/src/uet/oop/bomberman/entities/character/enemy/Oneal.java +++ b/src/uet/oop/bomberman/entities/character/enemy/Oneal.java @@ -27,14 +27,14 @@ protected void chooseSprite() { case 0: case 1: if(_moving) - _sprite = Sprite.movingSprite(Sprite.oneal_right1, Sprite.oneal_right2, Sprite.oneal_right3, _animate, 60); + _sprite = Sprite.movingSprite(Sprite.oneal_right1, Sprite.oneal_right2, Sprite.oneal_right3, _animate, Game.TICKS_PER_SECOND); else _sprite = Sprite.oneal_left1; break; case 2: case 3: if(_moving) - _sprite = Sprite.movingSprite(Sprite.oneal_left1, Sprite.oneal_left2, Sprite.oneal_left3, _animate, 60); + _sprite = Sprite.movingSprite(Sprite.oneal_left1, Sprite.oneal_left2, Sprite.oneal_left3, _animate, Game.TICKS_PER_SECOND); else _sprite = Sprite.oneal_left1; break; diff --git a/src/uet/oop/bomberman/entities/character/enemy/ai/AIMedium.java b/src/uet/oop/bomberman/entities/character/enemy/ai/AIMedium.java index 1fa7960..17a7bfc 100644 --- a/src/uet/oop/bomberman/entities/character/enemy/ai/AIMedium.java +++ b/src/uet/oop/bomberman/entities/character/enemy/ai/AIMedium.java @@ -1,21 +1,21 @@ package uet.oop.bomberman.entities.character.enemy.ai; -import uet.oop.bomberman.entities.character.Bomber; +import uet.oop.bomberman.entities.character.Character; import uet.oop.bomberman.entities.character.enemy.Enemy; public class AIMedium extends AI { - Bomber _bomber; + Character player; Enemy _e; - public AIMedium(Bomber bomber, Enemy e) { - _bomber = bomber; + public AIMedium(Character player, Enemy e) { + this.player = player; _e = e; } @Override public int calculateDirection() { // TODO: cài đặt thuật toán tìm đường đi - if(_bomber == null) + if(player == null) return random.nextInt(4); int vertical = random.nextInt(2); @@ -37,18 +37,18 @@ public int calculateDirection() { } } protected int calculateColDirection() { - if(_bomber.getXTile() < _e.getXTile()) + if(player.getXTile() < _e.getXTile()) return 3; - else if(_bomber.getXTile() > _e.getXTile()) + else if(player.getXTile() > _e.getXTile()) return 1; return -1; } protected int calculateRowDirection() { - if(_bomber.getYTile() < _e.getYTile()) + if(player.getYTile() < _e.getYTile()) return 0; - else if(_bomber.getYTile() > _e.getYTile()) + else if(player.getYTile() > _e.getYTile()) return 2; return -1; } diff --git a/src/uet/oop/bomberman/entities/tile/Grass.java b/src/uet/oop/bomberman/entities/tile/Grass.java index 6046143..acd57c0 100644 --- a/src/uet/oop/bomberman/entities/tile/Grass.java +++ b/src/uet/oop/bomberman/entities/tile/Grass.java @@ -4,7 +4,7 @@ import uet.oop.bomberman.entities.Entity; import uet.oop.bomberman.graphics.Sprite; -public class Grass extends Tile { +public class Grass extends NonDestroyableTile { public Grass(int x, int y, Sprite sprite) { super(x, y, sprite); @@ -19,4 +19,9 @@ public Grass(int x, int y, Sprite sprite) { public boolean collide(Entity e) { return true; } + + @Override + public boolean canBePassedThroughBy(Entity other) { + return true; + } } diff --git a/src/uet/oop/bomberman/entities/tile/NonDestroyableTile.java b/src/uet/oop/bomberman/entities/tile/NonDestroyableTile.java new file mode 100644 index 0000000..53b7101 --- /dev/null +++ b/src/uet/oop/bomberman/entities/tile/NonDestroyableTile.java @@ -0,0 +1,16 @@ +package uet.oop.bomberman.entities.tile; + +import uet.oop.bomberman.graphics.Sprite; + +public abstract class NonDestroyableTile extends Tile { + + public NonDestroyableTile(int x, int y, Sprite sprite) { + super(x, y, sprite); + } + + @Override + public boolean isDestroyable() { + return false; + } + +} diff --git a/src/uet/oop/bomberman/entities/tile/Portal.java b/src/uet/oop/bomberman/entities/tile/Portal.java index 9b077d7..6a5d13e 100644 --- a/src/uet/oop/bomberman/entities/tile/Portal.java +++ b/src/uet/oop/bomberman/entities/tile/Portal.java @@ -1,40 +1,48 @@ package uet.oop.bomberman.entities.tile; import uet.oop.bomberman.Board; -import uet.oop.bomberman.Game; import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.character.Bomber; -import uet.oop.bomberman.entities.tile.item.Item; +import uet.oop.bomberman.entities.character.Character; import uet.oop.bomberman.graphics.Sprite; import uet.oop.bomberman.sound.Sound; -public class Portal extends Tile { - protected Board _board; - public Portal(int x, int y,Board board, Sprite sprite) { +public class Portal extends NonDestroyableTile { + protected Board _board; + + public Portal(int x, int y, Board board, Sprite sprite) { super(x, y, sprite); - _board = board; + _board = board; } - + @Override - public boolean collide(Entity e) {//xu li khi 2 entity va cham - //true cho di qua - //false khong cho di qua + public boolean collide(Entity e) {// xu li khi 2 entity va cham + // true cho di qua + // false khong cho di qua // TODO: xử lý khi Bomber đi vào - if(e instanceof Bomber ) { - - if(_board.detectNoEnemies() == false) + if (e instanceof Character && ((Character)e).isPlayer()) { + + if (canBePassedThroughBy(e)) { + _board.nextLevel(); + Sound.play("CRYST_UP"); + } + + } + + return true; + } + + @Override + public boolean canBePassedThroughBy(Entity other) { + if (other instanceof Character && ((Character)other).isPlayer()) { + + if (!_board.isEnemyCleared()) return false; - - if(e.getXTile() == getX() && e.getYTile() == getY()) { - if(_board.detectNoEnemies()){ - _board.nextLevel(); - Sound.play("CRYST_UP"); - } + + if (other.getXTile() == getX() && other.getYTile() == getY()) { + return true; } - - return true; + } - return true; } diff --git a/src/uet/oop/bomberman/entities/tile/Tile.java b/src/uet/oop/bomberman/entities/tile/Tile.java index 33f9272..f2036c9 100644 --- a/src/uet/oop/bomberman/entities/tile/Tile.java +++ b/src/uet/oop/bomberman/entities/tile/Tile.java @@ -26,6 +26,12 @@ public boolean collide(Entity e) { return false;//khong cho di qua } + + @Override + public boolean canBePassedThroughBy(Entity other) { + return false; + } + @Override public void render(Screen screen) { screen.renderEntity( Coordinates.tileToPixel(_x), Coordinates.tileToPixel(_y), this); @@ -33,4 +39,6 @@ public void render(Screen screen) { @Override public void update() {} + + public abstract boolean isDestroyable(); } diff --git a/src/uet/oop/bomberman/entities/tile/Wall.java b/src/uet/oop/bomberman/entities/tile/Wall.java index 3af3dad..4404dc1 100644 --- a/src/uet/oop/bomberman/entities/tile/Wall.java +++ b/src/uet/oop/bomberman/entities/tile/Wall.java @@ -3,7 +3,7 @@ import uet.oop.bomberman.graphics.Sprite; -public class Wall extends Tile { +public class Wall extends NonDestroyableTile { public Wall(int x, int y, Sprite sprite) { super(x, y, sprite); diff --git a/src/uet/oop/bomberman/entities/tile/destroyable/DestroyableTile.java b/src/uet/oop/bomberman/entities/tile/destroyable/DestroyableTile.java index 5148855..6c2683f 100644 --- a/src/uet/oop/bomberman/entities/tile/destroyable/DestroyableTile.java +++ b/src/uet/oop/bomberman/entities/tile/destroyable/DestroyableTile.java @@ -42,6 +42,11 @@ public boolean collide(Entity e) { return false; } + @Override + public boolean canBePassedThroughBy(Entity other) { + return false; + } + public void addBelowSprite(Sprite sprite) { _belowSprite = sprite; } @@ -59,5 +64,10 @@ protected Sprite movingSprite(Sprite normal, Sprite x1, Sprite x2) { return x2; } + + @Override + public boolean isDestroyable() { + return true; + } } diff --git a/src/uet/oop/bomberman/entities/tile/item/BombItem.java b/src/uet/oop/bomberman/entities/tile/item/BombItem.java index cfdceba..4413976 100644 --- a/src/uet/oop/bomberman/entities/tile/item/BombItem.java +++ b/src/uet/oop/bomberman/entities/tile/item/BombItem.java @@ -1,27 +1,27 @@ package uet.oop.bomberman.entities.tile.item; import uet.oop.bomberman.Game; -import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.character.Bomber; import uet.oop.bomberman.graphics.Sprite; -import uet.oop.bomberman.sound.Sound; public class BombItem extends Item { - public BombItem(int x, int y, Sprite sprite) { + public static final int BOMB_LIMIT_BONUS = 1; + + public BombItem(int x, int y, Sprite sprite) { super(x, y, sprite); } @Override - public boolean collide(Entity e) { - // TODO: xử lý Bomber ăn Item - if (e instanceof Bomber) { - - Sound.play("Item"); - Game.addBombRate(1); - remove(); - } - return false; + protected void handleItemActive() { + } + + @Override + protected void handleItemInactive() { + } + + @Override + public String getDisplayActiveItem() { + return "Bomb:"; } } diff --git a/src/uet/oop/bomberman/entities/tile/item/FlameItem.java b/src/uet/oop/bomberman/entities/tile/item/FlameItem.java index e251754..8e7f17a 100644 --- a/src/uet/oop/bomberman/entities/tile/item/FlameItem.java +++ b/src/uet/oop/bomberman/entities/tile/item/FlameItem.java @@ -1,26 +1,26 @@ package uet.oop.bomberman.entities.tile.item; import uet.oop.bomberman.Game; -import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.character.Bomber; import uet.oop.bomberman.graphics.Sprite; -import uet.oop.bomberman.sound.Sound; public class FlameItem extends Item { + public static final int BOMB_RADIUS_BONUS = 1; + public FlameItem(int x, int y, Sprite sprite) { super(x, y, sprite); } @Override - public boolean collide(Entity e) { - // TODO: xử lý Bomber ăn - if (e instanceof Bomber) { - - Sound.play("Item"); - Game.addBombRadius(1); - remove(); - } - return false; + protected void handleItemActive() { + } + + @Override + protected void handleItemInactive() { + } + + @Override + public String getDisplayActiveItem() { + return "Flame:"; } } diff --git a/src/uet/oop/bomberman/entities/tile/item/Item.java b/src/uet/oop/bomberman/entities/tile/item/Item.java index b3a1b8f..a028765 100644 --- a/src/uet/oop/bomberman/entities/tile/item/Item.java +++ b/src/uet/oop/bomberman/entities/tile/item/Item.java @@ -1,17 +1,64 @@ package uet.oop.bomberman.entities.tile.item; +import uet.oop.bomberman.Game; import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.character.Bomber; -import uet.oop.bomberman.entities.tile.Tile; +import uet.oop.bomberman.entities.character.Character; +import uet.oop.bomberman.entities.tile.NonDestroyableTile; import uet.oop.bomberman.graphics.Sprite; import uet.oop.bomberman.sound.Sound; -public abstract class Item extends Tile { - protected int _duration = -1; //thoi gian cua item ,-1 la vo han +public abstract class Item extends NonDestroyableTile { + protected int _duration = 30 * Game.TICKS_PER_SECOND; // 30s protected boolean _active = false; + protected int _level; + public Item(int x, int y, Sprite sprite) { super(x, y, sprite); } - + + protected abstract void handleItemActive(); + + protected abstract void handleItemInactive(); + + @Override + public boolean collide(Entity e) { + if (isRemoved()) return false; + if (e instanceof Character && ((Character)e).isPlayer()) { + Character player = (Character) e; + Sound.play("Item"); + handleItemActive(); + _active = true; + player.addActiveItem(this); + remove(); + } + return false; + } + + @Override + public boolean canBePassedThroughBy(Entity e) { + return (e instanceof Character && ((Character)e).isPlayer()); + } + + @Override + public void update() { + if (!_active) + return; + if (_duration > 0) { + _duration--; + } else { + handleItemInactive(); + _active = false; + } + } + + public int getDuration() { + return _duration; + } + + public abstract String getDisplayActiveItem(); + + public boolean isActive() { + return _active; + } } diff --git a/src/uet/oop/bomberman/entities/tile/item/SpeedItem.java b/src/uet/oop/bomberman/entities/tile/item/SpeedItem.java index 7bc50b7..9094772 100644 --- a/src/uet/oop/bomberman/entities/tile/item/SpeedItem.java +++ b/src/uet/oop/bomberman/entities/tile/item/SpeedItem.java @@ -1,26 +1,26 @@ package uet.oop.bomberman.entities.tile.item; import uet.oop.bomberman.Game; -import uet.oop.bomberman.entities.Entity; -import uet.oop.bomberman.entities.character.Bomber; import uet.oop.bomberman.graphics.Sprite; -import uet.oop.bomberman.sound.Sound; public class SpeedItem extends Item { + public static final double SPEED_MULTIPLIER = 0.5; + public SpeedItem(int x, int y, Sprite sprite) { super(x, y, sprite); } @Override - public boolean collide(Entity e) { - // TODO: xử lý Bomber ăn Item - if (e instanceof Bomber) { - - Sound.play("Item"); - Game.addBomberSpeed(0.5); - remove(); - } - return false; + protected void handleItemActive() { + } + + @Override + protected void handleItemInactive() { + } + + @Override + public String getDisplayActiveItem() { + return "Speed:"; } } diff --git a/src/uet/oop/bomberman/graphics/Screen.java b/src/uet/oop/bomberman/graphics/Screen.java index 2494ea2..d59f3c5 100644 --- a/src/uet/oop/bomberman/graphics/Screen.java +++ b/src/uet/oop/bomberman/graphics/Screen.java @@ -4,6 +4,7 @@ import uet.oop.bomberman.Game; import uet.oop.bomberman.entities.Entity; import uet.oop.bomberman.entities.character.Bomber; +import uet.oop.bomberman.utils.Global; import java.awt.*; @@ -22,15 +23,10 @@ public Screen(int width, int height) { _height = height; _pixels = new int[width * height]; - - } - - public void clear() { - for (int i = 0; i < _pixels.length; i++) { - _pixels[i] = 0; - } + + Global.screenWidth = this.getRealWidth(); + Global.screenHeight = this.getRealHeight(); } - public void renderEntity(int xp, int yp, Entity entity) { //save entity pixels xp -= xOffset; yp -= yOffset; @@ -63,23 +59,27 @@ public void renderEntityWithBelowSprite(int xp, int yp, Entity entity, Sprite be } } } - + public void clear() { + for (int i = 0; i < _pixels.length; i++) { + _pixels[i] = 0; + } + } public static void setOffset(int xO, int yO) { xOffset = xO; yOffset = yO; } - public static int calculateXOffset(Board board, Bomber bomber) { - if(bomber == null) return 0; + public static int calculateXOffset(Board board, Entity entity) { + if(entity == null) return 0; int temp = xOffset; - double BomberX = bomber.getX() / 16; + double x = entity.getX() / 16; double complement = 0.5; int firstBreakpoint = board.getWidth() / 4; int lastBreakpoint = board.getWidth() - firstBreakpoint; - if( BomberX > firstBreakpoint + complement && BomberX < lastBreakpoint - complement) { - temp = (int)bomber.getX() - (Game.WIDTH / 2); + if( x > firstBreakpoint + complement && x < lastBreakpoint - complement) { + temp = (int)entity.getX() - (Game.WIDTH / 2); } return temp; diff --git a/src/uet/oop/bomberman/gui/Frame.java b/src/uet/oop/bomberman/gui/Frame.java index 1a331e4..99a635b 100644 --- a/src/uet/oop/bomberman/gui/Frame.java +++ b/src/uet/oop/bomberman/gui/Frame.java @@ -20,7 +20,7 @@ public Frame() { _containerpane = new JPanel(new BorderLayout()); _gamepane = new GamePanel(this); - _infopanel = new InfoPanel(_gamepane.getGame()); + _infopanel = new InfoPanel(_gamepane.getGame().getGameInfoManager()); _containerpane.add(_infopanel, BorderLayout.PAGE_START); _containerpane.add(_gamepane, BorderLayout.PAGE_END); @@ -45,5 +45,9 @@ public void setTime(int time) { public void setPoints(int points) { _infopanel.setPoints(points); } + + public void renderItemTime() { + _infopanel.renderItemTime(); + } } diff --git a/src/uet/oop/bomberman/gui/GameScreen.java b/src/uet/oop/bomberman/gui/GameScreen.java new file mode 100644 index 0000000..a473496 --- /dev/null +++ b/src/uet/oop/bomberman/gui/GameScreen.java @@ -0,0 +1,9 @@ +package uet.oop.bomberman.gui; + +import java.awt.*; + +public abstract class GameScreen { + public abstract void drawScreen(Graphics g); + public abstract void update(); + public abstract void onDestroy(); +} diff --git a/src/uet/oop/bomberman/gui/InfoPanel.java b/src/uet/oop/bomberman/gui/InfoPanel.java index de01310..b6eb091 100644 --- a/src/uet/oop/bomberman/gui/InfoPanel.java +++ b/src/uet/oop/bomberman/gui/InfoPanel.java @@ -1,36 +1,45 @@ package uet.oop.bomberman.gui; - import uet.oop.bomberman.Game; +import uet.oop.bomberman.base.IGameInfoManager; +import uet.oop.bomberman.entities.tile.item.Item; import javax.swing.*; import java.awt.*; +import java.util.List; /** * Swing Panel hiển thị thông tin thời gian, điểm mà người chơi đạt được */ public class InfoPanel extends JPanel { - private JLabel timeLabel; private JLabel pointsLabel; + private JLabel itemTimeLabel; + + private final IGameInfoManager gameInfoManager; - public InfoPanel(Game game) { + public InfoPanel(IGameInfoManager gameInfoManager) { + this.gameInfoManager = gameInfoManager; setLayout(new GridLayout()); - - timeLabel = new JLabel("Time: " + game.getBoard().getTime()); + timeLabel = new JLabel("Time: " + gameInfoManager.getTime()); timeLabel.setForeground(Color.white); timeLabel.setHorizontalAlignment(JLabel.CENTER); - - pointsLabel = new JLabel("Points: " + game.getBoard().getPoints()); + + pointsLabel = new JLabel("Points: " + gameInfoManager.getPoints()); pointsLabel.setForeground(Color.white); pointsLabel.setHorizontalAlignment(JLabel.CENTER); - + + itemTimeLabel = new JLabel(""); + itemTimeLabel.setForeground(Color.white); + itemTimeLabel.setHorizontalAlignment(JLabel.LEFT); + itemTimeLabel.setBorder(BorderFactory.createEmptyBorder(0, 40, 0, 0)); + + add(itemTimeLabel); add(timeLabel); add(pointsLabel); - setBackground(Color.black); setPreferredSize(new Dimension(0, 40)); } - + public void setTime(int t) { timeLabel.setText("Time: " + t); } @@ -38,5 +47,18 @@ public void setTime(int t) { public void setPoints(int t) { pointsLabel.setText("Score: " + t); } - + + public void renderItemTime() { + String label = ""; + List items = gameInfoManager.getActiveItems(); + for (int i = 0; i < items.size(); i++) { + Item item = items.get(i); + if ((item.getDuration()) == 0) { + continue; + } + label += item.getDisplayActiveItem() + item.getDuration() / Game.TICKS_PER_SECOND + " "; + } + itemTimeLabel.setText(label); + } + } diff --git a/src/uet/oop/bomberman/input/Keyboard.java b/src/uet/oop/bomberman/input/Keyboard.java index bb8daad..ac42490 100644 --- a/src/uet/oop/bomberman/input/Keyboard.java +++ b/src/uet/oop/bomberman/input/Keyboard.java @@ -1,37 +1,95 @@ package uet.oop.bomberman.input; +import uet.oop.bomberman.utils.EGameControl; + import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.util.Optional; /** * Tiếp nhận và xử lý các sự kiện nhập từ bàn phím */ public class Keyboard implements KeyListener { - - private boolean[] keys = new boolean[120]; //120 is enough to this game - public boolean up, down, left, right, space; - + + public interface KeyboardInputCallback { + void onKeyPressed(EGameControl gameControl); + } + + private boolean[] keys = new boolean[65536]; + public boolean up, down, left, right, space, pause, resume; + public boolean player1_up, player2_up, player1_down, player2_down, player1_left, player2_left, player1_right, + player2_right, player1_space, player2_space; + public Optional keyboardInputCallback; + public void update() { up = keys[KeyEvent.VK_UP] || keys[KeyEvent.VK_W]; down = keys[KeyEvent.VK_DOWN] || keys[KeyEvent.VK_S]; left = keys[KeyEvent.VK_LEFT] || keys[KeyEvent.VK_A]; right = keys[KeyEvent.VK_RIGHT] || keys[KeyEvent.VK_D]; space = keys[KeyEvent.VK_SPACE] || keys[KeyEvent.VK_X]; + + player1_up = keys[KeyEvent.VK_UP]; + player1_down = keys[KeyEvent.VK_DOWN]; + player1_left = keys[KeyEvent.VK_LEFT]; + player1_right = keys[KeyEvent.VK_RIGHT]; + player1_space = keys[KeyEvent.VK_SPACE]; + + player2_up = keys[KeyEvent.VK_W]; + player2_down = keys[KeyEvent.VK_S]; + player2_left = keys[KeyEvent.VK_A]; + player2_right = keys[KeyEvent.VK_D]; + player2_space = keys[KeyEvent.VK_X]; + + pause = keys[KeyEvent.VK_ESCAPE]; + resume = keys[KeyEvent.VK_ENTER]; + } + + private EGameControl keyToGameControl(int keyCode) { + if (keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_W) { + return EGameControl.UP; + } + + if (keyCode == KeyEvent.VK_DOWN || keyCode == KeyEvent.VK_S) { + return EGameControl.DOWN; + } + + if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_A) { + return EGameControl.LEFT; + } + + if (keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_D) { + return EGameControl.RIGHT; + } + + if (keyCode == KeyEvent.VK_SPACE || keyCode == KeyEvent.VK_X) { + return EGameControl.SPACE; + } + + if (keyCode == KeyEvent.VK_ENTER) { + return EGameControl.ENTER; + } + + return EGameControl.NONE; } @Override - public void keyTyped(KeyEvent e) {} + public void keyTyped(KeyEvent e) { + } @Override public void keyPressed(KeyEvent e) { keys[e.getKeyCode()] = true; - + + if (keyboardInputCallback.isPresent()) { + KeyboardInputCallback callback = keyboardInputCallback.get(); + callback.onKeyPressed(keyToGameControl(e.getKeyCode())); + } } @Override public void keyReleased(KeyEvent e) { keys[e.getKeyCode()] = false; - + } } diff --git a/src/uet/oop/bomberman/level/FileLevelLoader.java b/src/uet/oop/bomberman/level/FileLevelLoader.java index 3432f1e..da2285c 100644 --- a/src/uet/oop/bomberman/level/FileLevelLoader.java +++ b/src/uet/oop/bomberman/level/FileLevelLoader.java @@ -40,13 +40,13 @@ public void loadLevel(int level) { // TODO: cập nhật các giá trị đọc được vào _width, _height, _level, _map List list = new ArrayList<>(); try { - FileReader fr = new FileReader("res\\levels\\Level" + level + ".txt");//doc tep luu map + FileReader fr = new FileReader("res/levels/Level" + level + ".txt");// doc tep luu map BufferedReader br = new BufferedReader(fr); String line = br.readLine(); while (!line.equals("")) { list.add(line); line = br.readLine(); - //doc file txt luu vao list + // doc file txt luu vao list } } catch (Exception e) { e.printStackTrace(); @@ -61,7 +61,7 @@ public void loadLevel(int level) { _map[i][j] = list.get(i + 1).charAt(j); } } - //gan cac phan tu cho mang + // gan cac phan tu cho mang } @Override @@ -96,34 +96,57 @@ public void createEntities() { _board.addEntity(x + y * _width, new LayeredEntity(x, y, new Grass(x, y, Sprite.grass), - new Brick(x, y, Sprite.brick) - ) - ); + new Brick(x, y, Sprite.brick))); break; - // Thêm Bomber + // Thêm Bomber player case 'p': - _board.addCharacter(new Bomber(Coordinates.tileToPixel(x), Coordinates.tileToPixel(y) + Game.TILES_SIZE, _board)); + Bomber bomber = new Bomber( + Coordinates.tileToPixel(x), + Coordinates.tileToPixel(y) + Game.TILES_SIZE, + Game.BOMBERSPEED, + Game.BOMBRATE, + Game.BOMBRADIUS, + _board, + _board); + _board.addCharacter(bomber); + _board.setPlayer(bomber); + Screen.setOffset(0, 0); + _board.addEntity(x + y * _width, new Grass(x, y, Sprite.grass)); + break; + case 'a': + Bomber bomber2 = new Bomber( + Coordinates.tileToPixel(x), + Coordinates.tileToPixel(y) + Game.TILES_SIZE, + Game.BOMBERSPEED, + Game.BOMBRATE, + Game.BOMBRADIUS, + _board, + _board); + _board.addCharacter(bomber2); + _board.setPlayer2(bomber2); Screen.setOffset(0, 0); _board.addEntity(x + y * _width, new Grass(x, y, Sprite.grass)); break; - // Thêm balloon case '1': - _board.addCharacter(new Balloon(Coordinates.tileToPixel(x), Coordinates.tileToPixel(y) + Game.TILES_SIZE, _board)); + _board.addCharacter(new Balloon(Coordinates.tileToPixel(x), + Coordinates.tileToPixel(y) + Game.TILES_SIZE, _board)); _board.addEntity(x + y * _width, new Grass(x, y, Sprite.grass)); break; // Thêm oneal case '2': - _board.addCharacter(new Oneal(Coordinates.tileToPixel(x), Coordinates.tileToPixel(y) + Game.TILES_SIZE, _board)); + _board.addCharacter(new Oneal(Coordinates.tileToPixel(x), + Coordinates.tileToPixel(y) + Game.TILES_SIZE, _board)); _board.addEntity(pos, new Grass(x, y, Sprite.grass)); break; // Thêm doll case '3': - _board.addCharacter(new Doll(Coordinates.tileToPixel(x), Coordinates.tileToPixel(y) + Game.TILES_SIZE, _board)); + _board.addCharacter(new Doll(Coordinates.tileToPixel(x), + Coordinates.tileToPixel(y) + Game.TILES_SIZE, _board)); _board.addEntity(x + y * _width, new Grass(x, y, Sprite.grass)); break; // Thêm oneal - // Thêm BomItem + // Thêm BomItem case 'b': LayeredEntity layer = new LayeredEntity(x, y, new Grass(x, y, Sprite.grass), diff --git a/src/uet/oop/bomberman/screen/SelectGameModeScreen.java b/src/uet/oop/bomberman/screen/SelectGameModeScreen.java new file mode 100644 index 0000000..fce9572 --- /dev/null +++ b/src/uet/oop/bomberman/screen/SelectGameModeScreen.java @@ -0,0 +1,124 @@ +package uet.oop.bomberman.screen; + +import uet.oop.bomberman.Game; +import uet.oop.bomberman.gui.GameScreen; +import uet.oop.bomberman.input.Keyboard; +import uet.oop.bomberman.utils.*; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Optional; + +public class SelectGameModeScreen extends GameScreen { + ArrayList gameModes = new ArrayList(); + int selectorIndex = 0; + private Optional _input; + + public SelectGameModeScreen() { + gameModes.add(EGameMode.ONE_PLAYER.getStringLevel()); + gameModes.add(EGameMode.TWO_PLAYER.getStringLevel()); + } + + public void setInput(Keyboard input) { + _input = Optional.ofNullable(input); + + _input.get().keyboardInputCallback = Optional.of(new Keyboard.KeyboardInputCallback() { + @Override + public void onKeyPressed(EGameControl gameControl) { + switch (gameControl) { + case UP: + selectorIndex--; + break; + case DOWN: + selectorIndex++; + break; + case ENTER: + Global.currentScreen = EScreenName.SELECT_LEVEL_SCREEN; + if (selectorIndex == 1) { + Global.gameMode = EGameMode.TWO_PLAYER; + } else { + Global.gameMode = EGameMode.ONE_PLAYER; + } + break; + } + + if (selectorIndex < 0) { + selectorIndex = gameModes.size() - 1; + } else if (selectorIndex > gameModes.size() - 1) { + selectorIndex = 0; + } + } + }); + } + + @Override + public void drawScreen(Graphics g) { + // set background + g.setColor(Color.black); + g.fillRect(0, 0, Global.screenWidth, Global.screenHeight); + + drawTitle(g); + drawOptions(g); + drawSelector(g); + } + + private void drawTitle(Graphics g) { + String title = "SELECT GAME MODE"; + Font font = new Font("Arial", Font.BOLD, 20 * Game.SCALE); + g.setFont(font); + g.setColor(Color.white); + + FontMetrics fm = g.getFontMetrics(); + int x = (Global.screenWidth - fm.stringWidth(title)) / 2; + int marginTop = 20; + int y = marginTop + fm.getAscent(); + + g.drawString(title, x, y); + } + + private void drawOptions(Graphics g) { + Font font = new Font("Arial", Font.PLAIN, 10 * Game.SCALE); + g.setFont(font); + g.setColor(Color.white); + + int w = Global.screenWidth; + int h = Global.screenHeight; + FontMetrics fm = g.getFontMetrics(); + int textHeight = fm.getAscent() + fm.getDescent(); + int boxHeight = textHeight * this.gameModes.size(); + int marginTop = (h - boxHeight) / 2; + + for (int i=0; i < this.gameModes.size(); i++) { + String level = this.gameModes.get(i); + int x = (w - fm.stringWidth(level)) / 2; + int y = marginTop + fm.getAscent() + textHeight*i; + + g.drawString(level, x, y); + } + } + + private void drawSelector(Graphics g) { + String level = this.gameModes.get(selectorIndex); + int w = Global.screenWidth; + int h = Global.screenHeight; + FontMetrics fm = g.getFontMetrics(); + int textHeight = fm.getAscent() + fm.getDescent(); + int boxHeight = textHeight * this.gameModes.size(); + int marginTop = (h - boxHeight) / 2; + + int x = (w - fm.stringWidth(level)) / 2 - 30; + int y = marginTop + fm.getAscent() + textHeight*selectorIndex; + + g.drawString(">", x, y); + } + + @Override + public void update() { + + } + + @Override + public void onDestroy() { + this._input = null; + } +} diff --git a/src/uet/oop/bomberman/screen/SelectLevelScreen.java b/src/uet/oop/bomberman/screen/SelectLevelScreen.java new file mode 100644 index 0000000..716c860 --- /dev/null +++ b/src/uet/oop/bomberman/screen/SelectLevelScreen.java @@ -0,0 +1,128 @@ +package uet.oop.bomberman.screen; + +import uet.oop.bomberman.Board; +import uet.oop.bomberman.Game; +import uet.oop.bomberman.gui.GameScreen; +import uet.oop.bomberman.input.Keyboard; +import uet.oop.bomberman.utils.EGameControl; +import uet.oop.bomberman.utils.EGameLevel; +import uet.oop.bomberman.utils.EScreenName; +import uet.oop.bomberman.utils.Global; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Optional; + +public class SelectLevelScreen extends GameScreen { + ArrayList levels = new ArrayList(); + int selectorIndex = 0; + private Optional _input; + private Board _board; + + public SelectLevelScreen(Board board) { + _board = board; + + levels.add(EGameLevel.EASY.getStringLevel()); + levels.add(EGameLevel.MEDIUM.getStringLevel()); + levels.add(EGameLevel.HARD.getStringLevel()); + } + + public void setInput(Keyboard input) { + _input = Optional.ofNullable(input); + + _input.get().keyboardInputCallback = java.util.Optional.of(new Keyboard.KeyboardInputCallback() { + @Override + public void onKeyPressed(EGameControl gameControl) { + switch (gameControl) { + case UP: + selectorIndex--; + break; + case DOWN: + selectorIndex++; + break; + case ENTER: + Global.currentScreen = EScreenName.GAME_PLAY_SCREEN; + Global.gameLevel = selectorIndex + 1; + _board.loadLevel(Global.gameLevel); + onDestroy(); + break; + } + + if (selectorIndex < 0) { + selectorIndex = levels.size() - 1; + } else if (selectorIndex > levels.size() - 1) { + selectorIndex = 0; + } + } + }); + } + + @Override + public void drawScreen(Graphics g) { + // set background + g.setColor(Color.black); + g.fillRect(0, 0, Global.screenWidth, Global.screenHeight); + + drawTitle(g); + drawOptions(g); + drawSelector(g); + } + + private void drawTitle(Graphics g) { + String title = "SELECT LEVEL"; + Font font = new Font("Arial", Font.BOLD, 20 * Game.SCALE); + g.setFont(font); + g.setColor(Color.white); + + FontMetrics fm = g.getFontMetrics(); + int x = (Global.screenWidth - fm.stringWidth(title)) / 2; + int marginTop = 20; + int y = marginTop + fm.getAscent(); + + g.drawString(title, x, y); + } + + private void drawOptions(Graphics g) { + Font font = new Font("Arial", Font.PLAIN, 10 * Game.SCALE); + g.setFont(font); + g.setColor(Color.white); + + int w = Global.screenWidth; + int h = Global.screenHeight; + FontMetrics fm = g.getFontMetrics(); + int textHeight = fm.getAscent() + fm.getDescent(); + int boxHeight = textHeight * this.levels.size(); + int marginTop = (h - boxHeight) / 2; + + for (int i=0; i < this.levels.size(); i++) { + String level = this.levels.get(i); + int x = (w - fm.stringWidth(level)) / 2; + int y = marginTop + fm.getAscent() + textHeight*i; + + g.drawString(level, x, y); + } + } + + private void drawSelector(Graphics g) { + String level = this.levels.get(selectorIndex); + int w = Global.screenWidth; + int h = Global.screenHeight; + FontMetrics fm = g.getFontMetrics(); + int textHeight = fm.getAscent() + fm.getDescent(); + int boxHeight = textHeight * this.levels.size(); + int marginTop = (h - boxHeight) / 2; + + int x = (w - fm.stringWidth(level)) / 2 - 30; + int y = marginTop + fm.getAscent() + textHeight*selectorIndex; + + g.drawString(">", x, y); + } + + @Override + public void update() {} + + @Override + public void onDestroy() { + this._input = null; + } +} diff --git a/src/uet/oop/bomberman/sound/Sound.java b/src/uet/oop/bomberman/sound/Sound.java index efafdc1..1993f37 100644 --- a/src/uet/oop/bomberman/sound/Sound.java +++ b/src/uet/oop/bomberman/sound/Sound.java @@ -18,7 +18,7 @@ public void run() { clip.open(inputStream); clip.start(); } catch (Exception e) { - System.err.println(e.getMessage()); + System.err.println("Failed playing sound '" + sound + "': " + e.getMessage()); } } }).start(); @@ -34,7 +34,7 @@ public void run() { clip.open(inputStream); clip.stop(); } catch (Exception e) { - System.err.println(e.getMessage()); + System.err.println("Failed stopping sound '" + sound + "': " + e.getMessage()); } } }).start(); diff --git a/src/uet/oop/bomberman/utils/EGameControl.java b/src/uet/oop/bomberman/utils/EGameControl.java new file mode 100644 index 0000000..fa8cea4 --- /dev/null +++ b/src/uet/oop/bomberman/utils/EGameControl.java @@ -0,0 +1,11 @@ +package uet.oop.bomberman.utils; + +public enum EGameControl { + NONE, + UP, + DOWN, + LEFT, + RIGHT, + ENTER, + SPACE, +} diff --git a/src/uet/oop/bomberman/utils/EGameLevel.java b/src/uet/oop/bomberman/utils/EGameLevel.java new file mode 100644 index 0000000..7644772 --- /dev/null +++ b/src/uet/oop/bomberman/utils/EGameLevel.java @@ -0,0 +1,17 @@ +package uet.oop.bomberman.utils; + +public enum EGameLevel { + EASY("Easy"), + MEDIUM("Medium"), + HARD("Hard"); + + private final String level; + + EGameLevel(String level) { + this.level = level; + } + + public String getStringLevel() { + return this.level; + } +} diff --git a/src/uet/oop/bomberman/utils/EGameMode.java b/src/uet/oop/bomberman/utils/EGameMode.java new file mode 100644 index 0000000..9efd976 --- /dev/null +++ b/src/uet/oop/bomberman/utils/EGameMode.java @@ -0,0 +1,16 @@ +package uet.oop.bomberman.utils; + +public enum EGameMode { + ONE_PLAYER("1 Player"), + TWO_PLAYER("2 Players"); + + private final String mode; + + EGameMode(String level) { + this.mode = level; + } + + public String getStringLevel() { + return this.mode; + } +} diff --git a/src/uet/oop/bomberman/utils/EScreenName.java b/src/uet/oop/bomberman/utils/EScreenName.java new file mode 100644 index 0000000..be8c6be --- /dev/null +++ b/src/uet/oop/bomberman/utils/EScreenName.java @@ -0,0 +1,7 @@ +package uet.oop.bomberman.utils; + +public enum EScreenName { + SELECT_GAME_MODE, + SELECT_LEVEL_SCREEN, + GAME_PLAY_SCREEN, +} diff --git a/src/uet/oop/bomberman/utils/Global.java b/src/uet/oop/bomberman/utils/Global.java new file mode 100644 index 0000000..ff4616e --- /dev/null +++ b/src/uet/oop/bomberman/utils/Global.java @@ -0,0 +1,13 @@ +package uet.oop.bomberman.utils; + +public class Global { + public static int screenWidth; + public static int screenHeight; + + public static EScreenName currentScreen = EScreenName.SELECT_GAME_MODE; + public static EScreenName previousScreen = EScreenName.GAME_PLAY_SCREEN; + + // GAME PLAY + public static int gameLevel = 1; + public static EGameMode gameMode; +}