diff --git a/.gitignore b/.gitignore index a4088bc..121f580 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ hs_err_pid* # .DStore files *.DS_Store +.idea +lol2D.iml +lib diff --git a/src/main/java/lol/game/Arena.java b/src/main/java/lol/game/Arena.java index 8435bb3..47a820f 100644 --- a/src/main/java/lol/game/Arena.java +++ b/src/main/java/lol/game/Arena.java @@ -5,6 +5,7 @@ import java.net.*; import lol.common.*; import lol.game.action.*; +import java.util.function.*; public class Arena { private ArrayList teams; @@ -42,21 +43,24 @@ public ApplyAction() { championsWhoActed = new ArrayList<>(); } - public void visitSpawn(int teamID, int championID, int x, int y) { - if(!championsWhoActed.contains(championID)) { - if(teams.get(teamID).spawnChampion(championID, x, y)) { - championsWhoActed.add(championID); + private void tryExecuteChampionAction(int teamID, int championID, Predicate action) { + int uniqueChampionID = teamID * 1000 + championID; + if(!championsWhoActed.contains(uniqueChampionID)) { + if(action.test(teams.get(teamID))) { + championsWhoActed.add(uniqueChampionID); } } } + public void visitSpawn(int teamID, int championID, int x, int y) { + tryExecuteChampionAction(teamID, championID, + (team) -> team.spawnChampion(championID, x, y)); + } + public void visitMove(int teamID, int championID, int x, int y) { if(phase == Phase.GAME) { - if(!championsWhoActed.contains(championID)) { - if(teams.get(teamID).moveChampion(championID, x, y)) { - championsWhoActed.add(championID); - } - } + tryExecuteChampionAction(teamID, championID, + (team) -> team.moveChampion(championID, x, y)); } else { System.out.println("Team " + teamID + " tried to move a champion outside a GAME phase."); @@ -65,11 +69,8 @@ public void visitMove(int teamID, int championID, int x, int y) { public void visitAttack(int teamID, int championID, int x, int y) { if(phase == Phase.GAME) { - if(!championsWhoActed.contains(championID)) { - if(teams.get(teamID).championAttack(championID, x, y)) { - championsWhoActed.add(championID); - } - } + tryExecuteChampionAction(teamID, championID, + (team) -> team.championAttack(championID, x, y)); } else { System.out.println("Team " + teamID + " tried to move a champion outside a GAME phase."); diff --git a/src/main/java/lol/game/Attacker.java b/src/main/java/lol/game/Attacker.java index 60503ce..2675302 100644 --- a/src/main/java/lol/game/Attacker.java +++ b/src/main/java/lol/game/Attacker.java @@ -3,41 +3,32 @@ // class is abstract, since it should never be used alone public abstract class Attacker extends Destructible { - private int rangeOfAttack; - private int damages; - - public Attacker(int hp, int rangeOfAttack, int damages) { - super(hp); - this.rangeOfAttack = rangeOfAttack; - this.damages = damages; - } - - public int attackRange() { - return rangeOfAttack; - } - - public int damages() { - return damages; - } - - public void boostDamages(int boost) { - this.damages += boost; - } - - public boolean attack(Destructible d) { - if(canAttack(d.x(), d.y())) { - d.hit(damages); - return true; - } - return false; - } - - protected int distanceFrom(int toX, int toY) { - return Math.max(Math.abs(toX - x()), Math.abs(toY - y())); + private int rangeOfAttack; + private int damages; + + public Attacker(int hp, int rangeOfAttack, int damages) { + super(hp); + this.rangeOfAttack = rangeOfAttack; + this.damages = damages; + } + + public int attackRange() { + return rangeOfAttack; + } + + public boolean attack(Destructible d, Battlefield battlefield, BattlefieldTraversal traversal) { + if(canAttack(d.x(), d.y(), battlefield, traversal)) { + d.hit(damages); + return true; } + return false; + } + protected int distanceFrom(int toX, int toY) { + return Math.max(Math.abs(toX - x()), Math.abs(toY - y())); + } - public boolean canAttack(int toX, int toY) { - return distanceFrom(toX, toY) <= rangeOfAttack; - } -} + public boolean canAttack(int toX, int toY, Battlefield battlefield, BattlefieldTraversal traversal) { + return distanceFrom(toX, toY) <= rangeOfAttack && traversal.noObstacles(x(), y(),toX,toY, battlefield); + } +} \ No newline at end of file diff --git a/src/main/java/lol/game/Battlefield.java b/src/main/java/lol/game/Battlefield.java index e1a36db..118fcd7 100644 --- a/src/main/java/lol/game/Battlefield.java +++ b/src/main/java/lol/game/Battlefield.java @@ -129,6 +129,7 @@ public boolean placeAt(Destructible d, int x, int y) { return false; } + // Move a destructible object on the battlefield if `placeAt(d, x, y)` succeeds. public boolean moveTo(Destructible d, int x, int y) { int oldX = d.x(); diff --git a/src/main/java/lol/game/BattlefieldTraversal.java b/src/main/java/lol/game/BattlefieldTraversal.java index d07fb87..52f548e 100644 --- a/src/main/java/lol/game/BattlefieldTraversal.java +++ b/src/main/java/lol/game/BattlefieldTraversal.java @@ -45,4 +45,121 @@ public void visitAdjacent(int x, int y, int radius, TileVisitor visitor) { } } } -} \ No newline at end of file + + public boolean noObstacles(int fromX, int fromY, int toX, int toY, Battlefield battlefield) { + int dx = Math.abs(toX - fromX); + int dy = Math.abs(toY - fromY); + if(Math.max(dy, dx) <= 1 ){ + return true; + } + else if(dy == dx){ + if(!diagonalCheck(fromX, fromY, toX, toY, battlefield)) { + System.out.println("Obstacle on the way to the target of pos "+ toX +" "+ toY +" ! Can't shoot."); + return false; + } + } + else if(fromY == toY){ + if(!verticalCheck(fromX, fromY, toX, toY, battlefield)) { + System.out.println("Obstacle on the way to the target of pos "+ toX +" "+ toY +" ! Can't shoot."); + return false; + } + } + else if(fromX == toX){ + if(!horizontalCheck(fromX, fromY, toX, toY, battlefield)) { + System.out.println("Obstacle on the way to the target of pos "+ toX +" "+ toY +" ! Can't shoot."); + return false; + } + } + return true; + } + + + public boolean diagonalCheck(int x, int y, int toX, int toY, Battlefield battlefield) { + System.out.println(x); + System.out.println(y); + System.out.println("------------------------"); + if(x < toX && y < toY) { + for(int i = x; i < toX; i++) { + for(int j = y; j < toY; j++) { + if(!battlefield.canPlaceAt(i, j)) { + return false; + } + } + } + } + else if(x < toX && y > toY) { + for(int i = x; i < toX; i++) { + for(int j = y; j > toY; j--) { + if(!battlefield.canPlaceAt(i, j)) { + return false; + } + } + } + } + else if(x > toX && y > toY) { + for(int i = x; i > toX; i--) { + for(int j = y; j > toY; j--) { + if(!battlefield.canPlaceAt(i, j)) { + return false; + } + } + } + } + else if(x > toX && y < toY) { + for(int i = x; i > toX; i--) { + for(int j = y; j < toY; j++) { + if(!battlefield.canPlaceAt(i, j)) { + return false; + } + } + } + } + return true; + } + + // - Case when y = toY + public boolean verticalCheck(int x, int y, int toX, int toY, Battlefield battlefield) { + System.out.println(x); + System.out.println(y); + System.out.println("------------------------"); + if( x>toX ) { + for(int i = x; i > toX; i--){ + if(!battlefield.canPlaceAt(i, y)){ + return false; + } + } + } + else if( x < toX) { + for(int i = x; i > toX; i++){ + if(!battlefield.canPlaceAt(i, y)){ + return false; + } + } + } + return true; + } + + // - Case when x = toX + public boolean horizontalCheck(int x, int y, int toX, int toY, Battlefield battlefield) { + System.out.println(x); + System.out.println(y); + System.out.println("------------------------"); + if( y>=toY ) { + for(int j = y; j > toY; j--){ + if(!battlefield.canPlaceAt(x, j)){ + System.out.println("Obstacle on the way to the target of pos "+ toX +" "+ toY +" ! Can't shoot."); + return false; + } + } + } + else if( y <= toY) { + for(int j = y; j < toY; j++){ + System.out.println("Obstacle on the way to the target of pos "+ toX +" "+ toY +" ! Can't shoot."); + if(!battlefield.canPlaceAt(x, j)){ + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/lol/game/Team.java b/src/main/java/lol/game/Team.java index 245b8f4..3d2325c 100644 --- a/src/main/java/lol/game/Team.java +++ b/src/main/java/lol/game/Team.java @@ -63,7 +63,7 @@ public boolean championAttack(int championID, int x, int y) { boolean[] attacked = {false}; battlefield.visit(x, y, new TileVisitor(){ @Override public void visitDestructible(Destructible d) { - attacked[0] = champion.attack(d); + attacked[0] = champion.attack(d, battlefield, traversal); sound.attackSound(champion.name()); if(d.isDead()) { battlefield.destroy(d); diff --git a/src/main/java/lol/server/Server.java b/src/main/java/lol/server/Server.java index 66c6b67..8e07c1d 100644 --- a/src/main/java/lol/server/Server.java +++ b/src/main/java/lol/server/Server.java @@ -14,6 +14,7 @@ public class Server implements Runnable { private Arena arena; private LOL2D ui; private Battlefield battlefield; + private int MILLISECOND_PER_TURN = 2000; public Server(LOL2D ui, Battlefield battlefield) { this.ui = ui; @@ -52,7 +53,7 @@ private void startGame() throws IOException { spawnPhase(); arena.startGamePhase(); ui.update(); - wait(2000); + wait(MILLISECOND_PER_TURN); } private void wait(int timeInMS) { @@ -82,7 +83,7 @@ private void gameLoop() throws IOException { arena.applyTurn(turn); ui.update(); broadcast(turn); - wait(2000); + wait(MILLISECOND_PER_TURN); } } } diff --git a/src/main/java/lol/ui/BattlefieldView.java b/src/main/java/lol/ui/BattlefieldView.java index e17e42c..a31790f 100644 --- a/src/main/java/lol/ui/BattlefieldView.java +++ b/src/main/java/lol/ui/BattlefieldView.java @@ -21,6 +21,7 @@ public class BattlefieldView implements TileVisitor BattlefieldTraversal battlefieldTraversal; Sprites sprites; TilePane tiles; + TilePane wt = new TilePane(); Stage stage; Scene scene; @@ -44,7 +45,8 @@ public void update() { initTilePane(); battlefieldTraversal.visitFullMap(this); scene = new Scene(tiles); - stage.setScene(scene);}); + stage.setScene(scene); + displayWinningScreen();}); } ImageView groundView(Battlefield.GroundTile tile) { @@ -65,7 +67,19 @@ ImageView championView(Champion champion) { ImageView nexusView(Nexus nexus) { int teamID = nexus.teamOfNexus(); - return(sprites.nexusesView(teamID)); + double hpPercentage = (nexus.currentHP())/(double)(nexus.initialHP()); + if (hpPercentage<=1.0 && hpPercentage>=0.5){ + return(sprites.nexusesView(teamID)); + } + else if(hpPercentage< 0.5 && hpPercentage> 0.0){ + return (sprites.nexusesOnfire(teamID)); + } + else if(hpPercentage == 0.0){ + return (sprites.nexusesDestroyed(teamID)); + } + else{ + throw new RuntimeException("Unsupported Nexus color"); + } } ImageView towerView(Tower tower){ @@ -106,6 +120,40 @@ private void drawLifeBar(StackPane stack, Destructible d){ stack.getChildren().add(lifeBar); } + private void displayWinningScreen(){ + if (!battlefield.allNexusAlive()) { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Nexus nexus = battlefield.nexusOf(Nexus.BLUE); + if (nexus.isAlive()) { + displayWinner(Nexus.BLUE); + } else { + displayWinner(Nexus.RED); + } + scene = new Scene(wt); + } else { + scene = new Scene(tiles); + } + stage.setScene(scene); + } + + private void displayWinner(int teamId) { + wt.setPrefColumns(1); + wt.setPrefRows(1); + wt.setTileAlignment(Pos.CENTER); + wt.setMinSize(640, 640); + StackPane stack = new StackPane(); + sprites.finalSceneView(teamId).setFitWidth(640); + sprites.finalSceneView(teamId).setFitHeight(640); + sprites.finalSceneView(teamId).setPreserveRatio(true); + stack.setAlignment(Pos.CENTER); + stack.getChildren().addAll(new Rectangle(640,640, Color.WHITE ),sprites.finalSceneView(teamId)); + wt.getChildren().add(stack); + } + @Override public void visitChampion(Champion champion) { System.out.println("Display champion "); displayDestructible(champion, championView(champion)); diff --git a/src/main/java/lol/ui/Sprites.java b/src/main/java/lol/ui/Sprites.java index c69605a..1b99598 100644 --- a/src/main/java/lol/ui/Sprites.java +++ b/src/main/java/lol/ui/Sprites.java @@ -14,6 +14,9 @@ public class Sprites { private Image[] warriorImages = new Image[2]; private Image[] towerImages = new Image[2]; private Image[] nexusImages = new Image[2]; + private Image[] nexusOnfire = new Image[2]; + private Image[] nexusDestroyed = new Image[2]; + private Image[] winnerImage; public Sprites() { grassImage = new Image("sprites/grass-tile.png"); @@ -35,6 +38,14 @@ public Sprites() { towerImages[Nexus.BLUE] = new Image("sprites/blue-tower.png"); towerImages[Nexus.RED] = new Image("sprites/red-tower.png"); + nexusOnfire[Nexus.BLUE] = new Image("sprites/blue-nexus-onfire.png"); + nexusOnfire[Nexus.RED] = new Image("sprites/red-nexus-onfire.png"); + + nexusDestroyed[Nexus.BLUE] = new Image("sprites/blue-nexus-destroyed.png"); + nexusDestroyed[Nexus.RED] = new Image("sprites/red-nexus-destroyed.png"); + + winnerImage[Nexus.BLUE] = new Image("sprites/BlueWin.png"); + winnerImage[Nexus.RED] = new Image("sprites/RedWin.png"); } private ImageView makeView(Image image) { @@ -88,4 +99,18 @@ public ImageView monsterView(int monsterId) { return makeView(monsterImages[monsterId]); } + public ImageView nexusesOnfire(int teamID) { + checkTeamID(teamID, "Unsupported Nexus color. There is only 2 teams."); + return makeView(nexusOnfire[teamID]); + } + + public ImageView nexusesDestroyed(int teamID) { + checkTeamID(teamID, "Unsupported Nexus color. There is only 2 teams."); + return makeView(nexusDestroyed[teamID]); + } + + public ImageView finalSceneView(int teamID){ + checkTeamID(teamID, "Unsupported Winner's color. There is only 2 teams."); + return makeView(winnerImage[teamID]); + } } \ No newline at end of file diff --git a/src/main/resources/maps/ground.map b/src/main/resources/maps/ground.map index 90b1282..c5521ea 100644 --- a/src/main/resources/maps/ground.map +++ b/src/main/resources/maps/ground.map @@ -1,10 +1,10 @@ -~~~~~~~~~~ -~~~~~~~~~~ -~~~~~~~~~~ -~~~~~~~~~~ -~~~~~~~~~~ -~~~~~~~~~~ -~~~~~~~~~~ -~~~~~~~~~~ +~~**~~~*~~ +~~~*~~**~~ +~~~*~~~*** +~**~**~~~~ +~~~|~~**~~ +~****|*~** +**~~||~~~~ +~~~~~|~*** ~~~~~~~~~~ ~~~~~~~~~~ \ No newline at end of file diff --git a/src/main/resources/sprites/BlueWin.png b/src/main/resources/sprites/BlueWin.png new file mode 100644 index 0000000..99fe2c5 Binary files /dev/null and b/src/main/resources/sprites/BlueWin.png differ diff --git a/src/main/resources/sprites/RedWin.png b/src/main/resources/sprites/RedWin.png new file mode 100644 index 0000000..732e1cd Binary files /dev/null and b/src/main/resources/sprites/RedWin.png differ diff --git a/src/main/resources/sprites/blue-nexus-destroyed.png b/src/main/resources/sprites/blue-nexus-destroyed.png new file mode 100644 index 0000000..0a75d1d Binary files /dev/null and b/src/main/resources/sprites/blue-nexus-destroyed.png differ diff --git a/src/main/resources/sprites/blue-nexus-onfire.png b/src/main/resources/sprites/blue-nexus-onfire.png new file mode 100644 index 0000000..dd397f9 Binary files /dev/null and b/src/main/resources/sprites/blue-nexus-onfire.png differ diff --git a/src/main/resources/sprites/red-nexus-destroyed.png b/src/main/resources/sprites/red-nexus-destroyed.png new file mode 100644 index 0000000..1a26364 Binary files /dev/null and b/src/main/resources/sprites/red-nexus-destroyed.png differ diff --git a/src/main/resources/sprites/red-nexus-onfire.png b/src/main/resources/sprites/red-nexus-onfire.png new file mode 100644 index 0000000..1633f9b Binary files /dev/null and b/src/main/resources/sprites/red-nexus-onfire.png differ